PageSourceSearch

https://pafrancis.com/wp-content/plugins/codecanyon-AFjQRpNh-wooco…/scripts/video.js?ver=7.1.2

js pafrancis.com collected 2026-10-08 09:27:18 UTC 708,777 bytes, 25,674 lines download raw bytes

vendor: 15,833 bytes, lines 1-623
1/**
2 * @license
3 * Video.js 6.6.3 <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.6.3";
20
21var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
22
23
24
25
26
27function createCommonjsModule(fn, module) {
28	return module = { exports: {} }, fn(module, module.exports), module.exports;
29}
30
31var win;
32
33if (typeof window !== "undefined") {
34    win = window;
35} else if (typeof commonjsGlobal !== "undefined") {
36    win = commonjsGlobal;
37} else if (typeof self !== "undefined"){
38    win = self;
39} else {
40    win = {};
41}
42
43var window_1 = win;
44
45var empty = {};
46
47
48var empty$1 = (Object.freeze || Object)({
49	'default': empty
50});
51
52var minDoc = ( empty$1 && empty ) || empty$1;
53
54var topLevel = typeof commonjsGlobal !== 'undefined' ? commonjsGlobal :
55    typeof window !== 'undefined' ? window : {};
56
57
58var doccy;
59
60if (typeof document !== 'undefined') {
61    doccy = document;
62} else {
63    doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'];
64
65    if (!doccy) {
66        doccy = topLevel['__GLOBAL_DOCUMENT_CACHE@4'] = minDoc;
67    }
68}
69
70var document_1 = doccy;
71
72/**
73 * @file browser.js
74 * @module browser
75 */
76var USER_AGENT = window_1.navigator && window_1.navigator.userAgent || '';
77var webkitVersionMap = /AppleWebKit\/([\d.]+)/i.exec(USER_AGENT);
78var appleWebkitVersion = webkitVersionMap ? parseFloat(webkitVersionMap.pop()) : null;
79
80/*
81 * Device is an iPhone
82 *
83 * @type {Boolean}
84 * @constant
85 * @private
86 */
87var IS_IPAD = /iPad/i.test(USER_AGENT);
88
89// The Facebook app's UIWebView identifies as both an iPhone and iPad, so
90// to identify iPhones, we need to exclude iPads.
91// http://artsy.github.io/blog/2012/10/18/the-perils-of-ios-user-agent-sniffing/
92var IS_IPHONE = /iPhone/i.test(USER_AGENT) && !IS_IPAD;
93var IS_IPOD = /iPod/i.test(USER_AGENT);
94var IS_IOS = IS_IPHONE || IS_IPAD || IS_IPOD;
95
96var IOS_VERSION = function () {
97  var match = USER_AGENT.match(/OS (\d+)_/i);
98
99  if (match && match[1]) {
100    return match[1];
101  }
102  return null;
103}();
104
105var IS_ANDROID = /Android/i.test(USER_AGENT);
106var ANDROID_VERSION = function () {
107  // This matches Android Major.Minor.Patch versions
108  // ANDROID_VERSION is Major.Minor as a Number, if Minor isn't available, then only Major is returned
109  var match = USER_AGENT.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);
110
111  if (!match) {
112    return null;
113  }
114
115  var major = match[1] && parseFloat(match[1]);
116  var minor = match[2] && parseFloat(match[2]);
117
118  if (major && minor) {
119    return parseFloat(match[1] + '.' + match[2]);
120  } else if (major) {
121    return major;
122  }
123  return null;
124}();
125
126// Old Android is defined as Version older than 2.3, and requiring a webkit version of the android browser
127var IS_OLD_ANDROID = IS_ANDROID && /webkit/i.test(USER_AGENT) && ANDROID_VERSION < 2.3;
128var IS_NATIVE_ANDROID = IS_ANDROID && ANDROID_VERSION < 5 && appleWebkitVersion < 537;
129
130var IS_FIREFOX = /Firefox/i.test(USER_AGENT);
131var IS_EDGE = /Edge/i.test(USER_AGENT);
132var IS_CHROME = !IS_EDGE && /Chrome/i.test(USER_AGENT);
133var CHROME_VERSION = function () {
134  var match = USER_AGENT.match(/Chrome\/(\d+)/);
135
136  if (match && match[1]) {
137    return parseFloat(match[1]);
138  }
139  return null;
140}();
141var IS_IE8 = /MSIE\s8\.0/.test(USER_AGENT);
142var IE_VERSION = function () {
143  var result = /MSIE\s(\d+)\.\d/.exec(USER_AGENT);
144  var version = result && parseFloat(result[1]);
145
146  if (!version && /Trident\/7.0/i.test(USER_AGENT) && /rv:11.0/.test(USER_AGENT)) {
147    // IE 11 has a different user agent string than other IE versions
148    version = 11.0;
149  }
150
151  return version;
152}();
153
154var IS_SAFARI = /Safari/i.test(USER_AGENT) && !IS_CHROME && !IS_ANDROID && !IS_EDGE;
155var IS_ANY_SAFARI = IS_SAFARI || IS_IOS;
156
157var TOUCH_ENABLED = isReal() && ('ontouchstart' in window_1 || window_1.DocumentTouch && window_1.document instanceof window_1.DocumentTouch);
158
159var BACKGROUND_SIZE_SUPPORTED = isReal() && 'backgroundSize' in window_1.document.createElement('video').style;
160
161var browser = (Object.freeze || Object)({
162	IS_IPAD: IS_IPAD,
163	IS_IPHONE: IS_IPHONE,
164	IS_IPOD: IS_IPOD,
165	IS_IOS: IS_IOS,
166	IOS_VERSION: IOS_VERSION,
167	IS_ANDROID: IS_ANDROID,
168	ANDROID_VERSION: ANDROID_VERSION,
169	IS_OLD_ANDROID: IS_OLD_ANDROID,
170	IS_NATIVE_ANDROID: IS_NATIVE_ANDROID,
171	IS_FIREFOX: IS_FIREFOX,
172	IS_EDGE: IS_EDGE,
173	IS_CHROME: IS_CHROME,
174	CHROME_VERSION: CHROME_VERSION,
175	IS_IE8: IS_IE8,
176	IE_VERSION: IE_VERSION,
177	IS_SAFARI: IS_SAFARI,
178	IS_ANY_SAFARI: IS_ANY_SAFARI,
179	TOUCH_ENABLED: TOUCH_ENABLED,
180	BACKGROUND_SIZE_SUPPORTED: BACKGROUND_SIZE_SUPPORTED
181});
182
183var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
184  return typeof obj;
185} : function (obj) {
186  return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
187};
188
189
190
191
192
193
194
195
196
197
198
199var classCallCheck = function (instance, Constructor) {
200  if (!(instance instanceof Constructor)) {
201    throw new TypeError("Cannot call a class as a function");
202  }
203};
204
205
206
207
208
209
210
211
212
213
214
215var inherits = function (subClass, superClass) {
216  if (typeof superClass !== "function" && superClass !== null) {
217    throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
218  }
219
220  subClass.prototype = Object.create(superClass && superClass.prototype, {
221    constructor: {
222      value: subClass,
223      enumerable: false,
224      writable: true,
225      configurable: true
226    }
227  });
228  if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
229};
230
231
232
233
234
235
236
237
238
239
240
241var possibleConstructorReturn = function (self, call) {
242  if (!self) {
243    throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
244  }
245
246  return call && (typeof call === "object" || typeof call === "function") ? call : self;
247};
248
249
250
251
252
253
254
255
256
257
258
259var taggedTemplateLiteralLoose = function (strings, raw) {
260  strings.raw = raw;
261  return strings;
262};
263
264/**
265 * @file obj.js
266 * @module obj
267 */
268
269/**
270 * @callback obj:EachCallback
271 *
272 * @param {Mixed} value
273 *        The current key for the object that is being iterated over.
274 *
275 * @param {string} key
276 *        The current key-value for object that is being iterated over
277 */
278
279/**
280 * @callback obj:ReduceCallback
281 *
282 * @param {Mixed} accum
283 *        The value that is accumulating over the reduce loop.
284 *
285 * @param {Mixed} value
286 *        The current key for the object that is being iterated over.
287 *
288 * @param {string} key
289 *        The current key-value for object that is being iterated over
290 *
291 * @return {Mixed}
292 *         The new accumulated value.
293 */
294var toString = Object.prototype.toString;
295
296/**
297 * Get the keys of an Object
298 *
299 * @param {Object}
300 *        The Object to get the keys from
301 *
302 * @return {string[]}
303 *         An array of the keys from the object. Returns an empty array if the
304 *         object passed in was invalid or had no keys.
305 *
306 * @private
307 */
308var keys = function keys(object) {
309  return isObject(object) ? Object.keys(object) : [];
310};
311
312/**
313 * Array-like iteration for objects.
314 *
315 * @param {Object} object
316 *        The object to iterate over
317 *
318 * @param {obj:EachCallback} fn
319 *        The callback function which is called for each key in the object.
320 */
321function each(object, fn) {
322  keys(object).forEach(function (key) {
323    return fn(object[key], key);
324  });
325}
326
327/**
328 * Array-like reduce for objects.
329 *
330 * @param {Object} object
331 *        The Object that you want to reduce.
332 *
333 * @param {Function} fn
334 *         A callback function which is called for each key in the object. It
335 *         receives the accumulated value and the per-iteration value and key
336 *         as arguments.
337 *
338 * @param {Mixed} [initial = 0]
339 *        Starting value
340 *
341 * @return {Mixed}
342 *         The final accumulated value.
343 */
344function reduce(object, fn) {
345  var initial = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
346
347  return keys(object).reduce(function (accum, key) {
348    return fn(accum, object[key], key);
349  }, initial);
350}
351
352/**
353 * Object.assign-style object shallow merge/extend.
354 *
355 * @param  {Object} target
356 * @param  {Object} ...sources
357 * @return {Object}
358 */
359function assign(target) {
360  for (var _len = arguments.length, sources = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
361    sources[_key - 1] = arguments[_key];
362  }
363
364  if (Object.assign) {
365    return Object.assign.apply(Object, [target].concat(sources));
366  }
367
368  sources.forEach(function (source) {
369    if (!source) {
370      return;
371    }
372
373    each(source, function (value, key) {
374      target[key] = value;
375    });
376  });
377
378  return target;
379}
380
381/**
382 * Returns whether a value is an object of any kind - including DOM nodes,
383 * arrays, regular expressions, etc. Not functions, though.
384 *
385 * This avoids the gotcha where using `typeof` on a `null` value
386 * results in `'object'`.
387 *
388 * @param  {Object} value
389 * @return {Boolean}
390 */
391function isObject(value) {
392  return !!value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object';
393}
394
395/**
396 * Returns whether an object appears to be a "plain" object - that is, a
397 * direct instance of `Object`.
398 *
399 * @param  {Object} value
400 * @return {Boolean}
401 */
402function isPlain(value) {
403  return isObject(value) && toString.call(value) === '[object Object]' && value.constructor === Object;
404}
405
406/**
407 * @file log.js
408 * @module log
409 */
410var log = void 0;
411
412// This is the private tracking variable for logging level.
413var level = '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,109 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 `butttons` ?
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  }
2095  // Normalizes the event properties.
2096  event = fixEvent(event);
2097
2098  // If the passed element has a dispatcher, executes the established handlers.
2099  if (elemData.dispatcher) {
2100    elemData.dispatcher.call(elem, event, hash);
2101  }
2102
2103  // Unless explicitly stopped or the event does not bubble (e.g. media events)
2104  // recursively calls this function to bubble the event up the DOM.
2105  if (parent && !event.isPropagationStopped() && event.bubbles === true) {
2106    trigger.call(null, parent, event, hash);
2107
2108    // If at the top of the DOM, triggers the default action unless disabled.
2109  } else if (!parent && !event.defaultPrevented) {
2110    var targetData = getData(event.target);
2111
2112    // Checks if the target has a default action for this event.
2113    if (event.target[event.type]) {
2114      // Temporarily disables event dispatching on the target as we have already executed the handler.
2115      targetData.disabled = true;
2116      // Executes the default action.
2117      if (typeof event.target[event.type] === 'function') {
2118        event.target[event.type]();
2119      }
2120      // Re-enables event dispatching.
2121      targetData.disabled = false;
2122    }
2123  }
2124
2125  // Inform the triggerer if the default was prevented by returning false
2126  return !event.defaultPrevented;
2127}
2128
2129/**
2130 * Trigger a listener only once for an event
2131 *
2132 * @param {Element|Object} elem
2133 *        Element or object to bind to.
2134 *
2135 * @param {string|string[]} type
2136 *        Name/type of event
2137 *
2138 * @param {Event~EventListener} fn
2139 *        Event Listener function
2140 */
2141function one(elem, type, fn) {
2142  if (Array.isArray(type)) {
2143    return _handleMultipleEvents(one, elem, type, fn);
2144  }
2145  var func = function func() {
2146    off(elem, type, func);
2147    fn.apply(this, arguments);
2148  };
2149
2150  // copy the guid to the new function so it can removed using the original function's ID
2151  func.guid = fn.guid = fn.guid || newGUID();
2152  on(elem, type, func);
2153}
2154
2155var Events = (Object.freeze || Object)({
2156	fixEvent: fixEvent,
2157	on: on,
2158	off: off,
2159	trigger: trigger,
2160	one: one
2161});
2162
2163/**
2164 * @file setup.js - Functions for setting up a player without
2165 * user interaction based on the data-setup `attribute` of the video tag.
2166 *
2167 * @module setup
2168 */
2169var _windowLoaded = false;
2170var videojs$2 = void 0;
2171
2172/**
2173 * Set up any tags that have a data-setup `attribute` when the player is started.
2174 */
2175var autoSetup = function autoSetup() {
2176
2177  // Protect against breakage in non-browser environments.
2178  if (!isReal()) {
2179    return;
2180  }
2181
2182  // One day, when we stop supporting IE8, go back to this, but in the meantime...*hack hack hack*
2183  // var vids = Array.prototype.slice.call(document.getElementsByTagName('video'));
2184  // var audios = Array.prototype.slice.call(document.getElementsByTagName('audio'));
2185  // var mediaEls = vids.concat(audios);
2186
2187  // Because IE8 doesn't support calling slice on a node list, we need to loop
2188  // through each list of elements to build up a new, combined list of elements.
2189  var vids = document_1.getElementsByTagName('video');
2190  var audios = document_1.getElementsByTagName('audio');
2191  var divs = document_1.getElementsByTagName('video-js');
2192  var mediaEls = [];
2193
2194  if (vids && vids.length > 0) {
2195    for (var i = 0, e = vids.length; i < e; i++) {
2196      mediaEls.push(vids[i]);
2197    }
2198  }
2199
2200  if (audios && audios.length > 0) {
2201    for (var _i = 0, _e = audios.length; _i < _e; _i++) {
2202      mediaEls.push(audios[_i]);
2203    }
2204  }
2205
2206  if (divs && divs.length > 0) {
2207    for (var _i2 = 0, _e2 = divs.length; _i2 < _e2; _i2++) {
2208      mediaEls.push(divs[_i2]);
2209    }
2210  }
2211
2212  // Check if any media elements exist
2213  if (mediaEls && mediaEls.length > 0) {
2214
2215    for (var _i3 = 0, _e3 = mediaEls.length; _i3 < _e3; _i3++) {
2216      var mediaEl = mediaEls[_i3];
2217
2218      // Check if element exists, has getAttribute func.
2219      // IE seems to consider typeof el.getAttribute == 'object' instead of
2220      // 'function' like expected, at least when loading the player immediately.
2221      if (mediaEl && mediaEl.getAttribute) {
2222
2223        // Make sure this player hasn't already been set up.
2224        if (mediaEl.player === undefined) {
2225          var options = mediaEl.getAttribute('data-setup');
2226
2227          // Check if data-setup attr exists.
2228          // We only auto-setup if they've added the data-setup attr.
2229          if (options !== null) {
2230            // Create new video.js instance.
2231            videojs$2(mediaEl);
2232          }
2233        }
2234
2235        // If getAttribute isn't defined, we need to wait for the DOM.
2236      } else {
2237        autoSetupTimeout(1);
2238        break;
2239      }
2240    }
2241
2242    // No videos were found, so keep looping unless page is finished loading.
2243  } else if (!_windowLoaded) {
2244    autoSetupTimeout(1);
2245  }
2246};
2247
2248/**
2249 * Wait until the page is loaded before running autoSetup. This will be called in
2250 * autoSetup if `hasLoaded` returns false.
2251 *
2252 * @param {number} wait
2253 *        How long to wait in ms
2254 *
2255 * @param {module:videojs} [vjs]
2256 *        The videojs library function
2257 */
2258function autoSetupTimeout(wait, vjs) {
2259  if (vjs) {
2260    videojs$2 = vjs;
2261  }
2262
2263  window_1.setTimeout(autoSetup, wait);
2264}
2265
2266if (isReal() && document_1.readyState === 'complete') {
2267  _windowLoaded = true;
2268} else {
2269  /**
2270   * Listen for the load event on window, and set _windowLoaded to true.
2271   *
2272   * @listens load
2273   */
2274  one(window_1, 'load', function () {
2275    _windowLoaded = true;
2276  });
2277}
2278
2279/**
2280 * @file stylesheet.js
2281 * @module stylesheet
2282 */
2283/**
2284 * Create a DOM syle element given a className for it.
2285 *
2286 * @param {string} className
2287 *        The className to add to the created style element.
2288 *
2289 * @return {Element}
2290 *         The element that was created.
2291 */
2292var createStyleElement = function createStyleElement(className) {
2293  var style = document_1.createElement('style');
2294
2295  style.className = className;
2296
2297  return style;
2298};
2299
2300/**
2301 * Add text to a DOM element.
2302 *
2303 * @param {Element} el
2304 *        The Element to add text content to.
2305 *
2306 * @param {string} content
2307 *        The text to add to the element.
2308 */
2309var setTextContent = function setTextContent(el, content) {
2310  if (el.styleSheet) {
2311    el.styleSheet.cssText = content;
2312  } else {
2313    el.textContent = content;
2314  }
2315};
2316
2317/**
2318 * @file fn.js
2319 * @module fn
2320 */
2321/**
2322 * Bind (a.k.a proxy or Context). A simple method for changing the context of a function
2323 * It also stores a unique id on the function so it can be easily removed from events.
2324 *
2325 * @param {Mixed} context
2326 *        The object to bind as scope.
2327 *
2328 * @param {Function} fn
2329 *        The function to be bound to a scope.
2330 *
2331 * @param {number} [uid]
2332 *        An optional unique ID for the function to be set
2333 *
2334 * @return {Function}
2335 *         The new function that will be bound into the context given
2336 */
2337var bind = function bind(context, fn, uid) {
2338  // Make sure the function has a unique ID
2339  if (!fn.guid) {
2340    fn.guid = newGUID();
2341  }
2342
2343  // Create the new function that changes the context
2344  var bound = function bound() {
2345    return fn.apply(context, arguments);
2346  };
2347
2348  // Allow for the ability to individualize this function
2349  // Needed in the case where multiple objects might share the same prototype
2350  // IF both items add an event listener with the same function, then you try to remove just one
2351  // it will remove both because they both have the same guid.
2352  // when using this, you need to use the bind method when you remove the listener as well.
2353  // currently used in text tracks
2354  bound.guid = uid ? uid + '_' + fn.guid : fn.guid;
2355
2356  return bound;
2357};
2358
2359/**
2360 * Wraps the given function, `fn`, with a new function that only invokes `fn`
2361 * at most once per every `wait` milliseconds.
2362 *
2363 * @param  {Function} fn
2364 *         The function to be throttled.
2365 *
2366 * @param  {Number}   wait
2367 *         The number of milliseconds by which to throttle.
2368 *
2369 * @return {Function}
2370 */
2371var throttle = function throttle(fn, wait) {
2372  var last = Date.now();
2373
2374  var throttled = function throttled() {
2375    var now = Date.now();
2376
2377    if (now - last >= wait) {
2378      fn.apply(undefined, arguments);
2379      last = now;
2380    }
2381  };
2382
2383  return throttled;
2384};
2385
2386/**
2387 * @file src/js/event-target.js
2388 */
2389/**
2390 * `EventTarget` is a class that can have the same API as the DOM `EventTarget`. It
2391 * adds shorthand functions that wrap around lengthy functions. For example:
2392 * the `on` function is a wrapper around `addEventListener`.
2393 *
2394 * @see [EventTarget Spec]{@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget}
2395 * @class EventTarget
2396 */
2397var EventTarget = function EventTarget() {};
2398
2399/**
2400 * A Custom DOM event.
2401 *
2402 * @typedef {Object} EventTarget~Event
2403 * @see [Properties]{@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent}
2404 */
2405
2406/**
2407 * All event listeners should follow the following format.
2408 *
2409 * @callback EventTarget~EventListener
2410 * @this {EventTarget}
2411 *
2412 * @param {EventTarget~Event} event
2413 *        the event that triggered this function
2414 *
2415 * @param {Object} [hash]
2416 *        hash of data sent during the event
2417 */
2418
2419/**
2420 * An object containing event names as keys and booleans as values.
2421 *
2422 * > NOTE: If an event name is set to a true value here {@link EventTarget#trigger}
2423 *         will have extra functionality. See that function for more information.
2424 *
2425 * @property EventTarget.prototype.allowedEvents_
2426 * @private
2427 */
2428EventTarget.prototype.allowedEvents_ = {};
2429
2430/**
2431 * Adds an `event listener` to an instance of an `EventTarget`. An `event listener` is a
2432 * function that will get called when an event with a certain name gets triggered.
2433 *
2434 * @param {string|string[]} type
2435 *        An event name or an array of event names.
2436 *
2437 * @param {EventTarget~EventListener} fn
2438 *        The function to call with `EventTarget`s
2439 */
2440EventTarget.prototype.on = function (type, fn) {
2441  // Remove the addEventListener alias before calling Events.on
2442  // so we don't get into an infinite type loop
2443  var ael = this.addEventListener;
2444
2445  this.addEventListener = function () {};
2446  on(this, type, fn);
2447  this.addEventListener = ael;
2448};
2449
2450/**
2451 * An alias of {@link EventTarget#on}. Allows `EventTarget` to mimic
2452 * the standard DOM API.
2453 *
2454 * @function
2455 * @see {@link EventTarget#on}
2456 */
2457EventTarget.prototype.addEventListener = EventTarget.prototype.on;
2458
2459/**
2460 * Removes an `event listener` for a specific event from an instance of `EventTarget`.
2461 * This makes it so that the `event listener` will no longer get called when the
2462 * named event happens.
2463 *
2464 * @param {string|string[]} type
2465 *        An event name or an array of event names.
2466 *
2467 * @param {EventTarget~EventListener} fn
2468 *        The function to remove.
2469 */
vendor: 6,590 bytes, lines 2470-2707
2470EventTarget.prototype.off = function (type, fn) {
2471  off(this, type, fn);
2472};
2473
2474/**
2475 * An alias of {@link EventTarget#off}. Allows `EventTarget` to mimic
2476 * the standard DOM API.
2477 *
2478 * @function
2479 * @see {@link EventTarget#off}
2480 */
2481EventTarget.prototype.removeEventListener = EventTarget.prototype.off;
2482
2483/**
2484 * This function will add an `event listener` that gets triggered only once. After the
2485 * first trigger it will get removed. This is like adding an `event listener`
2486 * with {@link EventTarget#on} that calls {@link EventTarget#off} on itself.
2487 *
2488 * @param {string|string[]} type
2489 *        An event name or an array of event names.
2490 *
2491 * @param {EventTarget~EventListener} fn
2492 *        The function to be called once for each event name.
2493 */
2494EventTarget.prototype.one = function (type, fn) {
2495  // Remove the addEventListener alialing Events.on
2496  // so we don't get into an infinite type loop
2497  var ael = this.addEventListener;
2498
2499  this.addEventListener = function () {};
2500  one(this, type, fn);
2501  this.addEventListener = ael;
2502};
2503
2504/**
2505 * This function causes an event to happen. This will then cause any `event listeners`
2506 * that are waiting for that event, to get called. If there are no `event listeners`
2507 * for an event then nothing will happen.
2508 *
2509 * If the name of the `Event` that is being triggered is in `EventTarget.allowedEvents_`.
2510 * Trigger will also call the `on` + `uppercaseEventName` function.
2511 *
2512 * Example:
2513 * 'click' is in `EventTarget.allowedEvents_`, so, trigger will attempt to call
2514 * `onClick` if it exists.
2515 *
2516 * @param {string|EventTarget~Event|Object} event
2517 *        The name of the event, an `Event`, or an object with a key of type set to
2518 *        an event name.
2519 */
2520EventTarget.prototype.trigger = function (event) {
2521  var type = event.type || event;
2522
2523  if (typeof event === 'string') {
2524    event = { type: type };
2525  }
2526  event = fixEvent(event);
2527
2528  if (this.allowedEvents_[type] && this['on' + type]) {
2529    this['on' + type](event);
2530  }
2531
2532  trigger(this, event);
2533};
2534
2535/**
2536 * An alias of {@link EventTarget#trigger}. Allows `EventTarget` to mimic
2537 * the standard DOM API.
2538 *
2539 * @function
2540 * @see {@link EventTarget#trigger}
2541 */
2542EventTarget.prototype.dispatchEvent = EventTarget.prototype.trigger;
2543
2544/**
2545 * @file mixins/evented.js
2546 * @module evented
2547 */
2548/**
2549 * Returns whether or not an object has had the evented mixin applied.
2550 *
2551 * @param  {Object} object
2552 *         An object to test.
2553 *
2554 * @return {boolean}
2555 *         Whether or not the object appears to be evented.
2556 */
2557var isEvented = function isEvented(object) {
2558  return object instanceof EventTarget || !!object.eventBusEl_ && ['on', 'one', 'off', 'trigger'].every(function (k) {
2559    return typeof object[k] === 'function';
2560  });
2561};
2562
2563/**
2564 * Whether a value is a valid event type - non-empty string or array.
2565 *
2566 * @private
2567 * @param  {string|Array} type
2568 *         The type value to test.
2569 *
2570 * @return {boolean}
2571 *         Whether or not the type is a valid event type.
2572 */
2573var isValidEventType = function isValidEventType(type) {
2574  return (
2575    // The regex here verifies that the `type` contains at least one non-
2576    // whitespace character.
2577    typeof type === 'string' && /\S/.test(type) || Array.isArray(type) && !!type.length
2578  );
2579};
2580
2581/**
2582 * Validates a value to determine if it is a valid event target. Throws if not.
2583 *
2584 * @private
2585 * @throws {Error}
2586 *         If the target does not appear to be a valid event target.
2587 *
2588 * @param  {Object} target
2589 *         The object to test.
2590 */
2591var validateTarget = function validateTarget(target) {
2592  if (!target.nodeName && !isEvented(target)) {
2593    throw new Error('Invalid target; must be a DOM node or evented object.');
2594  }
2595};
2596
2597/**
2598 * Validates a value to determine if it is a valid event target. Throws if not.
2599 *
2600 * @private
2601 * @throws {Error}
2602 *         If the type does not appear to be a valid event type.
2603 *
2604 * @param  {string|Array} type
2605 *         The type to test.
2606 */
2607var validateEventType = function validateEventType(type) {
2608  if (!isValidEventType(type)) {
2609    throw new Error('Invalid event type; must be a non-empty string or array.');
2610  }
2611};
2612
2613/**
2614 * Validates a value to determine if it is a valid listener. Throws if not.
2615 *
2616 * @private
2617 * @throws {Error}
2618 *         If the listener is not a function.
2619 *
2620 * @param  {Function} listener
2621 *         The listener to test.
2622 */
2623var validateListener = function validateListener(listener) {
2624  if (typeof listener !== 'function') {
2625    throw new Error('Invalid listener; must be a function.');
2626  }
2627};
2628
2629/**
2630 * Takes an array of arguments given to `on()` or `one()`, validates them, and
2631 * normalizes them into an object.
2632 *
2633 * @private
2634 * @param  {Object} self
2635 *         The evented object on which `on()` or `one()` was called. This
2636 *         object will be bound as the `this` value for the listener.
2637 *
2638 * @param  {Array} args
2639 *         An array of arguments passed to `on()` or `one()`.
2640 *
2641 * @return {Object}
2642 *         An object containing useful values for `on()` or `one()` calls.
2643 */
2644var normalizeListenArgs = function normalizeListenArgs(self, args) {
2645
2646  // If the number of arguments is less than 3, the target is always the
2647  // evented object itself.
2648  var isTargetingSelf = args.length < 3 || args[0] === self || args[0] === self.eventBusEl_;
2649  var target = void 0;
2650  var type = void 0;
2651  var listener = void 0;
2652
2653  if (isTargetingSelf) {
2654    target = self.eventBusEl_;
2655
2656    // Deal with cases where we got 3 arguments, but we are still listening to
2657    // the evented object itself.
2658    if (args.length >= 3) {
2659      args.shift();
2660    }
2661
2662    type = args[0];
2663    listener = args[1];
2664  } else {
2665    target = args[0];
2666    type = args[1];
2667    listener = args[2];
2668  }
2669
2670  validateTarget(target);
2671  validateEventType(type);
2672  validateListener(listener);
2673
2674  listener = bind(self, listener);
2675
2676  return { isTargetingSelf: isTargetingSelf, target: target, type: type, listener: listener };
2677};
2678
2679/**
2680 * Adds the listener to the event type(s) on the target, normalizing for
2681 * the type of target.
2682 *
2683 * @private
2684 * @param  {Element|Object} target
2685 *         A DOM node or evented object.
2686 *
2687 * @param  {string} method
2688 *         The event binding method to use ("on" or "one").
2689 *
2690 * @param  {string|Array} type
2691 *         One or more event type(s).
2692 *
2693 * @param  {Function} listener
2694 *         A listener function.
2695 */
2696var listen = function listen(target, method, type, listener) {
2697  validateTarget(target);
2698
2699  if (target.nodeName) {
2700    Events[method](target, type, listener);
2701  } else {
2702    target[method](type, listener);
2703  }
2704};
2705
2706/**
2707 * Contains methods that provide event capabilit
vendor: 8,396 bytes, lines 2707-2949
2707es to an object which is passed
2708 * to {@link module:evented|evented}.
2709 *
2710 * @mixin EventedMixin
2711 */
2712var EventedMixin = {
2713
2714  /**
2715   * Add a listener to an event (or events) on this object or another evented
2716   * object.
2717   *
2718   * @param  {string|Array|Element|Object} targetOrType
2719   *         If this is a string or array, it represents the event type(s)
2720   *         that will trigger the listener.
2721   *
2722   *         Another evented object can be passed here instead, which will
2723   *         cause the listener to listen for events on _that_ object.
2724   *
2725   *         In either case, the listener's `this` value will be bound to
2726   *         this object.
2727   *
2728   * @param  {string|Array|Function} typeOrListener
2729   *         If the first argument was a string or array, this should be the
2730   *         listener function. Otherwise, this is a string or array of event
2731   *         type(s).
2732   *
2733   * @param  {Function} [listener]
2734   *         If the first argument was another evented object, this will be
2735   *         the listener function.
2736   */
2737  on: function on$$1() {
2738    var _this = this;
2739
2740    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
2741      args[_key] = arguments[_key];
2742    }
2743
2744    var _normalizeListenArgs = normalizeListenArgs(this, args),
2745        isTargetingSelf = _normalizeListenArgs.isTargetingSelf,
2746        target = _normalizeListenArgs.target,
2747        type = _normalizeListenArgs.type,
2748        listener = _normalizeListenArgs.listener;
2749
2750    listen(target, 'on', type, listener);
2751
2752    // If this object is listening to another evented object.
2753    if (!isTargetingSelf) {
2754
2755      // If this object is disposed, remove the listener.
2756      var removeListenerOnDispose = function removeListenerOnDispose() {
2757        return _this.off(target, type, listener);
2758      };
2759
2760      // Use the same function ID as the listener so we can remove it later it
2761      // using the ID of the original listener.
2762      removeListenerOnDispose.guid = listener.guid;
2763
2764      // Add a listener to the target's dispose event as well. This ensures
2765      // that if the target is disposed BEFORE this object, we remove the
2766      // removal listener that was just added. Otherwise, we create a memory leak.
2767      var removeRemoverOnTargetDispose = function removeRemoverOnTargetDispose() {
2768        return _this.off('dispose', removeListenerOnDispose);
2769      };
2770
2771      // Use the same function ID as the listener so we can remove it later
2772      // it using the ID of the original listener.
2773      removeRemoverOnTargetDispose.guid = listener.guid;
2774
2775      listen(this, 'on', 'dispose', removeListenerOnDispose);
2776      listen(target, 'on', 'dispose', removeRemoverOnTargetDispose);
2777    }
2778  },
2779
2780
2781  /**
2782   * Add a listener to an event (or events) on this object or another evented
2783   * object. The listener will only be called once and then removed.
2784   *
2785   * @param  {string|Array|Element|Object} targetOrType
2786   *         If this is a string or array, it represents the event type(s)
2787   *         that will trigger the listener.
2788   *
2789   *         Another evented object can be passed here instead, which will
2790   *         cause the listener to listen for events on _that_ object.
2791   *
2792   *         In either case, the listener's `this` value will be bound to
2793   *         this object.
2794   *
2795   * @param  {string|Array|Function} typeOrListener
2796   *         If the first argument was a string or array, this should be the
2797   *         listener function. Otherwise, this is a string or array of event
2798   *         type(s).
2799   *
2800   * @param  {Function} [listener]
2801   *         If the first argument was another evented object, this will be
2802   *         the listener function.
2803   */
2804  one: function one$$1() {
2805    var _this2 = this;
2806
2807    for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
2808      args[_key2] = arguments[_key2];
2809    }
2810
2811    var _normalizeListenArgs2 = normalizeListenArgs(this, args),
2812        isTargetingSelf = _normalizeListenArgs2.isTargetingSelf,
2813        target = _normalizeListenArgs2.target,
2814        type = _normalizeListenArgs2.type,
2815        listener = _normalizeListenArgs2.listener;
2816
2817    // Targeting this evented object.
2818
2819
2820    if (isTargetingSelf) {
2821      listen(target, 'one', type, listener);
2822
2823      // Targeting another evented object.
2824    } else {
2825      var wrapper = function wrapper() {
2826        for (var _len3 = arguments.length, largs = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
2827          largs[_key3] = arguments[_key3];
2828        }
2829
2830        _this2.off(target, type, wrapper);
2831        listener.apply(null, largs);
2832      };
2833
2834      // Use the same function ID as the listener so we can remove it later
2835      // it using the ID of the original listener.
2836      wrapper.guid = listener.guid;
2837      listen(target, 'one', type, wrapper);
2838    }
2839  },
2840
2841
2842  /**
2843   * Removes listener(s) from event(s) on an evented object.
2844   *
2845   * @param  {string|Array|Element|Object} [targetOrType]
2846   *         If this is a string or array, it represents the event type(s).
2847   *
2848   *         Another evented object can be passed here instead, in which case
2849   *         ALL 3 arguments are _required_.
2850   *
2851   * @param  {string|Array|Function} [typeOrListener]
2852   *         If the first argument was a string or array, this may 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; otherwise, _all_ listeners bound to the
2859   *         event type(s) will be removed.
2860   */
2861  off: function off$$1(targetOrType, typeOrListener, listener) {
2862
2863    // Targeting this evented object.
2864    if (!targetOrType || isValidEventType(targetOrType)) {
2865      off(this.eventBusEl_, targetOrType, typeOrListener);
2866
2867      // Targeting another evented object.
2868    } else {
2869      var target = targetOrType;
2870      var type = typeOrListener;
2871
2872      // Fail fast and in a meaningful way!
2873      validateTarget(target);
2874      validateEventType(type);
2875      validateListener(listener);
2876
2877      // Ensure there's at least a guid, even if the function hasn't been used
2878      listener = bind(this, listener);
2879
2880      // Remove the dispose listener on this evented object, which was given
2881      // the same guid as the event listener in on().
2882      this.off('dispose', listener);
2883
2884      if (target.nodeName) {
2885        off(target, type, listener);
2886        off(target, 'dispose', listener);
2887      } else if (isEvented(target)) {
2888        target.off(type, listener);
2889        target.off('dispose', listener);
2890      }
2891    }
2892  },
2893
2894
2895  /**
2896   * Fire an event on this evented object, causing its listeners to be called.
2897   *
2898   * @param   {string|Object} event
2899   *          An event type or an object with a type property.
2900   *
2901   * @param   {Object} [hash]
2902   *          An additional object to pass along to listeners.
2903   *
2904   * @returns {boolean}
2905   *          Whether or not the default behavior was prevented.
2906   */
2907  trigger: function trigger$$1(event, hash) {
2908    return trigger(this.eventBusEl_, event, hash);
2909  }
2910};
2911
2912/**
2913 * Applies {@link module:evented~EventedMixin|EventedMixin} to a target object.
2914 *
2915 * @param  {Object} target
2916 *         The object to which to add event methods.
2917 *
2918 * @param  {Object} [options={}]
2919 *         Options for customizing the mixin behavior.
2920 *
2921 * @param  {String} [options.eventBusKey]
2922 *         By default, adds a `eventBusEl_` DOM element to the target object,
2923 *         which is used as an event bus. If the target object already has a
2924 *         DOM element that should be used, pass its key here.
2925 *
2926 * @return {Object}
2927 *         The target object.
2928 */
2929function evented(target) {
2930  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
2931  var eventBusKey = options.eventBusKey;
2932
2933  // Set or create the eventBusEl_.
2934
2935  if (eventBusKey) {
2936    if (!target[eventBusKey].nodeName) {
2937      throw new Error('The eventBusKey "' + eventBusKey + '" does not refer to an element.');
2938    }
2939    target.eventBusEl_ = target[eventBusKey];
2940  } else {
2941    target.eventBusEl_ = createEl('span', { className: 'vjs-event-bus' });
2942  }
2943
2944  assign(target, EventedMixin);
2945
2946  // When any evented object is disposed, it removes all its listeners.
2947  target.on('dispose', function () {
2948    target.off();
2949    
vendor: 9,527 bytes, lines 2949-3308
2949window_1.setTimeout(function () {
2950      target.eventBusEl_ = null;
2951    }, 0);
2952  });
2953
2954  return target;
2955}
2956
2957/**
2958 * @file mixins/stateful.js
2959 * @module stateful
2960 */
2961/**
2962 * Contains methods that provide statefulness to an object which is passed
2963 * to {@link module:stateful}.
2964 *
2965 * @mixin StatefulMixin
2966 */
2967var StatefulMixin = {
2968
2969  /**
2970   * A hash containing arbitrary keys and values representing the state of
2971   * the object.
2972   *
2973   * @type {Object}
2974   */
2975  state: {},
2976
2977  /**
2978   * Set the state of an object by mutating its
2979   * {@link module:stateful~StatefulMixin.state|state} object in place.
2980   *
2981   * @fires   module:stateful~StatefulMixin#statechanged
2982   * @param   {Object|Function} stateUpdates
2983   *          A new set of properties to shallow-merge into the plugin state.
2984   *          Can be a plain object or a function returning a plain object.
2985   *
2986   * @returns {Object|undefined}
2987   *          An object containing changes that occurred. If no changes
2988   *          occurred, returns `undefined`.
2989   */
2990  setState: function setState(stateUpdates) {
2991    var _this = this;
2992
2993    // Support providing the `stateUpdates` state as a function.
2994    if (typeof stateUpdates === 'function') {
2995      stateUpdates = stateUpdates();
2996    }
2997
2998    var changes = void 0;
2999
3000    each(stateUpdates, function (value, key) {
3001
3002      // Record the change if the value is different from what's in the
3003      // current state.
3004      if (_this.state[key] !== value) {
3005        changes = changes || {};
3006        changes[key] = {
3007          from: _this.state[key],
3008          to: value
3009        };
3010      }
3011
3012      _this.state[key] = value;
3013    });
3014
3015    // Only trigger "statechange" if there were changes AND we have a trigger
3016    // function. This allows us to not require that the target object be an
3017    // evented object.
3018    if (changes && isEvented(this)) {
3019
3020      /**
3021       * An event triggered on an object that is both
3022       * {@link module:stateful|stateful} and {@link module:evented|evented}
3023       * indicating that its state has changed.
3024       *
3025       * @event    module:stateful~StatefulMixin#statechanged
3026       * @type     {Object}
3027       * @property {Object} changes
3028       *           A hash containing the properties that were changed and
3029       *           the values they were changed `from` and `to`.
3030       */
3031      this.trigger({
3032        changes: changes,
3033        type: 'statechanged'
3034      });
3035    }
3036
3037    return changes;
3038  }
3039};
3040
3041/**
3042 * Applies {@link module:stateful~StatefulMixin|StatefulMixin} to a target
3043 * object.
3044 *
3045 * If the target object is {@link module:evented|evented} and has a
3046 * `handleStateChanged` method, that method will be automatically bound to the
3047 * `statechanged` event on itself.
3048 *
3049 * @param   {Object} target
3050 *          The object to be made stateful.
3051 *
3052 * @param   {Object} [defaultState]
3053 *          A default set of properties to populate the newly-stateful object's
3054 *          `state` property.
3055 *
3056 * @returns {Object}
3057 *          Returns the `target`.
3058 */
3059function stateful(target, defaultState) {
3060  assign(target, StatefulMixin);
3061
3062  // This happens after the mixing-in because we need to replace the `state`
3063  // added in that step.
3064  target.state = assign({}, target.state, defaultState);
3065
3066  // Auto-bind the `handleStateChanged` method of the target object if it exists.
3067  if (typeof target.handleStateChanged === 'function' && isEvented(target)) {
3068    target.on('statechanged', target.handleStateChanged);
3069  }
3070
3071  return target;
3072}
3073
3074/**
3075 * @file to-title-case.js
3076 * @module to-title-case
3077 */
3078
3079/**
3080 * Uppercase the first letter of a string.
3081 *
3082 * @param {string} string
3083 *        String to be uppercased
3084 *
3085 * @return {string}
3086 *         The string with an uppercased first letter
3087 */
3088function toTitleCase(string) {
3089  if (typeof string !== 'string') {
3090    return string;
3091  }
3092
3093  return string.charAt(0).toUpperCase() + string.slice(1);
3094}
3095
3096/**
3097 * Compares the TitleCase versions of the two strings for equality.
3098 *
3099 * @param {string} str1
3100 *        The first string to compare
3101 *
3102 * @param {string} str2
3103 *        The second string to compare
3104 *
3105 * @return {boolean}
3106 *         Whether the TitleCase versions of the strings are equal
3107 */
3108function titleCaseEquals(str1, str2) {
3109  return toTitleCase(str1) === toTitleCase(str2);
3110}
3111
3112/**
3113 * @file merge-options.js
3114 * @module merge-options
3115 */
3116/**
3117 * Deep-merge one or more options objects, recursively merging **only** plain
3118 * object properties.
3119 *
3120 * @param   {Object[]} sources
3121 *          One or more objects to merge into a new object.
3122 *
3123 * @returns {Object}
3124 *          A new object that is the merged result of all sources.
3125 */
3126function mergeOptions() {
3127  var result = {};
3128
3129  for (var _len = arguments.length, sources = Array(_len), _key = 0; _key < _len; _key++) {
3130    sources[_key] = arguments[_key];
3131  }
3132
3133  sources.forEach(function (source) {
3134    if (!source) {
3135      return;
3136    }
3137
3138    each(source, function (value, key) {
3139      if (!isPlain(value)) {
3140        result[key] = value;
3141        return;
3142      }
3143
3144      if (!isPlain(result[key])) {
3145        result[key] = {};
3146      }
3147
3148      result[key] = mergeOptions(result[key], value);
3149    });
3150  });
3151
3152  return result;
3153}
3154
3155/**
3156 * Player Component - Base class for all UI objects
3157 *
3158 * @file component.js
3159 */
3160/**
3161 * Base class for all UI Components.
3162 * Components are UI objects which represent both a javascript object and an element
3163 * in the DOM. They can be children of other components, and can have
3164 * children themselves.
3165 *
3166 * Components can also use methods from {@link EventTarget}
3167 */
3168
3169var Component = function () {
3170
3171  /**
3172   * A callback that is called when a component is ready. Does not have any
3173   * paramters and any callback value will be ignored.
3174   *
3175   * @callback Component~ReadyCallback
3176   * @this Component
3177   */
3178
3179  /**
3180   * Creates an instance of this class.
3181   *
3182   * @param {Player} player
3183   *        The `Player` that this class should be attached to.
3184   *
3185   * @param {Object} [options]
3186   *        The key/value store of player options.
3187   *
3188   * @param {Object[]} [options.children]
3189   *        An array of children objects to intialize this component with. Children objects have
3190   *        a name property that will be used if more than one component of the same type needs to be
3191   *        added.
3192   *
3193   * @param {Component~ReadyCallback} [ready]
3194   *        Function that gets called when the `Component` is ready.
3195   */
3196  function Component(player, options, ready) {
3197    classCallCheck(this, Component);
3198
3199
3200    // The component might be the player itself and we can't pass `this` to super
3201    if (!player && this.play) {
3202      this.player_ = player = this; // eslint-disable-line
3203    } else {
3204      this.player_ = player;
3205    }
3206
3207    // Make a copy of prototype.options_ to protect against overriding defaults
3208    this.options_ = mergeOptions({}, this.options_);
3209
3210    // Updated options with supplied options
3211    options = this.options_ = mergeOptions(this.options_, options);
3212
3213    // Get ID from options or options element if one is supplied
3214    this.id_ = options.id || options.el && options.el.id;
3215
3216    // If there was no ID from the options, generate one
3217    if (!this.id_) {
3218      // Don't require the player ID function in the case of mock players
3219      var id = player && player.id && player.id() || 'no_player';
3220
3221      this.id_ = id + '_component_' + newGUID();
3222    }
3223
3224    this.name_ = options.name || null;
3225
3226    // Create element if one wasn't provided in options
3227    if (options.el) {
3228      this.el_ = options.el;
3229    } else if (options.createEl !== false) {
3230      this.el_ = this.createEl();
3231    }
3232
3233    // if evented is anything except false, we want to mixin in evented
3234    if (options.evented !== false) {
3235      // Make this an evented object and use `el_`, if available, as its event bus
3236      evented(this, { eventBusKey: this.el_ ? 'el_' : null });
3237    }
3238    stateful(this, this.constructor.defaultState);
3239
3240    this.children_ = [];
3241    this.childIndex_ = {};
3242    this.childNameIndex_ = {};
3243
3244    // Add any child components in options
3245    if (options.initChildren !== false) {
3246      this.initChildren();
3247    }
3248
3249    this.ready(ready);
3250    // Don't want to trigger ready here or it will before init is actually
3251    // finished for all children that run this constructor
3252
3253    if (options.reportTouchActivity !== false) {
3254      this.enableTouchActivity();
3255    }
3256  }
3257
3258  /**
3259   * Dispose of the `Component` and all child components.
3260   *
3261   * @fires Component#dispose
3262   */
3263
3264
3265  Component.prototype.dispose = function dispose() {
3266
3267    /**
3268     * Triggered when a `Component` is disposed.
3269     *
3270     * @event Component#dispose
3271     * @type {EventTarget~Event}
3272     *
3273     * @property {boolean} [bubbles=false]
3274     *           set to false so that the close event does not
3275     *           bubble up
3276     */
3277    this.trigger({ type: 'dispose', bubbles: false });
3278
3279    // Dispose all children.
3280    if (this.children_) {
3281      for (var i = this.children_.length - 1; i >= 0; i--) {
3282        if (this.children_[i].dispose) {
3283          this.children_[i].dispose();
3284        }
3285      }
3286    }
3287
3288    // Delete child references
3289    this.children_ = null;
3290    this.childIndex_ = null;
3291    this.childNameIndex_ = null;
3292
3293    if (this.el_) {
3294      // Remove element from DOM
3295      if (this.el_.parentNode) {
3296        this.el_.parentNode.removeChild(this.el_);
3297      }
3298
3299      removeData(this.el_);
3300      this.el_ = null;
3301    }
3302
3303    // remove reference to the player after disposing of the element
3304    this.player_ = null;
3305  };
3306
3307  /**
3308   * Return the {@link Player}
3308 that the `Component` has attached to.
3309   *
3310   * @return {Player}
3311   *         The player that this `Component` has attached to.
3312   */
3313
3314
3315  Component.prototype.player = function player() {
3316    return this.player_;
3317  };
3318
3319  /**
3320   * Deep merge of options objects with new options.
3321   * > Note: When both `obj` and `options` contain properties whose values are objects.
3322   *         The two properties get merged using {@link module:mergeOptions}
3323   *
3324   * @param {Object} obj
3325   *        The object that contains new options.
3326   *
3327   * @return {Object}
3328   *         A new object of `this.options_` and `obj` merged together.
3329   *
3330   * @deprecated since version 5
3331   */
3332
3333
3334  Component.prototype.options = function options(obj) {
3335    log$1.warn('this.options() has been deprecated and will be moved to the constructor in 6.0');
3336
3337    if (!obj) {
3338      return this.options_;
3339    }
3340
3341    this.options_ = mergeOptions(this.options_, obj);
3342    return this.options_;
3343  };
3344
3345  /**
3346   * Get the `Component`s DOM element
3347   *
3348   * @return {Element}
3349   *         The DOM element for this `Component`.
3350   */
3351
3352
3353  Component.prototype.el = function el() {
3354    return this.el_;
3355  };
3356
3357  /**
3358   * Create the `Component`s DOM element.
3359   *
3360   * @param {string} [tagName]
3361   *        Element's DOM node type. e.g. 'div'
3362   *
3363   * @param {Object} [properties]
3364   *        An object of properties that should be set.
3365   *
3366   * @param {Object} [attributes]
3367   *        An object of attributes that should be set.
3368   *
3369   * @return {Element}
3370   *         The element that gets created.
3371   */
3372
3373
3374  Component.prototype.createEl = function createEl$$1(tagName, properties, attributes) {
3375    return createEl(tagName, properties, attributes);
3376  };
3377
3378  /**
3379   * Localize a string given the string in english.
3380   *
3381   * If tokens are provided, it'll try and run a simple token replacement on the provided string.
3382   * The tokens it lo
vendor: 24,326 bytes, lines 3382-4202
3382ooks for look like `{1}` with the index being 1-indexed into the tokens array.
3383   *
3384   * If a `defaultValue` is provided, it'll use that over `string`,
3385   * if a value isn't found in provided language files.
3386   * This is useful if you want to have a descriptive key for token replacement
3387   * but have a succinct localized string and not require `en.json` to be included.
3388   *
3389   * Currently, it is used for the progress bar timing.
3390   * ```js
3391   * {
3392   *   "progress bar timing: currentTime={1} duration={2}": "{1} of {2}"
3393   * }
3394   * ```
3395   * It is then used like so:
3396   * ```js
3397   * this.localize('progress bar timing: currentTime={1} duration{2}',
3398   *               [this.player_.currentTime(), this.player_.duration()],
3399   *               '{1} of {2}');
3400   * ```
3401   *
3402   * Which outputs something like: `01:23 of 24:56`.
3403   *
3404   *
3405   * @param {string} string
3406   *        The string to localize and the key to lookup in the language files.
3407   * @param {string[]} [tokens]
3408   *        If the current item has token replacements, provide the tokens here.
3409   * @param {string} [defaultValue]
3410   *        Defaults to `string`. Can be a default value to use for token replacement
3411   *        if the lookup key is needed to be separate.
3412   *
3413   * @return {string}
3414   *         The localized string or if no localization exists the english string.
3415   */
3416
3417
3418  Component.prototype.localize = function localize(string, tokens) {
3419    var defaultValue = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : string;
3420
3421    var code = this.player_.language && this.player_.language();
3422    var languages = this.player_.languages && this.player_.languages();
3423    var language = languages && languages[code];
3424    var primaryCode = code && code.split('-')[0];
3425    var primaryLang = languages && languages[primaryCode];
3426
3427    var localizedString = defaultValue;
3428
3429    if (language && language[string]) {
3430      localizedString = language[string];
3431    } else if (primaryLang && primaryLang[string]) {
3432      localizedString = primaryLang[string];
3433    }
3434
3435    if (tokens) {
3436      localizedString = localizedString.replace(/\{(\d+)\}/g, function (match, index) {
3437        var value = tokens[index - 1];
3438        var ret = value;
3439
3440        if (typeof value === 'undefined') {
3441          ret = match;
3442        }
3443
3444        return ret;
3445      });
3446    }
3447
3448    return localizedString;
3449  };
3450
3451  /**
3452   * Return the `Component`s DOM element. This is where children get inserted.
3453   * This will usually be the the same as the element returned in {@link Component#el}.
3454   *
3455   * @return {Element}
3456   *         The content element for this `Component`.
3457   */
3458
3459
3460  Component.prototype.contentEl = function contentEl() {
3461    return this.contentEl_ || this.el_;
3462  };
3463
3464  /**
3465   * Get this `Component`s ID
3466   *
3467   * @return {string}
3468   *         The id of this `Component`
3469   */
3470
3471
3472  Component.prototype.id = function id() {
3473    return this.id_;
3474  };
3475
3476  /**
3477   * Get the `Component`s name. The name gets used to reference the `Component`
3478   * and is set during registration.
3479   *
3480   * @return {string}
3481   *         The name of this `Component`.
3482   */
3483
3484
3485  Component.prototype.name = function name() {
3486    return this.name_;
3487  };
3488
3489  /**
3490   * Get an array of all child components
3491   *
3492   * @return {Array}
3493   *         The children
3494   */
3495
3496
3497  Component.prototype.children = function children() {
3498    return this.children_;
3499  };
3500
3501  /**
3502   * Returns the child `Component` with the given `id`.
3503   *
3504   * @param {string} id
3505   *        The id of the child `Component` to get.
3506   *
3507   * @return {Component|undefined}
3508   *         The child `Component` with the given `id` or undefined.
3509   */
3510
3511
3512  Component.prototype.getChildById = function getChildById(id) {
3513    return this.childIndex_[id];
3514  };
3515
3516  /**
3517   * Returns the child `Component` with the given `name`.
3518   *
3519   * @param {string} name
3520   *        The name of the child `Component` to get.
3521   *
3522   * @return {Component|undefined}
3523   *         The child `Component` with the given `name` or undefined.
3524   */
3525
3526
3527  Component.prototype.getChild = function getChild(name) {
3528    if (!name) {
3529      return;
3530    }
3531
3532    name = toTitleCase(name);
3533
3534    return this.childNameIndex_[name];
3535  };
3536
3537  /**
3538   * Add a child `Component` inside the current `Component`.
3539   *
3540   *
3541   * @param {string|Component} child
3542   *        The name or instance of a child to add.
3543   *
3544   * @param {Object} [options={}]
3545   *        The key/value store of options that will get passed to children of
3546   *        the child.
3547   *
3548   * @param {number} [index=this.children_.length]
3549   *        The index to attempt to add a child into.
3550   *
3551   * @return {Component}
3552   *         The `Component` that gets added as a child. When using a string the
3553   *         `Component` will get created by this process.
3554   */
3555
3556
3557  Component.prototype.addChild = function addChild(child) {
3558    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
3559    var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.children_.length;
3560
3561    var component = void 0;
3562    var componentName = void 0;
3563
3564    // If child is a string, create component with options
3565    if (typeof child === 'string') {
3566      componentName = toTitleCase(child);
3567
3568      var componentClassName = options.componentClass || componentName;
3569
3570      // Set name through options
3571      options.name = componentName;
3572
3573      // Create a new object & element for this controls set
3574      // If there's no .player_, this is a player
3575      var ComponentClass = Component.getComponent(componentClassName);
3576
3577      if (!ComponentClass) {
3578        throw new Error('Component ' + componentClassName + ' does not exist');
3579      }
3580
3581      // data stored directly on the videojs object may be
3582      // misidentified as a component to retain
3583      // backwards-compatibility with 4.x. check to make sure the
3584      // component class can be instantiated.
3585      if (typeof ComponentClass !== 'function') {
3586        return null;
3587      }
3588
3589      component = new ComponentClass(this.player_ || this, options);
3590
3591      // child is a component instance
3592    } else {
3593      component = child;
3594    }
3595
3596    this.children_.splice(index, 0, component);
3597
3598    if (typeof component.id === 'function') {
3599      this.childIndex_[component.id()] = component;
3600    }
3601
3602    // If a name wasn't used to create the component, check if we can use the
3603    // name function of the component
3604    componentName = componentName || component.name && toTitleCase(component.name());
3605
3606    if (componentName) {
3607      this.childNameIndex_[componentName] = component;
3608    }
3609
3610    // Add the UI object's element to the container div (box)
3611    // Having an element is not required
3612    if (typeof component.el === 'function' && component.el()) {
3613      var childNodes = this.contentEl().children;
3614      var refNode = childNodes[index] || null;
3615
3616      this.contentEl().insertBefore(component.el(), refNode);
3617    }
3618
3619    // Return so it can stored on parent object if desired.
3620    return component;
3621  };
3622
3623  /**
3624   * Remove a child `Component` from this `Component`s list of children. Also removes
3625   * the child `Component`s element from this `Component`s element.
3626   *
3627   * @param {Component} component
3628   *        The child `Component` to remove.
3629   */
3630
3631
3632  Component.prototype.removeChild = function removeChild(component) {
3633    if (typeof component === 'string') {
3634      component = this.getChild(component);
3635    }
3636
3637    if (!component || !this.children_) {
3638      return;
3639    }
3640
3641    var childFound = false;
3642
3643    for (var i = this.children_.length - 1; i >= 0; i--) {
3644      if (this.children_[i] === component) {
3645        childFound = true;
3646        this.children_.splice(i, 1);
3647        break;
3648      }
3649    }
3650
3651    if (!childFound) {
3652      return;
3653    }
3654
3655    this.childIndex_[component.id()] = null;
3656    this.childNameIndex_[component.name()] = null;
3657
3658    var compEl = component.el();
3659
3660    if (compEl && compEl.parentNode === this.contentEl()) {
3661      this.contentEl().removeChild(component.el());
3662    }
3663  };
3664
3665  /**
3666   * Add and initialize default child `Component`s based upon options.
3667   */
3668
3669
3670  Component.prototype.initChildren = function initChildren() {
3671    var _this = this;
3672
3673    var children = this.options_.children;
3674
3675    if (children) {
3676      // `this` is `parent`
3677      var parentOptions = this.options_;
3678
3679      var handleAdd = function handleAdd(child) {
3680        var name = child.name;
3681        var opts = child.opts;
3682
3683        // Allow options for children to be set at the parent options
3684        // e.g. videojs(id, { controlBar: false });
3685        // instead of videojs(id, { children: { controlBar: false });
3686        if (parentOptions[name] !== undefined) {
3687          opts = parentOptions[name];
3688        }
3689
3690        // Allow for disabling default components
3691        // e.g. options['children']['posterImage'] = false
3692        if (opts === false) {
3693          return;
3694        }
3695
3696        // Allow options to be passed as a simple boolean if no configuration
3697        // is necessary.
3698        if (opts === true) {
3699          opts = {};
3700        }
3701
3702        // We also want to pass the original player options
3703        // to each component as well so they don't need to
3704        // reach back into the player for options later.
3705        opts.playerOptions = _this.options_.playerOptions;
3706
3707        // Create and add the child component.
3708        // Add a direct reference to the child by name on the parent instance.
3709        // If two of the same component are used, different names should be supplied
3710        // for each
3711        var newChild = _this.addChild(name, opts);
3712
3713        if (newChild) {
3714          _this[name] = newChild;
3715        }
3716      };
3717
3718      // Allow for an array of children details to passed in the options
3719      var workingChildren = void 0;
3720      var Tech = Component.getComponent('Tech');
3721
3722      if (Array.isArray(children)) {
3723        workingChildren = children;
3724      } else {
3725        workingChildren = Object.keys(children);
3726      }
3727
3728      workingChildren
3729      // children that are in this.options_ but also in workingChildren  would
3730      // give us extra children we do not want. So, we want to filter them out.
3731      .concat(Object.keys(this.options_).filter(function (child) {
3732        return !workingChildren.some(function (wchild) {
3733          if (typeof wchild === 'string') {
3734            return child === wchild;
3735          }
3736          return child === wchild.name;
3737        });
3738      })).map(function (child) {
3739        var name = void 0;
3740        var opts = void 0;
3741
3742        if (typeof child === 'string') {
3743          name = child;
3744          opts = children[name] || _this.options_[name] || {};
3745        } else {
3746          name = child.name;
3747          opts = child;
3748        }
3749
3750        return { name: name, opts: opts };
3751      }).filter(function (child) {
3752        // we have to make sure that child.name isn't in the techOrder since
3753        // techs are registerd as Components but can't aren't compatible
3754        // See https://github.com/videojs/video.js/issues/2772
3755        var c = Component.getComponent(child.opts.componentClass || toTitleCase(child.name));
3756
3757        return c && !Tech.isTech(c);
3758      }).forEach(handleAdd);
3759    }
3760  };
3761
3762  /**
3763   * Builds the default DOM class name. Should be overriden by sub-components.
3764   *
3765   * @return {string}
3766   *         The DOM class name for this object.
3767   *
3768   * @abstract
3769   */
3770
3771
3772  Component.prototype.buildCSSClass = function buildCSSClass() {
3773    // Child classes can include a function that does:
3774    // return 'CLASS NAME' + this._super();
3775    return '';
3776  };
3777
3778  /**
3779   * Bind a listener to the component's ready state.
3780   * Different from event listeners in that if the ready event has already happened
3781   * it will trigger the function immediately.
3782   *
3783   * @return {Component}
3784   *         Returns itself; method can be chained.
3785   */
3786
3787
3788  Component.prototype.ready = function ready(fn) {
3789    var sync = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
3790
3791    if (!fn) {
3792      return;
3793    }
3794
3795    if (!this.isReady_) {
3796      this.readyQueue_ = this.readyQueue_ || [];
3797      this.readyQueue_.push(fn);
3798      return;
3799    }
3800
3801    if (sync) {
3802      fn.call(this);
3803    } else {
3804      // Call the function asynchronously by default for consistency
3805      this.setTimeout(fn, 1);
3806    }
3807  };
3808
3809  /**
3810   * Trigger all the ready listeners for this `Component`.
3811   *
3812   * @fires Component#ready
3813   */
3814
3815
3816  Component.prototype.triggerReady = function triggerReady() {
3817    this.isReady_ = true;
3818
3819    // Ensure ready is triggerd asynchronously
3820    this.setTimeout(function () {
3821      var readyQueue = this.readyQueue_;
3822
3823      // Reset Ready Queue
3824      this.readyQueue_ = [];
3825
3826      if (readyQueue && readyQueue.length > 0) {
3827        readyQueue.forEach(function (fn) {
3828          fn.call(this);
3829        }, this);
3830      }
3831
3832      // Allow for using event listeners also
3833      /**
3834       * Triggered when a `Component` is ready.
3835       *
3836       * @event Component#ready
3837       * @type {EventTarget~Event}
3838       */
3839      this.trigger('ready');
3840    }, 1);
3841  };
3842
3843  /**
3844   * Find a single DOM element matching a `selector`. This can be within the `Component`s
3845   * `contentEl()` or another custom context.
3846   *
3847   * @param {string} selector
3848   *        A valid CSS selector, which will be passed to `querySelector`.
3849   *
3850   * @param {Element|string} [context=this.contentEl()]
3851   *        A DOM element within which to query. Can also be a selector string in
3852   *        which case the first matching element will get used as context. If
3853   *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
3854   *        nothing it falls back to `document`.
3855   *
3856   * @return {Element|null}
3857   *         the dom element that was found, or null
3858   *
3859   * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
3860   */
3861
3862
3863  Component.prototype.$ = function $$$1(selector, context) {
3864    return $(selector, context || this.contentEl());
3865  };
3866
3867  /**
3868   * Finds all DOM element matching a `selector`. This can be within the `Component`s
3869   * `contentEl()` or another custom context.
3870   *
3871   * @param {string} selector
3872   *        A valid CSS selector, which will be passed to `querySelectorAll`.
3873   *
3874   * @param {Element|string} [context=this.contentEl()]
3875   *        A DOM element within which to query. Can also be a selector string in
3876   *        which case the first matching element will get used as context. If
3877   *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
3878   *        nothing it falls back to `document`.
3879   *
3880   * @return {NodeList}
3881   *         a list of dom elements that were found
3882   *
3883   * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
3884   */
3885
3886
3887  Component.prototype.$$ = function $$$$1(selector, context) {
3888    return $$(selector, context || this.contentEl());
3889  };
3890
3891  /**
3892   * Check if a component's element has a CSS class name.
3893   *
3894   * @param {string} classToCheck
3895   *        CSS class name to check.
3896   *
3897   * @return {boolean}
3898   *         - True if the `Component` has the class.
3899   *         - False if the `Component` does not have the class`
3900   */
3901
3902
3903  Component.prototype.hasClass = function hasClass$$1(classToCheck) {
3904    return hasClass(this.el_, classToCheck);
3905  };
3906
3907  /**
3908   * Add a CSS class name to the `Component`s element.
3909   *
3910   * @param {string} classToAdd
3911   *        CSS class name to add
3912   */
3913
3914
3915  Component.prototype.addClass = function addClass$$1(classToAdd) {
3916    addClass(this.el_, classToAdd);
3917  };
3918
3919  /**
3920   * Remove a CSS class name from the `Component`s element.
3921   *
3922   * @param {string} classToRemove
3923   *        CSS class name to remove
3924   */
3925
3926
3927  Component.prototype.removeClass = function removeClass$$1(classToRemove) {
3928    removeClass(this.el_, classToRemove);
3929  };
3930
3931  /**
3932   * Add or remove a CSS class name from the component's element.
3933   * - `classToToggle` gets added when {@link Component#hasClass} would return false.
3934   * - `classToToggle` gets removed when {@link Component#hasClass} would return true.
3935   *
3936   * @param  {string} classToToggle
3937   *         The class to add or remove based on (@link Component#hasClass}
3938   *
3939   * @param  {boolean|Dom~predicate} [predicate]
3940   *         An {@link Dom~predicate} function or a boolean
3941   */
3942
3943
3944  Component.prototype.toggleClass = function toggleClass$$1(classToToggle, predicate) {
3945    toggleClass(this.el_, classToToggle, predicate);
3946  };
3947
3948  /**
3949   * Show the `Component`s element if it is hidden by removing the
3950   * 'vjs-hidden' class name from it.
3951   */
3952
3953
3954  Component.prototype.show = function show() {
3955    this.removeClass('vjs-hidden');
3956  };
3957
3958  /**
3959   * Hide the `Component`s element if it is currently showing by adding the
3960   * 'vjs-hidden` class name to it.
3961   */
3962
3963
3964  Component.prototype.hide = function hide() {
3965    this.addClass('vjs-hidden');
3966  };
3967
3968  /**
3969   * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing'
3970   * class name to it. Used during fadeIn/fadeOut.
3971   *
3972   * @private
3973   */
3974
3975
3976  Component.prototype.lockShowing = function lockShowing() {
3977    this.addClass('vjs-lock-showing');
3978  };
3979
3980  /**
3981   * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing'
3982   * class name from it. Used during fadeIn/fadeOut.
3983   *
3984   * @private
3985   */
3986
3987
3988  Component.prototype.unlockShowing = function unlockShowing() {
3989    this.removeClass('vjs-lock-showing');
3990  };
3991
3992  /**
3993   * Get the value of an attribute on the `Component`s element.
3994   *
3995   * @param {string} attribute
3996   *        Name of the attribute to get the value from.
3997   *
3998   * @return {string|null}
3999   *         - The value of the attribute that was asked for.
4000   *         - Can be an empty string on some browsers if the attribute does not exist
4001   *           or has no value
4002   *         - Most browsers will return null if the attibute does not exist or has
4003   *           no value.
4004   *
4005   * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute}
4006   */
4007
4008
4009  Component.prototype.getAttribute = function getAttribute$$1(attribute) {
4010    return getAttribute(this.el_, attribute);
4011  };
4012
4013  /**
4014   * Set the value of an attribute on the `Component`'s element
4015   *
4016   * @param {string} attribute
4017   *        Name of the attribute to set.
4018   *
4019   * @param {string} value
4020   *        Value to set the attribute to.
4021   *
4022   * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute}
4023   */
4024
4025
4026  Component.prototype.setAttribute = function setAttribute$$1(attribute, value) {
4027    setAttribute(this.el_, attribute, value);
4028  };
4029
4030  /**
4031   * Remove an attribute from the `Component`s element.
4032   *
4033   * @param {string} attribute
4034   *        Name of the attribute to remove.
4035   *
4036   * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute}
4037   */
4038
4039
4040  Component.prototype.removeAttribute = function removeAttribute$$1(attribute) {
4041    removeAttribute(this.el_, attribute);
4042  };
4043
4044  /**
4045   * Get or set the width of the component based upon the CSS styles.
4046   * See {@link Component#dimension} for more detailed information.
4047   *
4048   * @param {number|string} [num]
4049   *        The width that you want to set postfixed with '%', 'px' or nothing.
4050   *
4051   * @param {boolean} [skipListeners]
4052   *        Skip the componentresize event trigger
4053   *
4054   * @return {number|string}
4055   *         The width when getting, zero if there is no width. Can be a string
4056   *           postpixed with '%' or 'px'.
4057   */
4058
4059
4060  Component.prototype.width = function width(num, skipListeners) {
4061    return this.dimension('width', num, skipListeners);
4062  };
4063
4064  /**
4065   * Get or set the height of the component based upon the CSS styles.
4066   * See {@link Component#dimension} for more detailed information.
4067   *
4068   * @param {number|string} [num]
4069   *        The height that you want to set postfixed with '%', 'px' or nothing.
4070   *
4071   * @param {boolean} [skipListeners]
4072   *        Skip the componentresize event trigger
4073   *
4074   * @return {number|string}
4075   *         The width when getting, zero if there is no width. Can be a string
4076   *         postpixed with '%' or 'px'.
4077   */
4078
4079
4080  Component.prototype.height = function height(num, skipListeners) {
4081    return this.dimension('height', num, skipListeners);
4082  };
4083
4084  /**
4085   * Set both the width and height of the `Component` element at the same time.
4086   *
4087   * @param  {number|string} width
4088   *         Width to set the `Component`s element to.
4089   *
4090   * @param  {number|string} height
4091   *         Height to set the `Component`s element to.
4092   */
4093
4094
4095  Component.prototype.dimensions = function dimensions(width, height) {
4096    // Skip componentresize listeners on width for optimization
4097    this.width(width, true);
4098    this.height(height);
4099  };
4100
4101  /**
4102   * Get or set width or height of the `Component` element. This is the shared code
4103   * for the {@link Component#width} and {@link Component#height}.
4104   *
4105   * Things to know:
4106   * - If the width or height in an number this will return the number postfixed with 'px'.
4107   * - If the width/height is a percent this will return the percent postfixed with '%'
4108   * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function
4109   *   defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`.
4110   *   See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/}
4111   *   for more information
4112   * - If you want the computed style of the component, use {@link Component#currentWidth}
4113   *   and {@link {Component#currentHeight}
4114   *
4115   * @fires Component#componentresize
4116   *
4117   * @param {string} widthOrHeight
4118   8        'width' or 'height'
4119   *
4120   * @param  {number|string} [num]
4121   8         New dimension
4122   *
4123   * @param  {boolean} [skipListeners]
4124   *         Skip componentresize event trigger
4125   *
4126   * @return {number}
4127   *         The dimension when getting or 0 if unset
4128   */
4129
4130
4131  Component.prototype.dimension = function dimension(widthOrHeight, num, skipListeners) {
4132    if (num !== undefined) {
4133      // Set to zero if null or literally NaN (NaN !== NaN)
4134      if (num === null || num !== num) {
4135        num = 0;
4136      }
4137
4138      // Check if using css width/height (% or px) and adjust
4139      if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
4140        this.el_.style[widthOrHeight] = num;
4141      } else if (num === 'auto') {
4142        this.el_.style[widthOrHeight] = '';
4143      } else {
4144        this.el_.style[widthOrHeight] = num + 'px';
4145      }
4146
4147      // skipListeners allows us to avoid triggering the resize event when setting both width and height
4148      if (!skipListeners) {
4149        /**
4150         * Triggered when a component is resized.
4151         *
4152         * @event Component#componentresize
4153         * @type {EventTarget~Event}
4154         */
4155        this.trigger('componentresize');
4156      }
4157
4158      return;
4159    }
4160
4161    // Not setting a value, so getting it
4162    // Make sure element exists
4163    if (!this.el_) {
4164      return 0;
4165    }
4166
4167    // Get dimension value from style
4168    var val = this.el_.style[widthOrHeight];
4169    var pxIndex = val.indexOf('px');
4170
4171    if (pxIndex !== -1) {
4172      // Return the pixel value with no 'px'
4173      return parseInt(val.slice(0, pxIndex), 10);
4174    }
4175
4176    // No px so using % or no style was set, so falling back to offsetWidth/height
4177    // If component has display:none, offset will return 0
4178    // TODO: handle display:none and no dimension style using px
4179    return parseInt(this.el_['offset' + toTitleCase(widthOrHeight)], 10);
4180  };
4181
4182  /**
4183   * Get the width or the height of the `Component` elements computed style. Uses
4184   * `window.getComputedStyle`.
4185   *
4186   * @param {string} widthOrHeight
4187   *        A string containing 'width' or 'height'. Whichever one you want to get.
4188   *
4189   * @return {number}
4190   *         The dimension that gets asked for or 0 if nothing was set
4191   *         for that dimension.
4192   */
4193
4194
4195  Component.prototype.currentDimension = function currentDimension(widthOrHeight) {
4196    var computedWidthOrHeight = 0;
4197
4198    if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
4199      throw new Error('currentDimension only accepts width or height value');
4200    }
4201
4202    if (typeof 
vendor: 11,597 bytes, lines 4202-4543
4202window_1.getComputedStyle === 'function') {
4203      var computedStyle = window_1.getComputedStyle(this.el_);
4204
4205      computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight];
4206    }
4207
4208    // remove 'px' from variable and parse as integer
4209    computedWidthOrHeight = parseFloat(computedWidthOrHeight);
4210
4211    // if the computed value is still 0, it's possible that the browser is lying
4212    // and we want to check the offset values.
4213    // This code also runs on IE8 and wherever getComputedStyle doesn't exist.
4214    if (computedWidthOrHeight === 0) {
4215      var rule = 'offset' + toTitleCase(widthOrHeight);
4216
4217      computedWidthOrHeight = this.el_[rule];
4218    }
4219
4220    return computedWidthOrHeight;
4221  };
4222
4223  /**
4224   * An object that contains width and height values of the `Component`s
4225   * computed style. Uses `window.getComputedStyle`.
4226   *
4227   * @typedef {Object} Component~DimensionObject
4228   *
4229   * @property {number} width
4230   *           The width of the `Component`s computed style.
4231   *
4232   * @property {number} height
4233   *           The height of the `Component`s computed style.
4234   */
4235
4236  /**
4237   * Get an object that contains width and height values of the `Component`s
4238   * computed style.
4239   *
4240   * @return {Component~DimensionObject}
4241   *         The dimensions of the components element
4242   */
4243
4244
4245  Component.prototype.currentDimensions = function currentDimensions() {
4246    return {
4247      width: this.currentDimension('width'),
4248      height: this.currentDimension('height')
4249    };
4250  };
4251
4252  /**
4253   * Get the width of the `Component`s computed style. Uses `window.getComputedStyle`.
4254   *
4255   * @return {number} width
4256   *           The width of the `Component`s computed style.
4257   */
4258
4259
4260  Component.prototype.currentWidth = function currentWidth() {
4261    return this.currentDimension('width');
4262  };
4263
4264  /**
4265   * Get the height of the `Component`s computed style. Uses `window.getComputedStyle`.
4266   *
4267   * @return {number} height
4268   *           The height of the `Component`s computed style.
4269   */
4270
4271
4272  Component.prototype.currentHeight = function currentHeight() {
4273    return this.currentDimension('height');
4274  };
4275
4276  /**
4277   * Set the focus to this component
4278   */
4279
4280
4281  Component.prototype.focus = function focus() {
4282    this.el_.focus();
4283  };
4284
4285  /**
4286   * Remove the focus from this component
4287   */
4288
4289
4290  Component.prototype.blur = function blur() {
4291    this.el_.blur();
4292  };
4293
4294  /**
4295   * Emit a 'tap' events when touch event support gets detected. This gets used to
4296   * support toggling the controls through a tap on the video. They get enabled
4297   * because every sub-component would have extra overhead otherwise.
4298   *
4299   * @private
4300   * @fires Component#tap
4301   * @listens Component#touchstart
4302   * @listens Component#touchmove
4303   * @listens Component#touchleave
4304   * @listens Component#touchcancel
4305   * @listens Component#touchend
4306    */
4307
4308
4309  Component.prototype.emitTapEvents = function emitTapEvents() {
4310    // Track the start time so we can determine how long the touch lasted
4311    var touchStart = 0;
4312    var firstTouch = null;
4313
4314    // Maximum movement allowed during a touch event to still be considered a tap
4315    // Other popular libs use anywhere from 2 (hammer.js) to 15,
4316    // so 10 seems like a nice, round number.
4317    var tapMovementThreshold = 10;
4318
4319    // The maximum length a touch can be while still being considered a tap
4320    var touchTimeThreshold = 200;
4321
4322    var couldBeTap = void 0;
4323
4324    this.on('touchstart', function (event) {
4325      // If more than one finger, don't consider treating this as a click
4326      if (event.touches.length === 1) {
4327        // Copy pageX/pageY from the object
4328        firstTouch = {
4329          pageX: event.touches[0].pageX,
4330          pageY: event.touches[0].pageY
4331        };
4332        // Record start time so we can detect a tap vs. "touch and hold"
4333        touchStart = new Date().getTime();
4334        // Reset couldBeTap tracking
4335        couldBeTap = true;
4336      }
4337    });
4338
4339    this.on('touchmove', function (event) {
4340      // If more than one finger, don't consider treating this as a click
4341      if (event.touches.length > 1) {
4342        couldBeTap = false;
4343      } else if (firstTouch) {
4344        // Some devices will throw touchmoves for all but the slightest of taps.
4345        // So, if we moved only a small distance, this could still be a tap
4346        var xdiff = event.touches[0].pageX - firstTouch.pageX;
4347        var ydiff = event.touches[0].pageY - firstTouch.pageY;
4348        var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
4349
4350        if (touchDistance > tapMovementThreshold) {
4351          couldBeTap = false;
4352        }
4353      }
4354    });
4355
4356    var noTap = function noTap() {
4357      couldBeTap = false;
4358    };
4359
4360    // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
4361    this.on('touchleave', noTap);
4362    this.on('touchcancel', noTap);
4363
4364    // When the touch ends, measure how long it took and trigger the appropriate
4365    // event
4366    this.on('touchend', function (event) {
4367      firstTouch = null;
4368      // Proceed only if the touchmove/leave/cancel event didn't happen
4369      if (couldBeTap === true) {
4370        // Measure how long the touch lasted
4371        var touchTime = new Date().getTime() - touchStart;
4372
4373        // Make sure the touch was less than the threshold to be considered a tap
4374        if (touchTime < touchTimeThreshold) {
4375          // Don't let browser turn this into a click
4376          event.preventDefault();
4377          /**
4378           * Triggered when a `Component` is tapped.
4379           *
4380           * @event Component#tap
4381           * @type {EventTarget~Event}
4382           */
4383          this.trigger('tap');
4384          // It may be good to copy the touchend event object and change the
4385          // type to tap, if the other event properties aren't exact after
4386          // Events.fixEvent runs (e.g. event.target)
4387        }
4388      }
4389    });
4390  };
4391
4392  /**
4393   * This function reports user activity whenever touch events happen. This can get
4394   * turned off by any sub-components that wants touch events to act another way.
4395   *
4396   * Report user touch activity when touch events occur. User activity gets used to
4397   * determine when controls should show/hide. It is simple when it comes to mouse
4398   * events, because any mouse event should show the controls. So we capture mouse
4399   * events that bubble up to the player and report activity when that happens.
4400   * With touch events it isn't as easy as `touchstart` and `touchend` toggle player
4401   * controls. So touch events can't help us at the player level either.
4402   *
4403   * User activity gets checked asynchronously. So what could happen is a tap event
4404   * on the video turns the controls off. Then the `touchend` event bubbles up to
4405   * the player. Which, if it reported user activity, would turn the controls right
4406   * back on. We also don't want to completely block touch events from bubbling up.
4407   * Furthermore a `touchmove` event and anything other than a tap, should not turn
4408   * controls back on.
4409   *
4410   * @listens Component#touchstart
4411   * @listens Component#touchmove
4412   * @listens Component#touchend
4413   * @listens Component#touchcancel
4414   */
4415
4416
4417  Component.prototype.enableTouchActivity = function enableTouchActivity() {
4418    // Don't continue if the root player doesn't support reporting user activity
4419    if (!this.player() || !this.player().reportUserActivity) {
4420      return;
4421    }
4422
4423    // listener for reporting that the user is active
4424    var report = bind(this.player(), this.player().reportUserActivity);
4425
4426    var touchHolding = void 0;
4427
4428    this.on('touchstart', function () {
4429      report();
4430      // For as long as the they are touching the device or have their mouse down,
4431      // we consider them active even if they're not moving their finger or mouse.
4432      // So we want to continue to update that they are active
4433      this.clearInterval(touchHolding);
4434      // report at the same interval as activityCheck
4435      touchHolding = this.setInterval(report, 250);
4436    });
4437
4438    var touchEnd = function touchEnd(event) {
4439      report();
4440      // stop the interval that maintains activity if the touch is holding
4441      this.clearInterval(touchHolding);
4442    };
4443
4444    this.on('touchmove', report);
4445    this.on('touchend', touchEnd);
4446    this.on('touchcancel', touchEnd);
4447  };
4448
4449  /**
4450   * A callback that has no parameters and is bound into `Component`s context.
4451   *
4452   * @callback Component~GenericCallback
4453   * @this Component
4454   */
4455
4456  /**
4457   * Creates a function that runs after an `x` millisecond timeout. This function is a
4458   * wrapper around `window.setTimeout`. There are a few reasons to use this one
4459   * instead though:
4460   * 1. It gets cleared via  {@link Component#clearTimeout} when
4461   *    {@link Component#dispose} gets called.
4462   * 2. The function callback will gets turned into a {@link Component~GenericCallback}
4463   *
4464   * > Note: You can use `window.clearTimeout` on the id returned by this function. This
4465   *         will cause its dispose listener not to get cleaned up! Please use
4466   *         {@link Component#clearTimeout} or {@link Component#dispose}.
4467   *
4468   * @param {Component~GenericCallback} fn
4469   *        The function that will be run after `timeout`.
4470   *
4471   * @param {number} timeout
4472   *        Timeout in milliseconds to delay before executing the specified function.
4473   *
4474   * @return {number}
4475   *         Returns a timeout ID that gets used to identify the timeout. It can also
4476   *         get used in {@link Component#clearTimeout} to clear the timeout that
4477   *         was set.
4478   *
4479   * @listens Component#dispose
4480   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout}
4481   */
4482
4483
4484  Component.prototype.setTimeout = function setTimeout(fn, timeout) {
4485    var _this2 = this;
4486
4487    fn = bind(this, fn);
4488
4489    var timeoutId = window_1.setTimeout(fn, timeout);
4490    var disposeFn = function disposeFn() {
4491      return _this2.clearTimeout(timeoutId);
4492    };
4493
4494    disposeFn.guid = 'vjs-timeout-' + timeoutId;
4495
4496    this.on('dispose', disposeFn);
4497
4498    return timeoutId;
4499  };
4500
4501  /**
4502   * Clears a timeout that gets created via `window.setTimeout` or
4503   * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout}
4504   * use this function instead of `window.clearTimout`. If you don't your dispose
4505   * listener will not get cleaned up until {@link Component#dispose}!
4506   *
4507   * @param {number} timeoutId
4508   *        The id of the timeout to clear. The return value of
4509   *        {@link Component#setTimeout} or `window.setTimeout`.
4510   *
4511   * @return {number}
4512   *         Returns the timeout id that was cleared.
4513   *
4514   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout}
4515   */
4516
4517
4518  Component.prototype.clearTimeout = function clearTimeout(timeoutId) {
4519    window_1.clearTimeout(timeoutId);
4520
4521    var disposeFn = function disposeFn() {};
4522
4523    disposeFn.guid = 'vjs-timeout-' + timeoutId;
4524
4525    this.off('dispose', disposeFn);
4526
4527    return timeoutId;
4528  };
4529
4530  /**
4531   * Creates a function that gets run every `x` milliseconds. This function is a wrapper
4532   * around `window.setInterval`. There are a few reasons to use this one instead though.
4533   * 1. It gets cleared via  {@link Component#clearInterval} when
4534   *    {@link Component#dispose} gets called.
4535   * 2. The function callback will be a {@link Component~GenericCallback}
4536   *
4537   * @param {Component~GenericCallback} fn
4538   *        The function to run every `x` seconds.
4539   *
4540   * @param {number} interval
4541   *        Execute the specified function every `x` milliseconds.
4542   *
4543   * @return {number}
4544   *         Returns an id that can be used to identify the interval. It can also be be used in
4545   *         {@link Component#clearInterval} to clear the interval.
4546   *
4547   * @listens Component#dispose
4548   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval}
4549   */
4550
4551
4552  Component.prototype.setInterval = function setInterval(fn, interval) {
4553    var _this3 = this;
4554
4555    fn = bind(this, fn);
4556
4557    var intervalId = window_1.setInterval(fn, interval);
4558
4559    var disposeFn = function disposeFn() {
4560      return _this3.clearInterval(intervalId);
4561    };
4562
4563    disposeFn.guid = 'vjs-interval-' + intervalId;
4564
4565    this.on('dispose', disposeFn);
4566
4567    return intervalId;
4568  };
4569
4570  /**
4571   * Clears an interval that gets created via `window.setInterval` or
4572   * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval}
4573   * use this function instead of `window.clearInterval`. If you don't your dispose
4574   * listener will not get cleaned up until {@link Component#dispose}!
4575   *
4576   * @param {number} intervalId
4577   *        The id of the interval to clear. The return value of
4578   *        {@link Component#setInterval} or `window.setInterval`.
4579   *
4580   * @return {number}
4581   *         Returns the interval id that was cleared.
4582   *
4583   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval}
4584   */
4585
4586
4587  Component.prototype.clearInterval = function clearInterval(intervalId) {
4588    window_1.clearInterval(intervalId);
4589
4590    var disposeFn = function disposeFn() {};
4591
4592    disposeFn.guid = 'vjs-interval-' + intervalId;
4593
4594    this.off('dispose', disposeFn);
4595
4596    return intervalId;
4597  };
4598
4599  /**
4600   * Queues up a callback to be passed to requestAnimationFrame (rAF), but
4601   * with a few extra bonuses:
4602   *
4603   * - Supports browsers that do not support rAF by falling back to
4604   *   {@link Component#setTimeout}.
4605   *
4606   * - The callback is turned into a {@link Component~GenericCallback} (i.e.
4607   *   bound to the component).
4608   *
4609   * - Automatic cancellation of the rAF callback is handled if the component
4610   *   is disposed before it is called.
4611   *
4612   * @param  {Component~GenericCallback} fn
4613   *         A function that will be bound to this component and executed just
4614   *         before the browser's next repaint.
4615   *
4616   * @return {number}
4617   *         Returns an rAF ID that gets used to identify the timeout. It can
4618   *         also be used in {@link Component#cancelAnimationFrame} to cancel
4619   *         the animation frame callback.
4620   *
4621   * @listens Component#dispose
4622   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame}
4623   */
4624
4625
4626  Component.prototype.requestAnimationFrame = function requestAnimationFrame(fn) {
4627    var _this4 = this;
4628
4629    if (this.supportsRaf_) {
4630      fn = bind(this, fn);
4631
4632      var id = window_1.requestAnimationFrame(fn);
4633      var disposeFn = function disposeFn() {
4634        return _this4.cancelAnimationFrame(id);
4635      };
4636
4637      disposeFn.guid = 'vjs-raf-' + id;
4638      this.on('dispose', disposeFn);
4639
4640      return id;
4641    }
4642
4643    // Fall back to using a timer.
4644    return this.setTimeout(fn, 1000 / 60);
4645  };
4646
4647  /**
4648   * Cancels a queued callback passed to {@link Component#requestAnimationFrame}
4649   * (rAF).
4650   *
4651   * If you queue an rAF callback via {@link Component#requestAnimationFrame},
4652   * use this function instead of `window.cancelAnimationFrame`. If you don't,
4653   * your dispose listener will not get cleaned up until {@link Component#dispose}!
4654   *
4655   * @param {number} id
4656   *        The rAF ID to clear. The return value of {@link Component#requestAnimationFrame}.
4657   *
4658   * @return {number}
4659   *         Returns the rAF ID that was cleared.
4660   *
4661   * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/window/cancelAnimationFrame}
4662   */
4663
4664
4665  Component.prototype.cancelAnimationFrame = function cancelAnimationFrame(id) {
4666    if (this.supportsRaf_) {
4667      window_1.cancelAnimationFrame(id);
4668
4669      var disposeFn = function disposeFn() {};
4670
4671      disposeFn.guid = 'vjs-raf-' + id;
4672
4673      this.off('dispose', disposeFn);
4674
4675      return id;
4676    }
4677
4678    // Fall back to using a timer.
4679    return this.clearTimeout(id);
4680  };
4681
4682  /**
4683   * Register a `Component` with `videojs` given the name and the component.
4684   *
4685   * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s
4686   *         should be registered using {@link Tech.registerTech} or
4687   *         {@link videojs:videojs.registerTech}.
4688   *
4689   * > NOTE: This function can also be seen on videojs as
4690   *         {@link videojs:videojs.registerComponent}.
4691   *
4692   * @param {string} name
4693   *        The name of the `Component` to register.
4694   *
4695   * @param {Component} ComponentToRegister
4696   *        The `Component` class to register.
4697   *
4698   * @return {Component}
4699   *         The `Component` that was registered.
4700   */
4701
4702
4703  Component.registerComponent = function registerComponent(name, ComponentToRegister) {
4704    if (typeof name !== 'string' || !name) {
4705      throw new Error('Illegal component name, "' + name + '"; must be a non-empty string.');
4706    }
4707
4708    var Tech = Component.getComponent('Tech');
4709
4710    // We need to make sure this check is only done if Tech has been registered.
4711    var isTech = Tech && Tech.isTech(ComponentToRegister);
4712    var isComp = Component === ComponentToRegister || Component.prototype.isPrototypeOf(ComponentToRegister.prototype);
4713
4714    if (isTech || !isComp) {
4715      var reason = void 0;
4716
4717      if (isTech) {
4718        reason = 'techs must be registered using Tech.registerTech()';
4719      } else {
4720        reason = 'must be a Component subclass';
4721      }
4722
4723      throw new Error('Illegal component, "' + name + '"; ' + reason + '.');
4724    }
4725
4726    name = toTitleCase(name);
4727
4728    if (!Component.components_) {
4729      Component.components_ = {};
4730    }
4731
4732    var Player = Component.getComponent('Player');
4733
4734    if (name === 'Player' && Player && Player.players) {
4735      var players = Player.players;
4736      var playerNames = Object.keys(players);
4737
4738      // If we have players that were disposed, then their name will still be
4739      // in Players.players. So, we must loop through and verify that the value
4740      // for each item is not null. This allows registration of the Player component
4741      // after all players have been disposed or before any were created.
4742      if (players && playerNames.length > 0 && playerNames.map(function (pname) {
4743        return players[pname];
4744      }).every(Boolean)) {
4745        throw new Error('Can not register Player component after player has been created.');
4746      }
4747    }
4748
4749    Component.components_[name] = ComponentToRegister;
4750
4751    return ComponentToRegister;
4752  };
4753
4754  /**
4755   * Get a `Component` based on the name it was registered with.
4756   *
4757   * @param {string} name
4758   *        The Name of the component to get.
4759   *
4760   * @return {Component}
4761   *         The `Component` that got registered under the given name.
4762   *
4763   * @deprecated In `videojs` 6 this will not return `Component`s that were not
4764   *             registered using {@link Component.registerComponent}. Currently we
4765   *             check the global `videojs` object for a `Component` name and
4766   *             return that if it exists.
4767   */
4768
4769
4770  Component.getComponent = function getComponent(name) {
4771    if (!name) {
4772      return;
4773    }
4774
4775    name = toTitleCase(name);
4776
4777    if (Component.components_ && Component.components_[name]) {
4778      return Component.components_[name];
4779    }
4780  };
4781
4782  return Component;
4783}();
4784
4785/**
4786 * Whether or not this component supports `requestAnimationFrame`.
4787 *
4788 * This is exposed primarily for testing purposes.
4789 *
4790 * @private
4791 * @type {Boolean}
4792 */
4793
4794
4795Component.prototype.supportsRaf_ = typeof window_1.requestAnimationFrame === 'function' && typeof window_1.cancelAnimationFrame === 'function';
4796
4797Component.registerComponent('Component', Component);
4798
4799/**
4800 * @file time-ranges.js
4801 * @module time-ranges
4802 */
4803
4804/**
4805 * Returns the time for the specified index at the start or end
4806 * of a TimeRange object.
4807 *
4808 * @function time-ranges:indexFunction
4809 *
4810 * @param {number} [index=0]
4811 *        The range number to return the time for.
4812 *
4813 * @return {number}
4814 *         The time that offset at the specified index.
4815 *
4816 * @depricated index must be set to a value, in the future this will throw an error.
4817 */
4818
4819/**
4820 * An object that contains ranges of time for various reasons.
4821 *
4822 * @typedef {Object} TimeRange
4823 *
4824 * @property {number} length
4825 *           The number of time ranges represented by this Object
4826 *
4827 * @property {time-ranges:indexFunction} start
4828 *           Returns the time offset at which a specified time range begins.
4829 *
4830 * @property {time-ranges:indexFunction} end
4831 *           Returns the time offset at which a specified time range begins.
4832 *
4833 * @see https://developer.mozilla.org/en-US/docs/Web/API/TimeRanges
4834 */
4835
vendor: 6,212 bytes, lines 4836-5046
4836/**
4837 * Check if any of the time ranges are over the maximum index.
4838 *
4839 * @param {string} fnName
4840 *        The function name to use for logging
4841 *
4842 * @param {number} index
4843 *        The index to check
4844 *
4845 * @param {number} maxIndex
4846 *        The maximum possible index
4847 *
4848 * @throws {Error} if the timeRanges provided are over the maxIndex
4849 */
4850function rangeCheck(fnName, index, maxIndex) {
4851  if (typeof index !== 'number' || index < 0 || index > maxIndex) {
4852    throw new Error('Failed to execute \'' + fnName + '\' on \'TimeRanges\': The index provided (' + index + ') is non-numeric or out of bounds (0-' + maxIndex + ').');
4853  }
4854}
4855
4856/**
4857 * Check if any of the time ranges are over the maximum index.
4858 *
4859 * @param {string} fnName
4860 *        The function name to use for logging
4861 *
4862 * @param {string} valueIndex
4863 *        The proprety that should be used to get the time. should be 'start' or 'end'
4864 *
4865 * @param {Array} ranges
4866 *        An array of time ranges
4867 *
4868 * @param {Array} [rangeIndex=0]
4869 *        The index to start the search at
4870 *
4871 * @return {number}
4872 *         The time that offset at the specified index.
4873 *
4874 *
4875 * @depricated rangeIndex must be set to a value, in the future this will throw an error.
4876 * @throws {Error} if rangeIndex is more than the length of ranges
4877 */
4878function getRange(fnName, valueIndex, ranges, rangeIndex) {
4879  rangeCheck(fnName, rangeIndex, ranges.length - 1);
4880  return ranges[rangeIndex][valueIndex];
4881}
4882
4883/**
4884 * Create a time range object givent ranges of time.
4885 *
4886 * @param {Array} [ranges]
4887 *        An array of time ranges.
4888 */
4889function createTimeRangesObj(ranges) {
4890  if (ranges === undefined || ranges.length === 0) {
4891    return {
4892      length: 0,
4893      start: function start() {
4894        throw new Error('This TimeRanges object is empty');
4895      },
4896      end: function end() {
4897        throw new Error('This TimeRanges object is empty');
4898      }
4899    };
4900  }
4901  return {
4902    length: ranges.length,
4903    start: getRange.bind(null, 'start', 0, ranges),
4904    end: getRange.bind(null, 'end', 1, ranges)
4905  };
4906}
4907
4908/**
4909 * Should create a fake `TimeRange` object which mimics an HTML5 time range instance.
4910 *
4911 * @param {number|Array} start
4912 *        The start of a single range or an array of ranges
4913 *
4914 * @param {number} end
4915 *        The end of a single range.
4916 *
4917 * @private
4918 */
4919function createTimeRanges(start, end) {
4920  if (Array.isArray(start)) {
4921    return createTimeRangesObj(start);
4922  } else if (start === undefined || end === undefined) {
4923    return createTimeRangesObj();
4924  }
4925  return createTimeRangesObj([[start, end]]);
4926}
4927
4928/**
4929 * @file buffer.js
4930 * @module buffer
4931 */
4932/**
4933 * Compute the percentage of the media that has been buffered.
4934 *
4935 * @param {TimeRange} buffered
4936 *        The current `TimeRange` object representing buffered time ranges
4937 *
4938 * @param {number} duration
4939 *        Total duration of the media
4940 *
4941 * @return {number}
4942 *         Percent buffered of the total duration in decimal form.
4943 */
4944function bufferedPercent(buffered, duration) {
4945  var bufferedDuration = 0;
4946  var start = void 0;
4947  var end = void 0;
4948
4949  if (!duration) {
4950    return 0;
4951  }
4952
4953  if (!buffered || !buffered.length) {
4954    buffered = createTimeRanges(0, 0);
4955  }
4956
4957  for (var i = 0; i < buffered.length; i++) {
4958    start = buffered.start(i);
4959    end = buffered.end(i);
4960
4961    // buffered end can be bigger than duration by a very small fraction
4962    if (end > duration) {
4963      end = duration;
4964    }
4965
4966    bufferedDuration += end - start;
4967  }
4968
4969  return bufferedDuration / duration;
4970}
4971
4972/**
4973 * @file fullscreen-api.js
4974 * @module fullscreen-api
4975 * @private
4976 */
4977/**
4978 * Store the browser-specific methods for the fullscreen API.
4979 *
4980 * @type {Object}
4981 * @see [Specification]{@link https://fullscreen.spec.whatwg.org}
4982 * @see [Map Approach From Screenfull.js]{@link https://github.com/sindresorhus/screenfull.js}
4983 */
4984var FullscreenApi = {};
4985
4986// browser API methods
4987var apiMap = [['requestFullscreen', 'exitFullscreen', 'fullscreenElement', 'fullscreenEnabled', 'fullscreenchange', 'fullscreenerror'],
4988// WebKit
4989['webkitRequestFullscreen', 'webkitExitFullscreen', 'webkitFullscreenElement', 'webkitFullscreenEnabled', 'webkitfullscreenchange', 'webkitfullscreenerror'],
4990// Old WebKit (Safari 5.1)
4991['webkitRequestFullScreen', 'webkitCancelFullScreen', 'webkitCurrentFullScreenElement', 'webkitCancelFullScreen', 'webkitfullscreenchange', 'webkitfullscreenerror'],
4992// Mozilla
4993['mozRequestFullScreen', 'mozCancelFullScreen', 'mozFullScreenElement', 'mozFullScreenEnabled', 'mozfullscreenchange', 'mozfullscreenerror'],
4994// Microsoft
4995['msRequestFullscreen', 'msExitFullscreen', 'msFullscreenElement', 'msFullscreenEnabled', 'MSFullscreenChange', 'MSFullscreenError']];
4996
4997var specApi = apiMap[0];
4998var browserApi = void 0;
4999
5000// determine the supported set of functions
5001for (var i = 0; i < apiMap.length; i++) {
5002  // check for exitFullscreen function
5003  if (apiMap[i][1] in document_1) {
5004    browserApi = apiMap[i];
5005    break;
5006  }
5007}
5008
5009// map the browser API names to the spec API names
5010if (browserApi) {
5011  for (var _i = 0; _i < browserApi.length; _i++) {
5012    FullscreenApi[specApi[_i]] = browserApi[_i];
5013  }
5014}
5015
5016/**
5017 * @file media-error.js
5018 */
5019/**
5020 * A Custom `MediaError` class which mimics the standard HTML5 `MediaError` class.
5021 *
5022 * @param {number|string|Object|MediaError} value
5023 *        This can be of multiple types:
5024 *        - number: should be a standard error code
5025 *        - string: an error message (the code will be 0)
5026 *        - Object: arbitrary properties
5027 *        - `MediaError` (native): used to populate a video.js `MediaError` object
5028 *        - `MediaError` (video.js): will return itself if it's already a
5029 *          video.js `MediaError` object.
5030 *
5031 * @see [MediaError Spec]{@link https://dev.w3.org/html5/spec-author-view/video.html#mediaerror}
5032 * @see [Encrypted MediaError Spec]{@link https://www.w3.org/TR/2013/WD-encrypted-media-20130510/#error-codes}
5033 *
5034 * @class MediaError
5035 */
5036function MediaError(value) {
5037
5038  // Allow redundant calls to this constructor to avoid having `instanceof`
5039  // checks peppered around the code.
5040  if (value instanceof MediaError) {
5041    return value;
5042  }
5043
5044  if (typeof value === 'number') {
5045    this.code = value;
5046  } else if (typeof value === 'string') {
vendor: 4,765 bytes, lines 5047-5211
5047    // default code is zero, so this is a custom error
5048    this.message = value;
5049  } else if (isObject(value)) {
5050
5051    // We assign the `code` property manually because native `MediaError` objects
5052    // do not expose it as an own/enumerable property of the object.
5053    if (typeof value.code === 'number') {
5054      this.code = value.code;
5055    }
5056
5057    assign(this, value);
5058  }
5059
5060  if (!this.message) {
5061    this.message = MediaError.defaultMessages[this.code] || '';
5062  }
5063}
5064
5065/**
5066 * The error code that refers two one of the defined `MediaError` types
5067 *
5068 * @type {Number}
5069 */
5070MediaError.prototype.code = 0;
5071
5072/**
5073 * An optional message that to show with the error. Message is not part of the HTML5
5074 * video spec but allows for more informative custom errors.
5075 *
5076 * @type {String}
5077 */
5078MediaError.prototype.message = '';
5079
5080/**
5081 * An optional status code that can be set by plugins to allow even more detail about
5082 * the error. For example a plugin might provide a specific HTTP status code and an
5083 * error message for that code. Then when the plugin gets that error this class will
5084 * know how to display an error message for it. This allows a custom message to show
5085 * up on the `Player` error overlay.
5086 *
5087 * @type {Array}
5088 */
5089MediaError.prototype.status = null;
5090
5091/**
5092 * Errors indexed by the W3C standard. The order **CANNOT CHANGE**! See the
5093 * specification listed under {@link MediaError} for more information.
5094 *
5095 * @enum {array}
5096 * @readonly
5097 * @property {string} 0 - MEDIA_ERR_CUSTOM
5098 * @property {string} 1 - MEDIA_ERR_CUSTOM
5099 * @property {string} 2 - MEDIA_ERR_ABORTED
5100 * @property {string} 3 - MEDIA_ERR_NETWORK
5101 * @property {string} 4 - MEDIA_ERR_SRC_NOT_SUPPORTED
5102 * @property {string} 5 - MEDIA_ERR_ENCRYPTED
5103 */
5104MediaError.errorTypes = ['MEDIA_ERR_CUSTOM', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED', 'MEDIA_ERR_ENCRYPTED'];
5105
5106/**
5107 * The default `MediaError` messages based on the {@link MediaError.errorTypes}.
5108 *
5109 * @type {Array}
5110 * @constant
5111 */
5112MediaError.defaultMessages = {
5113  1: 'You aborted the media playback',
5114  2: 'A network error caused the media download to fail part-way.',
5115  3: 'The media playback was aborted due to a corruption problem or because the media used features your browser did not support.',
5116  4: 'The media could not be loaded, either because the server or network failed or because the format is not supported.',
5117  5: 'The media is encrypted and we do not have the keys to decrypt it.'
5118};
5119
5120// Add types as properties on MediaError
5121// e.g. MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = 4;
5122for (var errNum = 0; errNum < MediaError.errorTypes.length; errNum++) {
5123  MediaError[MediaError.errorTypes[errNum]] = errNum;
5124  // values should be accessible on both the class and instance
5125  MediaError.prototype[MediaError.errorTypes[errNum]] = errNum;
5126}
5127
5128var tuple = SafeParseTuple;
5129
5130function SafeParseTuple(obj, reviver) {
5131    var json;
5132    var error = null;
5133
5134    try {
5135        json = JSON.parse(obj, reviver);
5136    } catch (err) {
5137        error = err;
5138    }
5139
5140    return [error, json]
5141}
5142
5143/**
5144 * Returns whether an object is `Promise`-like (i.e. has a `then` method).
5145 *
5146 * @param  {Object}  value
5147 *         An object that may or may not be `Promise`-like.
5148 *
5149 * @return {Boolean}
5150 *         Whether or not the object is `Promise`-like.
5151 */
5152function isPromise(value) {
5153  return value !== undefined && typeof value.then === 'function';
5154}
5155
5156/**
5157 * Silence a Promise-like object.
5158 *
5159 * This is useful for avoiding non-harmful, but potentially confusing "uncaught
5160 * play promise" rejection error messages.
5161 *
5162 * @param  {Object} value
5163 *         An object that may or may not be `Promise`-like.
5164 */
5165function silencePromise(value) {
5166  if (isPromise(value)) {
5167    value.then(null, function (e) {});
5168  }
5169}
5170
5171/**
5172 * @file text-track-list-converter.js Utilities for capturing text track state and
5173 * re-creating tracks based on a capture.
5174 *
5175 * @module text-track-list-converter
5176 */
5177
5178/**
5179 * Examine a single {@link TextTrack} and return a JSON-compatible javascript object that
5180 * represents the {@link TextTrack}'s state.
5181 *
5182 * @param {TextTrack} track
5183 *        The text track to query.
5184 *
5185 * @return {Object}
5186 *         A serializable javascript representation of the TextTrack.
5187 * @private
5188 */
5189var trackToJson_ = function trackToJson_(track) {
5190  var ret = ['kind', 'label', 'language', 'id', 'inBandMetadataTrackDispatchType', 'mode', 'src'].reduce(function (acc, prop, i) {
5191
5192    if (track[prop]) {
5193      acc[prop] = track[prop];
5194    }
5195
5196    return acc;
5197  }, {
5198    cues: track.cues && Array.prototype.map.call(track.cues, function (cue) {
5199      return {
5200        startTime: cue.startTime,
5201        endTime: cue.endTime,
5202        text: cue.text,
5203        id: cue.id
5204      };
5205    })
5206  });
5207
5208  return ret;
5209};
5210
5211/**
5212 * Examine a {@link Tech} and return a JSON-compatible javascript array that represents the
5213 * state of all {@link TextTrack}s currently configured. The return array is compatible with
5214 * {@link text-track-list-converter:jsonToTextTracks}.
5215 *
5216 * @param {Tech} tech
5217 *        The tech object to query
5218 *
5219 * @return {Array}
5220 *         A serializable javascript representation of the {@link Tech}s
5221 *         {@link TextTrackList}.
5222 */
5223var textTracksToJson = function textTracksToJson(tech) {
5224
5225  var trackEls = tech.$$('track');
5226
5227  var trackObjs = Array.prototype.map.call(trackEls, function (t) {
5228    return t.track;
5229  });
5230  var tracks = Array.prototype.map.call(trackEls, function (trackEl) {
5231    var json = trackToJson_(trackEl.track);
5232
5233    if (trackEl.src) {
5234      json.src = trackEl.src;
5235    }
5236    return json;
5237  });
5238
5239  return tracks.concat(Array.prototype.filter.call(tech.textTracks(), function (track) {
5240    return trackObjs.indexOf(track) === -1;
5241  }).map(trackToJson_));
5242};
5243
5244/**
5245 * Create a set of remote {@link TextTrack}s on a {@link Tech} based on an array of javascript
5246 * object {@link TextTrack} representations.
5247 *
5248 * @param {Array} json
5249 *        An array of `TextTrack` representation objects, like those that would be
5250 *        produced by `textTracksToJson`.
5251 *
5252 * @param {Tech} tech
5253 *        The `Tech` to create the `TextTrack`s on.
5254 */
5255var jsonToTextTracks = function jsonToTextTracks(json, tech) {
5256  json.forEach(function (track) {
5257    var addedTrack = tech.addRemoteTextTrack(track).track;
5258
5259    if (!track.src && track.cues) {
5260      track.cues.forEach(function (cue) {
5261        return addedTrack.addCue(cue);
5262      });
5263    }
5264  });
5265
5266  return tech.textTracks();
5267};
5268
5269var textTrackConverter = { textTracksToJson: textTracksToJson, jsonToTextTracks: jsonToTextTracks, trackToJson_: trackToJson_ };
5270
5271/**
5272 * @file modal-dialog.js
5273 */
vendor: 12,535 bytes, lines 5273-5732
5273
5274var MODAL_CLASS_NAME = 'vjs-modal-dialog';
5275var ESC = 27;
5276
5277/**
5278 * The `ModalDialog` displays over the video and its controls, which blocks
5279 * interaction with the player until it is closed.
5280 *
5281 * Modal dialogs include a "Close" button and will close when that button
5282 * is activated - or when ESC is pressed anywhere.
5283 *
5284 * @extends Component
5285 */
5286
5287var ModalDialog = function (_Component) {
5288  inherits(ModalDialog, _Component);
5289
5290  /**
5291   * Create an instance of this class.
5292   *
5293   * @param {Player} player
5294   *        The `Player` that this class should be attached to.
5295   *
5296   * @param {Object} [options]
5297   *        The key/value store of player options.
5298   *
5299   * @param {Mixed} [options.content=undefined]
5300   *        Provide customized content for this modal.
5301   *
5302   * @param {string} [options.description]
5303   *        A text description for the modal, primarily for accessibility.
5304   *
5305   * @param {boolean} [options.fillAlways=false]
5306   *        Normally, modals are automatically filled only the first time
5307   *        they open. This tells the modal to refresh its content
5308   *        every time it opens.
5309   *
5310   * @param {string} [options.label]
5311   *        A text label for the modal, primarily for accessibility.
5312   *
5313   * @param {boolean} [options.temporary=true]
5314   *        If `true`, the modal can only be opened once; it will be
5315   *        disposed as soon as it's closed.
5316   *
5317   * @param {boolean} [options.uncloseable=false]
5318   *        If `true`, the user will not be able to close the modal
5319   *        through the UI in the normal ways. Programmatic closing is
5320   *        still possible.
5321   */
5322  function ModalDialog(player, options) {
5323    classCallCheck(this, ModalDialog);
5324
5325    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
5326
5327    _this.opened_ = _this.hasBeenOpened_ = _this.hasBeenFilled_ = false;
5328
5329    _this.closeable(!_this.options_.uncloseable);
5330    _this.content(_this.options_.content);
5331
5332    // Make sure the contentEl is defined AFTER any children are initialized
5333    // because we only want the contents of the modal in the contentEl
5334    // (not the UI elements like the close button).
5335    _this.contentEl_ = createEl('div', {
5336      className: MODAL_CLASS_NAME + '-content'
5337    }, {
5338      role: 'document'
5339    });
5340
5341    _this.descEl_ = createEl('p', {
5342      className: MODAL_CLASS_NAME + '-description vjs-control-text',
5343      id: _this.el().getAttribute('aria-describedby')
5344    });
5345
5346    textContent(_this.descEl_, _this.description());
5347    _this.el_.appendChild(_this.descEl_);
5348    _this.el_.appendChild(_this.contentEl_);
5349    return _this;
5350  }
5351
5352  /**
5353   * Create the `ModalDialog`'s DOM element
5354   *
5355   * @return {Element}
5356   *         The DOM element that gets created.
5357   */
5358
5359
5360  ModalDialog.prototype.createEl = function createEl$$1() {
5361    return _Component.prototype.createEl.call(this, 'div', {
5362      className: this.buildCSSClass(),
5363      tabIndex: -1
5364    }, {
5365      'aria-describedby': this.id() + '_description',
5366      'aria-hidden': 'true',
5367      'aria-label': this.label(),
5368      'role': 'dialog'
5369    });
5370  };
5371
5372  ModalDialog.prototype.dispose = function dispose() {
5373    this.contentEl_ = null;
5374    this.descEl_ = null;
5375    this.previouslyActiveEl_ = null;
5376
5377    _Component.prototype.dispose.call(this);
5378  };
5379
5380  /**
5381   * Builds the default DOM `className`.
5382   *
5383   * @return {string}
5384   *         The DOM `className` for this object.
5385   */
5386
5387
5388  ModalDialog.prototype.buildCSSClass = function buildCSSClass() {
5389    return MODAL_CLASS_NAME + ' vjs-hidden ' + _Component.prototype.buildCSSClass.call(this);
5390  };
5391
5392  /**
5393   * Handles `keydown` events on the document, looking for ESC, which closes
5394   * the modal.
5395   *
5396   * @param {EventTarget~Event} e
5397   *        The keypress that triggered this event.
5398   *
5399   * @listens keydown
5400   */
5401
5402
5403  ModalDialog.prototype.handleKeyPress = function handleKeyPress(e) {
5404    if (e.which === ESC && this.closeable()) {
5405      this.close();
5406    }
5407  };
5408
5409  /**
5410   * Returns the label string for this modal. Primarily used for accessibility.
5411   *
5412   * @return {string}
5413   *         the localized or raw label of this modal.
5414   */
5415
5416
5417  ModalDialog.prototype.label = function label() {
5418    return this.localize(this.options_.label || 'Modal Window');
5419  };
5420
5421  /**
5422   * Returns the description string for this modal. Primarily used for
5423   * accessibility.
5424   *
5425   * @return {string}
5426   *         The localized or raw description of this modal.
5427   */
5428
5429
5430  ModalDialog.prototype.description = function description() {
5431    var desc = this.options_.description || this.localize('This is a modal window.');
5432
5433    // Append a universal closeability message if the modal is closeable.
5434    if (this.closeable()) {
5435      desc += ' ' + this.localize('This modal can be closed by pressing the Escape key or activating the close button.');
5436    }
5437
5438    return desc;
5439  };
5440
5441  /**
5442   * Opens the modal.
5443   *
5444   * @fires ModalDialog#beforemodalopen
5445   * @fires ModalDialog#modalopen
5446   */
5447
5448
5449  ModalDialog.prototype.open = function open() {
5450    if (!this.opened_) {
5451      var player = this.player();
5452
5453      /**
5454        * Fired just before a `ModalDialog` is opened.
5455        *
5456        * @event ModalDialog#beforemodalopen
5457        * @type {EventTarget~Event}
5458        */
5459      this.trigger('beforemodalopen');
5460      this.opened_ = true;
5461
5462      // Fill content if the modal has never opened before and
5463      // never been filled.
5464      if (this.options_.fillAlways || !this.hasBeenOpened_ && !this.hasBeenFilled_) {
5465        this.fill();
5466      }
5467
5468      // If the player was playing, pause it and take note of its previously
5469      // playing state.
5470      this.wasPlaying_ = !player.paused();
5471
5472      if (this.options_.pauseOnOpen && this.wasPlaying_) {
5473        player.pause();
5474      }
5475
5476      if (this.closeable()) {
5477        this.on(this.el_.ownerDocument, 'keydown', bind(this, this.handleKeyPress));
5478      }
5479
5480      // Hide controls and note if they were enabled.
5481      this.hadControls_ = player.controls();
5482      player.controls(false);
5483
5484      this.show();
5485      this.conditionalFocus_();
5486      this.el().setAttribute('aria-hidden', 'false');
5487
5488      /**
5489        * Fired just after a `ModalDialog` is opened.
5490        *
5491        * @event ModalDialog#modalopen
5492        * @type {EventTarget~Event}
5493        */
5494      this.trigger('modalopen');
5495      this.hasBeenOpened_ = true;
5496    }
5497  };
5498
5499  /**
5500   * If the `ModalDialog` is currently open or closed.
5501   *
5502   * @param  {boolean} [value]
5503   *         If given, it will open (`true`) or close (`false`) the modal.
5504   *
5505   * @return {boolean}
5506   *         the current open state of the modaldialog
5507   */
5508
5509
5510  ModalDialog.prototype.opened = function opened(value) {
5511    if (typeof value === 'boolean') {
5512      this[value ? 'open' : 'close']();
5513    }
5514    return this.opened_;
5515  };
5516
5517  /**
5518   * Closes the modal, does nothing if the `ModalDialog` is
5519   * not open.
5520   *
5521   * @fires ModalDialog#beforemodalclose
5522   * @fires ModalDialog#modalclose
5523   */
5524
5525
5526  ModalDialog.prototype.close = function close() {
5527    if (!this.opened_) {
5528      return;
5529    }
5530    var player = this.player();
5531
5532    /**
5533      * Fired just before a `ModalDialog` is closed.
5534      *
5535      * @event ModalDialog#beforemodalclose
5536      * @type {EventTarget~Event}
5537      */
5538    this.trigger('beforemodalclose');
5539    this.opened_ = false;
5540
5541    if (this.wasPlaying_ && this.options_.pauseOnOpen) {
5542      player.play();
5543    }
5544
5545    if (this.closeable()) {
5546      this.off(this.el_.ownerDocument, 'keydown', bind(this, this.handleKeyPress));
5547    }
5548
5549    if (this.hadControls_) {
5550      player.controls(true);
5551    }
5552
5553    this.hide();
5554    this.el().setAttribute('aria-hidden', 'true');
5555
5556    /**
5557      * Fired just after a `ModalDialog` is closed.
5558      *
5559      * @event ModalDialog#modalclose
5560      * @type {EventTarget~Event}
5561      */
5562    this.trigger('modalclose');
5563    this.conditionalBlur_();
5564
5565    if (this.options_.temporary) {
5566      this.dispose();
5567    }
5568  };
5569
5570  /**
5571   * Check to see if the `ModalDialog` is closeable via the UI.
5572   *
5573   * @param  {boolean} [value]
5574   *         If given as a boolean, it will set the `closeable` option.
5575   *
5576   * @return {boolean}
5577   *         Returns the final value of the closable option.
5578   */
5579
5580
5581  ModalDialog.prototype.closeable = function closeable(value) {
5582    if (typeof value === 'boolean') {
5583      var closeable = this.closeable_ = !!value;
5584      var close = this.getChild('closeButton');
5585
5586      // If this is being made closeable and has no close button, add one.
5587      if (closeable && !close) {
5588
5589        // The close button should be a child of the modal - not its
5590        // content element, so temporarily change the content element.
5591        var temp = this.contentEl_;
5592
5593        this.contentEl_ = this.el_;
5594        close = this.addChild('closeButton', { controlText: 'Close Modal Dialog' });
5595        this.contentEl_ = temp;
5596        this.on(close, 'close', this.close);
5597      }
5598
5599      // If this is being made uncloseable and has a close button, remove it.
5600      if (!closeable && close) {
5601        this.off(close, 'close', this.close);
5602        this.removeChild(close);
5603        close.dispose();
5604      }
5605    }
5606    return this.closeable_;
5607  };
5608
5609  /**
5610   * Fill the modal's content element with the modal's "content" option.
5611   * The content element will be emptied before this change takes place.
5612   */
5613
5614
5615  ModalDialog.prototype.fill = function fill() {
5616    this.fillWith(this.content());
5617  };
5618
5619  /**
5620   * Fill the modal's content element with arbitrary content.
5621   * The content element will be emptied before this change takes place.
5622   *
5623   * @fires ModalDialog#beforemodalfill
5624   * @fires ModalDialog#modalfill
5625   *
5626   * @param {Mixed} [content]
5627   *        The same rules apply to this as apply to the `content` option.
5628   */
5629
5630
5631  ModalDialog.prototype.fillWith = function fillWith(content) {
5632    var contentEl = this.contentEl();
5633    var parentEl = contentEl.parentNode;
5634    var nextSiblingEl = contentEl.nextSibling;
5635
5636    /**
5637     * Fired just before a `ModalDialog` is filled with content.
5638     *
5639     * @event ModalDialog#beforemodalfill
5640     * @type {EventTarget~Event}
5641     */
5642    this.trigger('beforemodalfill');
5643    this.hasBeenFilled_ = true;
5644
5645    // Detach the content element from the DOM before performing
5646    // manipulation to avoid modifying the live DOM multiple times.
5647    parentEl.removeChild(contentEl);
5648    this.empty();
5649    insertContent(contentEl, content);
5650    /**
5651     * Fired just after a `ModalDialog` is filled with content.
5652     *
5653     * @event ModalDialog#modalfill
5654     * @type {EventTarget~Event}
5655     */
5656    this.trigger('modalfill');
5657
5658    // Re-inject the re-filled content element.
5659    if (nextSiblingEl) {
5660      parentEl.insertBefore(contentEl, nextSiblingEl);
5661    } else {
5662      parentEl.appendChild(contentEl);
5663    }
5664
5665    // make sure that the close button is last in the dialog DOM
5666    var closeButton = this.getChild('closeButton');
5667
5668    if (closeButton) {
5669      parentEl.appendChild(closeButton.el_);
5670    }
5671  };
5672
5673  /**
5674   * Empties the content element. This happens anytime the modal is filled.
5675   *
5676   * @fires ModalDialog#beforemodalempty
5677   * @fires ModalDialog#modalempty
5678   */
5679
5680
5681  ModalDialog.prototype.empty = function empty() {
5682    /**
5683     * Fired just before a `ModalDialog` is emptied.
5684     *
5685     * @event ModalDialog#beforemodalempty
5686     * @type {EventTarget~Event}
5687     */
5688    this.trigger('beforemodalempty');
5689    emptyEl(this.contentEl());
5690
5691    /**
5692     * Fired just after a `ModalDialog` is emptied.
5693     *
5694     * @event ModalDialog#modalempty
5695     * @type {EventTarget~Event}
5696     */
5697    this.trigger('modalempty');
5698  };
5699
5700  /**
5701   * Gets or sets the modal content, which gets normalized before being
5702   * rendered into the DOM.
5703   *
5704   * This does not update the DOM or fill the modal, but it is called during
5705   * that process.
5706   *
5707   * @param  {Mixed} [value]
5708   *         If defined, sets the internal content value to be used on the
5709   *         next call(s) to `fill`. This value is normalized before being
5710   *         inserted. To "clear" the internal content value, pass `null`.
5711   *
5712   * @return {Mixed}
5713   *         The current content of the modal dialog
5714   */
5715
5716
5717  ModalDialog.prototype.content = function content(value) {
5718    if (typeof value !== 'undefined') {
5719      this.content_ = value;
5720    }
5721    return this.content_;
5722  };
5723
5724  /**
5725   * conditionally focus the modal dialog if focus was previously on the player.
5726   *
5727   * @private
5728   */
5729
5730
5731  ModalDialog.prototype.conditionalFocus_ = function conditionalFocus_() {
5732    var activeEl = 
vendor: 1,619 bytes, lines 5732-5805
5732document_1.activeElement;
5733    var playerEl = this.player_.el_;
5734
5735    this.previouslyActiveEl_ = null;
5736
5737    if (playerEl.contains(activeEl) || playerEl === activeEl) {
5738      this.previouslyActiveEl_ = activeEl;
5739
5740      this.focus();
5741
5742      this.on(document_1, 'keydown', this.handleKeyDown);
5743    }
5744  };
5745
5746  /**
5747   * conditionally blur the element and refocus the last focused element
5748   *
5749   * @private
5750   */
5751
5752
5753  ModalDialog.prototype.conditionalBlur_ = function conditionalBlur_() {
5754    if (this.previouslyActiveEl_) {
5755      this.previouslyActiveEl_.focus();
5756      this.previouslyActiveEl_ = null;
5757    }
5758
5759    this.off(document_1, 'keydown', this.handleKeyDown);
5760  };
5761
5762  /**
5763   * Keydown handler. Attached when modal is focused.
5764   *
5765   * @listens keydown
5766   */
5767
5768
5769  ModalDialog.prototype.handleKeyDown = function handleKeyDown(event) {
5770    // exit early if it isn't a tab key
5771    if (event.which !== 9) {
5772      return;
5773    }
5774
5775    var focusableEls = this.focusableEls_();
5776    var activeEl = this.el_.querySelector(':focus');
5777    var focusIndex = void 0;
5778
5779    for (var i = 0; i < focusableEls.length; i++) {
5780      if (activeEl === focusableEls[i]) {
5781        focusIndex = i;
5782        break;
5783      }
5784    }
5785
5786    if (document_1.activeElement === this.el_) {
5787      focusIndex = 0;
5788    }
5789
5790    if (event.shiftKey && focusIndex === 0) {
5791      focusableEls[focusableEls.length - 1].focus();
5792      event.preventDefault();
5793    } else if (!event.shiftKey && focusIndex === focusableEls.length - 1) {
5794      focusableEls[0].focus();
5795      event.preventDefault();
5796    }
5797  };
5798
5799  /**
5800   * get all focusable elements
5801   *
5802   * @private
5803   */
5804
5805
vendor: 6,242 bytes, lines 5806-6059
5806  ModalDialog.prototype.focusableEls_ = function focusableEls_() {
5807    var allChildren = this.el_.querySelectorAll('*');
5808
5809    return Array.prototype.filter.call(allChildren, function (child) {
5810      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');
5811    });
5812  };
5813
5814  return ModalDialog;
5815}(Component);
5816
5817/**
5818 * Default options for `ModalDialog` default options.
5819 *
5820 * @type {Object}
5821 * @private
5822 */
5823
5824
5825ModalDialog.prototype.options_ = {
5826  pauseOnOpen: true,
5827  temporary: true
5828};
5829
5830Component.registerComponent('ModalDialog', ModalDialog);
5831
5832/**
5833 * @file track-list.js
5834 */
5835/**
5836 * Common functionaliy between {@link TextTrackList}, {@link AudioTrackList}, and
5837 * {@link VideoTrackList}
5838 *
5839 * @extends EventTarget
5840 */
5841
5842var TrackList = function (_EventTarget) {
5843  inherits(TrackList, _EventTarget);
5844
5845  /**
5846   * Create an instance of this class
5847   *
5848   * @param {Track[]} tracks
5849   *        A list of tracks to initialize the list with.
5850   *
5851   * @param {Object} [list]
5852   *        The child object with inheritance done manually for ie8.
5853   *
5854   * @abstract
5855   */
5856  function TrackList() {
5857    var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
5858
5859    var _ret;
5860
5861    var list = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
5862    classCallCheck(this, TrackList);
5863
5864    var _this = possibleConstructorReturn(this, _EventTarget.call(this));
5865
5866    if (!list) {
5867      list = _this; // eslint-disable-line
5868      if (IS_IE8) {
5869        list = document_1.createElement('custom');
5870        for (var prop in TrackList.prototype) {
5871          if (prop !== 'constructor') {
5872            list[prop] = TrackList.prototype[prop];
5873          }
5874        }
5875      }
5876    }
5877
5878    list.tracks_ = [];
5879
5880    /**
5881     * @memberof TrackList
5882     * @member {number} length
5883     *         The current number of `Track`s in the this Trackist.
5884     * @instance
5885     */
5886    Object.defineProperty(list, 'length', {
5887      get: function get$$1() {
5888        return this.tracks_.length;
5889      }
5890    });
5891
5892    for (var i = 0; i < tracks.length; i++) {
5893      list.addTrack(tracks[i]);
5894    }
5895
5896    // must return the object, as for ie8 it will not be this
5897    // but a reference to a document object
5898    return _ret = list, possibleConstructorReturn(_this, _ret);
5899  }
5900
5901  /**
5902   * Add a {@link Track} to the `TrackList`
5903   *
5904   * @param {Track} track
5905   *        The audio, video, or text track to add to the list.
5906   *
5907   * @fires TrackList#addtrack
5908   */
5909
5910
5911  TrackList.prototype.addTrack = function addTrack(track) {
5912    var index = this.tracks_.length;
5913
5914    if (!('' + index in this)) {
5915      Object.defineProperty(this, index, {
5916        get: function get$$1() {
5917          return this.tracks_[index];
5918        }
5919      });
5920    }
5921
5922    // Do not add duplicate tracks
5923    if (this.tracks_.indexOf(track) === -1) {
5924      this.tracks_.push(track);
5925      /**
5926       * Triggered when a track is added to a track list.
5927       *
5928       * @event TrackList#addtrack
5929       * @type {EventTarget~Event}
5930       * @property {Track} track
5931       *           A reference to track that was added.
5932       */
5933      this.trigger({
5934        track: track,
5935        type: 'addtrack'
5936      });
5937    }
5938  };
5939
5940  /**
5941   * Remove a {@link Track} from the `TrackList`
5942   *
5943   * @param {Track} rtrack
5944   *        The audio, video, or text track to remove from the list.
5945   *
5946   * @fires TrackList#removetrack
5947   */
5948
5949
5950  TrackList.prototype.removeTrack = function removeTrack(rtrack) {
5951    var track = void 0;
5952
5953    for (var i = 0, l = this.length; i < l; i++) {
5954      if (this[i] === rtrack) {
5955        track = this[i];
5956        if (track.off) {
5957          track.off();
5958        }
5959
5960        this.tracks_.splice(i, 1);
5961
5962        break;
5963      }
5964    }
5965
5966    if (!track) {
5967      return;
5968    }
5969
5970    /**
5971     * Triggered when a track is removed from track list.
5972     *
5973     * @event TrackList#removetrack
5974     * @type {EventTarget~Event}
5975     * @property {Track} track
5976     *           A reference to track that was removed.
5977     */
5978    this.trigger({
5979      track: track,
5980      type: 'removetrack'
5981    });
5982  };
5983
5984  /**
5985   * Get a Track from the TrackList by a tracks id
5986   *
5987   * @param {String} id - the id of the track to get
5988   * @method getTrackById
5989   * @return {Track}
5990   * @private
5991   */
5992
5993
5994  TrackList.prototype.getTrackById = function getTrackById(id) {
5995    var result = null;
5996
5997    for (var i = 0, l = this.length; i < l; i++) {
5998      var track = this[i];
5999
6000      if (track.id === id) {
6001        result = track;
6002        break;
6003      }
6004    }
6005
6006    return result;
6007  };
6008
6009  return TrackList;
6010}(EventTarget);
6011
6012/**
6013 * Triggered when a different track is selected/enabled.
6014 *
6015 * @event TrackList#change
6016 * @type {EventTarget~Event}
6017 */
6018
6019/**
6020 * Events that can be called with on + eventName. See {@link EventHandler}.
6021 *
6022 * @property {Object} TrackList#allowedEvents_
6023 * @private
6024 */
6025
6026
6027TrackList.prototype.allowedEvents_ = {
6028  change: 'change',
6029  addtrack: 'addtrack',
6030  removetrack: 'removetrack'
6031};
6032
6033// emulate attribute EventHandler support to allow for feature detection
6034for (var event in TrackList.prototype.allowedEvents_) {
6035  TrackList.prototype['on' + event] = null;
6036}
6037
6038/**
6039 * @file audio-track-list.js
6040 */
6041/**
6042 * Anywhere we call this function we diverge from the spec
6043 * as we only support one enabled audiotrack at a time
6044 *
6045 * @param {AudioTrackList} list
6046 *        list to work on
6047 *
6048 * @param {AudioTrack} track
6049 *        The track to skip
6050 *
6051 * @private
6052 */
6053var disableOthers = function disableOthers(list, track) {
6054  for (var i = 0; i < list.length; i++) {
6055    if (!Object.keys(list[i]).length || track.id === list[i].id) {
6056      continue;
6057    }
6058    // another audio track is enabled, disable it
6059    list[i].enabled = false;
6060  }
6061};
6062
6063/**
6064 * The current list of {@link AudioTrack} for a media file.
6065 *
6066 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist}
6067 * @extends TrackList
6068 */
6069
6070var AudioTrackList = function (_TrackList) {
6071  inherits(AudioTrackList, _TrackList);
6072
6073  /**
6074   * Create an instance of this class.
6075   *
6076   * @param {AudioTrack[]} [tracks=[]]
6077   *        A list of `AudioTrack` to instantiate the list with.
6078   */
6079  function AudioTrackList() {
6080    var _this, _ret;
6081
6082    var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
6083    classCallCheck(this, AudioTrackList);
6084
6085    var list = void 0;
6086
6087    // make sure only 1 track is enabled
6088    // sorted from last index to first index
6089    for (var i = tracks.length - 1; i >= 0; i--) {
6090      if (tracks[i].enabled) {
6091        disableOthers(tracks, tracks[i]);
6092        break;
6093      }
6094    }
6095
6096    // IE8 forces us to implement inheritance ourselves
6097    // as it does not support Object.defineProperty properly
6098    if (IS_IE8) {
6099      list = document_1.createElement('custom');
6100      for (var prop in TrackList.prototype) {
6101        if (prop !== 'constructor') {
6102          list[prop] = TrackList.prototype[prop];
6103        }
6104      }
6105      for (var _prop in AudioTrackList.prototype) {
6106        if (_prop !== 'constructor') {
6107          list[_prop] = AudioTrackList.prototype[_prop];
6108        }
6109      }
6110    }
6111
6112    list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
6113    list.changing_ = false;
6114
6115    return _ret = list, possibleConstructorReturn(_this, _ret);
6116  }
6117
6118  /**
6119   * Add an {@link AudioTrack} to the `AudioTrackList`.
6120   *
6121   * @param {AudioTrack} track
6122   *        The AudioTrack to add to the list
6123   *
6124   * @fires TrackList#addtrack
6125   */
6126
6127
6128  AudioTrackList.prototype.addTrack = function addTrack(track) {
6129    var _this2 = this;
6130
6131    if (track.enabled) {
6132      disableOthers(this, track);
6133    }
6134
6135    _TrackList.prototype.addTrack.call(this, track);
6136    // native tracks don't have this
6137    if (!track.addEventListener) {
6138      return;
6139    }
6140
6141    /**
6142     * @listens AudioTrack#enabledchange
6143     * @fires TrackList#change
6144     */
6145    track.addEventListener('enabledchange', function () {
6146      // when we are disabling other tracks (since we don't support
6147      // more than one track at a time) we will set changing_
6148      // to true so that we don't trigger additional change events
6149      if (_this2.changing_) {
6150        return;
6151      }
6152      _this2.changing_ = true;
6153      disableOthers(_this2, track);
6154      _this2.changing_ = false;
6155      _this2.trigger('change');
6156    });
6157  };
6158
6159  return AudioTrackList;
6160}(TrackList);
6161
6162/**
6163 * @file video-track-list.js
6164 */
6165/**
6166 * Un-select all other {@link VideoTrack}s that are selected.
6167 *
6168 * @param {VideoTrackList} list
6169 *        list to work on
6170 *
6171 * @param {VideoTrack} track
6172 *        The track to skip
6173 *
6174 * @private
6175 */
6176var disableOthers$1 = function disableOthers(list, track) {
6177  for (var i = 0; i < list.length; i++) {
6178    if (!Object.keys(list[i]).length || track.id === list[i].id) {
6179      continue;
6180    }
6181    // another video track is enabled, disable it
6182    list[i].selected = false;
6183  }
6184};
6185
6186/**
6187 * The current list of {@link VideoTrack} for a video.
6188 *
6189 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist}
6190 * @extends TrackList
6191 */
6192
6193var VideoTrackList = function (_TrackList) {
6194  inherits(VideoTrackList, _TrackList);
6195
6196  /**
6197   * Create an instance of this class.
6198   *
6199   * @param {VideoTrack[]} [tracks=[]]
6200   *        A list of `VideoTrack` to instantiate the list with.
6201   */
6202  function VideoTrackList() {
6203    var _this, _ret;
6204
6205    var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
6206    classCallCheck(this, VideoTrackList);
6207
6208    var list = void 0;
6209
6210    // make sure only 1 track is enabled
6211    // sorted from last index to first index
6212    for (var i = tracks.length - 1; i >= 0; i--) {
6213      if (tracks[i].selected) {
6214        disableOthers$1(tracks, tracks[i]);
6215        break;
6216      }
6217    }
6218
6219    // IE8 forces us to implement inheritance ourselves
6220    // as it does not support Object.defineProperty properly
6221    if (IS_IE8) {
6222      list = document_1.createElement('custom');
6223      for (var prop in TrackList.prototype) {
6224        if (prop !== 'constructor') {
6225          list[prop] = TrackList.prototype[prop];
6226        }
6227      }
6228      for (var _prop in VideoTrackList.prototype) {
6229        if (_prop !== 'constructor') {
6230          list[_prop] = VideoTrackList.prototype[_prop];
6231        }
6232      }
6233    }
6234
6235    list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
6236    list.changing_ = false;
6237
6238    /**
6239     * @member {number} VideoTrackList#selectedIndex
6240     *         The current index of the selected {@link VideoTrack`}.
6241     */
6242    Object.defineProperty(list, 'selectedIndex', {
6243      get: function get$$1() {
6244        for (var _i = 0; _i < this.length; _i++) {
6245          if (this[_i].selected) {
6246            return _i;
6247          }
6248        }
6249        return -1;
6250      },
6251      set: function set$$1() {}
6252    });
6253
6254    return _ret = list, possibleConstructorReturn(_this, _ret);
6255  }
6256
6257  /**
6258   * Add a {@link VideoTrack} to the `VideoTrackList`.
6259   *
6260   * @param {VideoTrack} track
6261   *        The VideoTrack to add to the list
6262   *
6263   * @fires TrackList#addtrack
6264   */
6265
6266
6267  VideoTrackList.prototype.addTrack = function addTrack(track) {
6268    var _this2 = this;
6269
6270    if (track.selected) {
6271      disableOthers$1(this, track);
6272    }
6273
6274    _TrackList.prototype.addTrack.call(this, track);
6275    // native tracks don't have this
6276    if (!track.addEventListener) {
6277      return;
6278    }
6279
6280    /**
6281     * @listens VideoTrack#selectedchange
6282     * @fires TrackList#change
6283     */
6284    track.addEventListener('selectedchange', function () {
6285      if (_this2.changing_) {
6286        return;
6287      }
6288      _this2.changing_ = true;
6289      disableOthers$1(_this2, track);
6290      _this2.changing_ = false;
6291      _this2.trigger('change');
6292    });
6293  };
6294
6295  return VideoTrackList;
6296}(TrackList);
6297
6298/**
6299 * @file text-track-list.js
6300 */
6301/**
6302 * The current list of {@link TextTrack} for a media file.
6303 *
6304 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist}
6305 * @extends TrackList
6306 */
6307
6308var TextTrackList = function (_TrackList) {
6309  inherits(TextTrackList, _TrackList);
6310
6311  /**
6312   * Create an instance of this class.
6313   *
6314   * @param {TextTrack[]} [tracks=[]]
6315   *        A list of `TextTrack` to instantiate the list with.
6316   */
6317  function TextTrackList() {
6318    var _this, _ret;
6319
6320    var tracks = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
6321    classCallCheck(this, TextTrackList);
6322
6323    var list = void 0;
6324
6325    // IE8 forces us to implement inheritance ourselves
6326    // as it does not support Object.defineProperty properly
6327    if (IS_IE8) {
6328      list = document_1.createElement('custom');
6329      for (var prop in TrackList.prototype) {
6330        if (prop !== 'constructor') {
6331          list[prop] = TrackList.prototype[prop];
6332        }
6333      }
6334      for (var _prop in TextTrackList.prototype) {
6335        if (_prop !== 'constructor') {
6336          list[_prop] = TextTrackList.prototype[_prop];
6337        }
6338      }
6339    }
6340
vendor: 7,767 bytes, lines 6341-6675
6341    list = (_this = possibleConstructorReturn(this, _TrackList.call(this, tracks, list)), _this);
6342    return _ret = list, possibleConstructorReturn(_this, _ret);
6343  }
6344
6345  /**
6346   * Add a {@link TextTrack} to the `TextTrackList`
6347   *
6348   * @param {TextTrack} track
6349   *        The text track to add to the list.
6350   *
6351   * @fires TrackList#addtrack
6352   */
6353
6354
6355  TextTrackList.prototype.addTrack = function addTrack(track) {
6356    _TrackList.prototype.addTrack.call(this, track);
6357
6358    /**
6359     * @listens TextTrack#modechange
6360     * @fires TrackList#change
6361     */
6362    track.addEventListener('modechange', bind(this, function () {
6363      this.trigger('change');
6364    }));
6365
6366    var nonLanguageTextTrackKind = ['metadata', 'chapters'];
6367
6368    if (nonLanguageTextTrackKind.indexOf(track.kind) === -1) {
6369      track.addEventListener('modechange', bind(this, function () {
6370        this.trigger('selectedlanguagechange');
6371      }));
6372    }
6373  };
6374
6375  return TextTrackList;
6376}(TrackList);
6377
6378/**
6379 * @file html-track-element-list.js
6380 */
6381
6382/**
6383 * The current list of {@link HtmlTrackElement}s.
6384 */
6385
6386var HtmlTrackElementList = function () {
6387
6388  /**
6389   * Create an instance of this class.
6390   *
6391   * @param {HtmlTrackElement[]} [tracks=[]]
6392   *        A list of `HtmlTrackElement` to instantiate the list with.
6393   */
6394  function HtmlTrackElementList() {
6395    var trackElements = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
6396    classCallCheck(this, HtmlTrackElementList);
6397
6398    var list = this; // eslint-disable-line
6399
6400    if (IS_IE8) {
6401      list = document_1.createElement('custom');
6402
6403      for (var prop in HtmlTrackElementList.prototype) {
6404        if (prop !== 'constructor') {
6405          list[prop] = HtmlTrackElementList.prototype[prop];
6406        }
6407      }
6408    }
6409
6410    list.trackElements_ = [];
6411
6412    /**
6413     * @memberof HtmlTrackElementList
6414     * @member {number} length
6415     *         The current number of `Track`s in the this Trackist.
6416     * @instance
6417     */
6418    Object.defineProperty(list, 'length', {
6419      get: function get$$1() {
6420        return this.trackElements_.length;
6421      }
6422    });
6423
6424    for (var i = 0, length = trackElements.length; i < length; i++) {
6425      list.addTrackElement_(trackElements[i]);
6426    }
6427
6428    if (IS_IE8) {
6429      return list;
6430    }
6431  }
6432
6433  /**
6434   * Add an {@link HtmlTrackElement} to the `HtmlTrackElementList`
6435   *
6436   * @param {HtmlTrackElement} trackElement
6437   *        The track element to add to the list.
6438   *
6439   * @private
6440   */
6441
6442
6443  HtmlTrackElementList.prototype.addTrackElement_ = function addTrackElement_(trackElement) {
6444    var index = this.trackElements_.length;
6445
6446    if (!('' + index in this)) {
6447      Object.defineProperty(this, index, {
6448        get: function get$$1() {
6449          return this.trackElements_[index];
6450        }
6451      });
6452    }
6453
6454    // Do not add duplicate elements
6455    if (this.trackElements_.indexOf(trackElement) === -1) {
6456      this.trackElements_.push(trackElement);
6457    }
6458  };
6459
6460  /**
6461   * Get an {@link HtmlTrackElement} from the `HtmlTrackElementList` given an
6462   * {@link TextTrack}.
6463   *
6464   * @param {TextTrack} track
6465   *        The track associated with a track element.
6466   *
6467   * @return {HtmlTrackElement|undefined}
6468   *         The track element that was found or undefined.
6469   *
6470   * @private
6471   */
6472
6473
6474  HtmlTrackElementList.prototype.getTrackElementByTrack_ = function getTrackElementByTrack_(track) {
6475    var trackElement_ = void 0;
6476
6477    for (var i = 0, length = this.trackElements_.length; i < length; i++) {
6478      if (track === this.trackElements_[i].track) {
6479        trackElement_ = this.trackElements_[i];
6480
6481        break;
6482      }
6483    }
6484
6485    return trackElement_;
6486  };
6487
6488  /**
6489   * Remove a {@link HtmlTrackElement} from the `HtmlTrackElementList`
6490   *
6491   * @param {HtmlTrackElement} trackElement
6492   *        The track element to remove from the list.
6493   *
6494   * @private
6495   */
6496
6497
6498  HtmlTrackElementList.prototype.removeTrackElement_ = function removeTrackElement_(trackElement) {
6499    for (var i = 0, length = this.trackElements_.length; i < length; i++) {
6500      if (trackElement === this.trackElements_[i]) {
6501        this.trackElements_.splice(i, 1);
6502
6503        break;
6504      }
6505    }
6506  };
6507
6508  return HtmlTrackElementList;
6509}();
6510
6511/**
6512 * @file text-track-cue-list.js
6513 */
6514/**
6515 * @typedef {Object} TextTrackCueList~TextTrackCue
6516 *
6517 * @property {string} id
6518 *           The unique id for this text track cue
6519 *
6520 * @property {number} startTime
6521 *           The start time for this text track cue
6522 *
6523 * @property {number} endTime
6524 *           The end time for this text track cue
6525 *
6526 * @property {boolean} pauseOnExit
6527 *           Pause when the end time is reached if true.
6528 *
6529 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcue}
6530 */
6531
6532/**
6533 * A List of TextTrackCues.
6534 *
6535 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist}
6536 */
6537
6538var TextTrackCueList = function () {
6539
6540  /**
6541   * Create an instance of this class..
6542   *
6543   * @param {Array} cues
6544   *        A list of cues to be initialized with
6545   */
6546  function TextTrackCueList(cues) {
6547    classCallCheck(this, TextTrackCueList);
6548
6549    var list = this; // eslint-disable-line
6550
6551    if (IS_IE8) {
6552      list = document_1.createElement('custom');
6553
6554      for (var prop in TextTrackCueList.prototype) {
6555        if (prop !== 'constructor') {
6556          list[prop] = TextTrackCueList.prototype[prop];
6557        }
6558      }
6559    }
6560
6561    TextTrackCueList.prototype.setCues_.call(list, cues);
6562
6563    /**
6564     * @memberof TextTrackCueList
6565     * @member {number} length
6566     *         The current number of `TextTrackCue`s in the TextTrackCueList.
6567     * @instance
6568     */
6569    Object.defineProperty(list, 'length', {
6570      get: function get$$1() {
6571        return this.length_;
6572      }
6573    });
6574
6575    if (IS_IE8) {
6576      return list;
6577    }
6578  }
6579
6580  /**
6581   * A setter for cues in this list. Creates getters
6582   * an an index for the cues.
6583   *
6584   * @param {Array} cues
6585   *        An array of cues to set
6586   *
6587   * @private
6588   */
6589
6590
6591  TextTrackCueList.prototype.setCues_ = function setCues_(cues) {
6592    var oldLength = this.length || 0;
6593    var i = 0;
6594    var l = cues.length;
6595
6596    this.cues_ = cues;
6597    this.length_ = cues.length;
6598
6599    var defineProp = function defineProp(index) {
6600      if (!('' + index in this)) {
6601        Object.defineProperty(this, '' + index, {
6602          get: function get$$1() {
6603            return this.cues_[index];
6604          }
6605        });
6606      }
6607    };
6608
6609    if (oldLength < l) {
6610      i = oldLength;
6611
6612      for (; i < l; i++) {
6613        defineProp.call(this, i);
6614      }
6615    }
6616  };
6617
6618  /**
6619   * Get a `TextTrackCue` that is currently in the `TextTrackCueList` by id.
6620   *
6621   * @param {string} id
6622   *        The id of the cue that should be searched for.
6623   *
6624   * @return {TextTrackCueList~TextTrackCue|null}
6625   *         A single cue or null if none was found.
6626   */
6627
6628
6629  TextTrackCueList.prototype.getCueById = function getCueById(id) {
6630    var result = null;
6631
6632    for (var i = 0, l = this.length; i < l; i++) {
6633      var cue = this[i];
6634
6635      if (cue.id === id) {
6636        result = cue;
6637        break;
6638      }
6639    }
6640
6641    return result;
6642  };
6643
6644  return TextTrackCueList;
6645}();
6646
6647/**
6648 * @file track-kinds.js
6649 */
6650
6651/**
6652 * All possible `VideoTrackKind`s
6653 *
6654 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-videotrack-kind
6655 * @typedef VideoTrack~Kind
6656 * @enum
6657 */
6658var VideoTrackKind = {
6659  alternative: 'alternative',
6660  captions: 'captions',
6661  main: 'main',
6662  sign: 'sign',
6663  subtitles: 'subtitles',
6664  commentary: 'commentary'
6665};
6666
6667/**
6668 * All possible `AudioTrackKind`s
6669 *
6670 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-audiotrack-kind
6671 * @typedef AudioTrack~Kind
6672 * @enum
6673 */
6674var AudioTrackKind = {
6675  'alternative': 'alternative',
6676  'descriptions': 'descriptions',
6677  'main': 'main',
6678  'main-desc': 'main-desc',
6679  'translation': 'translation',
6680  'commentary': 'commentary'
6681};
6682
6683/**
6684 * All possible `TextTrackKind`s
6685 *
6686 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-texttrack-kind
6687 * @typedef TextTrack~Kind
6688 * @enum
6689 */
6690var TextTrackKind = {
6691  subtitles: 'subtitles',
6692  captions: 'captions',
6693  descriptions: 'descriptions',
6694  chapters: 'chapters',
6695  metadata: 'metadata'
6696};
6697
6698/**
6699 * All possible `TextTrackMode`s
6700 *
6701 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
6702 * @typedef TextTrack~Mode
6703 * @enum
6704 */
6705var TextTrackMode = {
6706  disabled: 'disabled',
6707  hidden: 'hidden',
6708  showing: 'showing'
6709};
6710
6711/**
6712 * @file track.js
6713 */
6714/**
6715 * A Track class that contains all of the common functionality for {@link AudioTrack},
6716 * {@link VideoTrack}, and {@link TextTrack}.
6717 *
6718 * > Note: This class should not be used directly
6719 *
6720 * @see {@link https://html.spec.whatwg.org/multipage/embedded-content.html}
6721 * @extends EventTarget
6722 * @abstract
6723 */
6724
6725var Track = function (_EventTarget) {
6726  inherits(Track, _EventTarget);
6727
6728  /**
6729   * Create an instance of this class.
6730   *
6731   * @param {Object} [options={}]
6732   *        Object of option names and values
6733   *
6734   * @param {string} [options.kind='']
6735   *        A valid kind for the track type you are creating.
6736   *
6737   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
6738   *        A unique id for this AudioTrack.
6739   *
6740   * @param {string} [options.label='']
6741   *        The menu label for this track.
6742   *
6743   * @param {string} [options.language='']
6744   *        A valid two character language code.
6745   *
6746   * @abstract
6747   */
6748  function Track() {
6749    var _ret;
6750
6751    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
6752    classCallCheck(this, Track);
6753
6754    var _this = possibleConstructorReturn(this, _EventTarget.call(this));
6755
6756    var track = _this; // eslint-disable-line
6757
6758    if (IS_IE8) {
6759      track = document_1.createElement('custom');
6760      for (var prop in Track.prototype) {
6761        if (prop !== 'constructor') {
6762          track[prop] = Track.prototype[prop];
6763        }
6764      }
6765    }
6766
6767    var trackProps = {
6768      id: options.id || 'vjs_track_' + newGUID(),
6769      kind: options.kind || '',
6770      label: options.label || '',
6771      language: options.language || ''
6772    };
6773
6774    /**
6775     * @memberof Track
6776     * @member {string} id
6777     *         The id of this track. Cannot be changed after creation.
6778     * @instance
6779     *
6780     * @readonly
6781     */
6782
6783    /**
6784     * @memberof Track
6785     * @member {string} kind
6786     *         The kind of track that this is. Cannot be changed after creation.
6787     * @instance
6788     *
6789     * @readonly
6790     */
6791
6792    /**
6793     * @memberof Track
6794     * @member {string} label
6795     *         The label of this track. Cannot be changed after creation.
6796     * @instance
6797     *
6798     * @readonly
6799     */
6800
6801    /**
6802     * @memberof Track
6803     * @member {string} language
6804     *         The two letter language code for this track. Cannot be changed after
6805     *         creation.
6806     * @instance
6807     *
6808     * @readonly
6809     */
6810
6811    var _loop = function _loop(key) {
6812      Object.defineProperty(track, key, {
6813        get: function get$$1() {
6814          return trackProps[key];
6815        },
6816        set: function set$$1() {}
6817      });
6818    };
6819
6820    for (var key in trackProps) {
6821      _loop(key);
6822    }
6823
6824    return _ret = track, possibleConstructorReturn(_this, _ret);
6825  }
6826
6827  return Track;
6828}(EventTarget);
6829
6830/**
6831 * @file url.js
6832 * @module url
6833 */
6834/**
6835 * @typedef {Object} url:URLObject
6836 *
6837 * @property {string} protocol
6838 *           The protocol of the url that was parsed.
6839 *
6840 * @property {string} hostname
6841 *           The hostname of the url that was parsed.
6842 *
6843 * @property {string} port
6844 *           The port of the url that was parsed.
6845 *
6846 * @property {string} pathname
6847 *           The pathname of the url that was parsed.
6848 *
6849 * @property {string} search
6850 *           The search query of the url that was parsed.
6851 *
6852 * @property {string} hash
6853 *           The hash of the url that was parsed.
6854 *
6855 * @property {string} host
6856 *           The host of the url that was parsed.
6857 */
6858
6859/**
6860 * Resolve and parse the elements of a URL.
6861 *
6862 * @param  {String} url
6863 *         The url to parse
6864 *
6865 * @return {url:URLObject}
6866 *         An object of url details
6867 */
6868var parseUrl = function parseUrl(url) {
6869  var props = ['protocol', 'hostname', 'port', 'pathname', 'search', 'hash', 'host'];
6870
6871  // add the url to an anchor and let the browser parse the URL
6872  var a = document_1.createElement('a');
6873
6874  a.href = url;
6875
6876  // IE8 (and 9?) Fix
vendor: 4,164 bytes, lines 6877-7031
6877  // ie8 doesn't parse the URL correctly until the anchor is actually
6878  // added to the body, and an innerHTML is needed to trigger the parsing
6879  var addToBody = a.host === '' && a.protocol !== 'file:';
6880  var div = void 0;
6881
6882  if (addToBody) {
6883    div = document_1.createElement('div');
6884    div.innerHTML = '<a href="' + url + '"></a>';
6885    a = div.firstChild;
6886    // prevent the div from affecting layout
6887    div.setAttribute('style', 'display:none; position:absolute;');
6888    document_1.body.appendChild(div);
6889  }
6890
6891  // Copy the specific URL properties to a new object
6892  // This is also needed for IE8 because the anchor loses its
6893  // properties when it's removed from the dom
6894  var details = {};
6895
6896  for (var i = 0; i < props.length; i++) {
6897    details[props[i]] = a[props[i]];
6898  }
6899
6900  // IE9 adds the port to the host property unlike everyone else. If
6901  // a port identifier is added for standard ports, strip it.
6902  if (details.protocol === 'http:') {
6903    details.host = details.host.replace(/:80$/, '');
6904  }
6905
6906  if (details.protocol === 'https:') {
6907    details.host = details.host.replace(/:443$/, '');
6908  }
6909
6910  if (!details.protocol) {
6911    details.protocol = window_1.location.protocol;
6912  }
6913
6914  if (addToBody) {
6915    document_1.body.removeChild(div);
6916  }
6917
6918  return details;
6919};
6920
6921/**
6922 * Get absolute version of relative URL. Used to tell flash correct URL.
6923 *
6924 *
6925 * @param  {string} url
6926 *         URL to make absolute
6927 *
6928 * @return {string}
6929 *         Absolute URL
6930 *
6931 * @see http://stackoverflow.com/questions/470832/getting-an-absolute-url-from-a-relative-one-ie6-issue
6932 */
6933var getAbsoluteURL = function getAbsoluteURL(url) {
6934  // Check if absolute URL
6935  if (!url.match(/^https?:\/\//)) {
6936    // Convert to absolute URL. Flash hosted off-site needs an absolute URL.
6937    var div = document_1.createElement('div');
6938
6939    div.innerHTML = '<a href="' + url + '">x</a>';
6940    url = div.firstChild.href;
6941  }
6942
6943  return url;
6944};
6945
6946/**
6947 * Returns the extension of the passed file name. It will return an empty string
6948 * if passed an invalid path.
6949 *
6950 * @param {string} path
6951 *        The fileName path like '/path/to/file.mp4'
6952 *
6953 * @returns {string}
6954 *          The extension in lower case or an empty string if no
6955 *          extension could be found.
6956 */
6957var getFileExtension = function getFileExtension(path) {
6958  if (typeof path === 'string') {
6959    var splitPathRe = /^(\/?)([\s\S]*?)((?:\.{1,2}|[^\/]+?)(\.([^\.\/\?]+)))(?:[\/]*|[\?].*)$/i;
6960    var pathParts = splitPathRe.exec(path);
6961
6962    if (pathParts) {
6963      return pathParts.pop().toLowerCase();
6964    }
6965  }
6966
6967  return '';
6968};
6969
6970/**
6971 * Returns whether the url passed is a cross domain request or not.
6972 *
6973 * @param {string} url
6974 *        The url to check.
6975 *
6976 * @return {boolean}
6977 *         Whether it is a cross domain request or not.
6978 */
6979var isCrossOrigin = function isCrossOrigin(url) {
6980  var winLoc = window_1.location;
6981  var urlInfo = parseUrl(url);
6982
6983  // IE8 protocol relative urls will return ':' for protocol
6984  var srcProtocol = urlInfo.protocol === ':' ? winLoc.protocol : urlInfo.protocol;
6985
6986  // Check if url is for another domain/origin
6987  // IE8 doesn't know location.origin, so we won't rely on it here
6988  var crossOrigin = srcProtocol + urlInfo.host !== winLoc.protocol + winLoc.host;
6989
6990  return crossOrigin;
6991};
6992
6993var Url = (Object.freeze || Object)({
6994	parseUrl: parseUrl,
6995	getAbsoluteURL: getAbsoluteURL,
6996	getFileExtension: getFileExtension,
6997	isCrossOrigin: isCrossOrigin
6998});
6999
7000var isFunction_1 = isFunction;
7001
7002var toString$1 = Object.prototype.toString;
7003
7004function isFunction (fn) {
7005  var string = toString$1.call(fn);
7006  return string === '[object Function]' ||
7007    (typeof fn === 'function' && string !== '[object RegExp]') ||
7008    (typeof window !== 'undefined' &&
7009     // IE8 and below
7010     (fn === window.setTimeout ||
7011      fn === window.alert ||
7012      fn === window.confirm ||
7013      fn === window.prompt))
7014}
7015
7016var trim_1 = createCommonjsModule(function (module, exports) {
7017exports = module.exports = trim;
7018
7019function trim(str){
7020  return str.replace(/^\s*|\s*$/g, '');
7021}
7022
7023exports.left = function(str){
7024  return str.replace(/^\s*/, '');
7025};
7026
7027exports.right = function(str){
7028  return str.replace(/\s*$/, '');
7029};
7030});
7031
7032var forEach_1 = forEach;
7033
7034var toString$2 = Object.prototype.toString;
7035var hasOwnProperty = Object.prototype.hasOwnProperty;
7036
7037function forEach(list, iterator, context) {
7038    if (!isFunction_1(iterator)) {
7039        throw new TypeError('iterator must be a function')
7040    }
7041
7042    if (arguments.length < 3) {
7043        context = this;
7044    }
7045    
7046    if (toString$2.call(list) === '[object Array]')
7047        forEachArray$1(list, iterator, context);
7048    else if (typeof list === 'string')
7049        forEachString(list, iterator, context);
7050    else
7051        forEachObject(list, iterator, context);
7052}
7053
7054function forEachArray$1(array, iterator, context) {
7055    for (var i = 0, len = array.length; i < len; i++) {
7056        if (hasOwnProperty.call(array, i)) {
7057            iterator.call(context, array[i], i, array);
7058        }
7059    }
7060}
7061
7062function forEachString(string, iterator, context) {
7063    for (var i = 0, len = string.length; i < len; i++) {
7064        // no such thing as a sparse string.
7065        iterator.call(context, string.charAt(i), i, string);
7066    }
7067}
7068
7069function forEachObject(object, iterator, context) {
7070    for (var k in object) {
7071        if (hasOwnProperty.call(object, k)) {
7072            iterator.call(context, object[k], k, object);
7073        }
7074    }
7075}
7076
7077var isArray = function(arg) {
7078      return Object.prototype.toString.call(arg) === '[object Array]';
7079    };
7080
7081var parseHeaders = function (headers) {
7082  if (!headers)
7083    return {}
7084
7085  var result = {};
7086
7087  forEach_1(
7088      trim_1(headers).split('\n')
7089    , function (row) {
7090        var index = row.indexOf(':')
7091          , key = trim_1(row.slice(0, index)).toLowerCase()
7092          , value = trim_1(row.slice(index + 1));
7093
7094        if (typeof(result[key]) === 'undefined') {
7095          result[key] = value;
7096        } else if (isArray(result[key])) {
7097          result[key].push(value);
7098        } else {
7099          result[key] = [ result[key], value ];
7100        }
7101      }
7102  );
7103
7104  return result
7105};
7106
7107var immutable = extend;
7108
7109var hasOwnProperty$1 = Object.prototype.hasOwnProperty;
7110
7111function extend() {
7112    var target = {};
7113
7114    for (var i = 0; i < arguments.length; i++) {
7115        var source = arguments[i];
7116
7117        for (var key in source) {
7118            if (hasOwnProperty$1.call(source, key)) {
7119                target[key] = source[key];
7120            }
7121        }
7122    }
7123
7124    return target
7125}
7126
7127var xhr = createXHR;
7128createXHR.XMLHttpRequest = window_1.XMLHttpRequest || noop;
7129createXHR.XDomainRequest = "withCredentials" in (new createXHR.XMLHttpRequest()) ? createXHR.XMLHttpRequest : window_1.XDomainRequest;
7130
7131forEachArray(["get", "put", "post", "patch", "head", "delete"], function(method) {
7132    createXHR[method === "delete" ? "del" : method] = function(uri, options, callback) {
7133        options = initParams(uri, options, callback);
7134        options.method = method.toUpperCase();
7135        return _createXHR(options)
7136    };
7137});
7138
7139function forEachArray(array, iterator) {
7140    for (var i = 0; i < array.length; i++) {
7141        iterator(array[i]);
7142    }
7143}
7144
7145function isEmpty(obj){
7146    for(var i in obj){
7147        if(obj.hasOwnProperty(i)) return false
7148    }
7149    return true
7150}
7151
7152function initParams(uri, options, callback) {
7153    var params = uri;
7154
7155    if (isFunction_1(options)) {
7156        callback = options;
7157        if (typeof uri === "string") {
7158            params = {uri:uri};
7159        }
7160    } else {
7161        params = immutable(options, {uri: uri});
7162    }
7163
7164    params.callback = callback;
7165    return params
7166}
7167
7168function createXHR(uri, options, callback) {
7169    options = initParams(uri, options, callback);
7170    return _createXHR(options)
7171}
7172
7173function _createXHR(options) {
7174    if(typeof options.callback === "undefined"){
7175        throw new Error("callback argument missing")
7176    }
7177
7178    var called = false;
7179    var callback = function cbOnce(err, response, body){
7180        if(!called){
7181            called = true;
7182            options.callback(err, response, body);
7183        }
7184    };
7185
7186    function readystatechange() {
7187        if (xhr.readyState === 4) {
7188            setTimeout(loadFunc, 0);
7189        }
7190    }
7191
7192    function getBody() {
7193        // Chrome with requestType=blob throws errors arround when even testing access to responseText
7194        var body = undefined;
7195
7196        if (xhr.response) {
7197            body = xhr.response;
7198        } else {
7199            body = xhr.responseText || getXml(xhr);
7200        }
7201
7202        if (isJson) {
7203            try {
7204                body = JSON.parse(body);
7205            } catch (e) {}
7206        }
7207
7208        return body
7209    }
7210
7211    function errorFunc(evt) {
7212        clearTimeout(timeoutTimer);
7213        if(!(evt instanceof Error)){
7214            evt = new Error("" + (evt || "Unknown XMLHttpRequest Error") );
7215        }
7216        evt.statusCode = 0;
7217        return callback(evt, failureResponse)
7218    }
7219
7220    // will load the data & process the response in a special response object
7221    function loadFunc() {
7222        if (aborted) return
7223        var status;
7224        clearTimeout(timeoutTimer);
7225        if(options.useXDR && xhr.status===undefined) {
7226            //IE8 CORS GET successful response doesn't have a status field, but body is fine
7227            status = 200;
7228        } else {
7229            status = (xhr.status === 1223 ? 204 : xhr.status);
7230        }
7231        var response = failureResponse;
7232        var err = null;
7233
7234        if (status !== 0){
7235            response = {
7236                body: getBody(),
7237                statusCode: status,
7238                method: method,
7239                headers: {},
vendor: 4,867 bytes, lines 7240-7400
7240                url: uri,
7241                rawRequest: xhr
7242            };
7243            if(xhr.getAllResponseHeaders){ //remember xhr can in fact be XDR for CORS in IE
7244                response.headers = parseHeaders(xhr.getAllResponseHeaders());
7245            }
7246        } else {
7247            err = new Error("Internal XMLHttpRequest Error");
7248        }
7249        return callback(err, response, response.body)
7250    }
7251
7252    var xhr = options.xhr || null;
7253
7254    if (!xhr) {
7255        if (options.cors || options.useXDR) {
7256            xhr = new createXHR.XDomainRequest();
7257        }else{
7258            xhr = new createXHR.XMLHttpRequest();
7259        }
7260    }
7261
7262    var key;
7263    var aborted;
7264    var uri = xhr.url = options.uri || options.url;
7265    var method = xhr.method = options.method || "GET";
7266    var body = options.body || options.data;
7267    var headers = xhr.headers = options.headers || {};
7268    var sync = !!options.sync;
7269    var isJson = false;
7270    var timeoutTimer;
7271    var failureResponse = {
7272        body: undefined,
7273        headers: {},
7274        statusCode: 0,
7275        method: method,
7276        url: uri,
7277        rawRequest: xhr
7278    };
7279
7280    if ("json" in options && options.json !== false) {
7281        isJson = true;
7282        headers["accept"] || headers["Accept"] || (headers["Accept"] = "application/json"); //Don't override existing accept header declared by user
7283        if (method !== "GET" && method !== "HEAD") {
7284            headers["content-type"] || headers["Content-Type"] || (headers["Content-Type"] = "application/json"); //Don't override existing accept header declared by user
7285            body = JSON.stringify(options.json === true ? body : options.json);
7286        }
7287    }
7288
7289    xhr.onreadystatechange = readystatechange;
7290    xhr.onload = loadFunc;
7291    xhr.onerror = errorFunc;
7292    // IE9 must have onprogress be set to a unique function.
7293    xhr.onprogress = function () {
7294        // IE must die
7295    };
7296    xhr.onabort = function(){
7297        aborted = true;
7298    };
7299    xhr.ontimeout = errorFunc;
7300    xhr.open(method, uri, !sync, options.username, options.password);
7301    //has to be after open
7302    if(!sync) {
7303        xhr.withCredentials = !!options.withCredentials;
7304    }
7305    // Cannot set timeout with sync request
7306    // not setting timeout on the xhr object, because of old webkits etc. not handling that correctly
7307    // both npm's request and jquery 1.x use this kind of timeout, so this is being consistent
7308    if (!sync && options.timeout > 0 ) {
7309        timeoutTimer = setTimeout(function(){
7310            if (aborted) return
7311            aborted = true;//IE9 may still call readystatechange
7312            xhr.abort("timeout");
7313            var e = new Error("XMLHttpRequest timeout");
7314            e.code = "ETIMEDOUT";
7315            errorFunc(e);
7316        }, options.timeout );
7317    }
7318
7319    if (xhr.setRequestHeader) {
7320        for(key in headers){
7321            if(headers.hasOwnProperty(key)){
7322                xhr.setRequestHeader(key, headers[key]);
7323            }
7324        }
7325    } else if (options.headers && !isEmpty(options.headers)) {
7326        throw new Error("Headers cannot be set on an XDomainRequest object")
7327    }
7328
7329    if ("responseType" in options) {
7330        xhr.responseType = options.responseType;
7331    }
7332
7333    if ("beforeSend" in options &&
7334        typeof options.beforeSend === "function"
7335    ) {
7336        options.beforeSend(xhr);
7337    }
7338
7339    // Microsoft Edge browser sends "undefined" when send is called with undefined value.
7340    // XMLHttpRequest spec says to pass null as body to indicate no body
7341    // See https://github.com/naugtur/xhr/issues/100.
7342    xhr.send(body || null);
7343
7344    return xhr
7345
7346
7347}
7348
7349function getXml(xhr) {
7350    if (xhr.responseType === "document") {
7351        return xhr.responseXML
7352    }
7353    var firefoxBugTakenEffect = xhr.responseXML && xhr.responseXML.documentElement.nodeName === "parsererror";
7354    if (xhr.responseType === "" && !firefoxBugTakenEffect) {
7355        return xhr.responseXML
7356    }
7357
7358    return null
7359}
7360
7361function noop() {}
7362
7363/**
7364 * @file text-track.js
7365 */
7366/**
7367 * Takes a webvtt file contents and parses it into cues
7368 *
7369 * @param {string} srcContent
7370 *        webVTT file contents
7371 *
7372 * @param {TextTrack} track
7373 *        TextTrack to add cues to. Cues come from the srcContent.
7374 *
7375 * @private
7376 */
7377var parseCues = function parseCues(srcContent, track) {
7378  var parser = new window_1.WebVTT.Parser(window_1, window_1.vttjs, window_1.WebVTT.StringDecoder());
7379  var errors = [];
7380
7381  parser.oncue = function (cue) {
7382    track.addCue(cue);
7383  };
7384
7385  parser.onparsingerror = function (error) {
7386    errors.push(error);
7387  };
7388
7389  parser.onflush = function () {
7390    track.trigger({
7391      type: 'loadeddata',
7392      target: track
7393    });
7394  };
7395
7396  parser.parse(srcContent);
7397  if (errors.length > 0) {
7398    if (window_1.console && window_1.console.groupCollapsed) {
7399      window_1.console.groupCollapsed('Text Track parsing errors for ' + track.src);
7400    }
7401    errors.forEach(function (error) {
7402      return log$1.error(error);
7403    });
7404    if (window_1.console && window_1.console.groupEnd) {
7405      window_1.console.groupEnd();
7406    }
7407  }
7408
7409  parser.flush();
7410};
7411
7412/**
7413 * Load a `TextTrack` from a specifed url.
7414 *
7415 * @param {string} src
7416 *        Url to load track from.
7417 *
7418 * @param {TextTrack} track
7419 *        Track to add cues to. Comes from the content at the end of `url`.
7420 *
7421 * @private
7422 */
7423var loadTrack = function loadTrack(src, track) {
7424  var opts = {
7425    uri: src
7426  };
7427  var crossOrigin = isCrossOrigin(src);
7428
7429  if (crossOrigin) {
7430    opts.cors = crossOrigin;
7431  }
7432
7433  xhr(opts, bind(this, function (err, response, responseBody) {
7434    if (err) {
7435      return log$1.error(err, response);
7436    }
7437
7438    track.loaded_ = true;
7439
7440    // Make sure that vttjs has loaded, otherwise, wait till it finished loading
7441    // NOTE: this is only used for the alt/video.novtt.js build
7442    if (typeof window_1.WebVTT !== 'function') {
7443      if (track.tech_) {
7444        var loadHandler = function loadHandler() {
7445          return parseCues(responseBody, track);
7446        };
7447
7448        track.tech_.on('vttjsloaded', loadHandler);
7449        track.tech_.on('vttjserror', function () {
7450          log$1.error('vttjs failed to load, stopping trying to process ' + track.src);
7451          track.tech_.off('vttjsloaded', loadHandler);
7452        });
7453      }
7454    } else {
7455      parseCues(responseBody, track);
7456    }
7457  }));
7458};
7459
7460/**
7461 * A representation of a single `TextTrack`.
7462 *
7463 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
7464 * @extends Track
7465 */
7466
7467var TextTrack = function (_Track) {
7468  inherits(TextTrack, _Track);
7469
7470  /**
7471   * Create an instance of this class.
7472   *
7473   * @param {Object} options={}
7474   *        Object of option names and values
7475   *
7476   * @param {Tech} options.tech
7477   *        A reference to the tech that owns this TextTrack.
7478   *
7479   * @param {TextTrack~Kind} [options.kind='subtitles']
7480   *        A valid text track kind.
7481   *
7482   * @param {TextTrack~Mode} [options.mode='disabled']
7483   *        A valid text track mode.
7484   *
7485   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7486   *        A unique id for this TextTrack.
7487   *
7488   * @param {string} [options.label='']
7489   *        The menu label for this track.
7490   *
7491   * @param {string} [options.language='']
7492   *        A valid two character language code.
7493   *
7494   * @param {string} [options.srclang='']
7495   *        A valid two character language code. An alternative, but deprioritized
7496   *        vesion of `options.language`
7497   *
7498   * @param {string} [options.src]
7499   *        A url to TextTrack cues.
7500   *
7501   * @param {boolean} [options.default]
7502   *        If this track should default to on or off.
7503   */
7504  function TextTrack() {
7505    var _this, _ret;
7506
7507    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
7508    classCallCheck(this, TextTrack);
7509
7510    if (!options.tech) {
7511      throw new Error('A tech was not provided.');
7512    }
7513
7514    var settings = mergeOptions(options, {
7515      kind: TextTrackKind[options.kind] || 'subtitles',
7516      language: options.language || options.srclang || ''
7517    });
7518    var mode = TextTrackMode[settings.mode] || 'disabled';
7519    var default_ = settings['default'];
7520
7521    if (settings.kind === 'metadata' || settings.kind === 'chapters') {
7522      mode = 'hidden';
7523    }
7524    // on IE8 this will be a document element
7525    // for every other browser this will be a normal object
7526    var tt = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
7527
7528    tt.tech_ = settings.tech;
7529
7530    if (IS_IE8) {
7531      for (var prop in TextTrack.prototype) {
7532        if (prop !== 'constructor') {
7533          tt[prop] = TextTrack.prototype[prop];
7534        }
7535      }
7536    }
7537
7538    tt.cues_ = [];
7539    tt.activeCues_ = [];
7540
7541    var cues = new TextTrackCueList(tt.cues_);
7542    var activeCues = new TextTrackCueList(tt.activeCues_);
7543    var changed = false;
7544    var timeupdateHandler = bind(tt, function () {
7545
7546      // Accessing this.activeCues for the side-effects of updating itself
7547      // due to it's nature as a getter function. Do not remove or cues will
7548      // stop updating!
7549      /* eslint-disable no-unused-expressions */
7550      this.activeCues;
7551      /* eslint-enable no-unused-expressions */
7552      if (changed) {
7553        this.trigger('cuechange');
7554        changed = false;
vendor: 6,553 bytes, lines 7555-7810
7555      }
7556    });
7557
7558    if (mode !== 'disabled') {
7559      tt.tech_.ready(function () {
7560        tt.tech_.on('timeupdate', timeupdateHandler);
7561      }, true);
7562    }
7563
7564    /**
7565     * @memberof TextTrack
7566     * @member {boolean} default
7567     *         If this track was set to be on or off by default. Cannot be changed after
7568     *         creation.
7569     * @instance
7570     *
7571     * @readonly
7572     */
7573    Object.defineProperty(tt, 'default', {
7574      get: function get$$1() {
7575        return default_;
7576      },
7577      set: function set$$1() {}
7578    });
7579
7580    /**
7581     * @memberof TextTrack
7582     * @member {string} mode
7583     *         Set the mode of this TextTrack to a valid {@link TextTrack~Mode}. Will
7584     *         not be set if setting to an invalid mode.
7585     * @instance
7586     *
7587     * @fires TextTrack#modechange
7588     */
7589    Object.defineProperty(tt, 'mode', {
7590      get: function get$$1() {
7591        return mode;
7592      },
7593      set: function set$$1(newMode) {
7594        var _this2 = this;
7595
7596        if (!TextTrackMode[newMode]) {
7597          return;
7598        }
7599        mode = newMode;
7600        if (mode === 'showing') {
7601
7602          this.tech_.ready(function () {
7603            _this2.tech_.on('timeupdate', timeupdateHandler);
7604          }, true);
7605        }
7606        /**
7607         * An event that fires when mode changes on this track. This allows
7608         * the TextTrackList that holds this track to act accordingly.
7609         *
7610         * > Note: This is not part of the spec!
7611         *
7612         * @event TextTrack#modechange
7613         * @type {EventTarget~Event}
7614         */
7615        this.trigger('modechange');
7616      }
7617    });
7618
7619    /**
7620     * @memberof TextTrack
7621     * @member {TextTrackCueList} cues
7622     *         The text track cue list for this TextTrack.
7623     * @instance
7624     */
7625    Object.defineProperty(tt, 'cues', {
7626      get: function get$$1() {
7627        if (!this.loaded_) {
7628          return null;
7629        }
7630
7631        return cues;
7632      },
7633      set: function set$$1() {}
7634    });
7635
7636    /**
7637     * @memberof TextTrack
7638     * @member {TextTrackCueList} activeCues
7639     *         The list text track cues that are currently active for this TextTrack.
7640     * @instance
7641     */
7642    Object.defineProperty(tt, 'activeCues', {
7643      get: function get$$1() {
7644        if (!this.loaded_) {
7645          return null;
7646        }
7647
7648        // nothing to do
7649        if (this.cues.length === 0) {
7650          return activeCues;
7651        }
7652
7653        var ct = this.tech_.currentTime();
7654        var active = [];
7655
7656        for (var i = 0, l = this.cues.length; i < l; i++) {
7657          var cue = this.cues[i];
7658
7659          if (cue.startTime <= ct && cue.endTime >= ct) {
7660            active.push(cue);
7661          } else if (cue.startTime === cue.endTime && cue.startTime <= ct && cue.startTime + 0.5 >= ct) {
7662            active.push(cue);
7663          }
7664        }
7665
7666        changed = false;
7667
7668        if (active.length !== this.activeCues_.length) {
7669          changed = true;
7670        } else {
7671          for (var _i = 0; _i < active.length; _i++) {
7672            if (this.activeCues_.indexOf(active[_i]) === -1) {
7673              changed = true;
7674            }
7675          }
7676        }
7677
7678        this.activeCues_ = active;
7679        activeCues.setCues_(this.activeCues_);
7680
7681        return activeCues;
7682      },
7683      set: function set$$1() {}
7684    });
7685
7686    if (settings.src) {
7687      tt.src = settings.src;
7688      loadTrack(settings.src, tt);
7689    } else {
7690      tt.loaded_ = true;
7691    }
7692
7693    return _ret = tt, possibleConstructorReturn(_this, _ret);
7694  }
7695
7696  /**
7697   * Add a cue to the internal list of cues.
7698   *
7699   * @param {TextTrack~Cue} cue
7700   *        The cue to add to our internal list
7701   */
7702
7703
7704  TextTrack.prototype.addCue = function addCue(originalCue) {
7705    var cue = originalCue;
7706
7707    if (window_1.vttjs && !(originalCue instanceof window_1.vttjs.VTTCue)) {
7708      cue = new window_1.vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
7709
7710      for (var prop in originalCue) {
7711        if (!(prop in cue)) {
7712          cue[prop] = originalCue[prop];
7713        }
7714      }
7715
7716      // make sure that `id` is copied over
7717      cue.id = originalCue.id;
7718      cue.originalCue_ = originalCue;
7719    }
7720
7721    var tracks = this.tech_.textTracks();
7722
7723    for (var i = 0; i < tracks.length; i++) {
7724      if (tracks[i] !== this) {
7725        tracks[i].removeCue(cue);
7726      }
7727    }
7728
7729    this.cues_.push(cue);
7730    this.cues.setCues_(this.cues_);
7731  };
7732
7733  /**
7734   * Remove a cue from our internal list
7735   *
7736   * @param {TextTrack~Cue} removeCue
7737   *        The cue to remove from our internal list
7738   */
7739
7740
7741  TextTrack.prototype.removeCue = function removeCue(_removeCue) {
7742    var i = this.cues_.length;
7743
7744    while (i--) {
7745      var cue = this.cues_[i];
7746
7747      if (cue === _removeCue || cue.originalCue_ && cue.originalCue_ === _removeCue) {
7748        this.cues_.splice(i, 1);
7749        this.cues.setCues_(this.cues_);
7750        break;
7751      }
7752    }
7753  };
7754
7755  return TextTrack;
7756}(Track);
7757
7758/**
7759 * cuechange - One or more cues in the track have become active or stopped being active.
7760 */
7761
7762
7763TextTrack.prototype.allowedEvents_ = {
7764  cuechange: 'cuechange'
7765};
7766
7767/**
7768 * A representation of a single `AudioTrack`. If it is part of an {@link AudioTrackList}
7769 * only one `AudioTrack` in the list will be enabled at a time.
7770 *
7771 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotrack}
7772 * @extends Track
7773 */
7774
7775var AudioTrack = function (_Track) {
7776  inherits(AudioTrack, _Track);
7777
7778  /**
7779   * Create an instance of this class.
7780   *
7781   * @param {Object} [options={}]
7782   *        Object of option names and values
7783   *
7784   * @param {AudioTrack~Kind} [options.kind='']
7785   *        A valid audio track kind
7786   *
7787   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7788   *        A unique id for this AudioTrack.
7789   *
7790   * @param {string} [options.label='']
7791   *        The menu label for this track.
7792   *
7793   * @param {string} [options.language='']
7794   *        A valid two character language code.
7795   *
7796   * @param {boolean} [options.enabled]
7797   *        If this track is the one that is currently playing. If this track is part of
7798   *        an {@link AudioTrackList}, only one {@link AudioTrack} will be enabled.
7799   */
7800  function AudioTrack() {
7801    var _this, _ret;
7802
7803    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
7804    classCallCheck(this, AudioTrack);
7805
7806    var settings = mergeOptions(options, {
7807      kind: AudioTrackKind[options.kind] || ''
7808    });
7809    // on IE8 this will be a document element
7810    // for every other browser this will be a normal object
7811    var track = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
7812    var enabled = false;
7813
7814    if (IS_IE8) {
7815      for (var prop in AudioTrack.prototype) {
7816        if (prop !== 'constructor') {
7817          track[prop] = AudioTrack.prototype[prop];
7818        }
7819      }
7820    }
7821    /**
7822     * @memberof AudioTrack
7823     * @member {boolean} enabled
7824     *         If this `AudioTrack` is enabled or not. When setting this will
7825     *         fire {@link AudioTrack#enabledchange} if the state of enabled is changed.
7826     * @instance
7827     *
7828     * @fires VideoTrack#selectedchange
7829     */
7830    Object.defineProperty(track, 'enabled', {
7831      get: function get$$1() {
7832        return enabled;
7833      },
7834      set: function set$$1(newEnabled) {
7835        // an invalid or unchanged value
7836        if (typeof newEnabled !== 'boolean' || newEnabled === enabled) {
7837          return;
7838        }
7839        enabled = newEnabled;
7840
7841        /**
7842         * An event that fires when enabled changes on this track. This allows
7843         * the AudioTrackList that holds this track to act accordingly.
7844         *
7845         * > Note: This is not part of the spec! Native tracks will do
7846         *         this internally without an event.
7847         *
7848         * @event AudioTrack#enabledchange
7849         * @type {EventTarget~Event}
7850         */
7851        this.trigger('enabledchange');
7852      }
7853    });
7854
7855    // if the user sets this track to selected then
7856    // set selected to that true value otherwise
7857    // we keep it false
7858    if (settings.enabled) {
7859      track.enabled = settings.enabled;
7860    }
7861    track.loaded_ = true;
7862
7863    return _ret = track, possibleConstructorReturn(_this, _ret);
7864  }
7865
7866  return AudioTrack;
7867}(Track);
7868
7869/**
7870 * A representation of a single `VideoTrack`.
7871 *
7872 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#videotrack}
7873 * @extends Track
7874 */
7875
7876var VideoTrack = function (_Track) {
7877  inherits(VideoTrack, _Track);
7878
7879  /**
7880   * Create an instance of this class.
7881   *
7882   * @param {Object} [options={}]
7883   *        Object of option names and values
7884   *
7885   * @param {string} [options.kind='']
7886   *        A valid {@link VideoTrack~Kind}
7887   *
7888   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
7889   *        A unique id for this AudioTrack.
7890   *
7891   * @param {string} [options.label='']
7892   *        The menu label for this track.
7893   *
7894   * @param {string} [options.language='']
7895   *        A valid two character language code.
7896   *
7897   * @param {boolean} [options.selected]
7898   *        If this track is the one that is currently playing.
7899   */
7900  function VideoTrack() {
7901    var _this, _ret;
7902
7903    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
7904    classCallCheck(this, VideoTrack);
7905
7906    var settings = mergeOptions(options, {
7907      kind: VideoTrackKind[options.kind] || ''
7908    });
7909
7910    // on IE8 this will be a document element
7911    // for every other browser this will be a normal object
7912    var track = (_this = possibleConstructorReturn(this, _Track.call(this, settings)), _this);
7913    var selected = false;
7914
7915    if (IS_IE8) {
7916      for (var prop in VideoTrack.prototype) {
7917        if (prop !== 'constructor') {
7918          track[prop] = VideoTrack.prototype[prop];
7919        }
7920      }
7921    }
7922
7923    /**
7924     * @memberof VideoTrack
7925     * @member {boolean} selected
7926     *         If this `VideoTrack` is selected or not. When setting this will
7927     *         fire {@link VideoTrack#selectedchange} if the state of selected changed.
7928     * @instance
7929     *
7930     * @fires VideoTrack#selectedchange
7931     */
7932    Object.defineProperty(track, 'selected', {
7933      get: function get$$1() {
7934        return selected;
7935      },
7936      set: function set$$1(newSelected) {
7937        // an invalid or unchanged value
7938        if (typeof newSelected !== 'boolean' || newSelected === selected) {
7939          return;
7940        }
7941        selected = newSelected;
7942
7943        /**
7944         * An event that fires when selected changes on this track. This allows
7945         * the VideoTrackList that holds this track to act accordingly.
7946         *
7947         * > Note: This is not part of the spec! Native tracks will do
7948         *         this internally without an event.
7949         *
7950         * @event VideoTrack#selectedchange
7951         * @type {EventTarget~Event}
7952         */
7953        this.trigger('selectedchange');
7954      }
7955    });
7956
7957    // if the user sets this track to selected then
7958    // set selected to that true value otherwise
7959    // we keep it false
7960    if (settings.selected) {
7961      track.selected = settings.selected;
7962    }
7963
7964    return _ret = track, possibleConstructorReturn(_this, _ret);
7965  }
7966
7967  return VideoTrack;
7968}(Track);
7969
7970/**
7971 * @file html-track-element.js
7972 */
7973
7974/**
7975 * @memberof HTMLTrackElement
7976 * @typedef {HTMLTrackElement~ReadyState}
7977 * @enum {number}
7978 */
7979var NONE = 0;
7980var LOADING = 1;
7981var LOADED = 2;
7982var ERROR = 3;
7983
7984/**
7985 * A single track represented in the DOM.
7986 *
7987 * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#htmltrackelement}
7988 * @extends EventTarget
7989 */
7990
7991var HTMLTrackElement = function (_EventTarget) {
7992  inherits(HTMLTrackElement, _EventTarget);
7993
7994  /**
7995   * Create an instance of this class.
7996   *
7997   * @param {Object} options={}
7998   *        Object of option names and values
7999   *
8000   * @param {Tech} options.tech
8001   *        A reference to the tech that owns this HTMLTrackElement.
8002   *
8003   * @param {TextTrack~Kind} [options.kind='subtitles']
8004   *        A valid text track kind.
8005   *
8006   * @param {TextTrack~Mode} [options.mode='disabled']
8007   *        A valid text track mode.
8008   *
8009   * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
8010   *        A unique id for this TextTrack.
8011   *
8012   * @param {string} [options.label='']
8013   *        The menu label for this track.
8014   *
8015   * @param {string} [options.language='']
8016   *        A valid two character language code.
8017   *
8018   * @param {string} [options.srclang='']
8019   *        A valid two character language code. An alternative, but deprioritized
8020   *        vesion of `options.language`
8021   *
8022   * @param {string} [options.src]
8023   *        A url to TextTrack cues.
8024   *
8025   * @param {boolean} [options.default]
8026   *        If this track should default to on or off.
8027   */
8028  function HTMLTrackElement() {
8029    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
8030    classCallCheck(this, HTMLTrackElement);
8031
8032    var _this = possibleConstructorReturn(this, _EventTarget.call(this));
8033
8034    var readyState = void 0;
8035    var trackElement = _this; // eslint-disable-line
8036
8037    if (IS_IE8) {
8038      trackElement = document_1.createElement('custom');
8039
8040      for (var prop in HTMLTrackElement.prototype) {
8041        if (prop !== 'constructor') {
8042          trackElement[prop] = HTMLTrackElement.prototype[prop];
8043        }
8044      }
8045    }
8046
8047    var track = new TextTrack(options);
8048
8049    trackElement.kind = track.kind;
8050    trackElement.src = track.src;
8051    trackElement.srclang = track.language;
8052    trackElement.label = track.label;
8053    trackElement['default'] = track['default'];
8054
8055    /**
8056     * @memberof HTMLTrackElement
8057     * @member {HTMLTrackElement~ReadyState} readyState
8058     *         The current ready state of the track element.
8059     * @instance
8060     */
8061    Object.defineProperty(trackElement, 'readyState', {
8062      get: function get$$1() {
8063        return readyState;
8064      }
8065    });
8066
8067    /**
8068     * @memberof HTMLTrackElement
8069     * @member {TextTrack} track
8070     *         The underlying TextTrack object.
8071     * @instance
8072     *
8073     */
8074    Object.defineProperty(trackElement, 'track', {
8075      get: function get$$1() {
8076        return track;
8077      }
8078    });
8079
8080    readyState = NONE;
8081
8082    /**
8083     * @listens TextTrack#loadeddata
8084     * @fires HTMLTrackElement#load
8085     */
8086    track.addEventListener('loadeddata', function () {
8087      readyState = LOADED;
8088
8089      trackElement.trigger({
8090        type: 'load',
8091        target: trackElement
8092      });
8093    });
8094
8095    if (IS_IE8) {
8096      var _ret;
8097
8098      return _ret = trackElement, possibleConstructorReturn(_this, _ret);
8099    }
8100    return _this;
8101  }
8102
8103  return HTMLTrackElement;
8104}(EventTarget);
8105
8106HTMLTrackElement.prototype.allowedEvents_ = {
8107  load: 'load'
8108};
8109
8110HTMLTrackElement.NONE = NONE;
8111HTMLTrackElement.LOADING = LOADING;
8112HTMLTrackElement.LOADED = LOADED;
8113HTMLTrackElement.ERROR = ERROR;
8114
8115/*
8116 * This file contains all track properties that are used in
8117 * player.js, tech.js, html5.js and possibly other techs in the future.
8118 */
8119
8120var NORMAL = {
8121  audio: {
8122    ListClass: AudioTrackList,
8123    TrackClass: AudioTrack,
8124    capitalName: 'Audio'
8125  },
8126  video: {
8127    ListClass: VideoTrackList,
8128    TrackClass: VideoTrack,
8129    capitalName: 'Video'
8130  },
8131  text: {
8132    ListClass: TextTrackList,
8133    TrackClass: TextTrack,
8134    capitalName: 'Text'
8135  }
8136};
8137
8138Object.keys(NORMAL).forEach(function (type) {
8139  NORMAL[type].getterName = type + 'Tracks';
8140  NORMAL[type].privateName = type + 'Tracks_';
8141});
8142
8143var REMOTE = {
8144  remoteText: {
8145    ListClass: TextTrackList,
8146    TrackClass: TextTrack,
8147    capitalName: 'RemoteText',
8148    getterName: 'remoteTextTracks',
8149    privateName: 'remoteTextTracks_'
8150  },
8151  remoteTextEl: {
8152    ListClass: HtmlTrackElementList,
8153    TrackClass: HTMLTrackElement,
8154    capitalName: 'RemoteTextTrackEls',
8155    getterName: 'remoteTextTrackEls',
8156    privateName: 'remoteTextTrackEls_'
8157  }
8158};
8159
8160var ALL = mergeOptions(NORMAL, REMOTE);
8161
8162REMOTE.names = Object.keys(REMOTE);
8163NORMAL.names = Object.keys(NORMAL);
8164ALL.names = [].concat(REMOTE.names).concat(NORMAL.names);
8165
8166/**
8167 * Copyright 2013 vtt.js Contributors
8168 *
8169 * Licensed under the Apache License, Version 2.0 (the "License");
vendor: 16,438 bytes, lines 8170-8707
8170 * you may not use this file except in compliance with the License.
8171 * You may obtain a copy of the License at
8172 *
8173 *   http://www.apache.org/licenses/LICENSE-2.0
8174 *
8175 * Unless required by applicable law or agreed to in writing, software
8176 * distributed under the License is distributed on an "AS IS" BASIS,
8177 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
8178 * See the License for the specific language governing permissions and
8179 * limitations under the License.
8180 */
8181
8182/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
8183/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
8184var _objCreate = Object.create || (function() {
8185  function F() {}
8186  return function(o) {
8187    if (arguments.length !== 1) {
8188      throw new Error('Object.create shim only accepts one parameter.');
8189    }
8190    F.prototype = o;
8191    return new F();
8192  };
8193})();
8194
8195// Creates a new ParserError object from an errorData object. The errorData
8196// object should have default code and message properties. The default message
8197// property can be overriden by passing in a message parameter.
8198// See ParsingError.Errors below for acceptable errors.
8199function ParsingError(errorData, message) {
8200  this.name = "ParsingError";
8201  this.code = errorData.code;
8202  this.message = message || errorData.message;
8203}
8204ParsingError.prototype = _objCreate(Error.prototype);
8205ParsingError.prototype.constructor = ParsingError;
8206
8207// ParsingError metadata for acceptable ParsingErrors.
8208ParsingError.Errors = {
8209  BadSignature: {
8210    code: 0,
8211    message: "Malformed WebVTT signature."
8212  },
8213  BadTimeStamp: {
8214    code: 1,
8215    message: "Malformed time stamp."
8216  }
8217};
8218
8219// Try to parse input as a time stamp.
8220function parseTimeStamp(input) {
8221
8222  function computeSeconds(h, m, s, f) {
8223    return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
8224  }
8225
8226  var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);
8227  if (!m) {
8228    return null;
8229  }
8230
8231  if (m[3]) {
8232    // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
8233    return computeSeconds(m[1], m[2], m[3].replace(":", ""), m[4]);
8234  } else if (m[1] > 59) {
8235    // Timestamp takes the form of [hours]:[minutes].[milliseconds]
8236    // First position is hours as it's over 59.
8237    return computeSeconds(m[1], m[2], 0,  m[4]);
8238  } else {
8239    // Timestamp takes the form of [minutes]:[seconds].[milliseconds]
8240    return computeSeconds(0, m[1], m[2], m[4]);
8241  }
8242}
8243
8244// A settings object holds key/value pairs and will ignore anything but the first
8245// assignment to a specific key.
8246function Settings() {
8247  this.values = _objCreate(null);
8248}
8249
8250Settings.prototype = {
8251  // Only accept the first assignment to any key.
8252  set: function(k, v) {
8253    if (!this.get(k) && v !== "") {
8254      this.values[k] = v;
8255    }
8256  },
8257  // Return the value for a key, or a default value.
8258  // If 'defaultKey' is passed then 'dflt' is assumed to be an object with
8259  // a number of possible default values as properties where 'defaultKey' is
8260  // the key of the property that will be chosen; otherwise it's assumed to be
8261  // a single value.
8262  get: function(k, dflt, defaultKey) {
8263    if (defaultKey) {
8264      return this.has(k) ? this.values[k] : dflt[defaultKey];
8265    }
8266    return this.has(k) ? this.values[k] : dflt;
8267  },
8268  // Check whether we have a value for a key.
8269  has: function(k) {
8270    return k in this.values;
8271  },
8272  // Accept a setting if its one of the given alternatives.
8273  alt: function(k, v, a) {
8274    for (var n = 0; n < a.length; ++n) {
8275      if (v === a[n]) {
8276        this.set(k, v);
8277        break;
8278      }
8279    }
8280  },
8281  // Accept a setting if its a valid (signed) integer.
8282  integer: function(k, v) {
8283    if (/^-?\d+$/.test(v)) { // integer
8284      this.set(k, parseInt(v, 10));
8285    }
8286  },
8287  // Accept a setting if its a valid percentage.
8288  percent: function(k, v) {
8289    var m;
8290    if ((m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/))) {
8291      v = parseFloat(v);
8292      if (v >= 0 && v <= 100) {
8293        this.set(k, v);
8294        return true;
8295      }
8296    }
8297    return false;
8298  }
8299};
8300
8301// Helper function to parse input into groups separated by 'groupDelim', and
8302// interprete each group as a key/value pair separated by 'keyValueDelim'.
8303function parseOptions(input, callback, keyValueDelim, groupDelim) {
8304  var groups = groupDelim ? input.split(groupDelim) : [input];
8305  for (var i in groups) {
8306    if (typeof groups[i] !== "string") {
8307      continue;
8308    }
8309    var kv = groups[i].split(keyValueDelim);
8310    if (kv.length !== 2) {
8311      continue;
8312    }
8313    var k = kv[0];
8314    var v = kv[1];
8315    callback(k, v);
8316  }
8317}
8318
8319function parseCue(input, cue, regionList) {
8320  // Remember the original input if we need to throw an error.
8321  var oInput = input;
8322  // 4.1 WebVTT timestamp
8323  function consumeTimeStamp() {
8324    var ts = parseTimeStamp(input);
8325    if (ts === null) {
8326      throw new ParsingError(ParsingError.Errors.BadTimeStamp,
8327                            "Malformed timestamp: " + oInput);
8328    }
8329    // Remove time stamp from input.
8330    input = input.replace(/^[^\sa-zA-Z-]+/, "");
8331    return ts;
8332  }
8333
8334  // 4.4.2 WebVTT cue settings
8335  function consumeCueSettings(input, cue) {
8336    var settings = new Settings();
8337
8338    parseOptions(input, function (k, v) {
8339      switch (k) {
8340      case "region":
8341        // Find the last region we parsed with the same region id.
8342        for (var i = regionList.length - 1; i >= 0; i--) {
8343          if (regionList[i].id === v) {
8344            settings.set(k, regionList[i].region);
8345            break;
8346          }
8347        }
8348        break;
8349      case "vertical":
8350        settings.alt(k, v, ["rl", "lr"]);
8351        break;
8352      case "line":
8353        var vals = v.split(","),
8354            vals0 = vals[0];
8355        settings.integer(k, vals0);
8356        settings.percent(k, vals0) ? settings.set("snapToLines", false) : null;
8357        settings.alt(k, vals0, ["auto"]);
8358        if (vals.length === 2) {
8359          settings.alt("lineAlign", vals[1], ["start", "middle", "end"]);
8360        }
8361        break;
8362      case "position":
8363        vals = v.split(",");
8364        settings.percent(k, vals[0]);
8365        if (vals.length === 2) {
8366          settings.alt("positionAlign", vals[1], ["start", "middle", "end"]);
8367        }
8368        break;
8369      case "size":
8370        settings.percent(k, v);
8371        break;
8372      case "align":
8373        settings.alt(k, v, ["start", "middle", "end", "left", "right"]);
8374        break;
8375      }
8376    }, /:/, /\s/);
8377
8378    // Apply default values for any missing fields.
8379    cue.region = settings.get("region", null);
8380    cue.vertical = settings.get("vertical", "");
8381    cue.line = settings.get("line", "auto");
8382    cue.lineAlign = settings.get("lineAlign", "start");
8383    cue.snapToLines = settings.get("snapToLines", true);
8384    cue.size = settings.get("size", 100);
8385    cue.align = settings.get("align", "middle");
8386    cue.position = settings.get("position", {
8387      start: 0,
8388      left: 0,
8389      middle: 50,
8390      end: 100,
8391      right: 100
8392    }, cue.align);
8393    cue.positionAlign = settings.get("positionAlign", {
8394      start: "start",
8395      left: "start",
8396      middle: "middle",
8397      end: "end",
8398      right: "end"
8399    }, cue.align);
8400  }
8401
8402  function skipWhitespace() {
8403    input = input.replace(/^\s+/, "");
8404  }
8405
8406  // 4.1 WebVTT cue timings.
8407  skipWhitespace();
8408  cue.startTime = consumeTimeStamp();   // (1) collect cue start time
8409  skipWhitespace();
8410  if (input.substr(0, 3) !== "-->") {     // (3) next characters must match "-->"
8411    throw new ParsingError(ParsingError.Errors.BadTimeStamp,
8412                           "Malformed time stamp (time stamps must be separated by '-->'): " +
8413                           oInput);
8414  }
8415  input = input.substr(3);
8416  skipWhitespace();
8417  cue.endTime = consumeTimeStamp();     // (5) collect cue end time
8418
8419  // 4.1 WebVTT cue settings list.
8420  skipWhitespace();
8421  consumeCueSettings(input, cue);
8422}
8423
8424var ESCAPE = {
8425  "&amp;": "&",
8426  "&lt;": "<",
8427  "&gt;": ">",
8428  "&lrm;": "\u200e",
8429  "&rlm;": "\u200f",
8430  "&nbsp;": "\u00a0"
8431};
8432
8433var TAG_NAME = {
8434  c: "span",
8435  i: "i",
8436  b: "b",
8437  u: "u",
8438  ruby: "ruby",
8439  rt: "rt",
8440  v: "span",
8441  lang: "span"
8442};
8443
8444var TAG_ANNOTATION = {
8445  v: "title",
8446  lang: "lang"
8447};
8448
8449var NEEDS_PARENT = {
8450  rt: "ruby"
8451};
8452
8453// Parse content into a document fragment.
8454function parseContent(window, input) {
8455  function nextToken() {
8456    // Check for end-of-string.
8457    if (!input) {
8458      return null;
8459    }
8460
8461    // Consume 'n' characters from the input.
8462    function consume(result) {
8463      input = input.substr(result.length);
8464      return result;
8465    }
8466
8467    var m = input.match(/^([^<]*)(<[^>]+>?)?/);
8468    // If there is some text before the next tag, return it, otherwise return
8469    // the tag.
8470    return consume(m[1] ? m[1] : m[2]);
8471  }
8472
8473  // Unescape a string 's'.
8474  function unescape1(e) {
8475    return ESCAPE[e];
8476  }
8477  function unescape(s) {
8478    while ((m = s.match(/&(amp|lt|gt|lrm|rlm|nbsp);/))) {
8479      s = s.replace(m[0], unescape1);
8480    }
8481    return s;
8482  }
8483
8484  function shouldAdd(current, element) {
8485    return !NEEDS_PARENT[element.localName] ||
8486           NEEDS_PARENT[element.localName] === current.localName;
8487  }
8488
8489  // Create an element for this tag.
8490  function createElement(type, annotation) {
8491    var tagName = TAG_NAME[type];
8492    if (!tagName) {
8493      return null;
8494    }
8495    var element = window.document.createElement(tagName);
8496    element.localName = tagName;
8497    var name = TAG_ANNOTATION[type];
8498    if (name && annotation) {
8499      element[name] = annotation.trim();
8500    }
8501    return element;
8502  }
8503
8504  var rootDiv = window.document.createElement("div"),
8505      current = rootDiv,
8506      t,
8507      tagStack = [];
8508
8509  while ((t = nextToken()) !== null) {
8510    if (t[0] === '<') {
8511      if (t[1] === "/") {
8512        // If the closing tag matches, move back up to the parent node.
8513        if (tagStack.length &&
8514            tagStack[tagStack.length - 1] === t.substr(2).replace(">", "")) {
8515          tagStack.pop();
8516          current = current.parentNode;
8517        }
8518        // Otherwise just ignore the end tag.
8519        continue;
8520      }
8521      var ts = parseTimeStamp(t.substr(1, t.length - 2));
8522      var node;
8523      if (ts) {
8524        // Timestamps are lead nodes as well.
8525        node = window.document.createProcessingInstruction("timestamp", ts);
8526        current.appendChild(node);
8527        continue;
8528      }
8529      var m = t.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/);
8530      // If we can't parse the tag, skip to the next tag.
8531      if (!m) {
8532        continue;
8533      }
8534      // Try to construct an element, and ignore the tag if we couldn't.
8535      node = createElement(m[1], m[3]);
8536      if (!node) {
8537        continue;
8538      }
8539      // Determine if the tag should be added based on the context of where it
8540      // is placed in the cuetext.
8541      if (!shouldAdd(current, node)) {
8542        continue;
8543      }
8544      // Set the class list (as a list of classes, separated by space).
8545      if (m[2]) {
8546        node.className = m[2].substr(1).replace('.', ' ');
8547      }
8548      // Append the node to the current node, and enter the scope of the new
8549      // node.
8550      tagStack.push(m[1]);
8551      current.appendChild(node);
8552      current = node;
8553      continue;
8554    }
8555
8556    // Text nodes are leaf nodes.
8557    current.appendChild(window.document.createTextNode(unescape(t)));
8558  }
8559
8560  return rootDiv;
8561}
8562
8563// This is a list of all the Unicode characters that have a strong
8564// right-to-left category. What this means is that these characters are
8565// written right-to-left for sure. It was generated by pulling all the strong
8566// right-to-left characters out of the Unicode data table. That table can
8567// found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
8568var strongRTLRanges = [[0x5be, 0x5be], [0x5c0, 0x5c0], [0x5c3, 0x5c3], [0x5c6, 0x5c6],
8569 [0x5d0, 0x5ea], [0x5f0, 0x5f4], [0x608, 0x608], [0x60b, 0x60b], [0x60d, 0x60d],
8570 [0x61b, 0x61b], [0x61e, 0x64a], [0x66d, 0x66f], [0x671, 0x6d5], [0x6e5, 0x6e6],
8571 [0x6ee, 0x6ef], [0x6fa, 0x70d], [0x70f, 0x710], [0x712, 0x72f], [0x74d, 0x7a5],
8572 [0x7b1, 0x7b1], [0x7c0, 0x7ea], [0x7f4, 0x7f5], [0x7fa, 0x7fa], [0x800, 0x815],
8573 [0x81a, 0x81a], [0x824, 0x824], [0x828, 0x828], [0x830, 0x83e], [0x840, 0x858],
8574 [0x85e, 0x85e], [0x8a0, 0x8a0], [0x8a2, 0x8ac], [0x200f, 0x200f],
8575 [0xfb1d, 0xfb1d], [0xfb1f, 0xfb28], [0xfb2a, 0xfb36], [0xfb38, 0xfb3c],
8576 [0xfb3e, 0xfb3e], [0xfb40, 0xfb41], [0xfb43, 0xfb44], [0xfb46, 0xfbc1],
8577 [0xfbd3, 0xfd3d], [0xfd50, 0xfd8f], [0xfd92, 0xfdc7], [0xfdf0, 0xfdfc],
8578 [0xfe70, 0xfe74], [0xfe76, 0xfefc], [0x10800, 0x10805], [0x10808, 0x10808],
8579 [0x1080a, 0x10835], [0x10837, 0x10838], [0x1083c, 0x1083c], [0x1083f, 0x10855],
8580 [0x10857, 0x1085f], [0x10900, 0x1091b], [0x10920, 0x10939], [0x1093f, 0x1093f],
8581 [0x10980, 0x109b7], [0x109be, 0x109bf], [0x10a00, 0x10a00], [0x10a10, 0x10a13],
8582 [0x10a15, 0x10a17], [0x10a19, 0x10a33], [0x10a40, 0x10a47], [0x10a50, 0x10a58],
8583 [0x10a60, 0x10a7f], [0x10b00, 0x10b35], [0x10b40, 0x10b55], [0x10b58, 0x10b72],
8584 [0x10b78, 0x10b7f], [0x10c00, 0x10c48], [0x1ee00, 0x1ee03], [0x1ee05, 0x1ee1f],
8585 [0x1ee21, 0x1ee22], [0x1ee24, 0x1ee24], [0x1ee27, 0x1ee27], [0x1ee29, 0x1ee32],
8586 [0x1ee34, 0x1ee37], [0x1ee39, 0x1ee39], [0x1ee3b, 0x1ee3b], [0x1ee42, 0x1ee42],
8587 [0x1ee47, 0x1ee47], [0x1ee49, 0x1ee49], [0x1ee4b, 0x1ee4b], [0x1ee4d, 0x1ee4f],
8588 [0x1ee51, 0x1ee52], [0x1ee54, 0x1ee54], [0x1ee57, 0x1ee57], [0x1ee59, 0x1ee59],
8589 [0x1ee5b, 0x1ee5b], [0x1ee5d, 0x1ee5d], [0x1ee5f, 0x1ee5f], [0x1ee61, 0x1ee62],
8590 [0x1ee64, 0x1ee64], [0x1ee67, 0x1ee6a], [0x1ee6c, 0x1ee72], [0x1ee74, 0x1ee77],
8591 [0x1ee79, 0x1ee7c], [0x1ee7e, 0x1ee7e], [0x1ee80, 0x1ee89], [0x1ee8b, 0x1ee9b],
8592 [0x1eea1, 0x1eea3], [0x1eea5, 0x1eea9], [0x1eeab, 0x1eebb], [0x10fffd, 0x10fffd]];
8593
8594function isStrongRTLChar(charCode) {
8595  for (var i = 0; i < strongRTLRanges.length; i++) {
8596    var currentRange = strongRTLRanges[i];
8597    if (charCode >= currentRange[0] && charCode <= currentRange[1]) {
8598      return true;
8599    }
8600  }
8601
8602  return false;
8603}
8604
8605function determineBidi(cueDiv) {
8606  var nodeStack = [],
8607      text = "",
8608      charCode;
8609
8610  if (!cueDiv || !cueDiv.childNodes) {
8611    return "ltr";
8612  }
8613
8614  function pushNodes(nodeStack, node) {
8615    for (var i = node.childNodes.length - 1; i >= 0; i--) {
8616      nodeStack.push(node.childNodes[i]);
8617    }
8618  }
8619
8620  function nextTextNode(nodeStack) {
8621    if (!nodeStack || !nodeStack.length) {
8622      return null;
8623    }
8624
8625    var node = nodeStack.pop(),
8626        text = node.textContent || node.innerText;
8627    if (text) {
8628      // TODO: This should match all unicode type B characters (paragraph
8629      // separator characters). See issue #115.
8630      var m = text.match(/^.*(\n|\r)/);
8631      if (m) {
8632        nodeStack.length = 0;
8633        return m[0];
8634      }
8635      return text;
8636    }
8637    if (node.tagName === "ruby") {
8638      return nextTextNode(nodeStack);
8639    }
8640    if (node.childNodes) {
8641      pushNodes(nodeStack, node);
8642      return nextTextNode(nodeStack);
8643    }
8644  }
8645
8646  pushNodes(nodeStack, cueDiv);
8647  while ((text = nextTextNode(nodeStack))) {
8648    for (var i = 0; i < text.length; i++) {
8649      charCode = text.charCodeAt(i);
8650      if (isStrongRTLChar(charCode)) {
8651        return "rtl";
8652      }
8653    }
8654  }
8655  return "ltr";
8656}
8657
8658function computeLinePos(cue) {
8659  if (typeof cue.line === "number" &&
8660      (cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) {
8661    return cue.line;
8662  }
8663  if (!cue.track || !cue.track.textTrackList ||
8664      !cue.track.textTrackList.mediaElement) {
8665    return -1;
8666  }
8667  var track = cue.track,
8668      trackList = track.textTrackList,
8669      count = 0;
8670  for (var i = 0; i < trackList.length && trackList[i] !== track; i++) {
8671    if (trackList[i].mode === "showing") {
8672      count++;
8673    }
8674  }
8675  return ++count * -1;
8676}
8677
8678function StyleBox() {
8679}
8680
8681// Apply styles to a div. If there is no div passed then it defaults to the
8682// div on 'this'.
8683StyleBox.prototype.applyStyles = function(styles, div) {
8684  div = div || this.div;
8685  for (var prop in styles) {
8686    if (styles.hasOwnProperty(prop)) {
8687      div.style[prop] = styles[prop];
8688    }
8689  }
8690};
8691
8692StyleBox.prototype.formatStyle = function(val, unit) {
8693  return val === 0 ? 0 : val + unit;
8694};
8695
8696// Constructs the computed display state of the cue (a div). Places the div
8697// into the overlay which should be a block level element (usually a div).
8698function CueStyleBox(window, cue, styleOptions) {
8699  var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
8700  var color = "rgba(255, 255, 255, 1)";
8701  var backgroundColor = "rgba(0, 0, 0, 0.8)";
8702
8703  if (isIE8) {
8704    color = "rgb(255, 255, 255)";
8705    backgroundColor = "rgb(0, 0, 0)";
8706  }
8707
vendor: 5,880 bytes, lines 8708-8884
8708  StyleBox.call(this);
8709  this.cue = cue;
8710
8711  // Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will
8712  // have inline positioning and will function as the cue background box.
8713  this.cueDiv = parseContent(window, cue.text);
8714  var styles = {
8715    color: color,
8716    backgroundColor: backgroundColor,
8717    position: "relative",
8718    left: 0,
8719    right: 0,
8720    top: 0,
8721    bottom: 0,
8722    display: "inline"
8723  };
8724
8725  if (!isIE8) {
8726    styles.writingMode = cue.vertical === "" ? "horizontal-tb"
8727                                             : cue.vertical === "lr" ? "vertical-lr"
8728                                                                     : "vertical-rl";
8729    styles.unicodeBidi = "plaintext";
8730  }
8731  this.applyStyles(styles, this.cueDiv);
8732
8733  // Create an absolutely positioned div that will be used to position the cue
8734  // div. Note, all WebVTT cue-setting alignments are equivalent to the CSS
8735  // mirrors of them except "middle" which is "center" in CSS.
8736  this.div = window.document.createElement("div");
8737  styles = {
8738    textAlign: cue.align === "middle" ? "center" : cue.align,
8739    font: styleOptions.font,
8740    whiteSpace: "pre-line",
8741    position: "absolute"
8742  };
8743
8744  if (!isIE8) {
8745    styles.direction = determineBidi(this.cueDiv);
8746    styles.writingMode = cue.vertical === "" ? "horizontal-tb"
8747                                             : cue.vertical === "lr" ? "vertical-lr"
8748                                                                     : "vertical-rl".
8749    stylesunicodeBidi =  "plaintext";
8750  }
8751
8752  this.applyStyles(styles);
8753
8754  this.div.appendChild(this.cueDiv);
8755
8756  // Calculate the distance from the reference edge of the viewport to the text
8757  // position of the cue box. The reference edge will be resolved later when
8758  // the box orientation styles are applied.
8759  var textPos = 0;
8760  switch (cue.positionAlign) {
8761  case "start":
8762    textPos = cue.position;
8763    break;
8764  case "middle":
8765    textPos = cue.position - (cue.size / 2);
8766    break;
8767  case "end":
8768    textPos = cue.position - cue.size;
8769    break;
8770  }
8771
8772  // Horizontal box orientation; textPos is the distance from the left edge of the
8773  // area to the left edge of the box and cue.size is the distance extending to
8774  // the right from there.
8775  if (cue.vertical === "") {
8776    this.applyStyles({
8777      left:  this.formatStyle(textPos, "%"),
8778      width: this.formatStyle(cue.size, "%")
8779    });
8780  // Vertical box orientation; textPos is the distance from the top edge of the
8781  // area to the top edge of the box and cue.size is the height extending
8782  // downwards from there.
8783  } else {
8784    this.applyStyles({
8785      top: this.formatStyle(textPos, "%"),
8786      height: this.formatStyle(cue.size, "%")
8787    });
8788  }
8789
8790  this.move = function(box) {
8791    this.applyStyles({
8792      top: this.formatStyle(box.top, "px"),
8793      bottom: this.formatStyle(box.bottom, "px"),
8794      left: this.formatStyle(box.left, "px"),
8795      right: this.formatStyle(box.right, "px"),
8796      height: this.formatStyle(box.height, "px"),
8797      width: this.formatStyle(box.width, "px")
8798    });
8799  };
8800}
8801CueStyleBox.prototype = _objCreate(StyleBox.prototype);
8802CueStyleBox.prototype.constructor = CueStyleBox;
8803
8804// Represents the co-ordinates of an Element in a way that we can easily
8805// compute things with such as if it overlaps or intersects with another Element.
8806// Can initialize it with either a StyleBox or another BoxPosition.
8807function BoxPosition(obj) {
8808  var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
8809
8810  // Either a BoxPosition was passed in and we need to copy it, or a StyleBox
8811  // was passed in and we need to copy the results of 'getBoundingClientRect'
8812  // as the object returned is readonly. All co-ordinate values are in reference
8813  // to the viewport origin (top left).
8814  var lh, height, width, top;
8815  if (obj.div) {
8816    height = obj.div.offsetHeight;
8817    width = obj.div.offsetWidth;
8818    top = obj.div.offsetTop;
8819
8820    var rects = (rects = obj.div.childNodes) && (rects = rects[0]) &&
8821                rects.getClientRects && rects.getClientRects();
8822    obj = obj.div.getBoundingClientRect();
8823    // In certain cases the outter div will be slightly larger then the sum of
8824    // the inner div's lines. This could be due to bold text, etc, on some platforms.
8825    // In this case we should get the average line height and use that. This will
8826    // result in the desired behaviour.
8827    lh = rects ? Math.max((rects[0] && rects[0].height) || 0, obj.height / rects.length)
8828               : 0;
8829
8830  }
8831  this.left = obj.left;
8832  this.right = obj.right;
8833  this.top = obj.top || top;
8834  this.height = obj.height || height;
8835  this.bottom = obj.bottom || (top + (obj.height || height));
8836  this.width = obj.width || width;
8837  this.lineHeight = lh !== undefined ? lh : obj.lineHeight;
8838
8839  if (isIE8 && !this.lineHeight) {
8840    this.lineHeight = 13;
8841  }
8842}
8843
8844// Move the box along a particular axis. Optionally pass in an amount to move
8845// the box. If no amount is passed then the default is the line height of the
8846// box.
8847BoxPosition.prototype.move = function(axis, toMove) {
8848  toMove = toMove !== undefined ? toMove : this.lineHeight;
8849  switch (axis) {
8850  case "+x":
8851    this.left += toMove;
8852    this.right += toMove;
8853    break;
8854  case "-x":
8855    this.left -= toMove;
8856    this.right -= toMove;
8857    break;
8858  case "+y":
8859    this.top += toMove;
8860    this.bottom += toMove;
8861    break;
8862  case "-y":
8863    this.top -= toMove;
8864    this.bottom -= toMove;
8865    break;
8866  }
8867};
8868
8869// Check if this box overlaps another box, b2.
8870BoxPosition.prototype.overlaps = function(b2) {
8871  return this.left < b2.right &&
8872         this.right > b2.left &&
8873         this.top < b2.bottom &&
8874         this.bottom > b2.top;
8875};
8876
8877// Check if this box overlaps any other boxes in boxes.
8878BoxPosition.prototype.overlapsAny = function(boxes) {
8879  for (var i = 0; i < boxes.length; i++) {
8880    if (this.overlaps(boxes[i])) {
8881      return true;
8882    }
8883  }
8884  return false;
vendor: 6,016 bytes, lines 8885-9048
8885};
8886
8887// Check if this box is within another box.
8888BoxPosition.prototype.within = function(container) {
8889  return this.top >= container.top &&
8890         this.bottom <= container.bottom &&
8891         this.left >= container.left &&
8892         this.right <= container.right;
8893};
8894
8895// Check if this box is entirely within the container or it is overlapping
8896// on the edge opposite of the axis direction passed. For example, if "+x" is
8897// passed and the box is overlapping on the left edge of the container, then
8898// return true.
8899BoxPosition.prototype.overlapsOppositeAxis = function(container, axis) {
8900  switch (axis) {
8901  case "+x":
8902    return this.left < container.left;
8903  case "-x":
8904    return this.right > container.right;
8905  case "+y":
8906    return this.top < container.top;
8907  case "-y":
8908    return this.bottom > container.bottom;
8909  }
8910};
8911
8912// Find the percentage of the area that this box is overlapping with another
8913// box.
8914BoxPosition.prototype.intersectPercentage = function(b2) {
8915  var x = Math.max(0, Math.min(this.right, b2.right) - Math.max(this.left, b2.left)),
8916      y = Math.max(0, Math.min(this.bottom, b2.bottom) - Math.max(this.top, b2.top)),
8917      intersectArea = x * y;
8918  return intersectArea / (this.height * this.width);
8919};
8920
8921// Convert the positions from this box to CSS compatible positions using
8922// the reference container's positions. This has to be done because this
8923// box's positions are in reference to the viewport origin, whereas, CSS
8924// values are in referecne to their respective edges.
8925BoxPosition.prototype.toCSSCompatValues = function(reference) {
8926  return {
8927    top: this.top - reference.top,
8928    bottom: reference.bottom - this.bottom,
8929    left: this.left - reference.left,
8930    right: reference.right - this.right,
8931    height: this.height,
8932    width: this.width
8933  };
8934};
8935
8936// Get an object that represents the box's position without anything extra.
8937// Can pass a StyleBox, HTMLElement, or another BoxPositon.
8938BoxPosition.getSimpleBoxPosition = function(obj) {
8939  var height = obj.div ? obj.div.offsetHeight : obj.tagName ? obj.offsetHeight : 0;
8940  var width = obj.div ? obj.div.offsetWidth : obj.tagName ? obj.offsetWidth : 0;
8941  var top = obj.div ? obj.div.offsetTop : obj.tagName ? obj.offsetTop : 0;
8942
8943  obj = obj.div ? obj.div.getBoundingClientRect() :
8944                obj.tagName ? obj.getBoundingClientRect() : obj;
8945  var ret = {
8946    left: obj.left,
8947    right: obj.right,
8948    top: obj.top || top,
8949    height: obj.height || height,
8950    bottom: obj.bottom || (top + (obj.height || height)),
8951    width: obj.width || width
8952  };
8953  return ret;
8954};
8955
8956// Move a StyleBox to its specified, or next best, position. The containerBox
8957// is the box that contains the StyleBox, such as a div. boxPositions are
8958// a list of other boxes that the styleBox can't overlap with.
8959function moveBoxToLinePosition(window, styleBox, containerBox, boxPositions) {
8960
8961  // Find the best position for a cue box, b, on the video. The axis parameter
8962  // is a list of axis, the order of which, it will move the box along. For example:
8963  // Passing ["+x", "-x"] will move the box first along the x axis in the positive
8964  // direction. If it doesn't find a good position for it there it will then move
8965  // it along the x axis in the negative direction.
8966  function findBestPosition(b, axis) {
8967    var bestPosition,
8968        specifiedPosition = new BoxPosition(b),
8969        percentage = 1; // Highest possible so the first thing we get is better.
8970
8971    for (var i = 0; i < axis.length; i++) {
8972      while (b.overlapsOppositeAxis(containerBox, axis[i]) ||
8973             (b.within(containerBox) && b.overlapsAny(boxPositions))) {
8974        b.move(axis[i]);
8975      }
8976      // We found a spot where we aren't overlapping anything. This is our
8977      // best position.
8978      if (b.within(containerBox)) {
8979        return b;
8980      }
8981      var p = b.intersectPercentage(containerBox);
8982      // If we're outside the container box less then we were on our last try
8983      // then remember this position as the best position.
8984      if (percentage > p) {
8985        bestPosition = new BoxPosition(b);
8986        percentage = p;
8987      }
8988      // Reset the box position to the specified position.
8989      b = new BoxPosition(specifiedPosition);
8990    }
8991    return bestPosition || specifiedPosition;
8992  }
8993
8994  var boxPosition = new BoxPosition(styleBox),
8995      cue = styleBox.cue,
8996      linePos = computeLinePos(cue),
8997      axis = [];
8998
8999  // If we have a line number to align the cue to.
9000  if (cue.snapToLines) {
9001    var size;
9002    switch (cue.vertical) {
9003    case "":
9004      axis = [ "+y", "-y" ];
9005      size = "height";
9006      break;
9007    case "rl":
9008      axis = [ "+x", "-x" ];
9009      size = "width";
9010      break;
9011    case "lr":
9012      axis = [ "-x", "+x" ];
9013      size = "width";
9014      break;
9015    }
9016
9017    var step = boxPosition.lineHeight,
9018        position = step * Math.round(linePos),
9019        maxPosition = containerBox[size] + step,
9020        initialAxis = axis[0];
9021
9022    // If the specified intial position is greater then the max position then
9023    // clamp the box to the amount of steps it would take for the box to
9024    // reach the max position.
9025    if (Math.abs(position) > maxPosition) {
9026      position = position < 0 ? -1 : 1;
9027      position *= Math.ceil(maxPosition / step) * step;
9028    }
9029
9030    // If computed line position returns negative then line numbers are
9031    // relative to the bottom of the video instead of the top. Therefore, we
9032    // need to increase our initial position by the length or width of the
9033    // video, depending on the writing direction, and reverse our axis directions.
9034    if (linePos < 0) {
9035      position += cue.vertical === "" ? containerBox.height : containerBox.width;
9036      axis = axis.reverse();
9037    }
9038
9039    // Move the box to the specified position. This may not be its best
9040    // position.
9041    boxPosition.move(initialAxis, position);
9042
9043  } else {
9044    // If we have a percentage line value for the cue.
9045    var calculatedPercentage = (boxPosition.lineHeight / containerBox.height) * 100;
9046
9047    switch (cue.lineAlign) {
9048    case "middle
vendor: 10,353 bytes, lines 9048-9409
9048":
9049      linePos -= (calculatedPercentage / 2);
9050      break;
9051    case "end":
9052      linePos -= calculatedPercentage;
9053      break;
9054    }
9055
9056    // Apply initial line position to the cue box.
9057    switch (cue.vertical) {
9058    case "":
9059      styleBox.applyStyles({
9060        top: styleBox.formatStyle(linePos, "%")
9061      });
9062      break;
9063    case "rl":
9064      styleBox.applyStyles({
9065        left: styleBox.formatStyle(linePos, "%")
9066      });
9067      break;
9068    case "lr":
9069      styleBox.applyStyles({
9070        right: styleBox.formatStyle(linePos, "%")
9071      });
9072      break;
9073    }
9074
9075    axis = [ "+y", "-x", "+x", "-y" ];
9076
9077    // Get the box position again after we've applied the specified positioning
9078    // to it.
9079    boxPosition = new BoxPosition(styleBox);
9080  }
9081
9082  var bestPosition = findBestPosition(boxPosition, axis);
9083  styleBox.move(bestPosition.toCSSCompatValues(containerBox));
9084}
9085
9086function WebVTT$1() {
9087  // Nothing
9088}
9089
9090// Helper to allow strings to be decoded instead of the default binary utf8 data.
9091WebVTT$1.StringDecoder = function() {
9092  return {
9093    decode: function(data) {
9094      if (!data) {
9095        return "";
9096      }
9097      if (typeof data !== "string") {
9098        throw new Error("Error - expected string data.");
9099      }
9100      return decodeURIComponent(encodeURIComponent(data));
9101    }
9102  };
9103};
9104
9105WebVTT$1.convertCueToDOMTree = function(window, cuetext) {
9106  if (!window || !cuetext) {
9107    return null;
9108  }
9109  return parseContent(window, cuetext);
9110};
9111
9112var FONT_SIZE_PERCENT = 0.05;
9113var FONT_STYLE = "sans-serif";
9114var CUE_BACKGROUND_PADDING = "1.5%";
9115
9116// Runs the processing model over the cues and regions passed to it.
9117// @param overlay A block level element (usually a div) that the computed cues
9118//                and regions will be placed into.
9119WebVTT$1.processCues = function(window, cues, overlay) {
9120  if (!window || !cues || !overlay) {
9121    return null;
9122  }
9123
9124  // Remove all previous children.
9125  while (overlay.firstChild) {
9126    overlay.removeChild(overlay.firstChild);
9127  }
9128
9129  var paddedOverlay = window.document.createElement("div");
9130  paddedOverlay.style.position = "absolute";
9131  paddedOverlay.style.left = "0";
9132  paddedOverlay.style.right = "0";
9133  paddedOverlay.style.top = "0";
9134  paddedOverlay.style.bottom = "0";
9135  paddedOverlay.style.margin = CUE_BACKGROUND_PADDING;
9136  overlay.appendChild(paddedOverlay);
9137
9138  // Determine if we need to compute the display states of the cues. This could
9139  // be the case if a cue's state has been changed since the last computation or
9140  // if it has not been computed yet.
9141  function shouldCompute(cues) {
9142    for (var i = 0; i < cues.length; i++) {
9143      if (cues[i].hasBeenReset || !cues[i].displayState) {
9144        return true;
9145      }
9146    }
9147    return false;
9148  }
9149
9150  // We don't need to recompute the cues' display states. Just reuse them.
9151  if (!shouldCompute(cues)) {
9152    for (var i = 0; i < cues.length; i++) {
9153      paddedOverlay.appendChild(cues[i].displayState);
9154    }
9155    return;
9156  }
9157
9158  var boxPositions = [],
9159      containerBox = BoxPosition.getSimpleBoxPosition(paddedOverlay),
9160      fontSize = Math.round(containerBox.height * FONT_SIZE_PERCENT * 100) / 100;
9161  var styleOptions = {
9162    font: fontSize + "px " + FONT_STYLE
9163  };
9164
9165  (function() {
9166    var styleBox, cue;
9167
9168    for (var i = 0; i < cues.length; i++) {
9169      cue = cues[i];
9170
9171      // Compute the intial position and styles of the cue div.
9172      styleBox = new CueStyleBox(window, cue, styleOptions);
9173      paddedOverlay.appendChild(styleBox.div);
9174
9175      // Move the cue div to it's correct line position.
9176      moveBoxToLinePosition(window, styleBox, containerBox, boxPositions);
9177
9178      // Remember the computed div so that we don't have to recompute it later
9179      // if we don't have too.
9180      cue.displayState = styleBox.div;
9181
9182      boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox));
9183    }
9184  })();
9185};
9186
9187WebVTT$1.Parser = function(window, vttjs, decoder) {
9188  if (!decoder) {
9189    decoder = vttjs;
9190    vttjs = {};
9191  }
9192  if (!vttjs) {
9193    vttjs = {};
9194  }
9195
9196  this.window = window;
9197  this.vttjs = vttjs;
9198  this.state = "INITIAL";
9199  this.buffer = "";
9200  this.decoder = decoder || new TextDecoder("utf8");
9201  this.regionList = [];
9202};
9203
9204WebVTT$1.Parser.prototype = {
9205  // If the error is a ParsingError then report it to the consumer if
9206  // possible. If it's not a ParsingError then throw it like normal.
9207  reportOrThrowError: function(e) {
9208    if (e instanceof ParsingError) {
9209      this.onparsingerror && this.onparsingerror(e);
9210    } else {
9211      throw e;
9212    }
9213  },
9214  parse: function (data) {
9215    var self = this;
9216
9217    // If there is no data then we won't decode it, but will just try to parse
9218    // whatever is in buffer already. This may occur in circumstances, for
9219    // example when flush() is called.
9220    if (data) {
9221      // Try to decode the data that we received.
9222      self.buffer += self.decoder.decode(data, {stream: true});
9223    }
9224
9225    function collectNextLine() {
9226      var buffer = self.buffer;
9227      var pos = 0;
9228      while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
9229        ++pos;
9230      }
9231      var line = buffer.substr(0, pos);
9232      // Advance the buffer early in case we fail below.
9233      if (buffer[pos] === '\r') {
9234        ++pos;
9235      }
9236      if (buffer[pos] === '\n') {
9237        ++pos;
9238      }
9239      self.buffer = buffer.substr(pos);
9240      return line;
9241    }
9242
9243    // 3.4 WebVTT region and WebVTT region settings syntax
9244    function parseRegion(input) {
9245      var settings = new Settings();
9246
9247      parseOptions(input, function (k, v) {
9248        switch (k) {
9249        case "id":
9250          settings.set(k, v);
9251          break;
9252        case "width":
9253          settings.percent(k, v);
9254          break;
9255        case "lines":
9256          settings.integer(k, v);
9257          break;
9258        case "regionanchor":
9259        case "viewportanchor":
9260          var xy = v.split(',');
9261          if (xy.length !== 2) {
9262            break;
9263          }
9264          // We have to make sure both x and y parse, so use a temporary
9265          // settings object here.
9266          var anchor = new Settings();
9267          anchor.percent("x", xy[0]);
9268          anchor.percent("y", xy[1]);
9269          if (!anchor.has("x") || !anchor.has("y")) {
9270            break;
9271          }
9272          settings.set(k + "X", anchor.get("x"));
9273          settings.set(k + "Y", anchor.get("y"));
9274          break;
9275        case "scroll":
9276          settings.alt(k, v, ["up"]);
9277          break;
9278        }
9279      }, /=/, /\s/);
9280
9281      // Create the region, using default values for any values that were not
9282      // specified.
9283      if (settings.has("id")) {
9284        var region = new (self.vttjs.VTTRegion || self.window.VTTRegion)();
9285        region.width = settings.get("width", 100);
9286        region.lines = settings.get("lines", 3);
9287        region.regionAnchorX = settings.get("regionanchorX", 0);
9288        region.regionAnchorY = settings.get("regionanchorY", 100);
9289        region.viewportAnchorX = settings.get("viewportanchorX", 0);
9290        region.viewportAnchorY = settings.get("viewportanchorY", 100);
9291        region.scroll = settings.get("scroll", "");
9292        // Register the region.
9293        self.onregion && self.onregion(region);
9294        // Remember the VTTRegion for later in case we parse any VTTCues that
9295        // reference it.
9296        self.regionList.push({
9297          id: settings.get("id"),
9298          region: region
9299        });
9300      }
9301    }
9302
9303    // draft-pantos-http-live-streaming-20
9304    // https://tools.ietf.org/html/draft-pantos-http-live-streaming-20#section-3.5
9305    // 3.5 WebVTT
9306    function parseTimestampMap(input) {
9307      var settings = new Settings();
9308
9309      parseOptions(input, function(k, v) {
9310        switch(k) {
9311        case "MPEGT":
9312          settings.integer(k + 'S', v);
9313          break;
9314        case "LOCA":
9315          settings.set(k + 'L', parseTimeStamp(v));
9316          break;
9317        }
9318      }, /[^\d]:/, /,/);
9319
9320      self.ontimestampmap && self.ontimestampmap({
9321        "MPEGTS": settings.get("MPEGTS"),
9322        "LOCAL": settings.get("LOCAL")
9323      });
9324    }
9325
9326    // 3.2 WebVTT metadata header syntax
9327    function parseHeader(input) {
9328      if (input.match(/X-TIMESTAMP-MAP/)) {
9329        // This line contains HLS X-TIMESTAMP-MAP metadata
9330        parseOptions(input, function(k, v) {
9331          switch(k) {
9332          case "X-TIMESTAMP-MAP":
9333            parseTimestampMap(v);
9334            break;
9335          }
9336        }, /=/);
9337      } else {
9338        parseOptions(input, function (k, v) {
9339          switch (k) {
9340          case "Region":
9341            // 3.3 WebVTT region metadata header syntax
9342            parseRegion(v);
9343            break;
9344          }
9345        }, /:/);
9346      }
9347
9348    }
9349
9350    // 5.1 WebVTT file parsing.
9351    try {
9352      var line;
9353      if (self.state === "INITIAL") {
9354        // We can't start parsing until we have the first line.
9355        if (!/\r\n|\n/.test(self.buffer)) {
9356          return this;
9357        }
9358
9359        line = collectNextLine();
9360
9361        var m = line.match(/^WEBVTT([ \t].*)?$/);
9362        if (!m || !m[0]) {
9363          throw new ParsingError(ParsingError.Errors.BadSignature);
9364        }
9365
9366        self.state = "HEADER";
9367      }
9368
9369      var alreadyCollectedLine = false;
9370      while (self.buffer) {
9371        // We can't parse a line until we have the full line.
9372        if (!/\r\n|\n/.test(self.buffer)) {
9373          return this;
9374        }
9375
9376        if (!alreadyCollectedLine) {
9377          line = collectNextLine();
9378        } else {
9379          alreadyCollectedLine = false;
9380        }
9381
9382        switch (self.state) {
9383        case "HEADER":
9384          // 13-18 - Allow a header (metadata) under the WEBVTT line.
9385          if (/:/.test(line)) {
9386            parseHeader(line);
9387          } else if (!line) {
9388            // An empty line terminates the header and starts the body (cues).
9389            self.state = "ID";
9390          }
9391          continue;
9392        case "NOTE":
9393          // Ignore NOTE blocks.
9394          if (!line) {
9395            self.state = "ID";
9396          }
9397          continue;
9398        case "ID":
9399          // Check for the start of NOTE blocks.
9400          if (/^NOTE($|[ \t])/.test(line)) {
9401            self.state = "NOTE";
9402            break;
9403          }
9404          // 19-29 - Allow any number of line terminators, then initialize new cue values.
9405          if (!line) {
9406            continue;
9407          }
9408          self.cue = new (self.vttjs.VTTCue || self.window.VTTCue)(0, 0, "");
9409          
vendor: 2,935 bytes, lines 9409-9499
9409self.state = "CUE";
9410          // 30-39 - Check if self line contains an optional identifier or timing data.
9411          if (line.indexOf("-->") === -1) {
9412            self.cue.id = line;
9413            continue;
9414          }
9415          // Process line as start of a cue.
9416          /*falls through*/
9417        case "CUE":
9418          // 40 - Collect cue timings and settings.
9419          try {
9420            parseCue(line, self.cue, self.regionList);
9421          } catch (e) {
9422            self.reportOrThrowError(e);
9423            // In case of an error ignore rest of the cue.
9424            self.cue = null;
9425            self.state = "BADCUE";
9426            continue;
9427          }
9428          self.state = "CUETEXT";
9429          continue;
9430        case "CUETEXT":
9431          var hasSubstring = line.indexOf("-->") !== -1;
9432          // 34 - If we have an empty line then report the cue.
9433          // 35 - If we have the special substring '-->' then report the cue,
9434          // but do not collect the line as we need to process the current
9435          // one as a new cue.
9436          if (!line || hasSubstring && (alreadyCollectedLine = true)) {
9437            // We are done parsing self cue.
9438            self.oncue && self.oncue(self.cue);
9439            self.cue = null;
9440            self.state = "ID";
9441            continue;
9442          }
9443          if (self.cue.text) {
9444            self.cue.text += "\n";
9445          }
9446          self.cue.text += line;
9447          continue;
9448        case "BADCUE": // BADCUE
9449          // 54-62 - Collect and discard the remaining cue.
9450          if (!line) {
9451            self.state = "ID";
9452          }
9453          continue;
9454        }
9455      }
9456    } catch (e) {
9457      self.reportOrThrowError(e);
9458
9459      // If we are currently parsing a cue, report what we have.
9460      if (self.state === "CUETEXT" && self.cue && self.oncue) {
9461        self.oncue(self.cue);
9462      }
9463      self.cue = null;
9464      // Enter BADWEBVTT state if header was not parsed correctly otherwise
9465      // another exception occurred so enter BADCUE state.
9466      self.state = self.state === "INITIAL" ? "BADWEBVTT" : "BADCUE";
9467    }
9468    return this;
9469  },
9470  flush: function () {
9471    var self = this;
9472    try {
9473      // Finish decoding the stream.
9474      self.buffer += self.decoder.decode();
9475      // Synthesize the end of the current cue or region.
9476      if (self.cue || self.state === "HEADER") {
9477        self.buffer += "\n\n";
9478        self.parse();
9479      }
9480      // If we've flushed, parsed, and we're still on the INITIAL state then
9481      // that means we don't have enough of the stream to parse the first
9482      // line.
9483      if (self.state === "INITIAL") {
9484        throw new ParsingError(ParsingError.Errors.BadSignature);
9485      }
9486    } catch(e) {
9487      self.reportOrThrowError(e);
9488    }
9489    self.onflush && self.onflush();
9490    return this;
9491  }
9492};
9493
9494var vtt$1 = WebVTT$1;
9495
9496/**
9497 * Copyright 2013 vtt.js Contributors
9498 *
9499 * Licensed under the Apache License, Version 2.0 (the "License");
9500 * you may not use this file except in compliance with the License.
9501 * You may obtain a copy of the License at
9502 *
9503 *   http://www.apache.org/licenses/LICENSE-2.0
9504 *
9505 * Unless required by applicable law or agreed to in writing, software
9506 * distributed under the License is distributed on an "AS IS" BASIS,
9507 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9508 * See the License for the specific language governing permissions and
9509 * limitations under the License.
9510 */
9511
9512var autoKeyword = "auto";
9513var directionSetting = {
9514  "": true,
9515  "lr": true,
9516  "rl": true
9517};
9518var alignSetting = {
9519  "start": true,
9520  "middle": true,
9521  "end": true,
9522  "left": true,
9523  "right": true
9524};
9525
9526function findDirectionSetting(value) {
9527  if (typeof value !== "string") {
9528    return false;
9529  }
9530  var dir = directionSetting[value.toLowerCase()];
9531  return dir ? value.toLowerCase() : false;
9532}
9533
9534function findAlignSetting(value) {
9535  if (typeof value !== "string") {
9536    return false;
9537  }
9538  var align = alignSetting[value.toLowerCase()];
9539  return align ? value.toLowerCase() : false;
9540}
9541
9542function extend$1(obj) {
9543  var i = 1;
9544  for (; i < arguments.length; i++) {
9545    var cobj = arguments[i];
9546    for (var p in cobj) {
9547      obj[p] = cobj[p];
9548    }
9549  }
9550
9551  return obj;
9552}
9553
9554function VTTCue(startTime, endTime, text) {
9555  var cue = this;
9556  var isIE8 = (/MSIE\s8\.0/).test(navigator.userAgent);
9557  var baseObj = {};
9558
9559  if (isIE8) {
9560    cue = document.createElement('custom');
9561  } else {
9562    baseObj.enumerable = true;
9563  }
9564
9565  /**
9566   * Shim implementation specific properties. These properties are not in
9567   * the spec.
9568   */
9569
9570  // Lets us know when the VTTCue's data has changed in such a way that we need
9571  // to recompute its display state. This lets us compute its display state
9572  // lazily.
9573  cue.hasBeenReset = false;
9574
9575  /**
9576   * VTTCue and TextTrackCue properties
9577   * http://dev.w3.org/html5/webvtt/#vttcue-interface
9578   */
9579
9580  var _id = "";
9581  var _pauseOnExit = false;
9582  var _startTime = startTime;
9583  var _endTime = endTime;
9584  var _text = text;
9585  var _region = null;
9586  var _vertical = "";
9587  var _snapToLines = true;
9588  var _line = "auto";
9589  var _lineAlign = "start";
9590  var _position = 50;
9591  var _positionAlign = "middle";
9592  var _size = 50;
9593  var _align = "middle";
9594
9595  Object.defineProperty(cue,
9596    "id", extend$1({}, baseObj, {
9597      get: function() {
9598        return _id;
9599      },
9600      set: function(value) {
9601        _id = "" + value;
9602      }
9603    }));
9604
9605  Object.defineProperty(cue,
9606    "pauseOnExit", extend$1({}, baseObj, {
9607      get: function() {
9608        return _pauseOnExit;
9609      },
9610      set: function(value) {
9611        _pauseOnExit = !!value;
9612      }
9613    }));
9614
9615  Object.defineProperty(cue,
9616    "startTime", extend$1({}, baseObj, {
9617      get: function() {
9618        return _startTime;
9619      },
9620      set: function(value) {
9621        if (typeof value !== "number") {
9622          throw new TypeError("Start time must be set to a number.");
9623        }
9624        _startTime = value;
9625        this.hasBeenReset = true;
9626      }
9627    }));
9628
9629  Object.defineProperty(cue,
9630    "endTime", extend$1({}, baseObj, {
9631      get: function() {
9632        return _endTime;
9633      },
9634      set: function(value) {
9635        if (typeof value !== "number") {
9636          throw new TypeError("End time must be set to a number.");
9637        }
9638        _endTime = value;
9639        this.hasBeenReset = true;
9640      }
9641    }));
9642
9643  Object.defineProperty(cue,
9644    "text", extend$1({}, baseObj, {
9645      get: function() {
9646        return _text;
9647      },
9648      set: function(value) {
9649        _text = "" + value;
9650        this.hasBeenReset = true;
9651      }
9652    }));
9653
9654  Object.defineProperty(cue,
9655    "region", extend$1({}, baseObj, {
9656      get: function() {
9657        return _region;
9658      },
9659      set: function(value) {
9660        _region = value;
9661        this.hasBeenReset = true;
9662      }
9663    }));
9664
9665  Object.defineProperty(cue,
9666    "vertical", extend$1({}, baseObj, {
9667      get: function() {
9668        return _vertical;
9669      },
9670      set: function(value) {
9671        var setting = findDirectionSetting(value);
9672        // Have to check for false because the setting an be an empty string.
9673        if (setting === false) {
9674          throw new SyntaxError("An invalid or illegal string was specified.");
9675        }
9676        _vertical = setting;
9677        this.hasBeenReset = true;
9678      }
9679    }));
9680
9681  Object.defineProperty(cue,
9682    "snapToLines", extend$1({}, baseObj, {
9683      get: function() {
9684        return _snapToLines;
9685      },
9686      set: function(value) {
9687        _snapToLines = !!value;
9688        this.hasBeenReset = true;
9689      }
9690    }));
9691
9692  Object.defineProperty(cue,
9693    "line", extend$1({}, baseObj, {
9694      get: function() {
9695        return _line;
9696      },
9697      set: function(value) {
vendor: 4,245 bytes, lines 9698-9863
9698        if (typeof value !== "number" && value !== autoKeyword) {
9699          throw new SyntaxError("An invalid number or illegal string was specified.");
9700        }
9701        _line = value;
9702        this.hasBeenReset = true;
9703      }
9704    }));
9705
9706  Object.defineProperty(cue,
9707    "lineAlign", extend$1({}, baseObj, {
9708      get: function() {
9709        return _lineAlign;
9710      },
9711      set: function(value) {
9712        var setting = findAlignSetting(value);
9713        if (!setting) {
9714          throw new SyntaxError("An invalid or illegal string was specified.");
9715        }
9716        _lineAlign = setting;
9717        this.hasBeenReset = true;
9718      }
9719    }));
9720
9721  Object.defineProperty(cue,
9722    "position", extend$1({}, baseObj, {
9723      get: function() {
9724        return _position;
9725      },
9726      set: function(value) {
9727        if (value < 0 || value > 100) {
9728          throw new Error("Position must be between 0 and 100.");
9729        }
9730        _position = value;
9731        this.hasBeenReset = true;
9732      }
9733    }));
9734
9735  Object.defineProperty(cue,
9736    "positionAlign", extend$1({}, baseObj, {
9737      get: function() {
9738        return _positionAlign;
9739      },
9740      set: function(value) {
9741        var setting = findAlignSetting(value);
9742        if (!setting) {
9743          throw new SyntaxError("An invalid or illegal string was specified.");
9744        }
9745        _positionAlign = setting;
9746        this.hasBeenReset = true;
9747      }
9748    }));
9749
9750  Object.defineProperty(cue,
9751    "size", extend$1({}, baseObj, {
9752      get: function() {
9753        return _size;
9754      },
9755      set: function(value) {
9756        if (value < 0 || value > 100) {
9757          throw new Error("Size must be between 0 and 100.");
9758        }
9759        _size = value;
9760        this.hasBeenReset = true;
9761      }
9762    }));
9763
9764  Object.defineProperty(cue,
9765    "align", extend$1({}, baseObj, {
9766      get: function() {
9767        return _align;
9768      },
9769      set: function(value) {
9770        var setting = findAlignSetting(value);
9771        if (!setting) {
9772          throw new SyntaxError("An invalid or illegal string was specified.");
9773        }
9774        _align = setting;
9775        this.hasBeenReset = true;
9776      }
9777    }));
9778
9779  /**
9780   * Other <track> spec defined properties
9781   */
9782
9783  // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state
9784  cue.displayState = undefined;
9785
9786  if (isIE8) {
9787    return cue;
9788  }
9789}
9790
9791/**
9792 * VTTCue methods
9793 */
9794
9795VTTCue.prototype.getCueAsHTML = function() {
9796  // Assume WebVTT.convertCueToDOMTree is on the global.
9797  return WebVTT.convertCueToDOMTree(window, this.text);
9798};
9799
9800var vttcue = VTTCue;
9801
9802/**
9803 * Copyright 2013 vtt.js Contributors
9804 *
9805 * Licensed under the Apache License, Version 2.0 (the "License");
9806 * you may not use this file except in compliance with the License.
9807 * You may obtain a copy of the License at
9808 *
9809 *   http://www.apache.org/licenses/LICENSE-2.0
9810 *
9811 * Unless required by applicable law or agreed to in writing, software
9812 * distributed under the License is distributed on an "AS IS" BASIS,
9813 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9814 * See the License for the specific language governing permissions and
9815 * limitations under the License.
9816 */
9817
9818var scrollSetting = {
9819  "": true,
9820  "up": true
9821};
9822
9823function findScrollSetting(value) {
9824  if (typeof value !== "string") {
9825    return false;
9826  }
9827  var scroll = scrollSetting[value.toLowerCase()];
9828  return scroll ? value.toLowerCase() : false;
9829}
9830
9831function isValidPercentValue(value) {
9832  return typeof value === "number" && (value >= 0 && value <= 100);
9833}
9834
9835// VTTRegion shim http://dev.w3.org/html5/webvtt/#vttregion-interface
9836function VTTRegion() {
9837  var _width = 100;
9838  var _lines = 3;
9839  var _regionAnchorX = 0;
9840  var _regionAnchorY = 100;
9841  var _viewportAnchorX = 0;
9842  var _viewportAnchorY = 100;
9843  var _scroll = "";
9844
9845  Object.defineProperties(this, {
9846    "width": {
9847      enumerable: true,
9848      get: function() {
9849        return _width;
9850      },
9851      set: function(value) {
9852        if (!isValidPercentValue(value)) {
9853          throw new Error("Width must be between 0 and 100.");
9854        }
9855        _width = value;
9856      }
9857    },
9858    "lines": {
9859      enumerable: true,
9860      get: function() {
9861        return _lines;
9862      },
9863      set: function(value) {
vendor: 281,102 bytes, lines 9864-20209
9864        if (typeof value !== "number") {
9865          throw new TypeError("Lines must be set to a number.");
9866        }
9867        _lines = value;
9868      }
9869    },
9870    "regionAnchorY": {
9871      enumerable: true,
9872      get: function() {
9873        return _regionAnchorY;
9874      },
9875      set: function(value) {
9876        if (!isValidPercentValue(value)) {
9877          throw new Error("RegionAnchorX must be between 0 and 100.");
9878        }
9879        _regionAnchorY = value;
9880      }
9881    },
9882    "regionAnchorX": {
9883      enumerable: true,
9884      get: function() {
9885        return _regionAnchorX;
9886      },
9887      set: function(value) {
9888        if(!isValidPercentValue(value)) {
9889          throw new Error("RegionAnchorY must be between 0 and 100.");
9890        }
9891        _regionAnchorX = value;
9892      }
9893    },
9894    "viewportAnchorY": {
9895      enumerable: true,
9896      get: function() {
9897        return _viewportAnchorY;
9898      },
9899      set: function(value) {
9900        if (!isValidPercentValue(value)) {
9901          throw new Error("ViewportAnchorY must be between 0 and 100.");
9902        }
9903        _viewportAnchorY = value;
9904      }
9905    },
9906    "viewportAnchorX": {
9907      enumerable: true,
9908      get: function() {
9909        return _viewportAnchorX;
9910      },
9911      set: function(value) {
9912        if (!isValidPercentValue(value)) {
9913          throw new Error("ViewportAnchorX must be between 0 and 100.");
9914        }
9915        _viewportAnchorX = value;
9916      }
9917    },
9918    "scroll": {
9919      enumerable: true,
9920      get: function() {
9921        return _scroll;
9922      },
9923      set: function(value) {
9924        var setting = findScrollSetting(value);
9925        // Have to check for false as an empty string is a legal value.
9926        if (setting === false) {
9927          throw new SyntaxError("An invalid or illegal string was specified.");
9928        }
9929        _scroll = setting;
9930      }
9931    }
9932  });
9933}
9934
9935var vttregion = VTTRegion;
9936
9937var browserIndex = createCommonjsModule(function (module) {
9938/**
9939 * Copyright 2013 vtt.js Contributors
9940 *
9941 * Licensed under the Apache License, Version 2.0 (the "License");
9942 * you may not use this file except in compliance with the License.
9943 * You may obtain a copy of the License at
9944 *
9945 *   http://www.apache.org/licenses/LICENSE-2.0
9946 *
9947 * Unless required by applicable law or agreed to in writing, software
9948 * distributed under the License is distributed on an "AS IS" BASIS,
9949 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9950 * See the License for the specific language governing permissions and
9951 * limitations under the License.
9952 */
9953
9954// Default exports for Node. Export the extended versions of VTTCue and
9955// VTTRegion in Node since we likely want the capability to convert back and
9956// forth between JSON. If we don't then it's not that big of a deal since we're
9957// off browser.
9958
9959
9960
9961var vttjs = module.exports = {
9962  WebVTT: vtt$1,
9963  VTTCue: vttcue,
9964  VTTRegion: vttregion
9965};
9966
9967window_1.vttjs = vttjs;
9968window_1.WebVTT = vttjs.WebVTT;
9969
9970var cueShim = vttjs.VTTCue;
9971var regionShim = vttjs.VTTRegion;
9972var nativeVTTCue = window_1.VTTCue;
9973var nativeVTTRegion = window_1.VTTRegion;
9974
9975vttjs.shim = function() {
9976  window_1.VTTCue = cueShim;
9977  window_1.VTTRegion = regionShim;
9978};
9979
9980vttjs.restore = function() {
9981  window_1.VTTCue = nativeVTTCue;
9982  window_1.VTTRegion = nativeVTTRegion;
9983};
9984
9985if (!window_1.VTTCue) {
9986  vttjs.shim();
9987}
9988});
9989
9990/**
9991 * @file tech.js
9992 */
9993
9994/**
9995 * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string
9996 * that just contains the src url alone.
9997 * * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};`
9998   * `var SourceString = 'http://example.com/some-video.mp4';`
9999 *
10000 * @typedef {Object|string} Tech~SourceObject
10001 *
10002 * @property {string} src
10003 *           The url to the source
10004 *
10005 * @property {string} type
10006 *           The mime type of the source
10007 */
10008
10009/**
10010 * A function used by {@link Tech} to create a new {@link TextTrack}.
10011 *
10012 * @private
10013 *
10014 * @param {Tech} self
10015 *        An instance of the Tech class.
10016 *
10017 * @param {string} kind
10018 *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
10019 *
10020 * @param {string} [label]
10021 *        Label to identify the text track
10022 *
10023 * @param {string} [language]
10024 *        Two letter language abbreviation
10025 *
10026 * @param {Object} [options={}]
10027 *        An object with additional text track options
10028 *
10029 * @return {TextTrack}
10030 *          The text track that was created.
10031 */
10032function createTrackHelper(self, kind, label, language) {
10033  var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
10034
10035  var tracks = self.textTracks();
10036
10037  options.kind = kind;
10038
10039  if (label) {
10040    options.label = label;
10041  }
10042  if (language) {
10043    options.language = language;
10044  }
10045  options.tech = self;
10046
10047  var track = new ALL.text.TrackClass(options);
10048
10049  tracks.addTrack(track);
10050
10051  return track;
10052}
10053
10054/**
10055 * This is the base class for media playback technology controllers, such as
10056 * {@link Flash} and {@link HTML5}
10057 *
10058 * @extends Component
10059 */
10060
10061var Tech = function (_Component) {
10062  inherits(Tech, _Component);
10063
10064  /**
10065   * Create an instance of this Tech.
10066   *
10067   * @param {Object} [options]
10068   *        The key/value store of player options.
10069   *
10070   * @param {Component~ReadyCallback} ready
10071   *        Callback function to call when the `HTML5` Tech is ready.
10072   */
10073  function Tech() {
10074    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
10075    var ready = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
10076    classCallCheck(this, Tech);
10077
10078    // we don't want the tech to report user activity automatically.
10079    // This is done manually in addControlsListeners
10080    options.reportTouchActivity = false;
10081
10082    // keep track of whether the current source has played at all to
10083    // implement a very limited played()
10084    var _this = possibleConstructorReturn(this, _Component.call(this, null, options, ready));
10085
10086    _this.hasStarted_ = false;
10087    _this.on('playing', function () {
10088      this.hasStarted_ = true;
10089    });
10090    _this.on('loadstart', function () {
10091      this.hasStarted_ = false;
10092    });
10093
10094    ALL.names.forEach(function (name) {
10095      var props = ALL[name];
10096
10097      if (options && options[props.getterName]) {
10098        _this[props.privateName] = options[props.getterName];
10099      }
10100    });
10101
10102    // Manually track progress in cases where the browser/flash player doesn't report it.
10103    if (!_this.featuresProgressEvents) {
10104      _this.manualProgressOn();
10105    }
10106
10107    // Manually track timeupdates in cases where the browser/flash player doesn't report it.
10108    if (!_this.featuresTimeupdateEvents) {
10109      _this.manualTimeUpdatesOn();
10110    }
10111
10112    ['Text', 'Audio', 'Video'].forEach(function (track) {
10113      if (options['native' + track + 'Tracks'] === false) {
10114        _this['featuresNative' + track + 'Tracks'] = false;
10115      }
10116    });
10117
10118    if (options.nativeCaptions === false || options.nativeTextTracks === false) {
10119      _this.featuresNativeTextTracks = false;
10120    } else if (options.nativeCaptions === true || options.nativeTextTracks === true) {
10121      _this.featuresNativeTextTracks = true;
10122    }
10123
10124    if (!_this.featuresNativeTextTracks) {
10125      _this.emulateTextTracks();
10126    }
10127
10128    _this.autoRemoteTextTracks_ = new ALL.text.ListClass();
10129
10130    _this.initTrackListeners();
10131
10132    // Turn on component tap events only if not using native controls
10133    if (!options.nativeControlsForTouch) {
10134      _this.emitTapEvents();
10135    }
10136
10137    if (_this.constructor) {
10138      _this.name_ = _this.constructor.name || 'Unknown Tech';
10139    }
10140    return _this;
10141  }
10142
10143  /* Fallbacks for unsupported event types
10144  ================================================================================ */
10145
10146  /**
10147   * Polyfill the `progress` event for browsers that don't support it natively.
10148   *
10149   * @see {@link Tech#trackProgress}
10150   */
10151
10152
10153  Tech.prototype.manualProgressOn = function manualProgressOn() {
10154    this.on('durationchange', this.onDurationChange);
10155
10156    this.manualProgress = true;
10157
10158    // Trigger progress watching when a source begins loading
10159    this.one('ready', this.trackProgress);
10160  };
10161
10162  /**
10163   * Turn off the polyfill for `progress` events that was created in
10164   * {@link Tech#manualProgressOn}
10165   */
10166
10167
10168  Tech.prototype.manualProgressOff = function manualProgressOff() {
10169    this.manualProgress = false;
10170    this.stopTrackingProgress();
10171
10172    this.off('durationchange', this.onDurationChange);
10173  };
10174
10175  /**
10176   * This is used to trigger a `progress` event when the buffered percent changes. It
10177   * sets an interval function that will be called every 500 milliseconds to check if the
10178   * buffer end percent has changed.
10179   *
10180   * > This function is called by {@link Tech#manualProgressOn}
10181   *
10182   * @param {EventTarget~Event} event
10183   *        The `ready` event that caused this to run.
10184   *
10185   * @listens Tech#ready
10186   * @fires Tech#progress
10187   */
10188
10189
10190  Tech.prototype.trackProgress = function trackProgress(event) {
10191    this.stopTrackingProgress();
10192    this.progressInterval = this.setInterval(bind(this, function () {
10193      // Don't trigger unless buffered amount is greater than last time
10194
10195      var numBufferedPercent = this.bufferedPercent();
10196
10197      if (this.bufferedPercent_ !== numBufferedPercent) {
10198        /**
10199         * See {@link Player#progress}
10200         *
10201         * @event Tech#progress
10202         * @type {EventTarget~Event}
10203         */
10204        this.trigger('progress');
10205      }
10206
10207      this.bufferedPercent_ = numBufferedPercent;
10208
10209      if (numBufferedPercent === 1) {
10210        this.stopTrackingProgress();
10211      }
10212    }), 500);
10213  };
10214
10215  /**
10216   * Update our internal duration on a `durationchange` event by calling
10217   * {@link Tech#duration}.
10218   *
10219   * @param {EventTarget~Event} event
10220   *        The `durationchange` event that caused this to run.
10221   *
10222   * @listens Tech#durationchange
10223   */
10224
10225
10226  Tech.prototype.onDurationChange = function onDurationChange(event) {
10227    this.duration_ = this.duration();
10228  };
10229
10230  /**
10231   * Get and create a `TimeRange` object for buffering.
10232   *
10233   * @return {TimeRange}
10234   *         The time range object that was created.
10235   */
10236
10237
10238  Tech.prototype.buffered = function buffered() {
10239    return createTimeRanges(0, 0);
10240  };
10241
10242  /**
10243   * Get the percentage of the current video that is currently buffered.
10244   *
10245   * @return {number}
10246   *         A number from 0 to 1 that represents the decimal percentage of the
10247   *         video that is buffered.
10248   *
10249   */
10250
10251
10252  Tech.prototype.bufferedPercent = function bufferedPercent$$1() {
10253    return bufferedPercent(this.buffered(), this.duration_);
10254  };
10255
10256  /**
10257   * Turn off the polyfill for `progress` events that was created in
10258   * {@link Tech#manualProgressOn}
10259   * Stop manually tracking progress events by clearing the interval that was set in
10260   * {@link Tech#trackProgress}.
10261   */
10262
10263
10264  Tech.prototype.stopTrackingProgress = function stopTrackingProgress() {
10265    this.clearInterval(this.progressInterval);
10266  };
10267
10268  /**
10269   * Polyfill the `timeupdate` event for browsers that don't support it.
10270   *
10271   * @see {@link Tech#trackCurrentTime}
10272   */
10273
10274
10275  Tech.prototype.manualTimeUpdatesOn = function manualTimeUpdatesOn() {
10276    this.manualTimeUpdates = true;
10277
10278    this.on('play', this.trackCurrentTime);
10279    this.on('pause', this.stopTrackingCurrentTime);
10280  };
10281
10282  /**
10283   * Turn off the polyfill for `timeupdate` events that was created in
10284   * {@link Tech#manualTimeUpdatesOn}
10285   */
10286
10287
10288  Tech.prototype.manualTimeUpdatesOff = function manualTimeUpdatesOff() {
10289    this.manualTimeUpdates = false;
10290    this.stopTrackingCurrentTime();
10291    this.off('play', this.trackCurrentTime);
10292    this.off('pause', this.stopTrackingCurrentTime);
10293  };
10294
10295  /**
10296   * Sets up an interval function to track current time and trigger `timeupdate` every
10297   * 250 milliseconds.
10298   *
10299   * @listens Tech#play
10300   * @triggers Tech#timeupdate
10301   */
10302
10303
10304  Tech.prototype.trackCurrentTime = function trackCurrentTime() {
10305    if (this.currentTimeInterval) {
10306      this.stopTrackingCurrentTime();
10307    }
10308    this.currentTimeInterval = this.setInterval(function () {
10309      /**
10310       * Triggered at an interval of 250ms to indicated that time is passing in the video.
10311       *
10312       * @event Tech#timeupdate
10313       * @type {EventTarget~Event}
10314       */
10315      this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
10316
10317      // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
10318    }, 250);
10319  };
10320
10321  /**
10322   * Stop the interval function created in {@link Tech#trackCurrentTime} so that the
10323   * `timeupdate` event is no longer triggered.
10324   *
10325   * @listens {Tech#pause}
10326   */
10327
10328
10329  Tech.prototype.stopTrackingCurrentTime = function stopTrackingCurrentTime() {
10330    this.clearInterval(this.currentTimeInterval);
10331
10332    // #1002 - if the video ends right before the next timeupdate would happen,
10333    // the progress bar won't make it all the way to the end
10334    this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
10335  };
10336
10337  /**
10338   * Turn off all event polyfills, clear the `Tech`s {@link AudioTrackList},
10339   * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech.
10340   *
10341   * @fires Component#dispose
10342   */
10343
10344
10345  Tech.prototype.dispose = function dispose() {
10346
10347    // clear out all tracks because we can't reuse them between techs
10348    this.clearTracks(NORMAL.names);
10349
10350    // Turn off any manual progress or timeupdate tracking
10351    if (this.manualProgress) {
10352      this.manualProgressOff();
10353    }
10354
10355    if (this.manualTimeUpdates) {
10356      this.manualTimeUpdatesOff();
10357    }
10358
10359    _Component.prototype.dispose.call(this);
10360  };
10361
10362  /**
10363   * Clear out a single `TrackList` or an array of `TrackLists` given their names.
10364   *
10365   * > Note: Techs without source handlers should call this between sources for `video`
10366   *         & `audio` tracks. You don't want to use them between tracks!
10367   *
10368   * @param {string[]|string} types
10369   *        TrackList names to clear, valid names are `video`, `audio`, and
10370   *        `text`.
10371   */
10372
10373
10374  Tech.prototype.clearTracks = function clearTracks(types) {
10375    var _this2 = this;
10376
10377    types = [].concat(types);
10378    // clear out all tracks because we can't reuse them between techs
10379    types.forEach(function (type) {
10380      var list = _this2[type + 'Tracks']() || [];
10381      var i = list.length;
10382
10383      while (i--) {
10384        var track = list[i];
10385
10386        if (type === 'text') {
10387          _this2.removeRemoteTextTrack(track);
10388        }
10389        list.removeTrack(track);
10390      }
10391    });
10392  };
10393
10394  /**
10395   * Remove any TextTracks added via addRemoteTextTrack that are
10396   * flagged for automatic garbage collection
10397   */
10398
10399
10400  Tech.prototype.cleanupAutoTextTracks = function cleanupAutoTextTracks() {
10401    var list = this.autoRemoteTextTracks_ || [];
10402    var i = list.length;
10403
10404    while (i--) {
10405      var track = list[i];
10406
10407      this.removeRemoteTextTrack(track);
10408    }
10409  };
10410
10411  /**
10412   * Reset the tech, which will removes all sources and reset the internal readyState.
10413   *
10414   * @abstract
10415   */
10416
10417
10418  Tech.prototype.reset = function reset() {};
10419
10420  /**
10421   * Get or set an error on the Tech.
10422   *
10423   * @param {MediaError} [err]
10424   *        Error to set on the Tech
10425   *
10426   * @return {MediaError|null}
10427   *         The current error object on the tech, or null if there isn't one.
10428   */
10429
10430
10431  Tech.prototype.error = function error(err) {
10432    if (err !== undefined) {
10433      this.error_ = new MediaError(err);
10434      this.trigger('error');
10435    }
10436    return this.error_;
10437  };
10438
10439  /**
10440   * Returns the `TimeRange`s that have been played through for the current source.
10441   *
10442   * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`.
10443   *         It only checks wether the source has played at all or not.
10444   *
10445   * @return {TimeRange}
10446   *         - A single time range if this video has played
10447   *         - An empty set of ranges if not.
10448   */
10449
10450
10451  Tech.prototype.played = function played() {
10452    if (this.hasStarted_) {
10453      return createTimeRanges(0, 0);
10454    }
10455    return createTimeRanges();
10456  };
10457
10458  /**
10459   * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was
10460   * previously called.
10461   *
10462   * @fires Tech#timeupdate
10463   */
10464
10465
10466  Tech.prototype.setCurrentTime = function setCurrentTime() {
10467    // improve the accuracy of manual timeupdates
10468    if (this.manualTimeUpdates) {
10469      /**
10470       * A manual `timeupdate` event.
10471       *
10472       * @event Tech#timeupdate
10473       * @type {EventTarget~Event}
10474       */
10475      this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
10476    }
10477  };
10478
10479  /**
10480   * Turn on listeners for {@link VideoTrackList}, {@link {AudioTrackList}, and
10481   * {@link TextTrackList} events.
10482   *
10483   * This adds {@link EventTarget~EventListeners} for `addtrack`, and  `removetrack`.
10484   *
10485   * @fires Tech#audiotrackchange
10486   * @fires Tech#videotrackchange
10487   * @fires Tech#texttrackchange
10488   */
10489
10490
10491  Tech.prototype.initTrackListeners = function initTrackListeners() {
10492    var _this3 = this;
10493
10494    /**
10495     * Triggered when tracks are added or removed on the Tech {@link AudioTrackList}
10496     *
10497     * @event Tech#audiotrackchange
10498     * @type {EventTarget~Event}
10499     */
10500
10501    /**
10502     * Triggered when tracks are added or removed on the Tech {@link VideoTrackList}
10503     *
10504     * @event Tech#videotrackchange
10505     * @type {EventTarget~Event}
10506     */
10507
10508    /**
10509     * Triggered when tracks are added or removed on the Tech {@link TextTrackList}
10510     *
10511     * @event Tech#texttrackchange
10512     * @type {EventTarget~Event}
10513     */
10514    NORMAL.names.forEach(function (name) {
10515      var props = NORMAL[name];
10516      var trackListChanges = function trackListChanges() {
10517        _this3.trigger(name + 'trackchange');
10518      };
10519
10520      var tracks = _this3[props.getterName]();
10521
10522      tracks.addEventListener('removetrack', trackListChanges);
10523      tracks.addEventListener('addtrack', trackListChanges);
10524
10525      _this3.on('dispose', function () {
10526        tracks.removeEventListener('removetrack', trackListChanges);
10527        tracks.removeEventListener('addtrack', trackListChanges);
10528      });
10529    });
10530  };
10531
10532  /**
10533   * Emulate TextTracks using vtt.js if necessary
10534   *
10535   * @fires Tech#vttjsloaded
10536   * @fires Tech#vttjserror
10537   */
10538
10539
10540  Tech.prototype.addWebVttScript_ = function addWebVttScript_() {
10541    var _this4 = this;
10542
10543    if (window_1.WebVTT) {
10544      return;
10545    }
10546
10547    // Initially, Tech.el_ is a child of a dummy-div wait until the Component system
10548    // signals that the Tech is ready at which point Tech.el_ is part of the DOM
10549    // before inserting the WebVTT script
10550    if (document_1.body.contains(this.el())) {
10551
10552      // load via require if available and vtt.js script location was not passed in
10553      // as an option. novtt builds will turn the above require call into an empty object
10554      // which will cause this if check to always fail.
10555      if (!this.options_['vtt.js'] && isPlain(browserIndex) && Object.keys(browserIndex).length > 0) {
10556        this.trigger('vttjsloaded');
10557        return;
10558      }
10559
10560      // load vtt.js via the script location option or the cdn of no location was
10561      // passed in
10562      var script = document_1.createElement('script');
10563
10564      script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.12.4/vtt.min.js';
10565      script.onload = function () {
10566        /**
10567         * Fired when vtt.js is loaded.
10568         *
10569         * @event Tech#vttjsloaded
10570         * @type {EventTarget~Event}
10571         */
10572        _this4.trigger('vttjsloaded');
10573      };
10574      script.onerror = function () {
10575        /**
10576         * Fired when vtt.js was not loaded due to an error
10577         *
10578         * @event Tech#vttjsloaded
10579         * @type {EventTarget~Event}
10580         */
10581        _this4.trigger('vttjserror');
10582      };
10583      this.on('dispose', function () {
10584        script.onload = null;
10585        script.onerror = null;
10586      });
10587      // but have not loaded yet and we set it to true before the inject so that
10588      // we don't overwrite the injected window.WebVTT if it loads right away
10589      window_1.WebVTT = true;
10590      this.el().parentNode.appendChild(script);
10591    } else {
10592      this.ready(this.addWebVttScript_);
10593    }
10594  };
10595
10596  /**
10597   * Emulate texttracks
10598   *
10599   */
10600
10601
10602  Tech.prototype.emulateTextTracks = function emulateTextTracks() {
10603    var _this5 = this;
10604
10605    var tracks = this.textTracks();
10606    var remoteTracks = this.remoteTextTracks();
10607    var handleAddTrack = function handleAddTrack(e) {
10608      return tracks.addTrack(e.track);
10609    };
10610    var handleRemoveTrack = function handleRemoveTrack(e) {
10611      return tracks.removeTrack(e.track);
10612    };
10613
10614    remoteTracks.on('addtrack', handleAddTrack);
10615    remoteTracks.on('removetrack', handleRemoveTrack);
10616
10617    this.addWebVttScript_();
10618
10619    var updateDisplay = function updateDisplay() {
10620      return _this5.trigger('texttrackchange');
10621    };
10622
10623    var textTracksChanges = function textTracksChanges() {
10624      updateDisplay();
10625
10626      for (var i = 0; i < tracks.length; i++) {
10627        var track = tracks[i];
10628
10629        track.removeEventListener('cuechange', updateDisplay);
10630        if (track.mode === 'showing') {
10631          track.addEventListener('cuechange', updateDisplay);
10632        }
10633      }
10634    };
10635
10636    textTracksChanges();
10637    tracks.addEventListener('change', textTracksChanges);
10638    tracks.addEventListener('addtrack', textTracksChanges);
10639    tracks.addEventListener('removetrack', textTracksChanges);
10640
10641    this.on('dispose', function () {
10642      remoteTracks.off('addtrack', handleAddTrack);
10643      remoteTracks.off('removetrack', handleRemoveTrack);
10644      tracks.removeEventListener('change', textTracksChanges);
10645      tracks.removeEventListener('addtrack', textTracksChanges);
10646      tracks.removeEventListener('removetrack', textTracksChanges);
10647
10648      for (var i = 0; i < tracks.length; i++) {
10649        var track = tracks[i];
10650
10651        track.removeEventListener('cuechange', updateDisplay);
10652      }
10653    });
10654  };
10655
10656  /**
10657   * Create and returns a remote {@link TextTrack} object.
10658   *
10659   * @param {string} kind
10660   *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
10661   *
10662   * @param {string} [label]
10663   *        Label to identify the text track
10664   *
10665   * @param {string} [language]
10666   *        Two letter language abbreviation
10667   *
10668   * @return {TextTrack}
10669   *         The TextTrack that gets created.
10670   */
10671
10672
10673  Tech.prototype.addTextTrack = function addTextTrack(kind, label, language) {
10674    if (!kind) {
10675      throw new Error('TextTrack kind is required but was not provided');
10676    }
10677
10678    return createTrackHelper(this, kind, label, language);
10679  };
10680
10681  /**
10682   * Create an emulated TextTrack for use by addRemoteTextTrack
10683   *
10684   * This is intended to be overridden by classes that inherit from
10685   * Tech in order to create native or custom TextTracks.
10686   *
10687   * @param {Object} options
10688   *        The object should contain the options to initialize the TextTrack with.
10689   *
10690   * @param {string} [options.kind]
10691   *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
10692   *
10693   * @param {string} [options.label].
10694   *        Label to identify the text track
10695   *
10696   * @param {string} [options.language]
10697   *        Two letter language abbreviation.
10698   *
10699   * @return {HTMLTrackElement}
10700   *         The track element that gets created.
10701   */
10702
10703
10704  Tech.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
10705    var track = mergeOptions(options, {
10706      tech: this
10707    });
10708
10709    return new REMOTE.remoteTextEl.TrackClass(track);
10710  };
10711
10712  /**
10713   * Creates a remote text track object and returns an html track element.
10714   *
10715   * > Note: This can be an emulated {@link HTMLTrackElement} or a native one.
10716   *
10717   * @param {Object} options
10718   *        See {@link Tech#createRemoteTextTrack} for more detailed properties.
10719   *
10720   * @param {boolean} [manualCleanup=true]
10721   *        - When false: the TextTrack will be automatically removed from the video
10722   *          element whenever the source changes
10723   *        - When True: The TextTrack will have to be cleaned up manually
10724   *
10725   * @return {HTMLTrackElement}
10726   *         An Html Track Element.
10727   *
10728   * @deprecated The default functionality for this function will be equivalent
10729   *             to "manualCleanup=false" in the future. The manualCleanup parameter will
10730   *             also be removed.
10731   */
10732
10733
10734  Tech.prototype.addRemoteTextTrack = function addRemoteTextTrack() {
10735    var _this6 = this;
10736
10737    var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
10738    var manualCleanup = arguments[1];
10739
10740    var htmlTrackElement = this.createRemoteTextTrack(options);
10741
10742    if (manualCleanup !== true && manualCleanup !== false) {
10743      // deprecation warning
10744      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');
10745      manualCleanup = true;
10746    }
10747
10748    // store HTMLTrackElement and TextTrack to remote list
10749    this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
10750    this.remoteTextTracks().addTrack(htmlTrackElement.track);
10751
10752    if (manualCleanup !== true) {
10753      // create the TextTrackList if it doesn't exist
10754      this.ready(function () {
10755        return _this6.autoRemoteTextTracks_.addTrack(htmlTrackElement.track);
10756      });
10757    }
10758
10759    return htmlTrackElement;
10760  };
10761
10762  /**
10763   * Remove a remote text track from the remote `TextTrackList`.
10764   *
10765   * @param {TextTrack} track
10766   *        `TextTrack` to remove from the `TextTrackList`
10767   */
10768
10769
10770  Tech.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
10771    var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track);
10772
10773    // remove HTMLTrackElement and TextTrack from remote list
10774    this.remoteTextTrackEls().removeTrackElement_(trackElement);
10775    this.remoteTextTracks().removeTrack(track);
10776    this.autoRemoteTextTracks_.removeTrack(track);
10777  };
10778
10779  /**
10780   * Gets available media playback quality metrics as specified by the W3C's Media
10781   * Playback Quality API.
10782   *
10783   * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
10784   *
10785   * @return {Object}
10786   *         An object with supported media playback quality metrics
10787   *
10788   * @abstract
10789   */
10790
10791
10792  Tech.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
10793    return {};
10794  };
10795
10796  /**
10797   * A method to set a poster from a `Tech`.
10798   *
10799   * @abstract
10800   */
10801
10802
10803  Tech.prototype.setPoster = function setPoster() {};
10804
10805  /**
10806   * A method to check for the presence of the 'playsinine' <video> attribute.
10807   *
10808   * @abstract
10809   */
10810
10811
10812  Tech.prototype.playsinline = function playsinline() {};
10813
10814  /**
10815   * A method to set or unset the 'playsinine' <video> attribute.
10816   *
10817   * @abstract
10818   */
10819
10820
10821  Tech.prototype.setPlaysinline = function setPlaysinline() {};
10822
10823  /*
10824   * Check if the tech can support the given mime-type.
10825   *
10826   * The base tech does not support any type, but source handlers might
10827   * overwrite this.
10828   *
10829   * @param  {string} type
10830   *         The mimetype to check for support
10831   *
10832   * @return {string}
10833   *         'probably', 'maybe', or empty string
10834   *
10835   * @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType}
10836   *
10837   * @abstract
10838   */
10839
10840
10841  Tech.prototype.canPlayType = function canPlayType() {
10842    return '';
10843  };
10844
10845  /**
10846   * Check if the type is supported by this tech.
10847   *
10848   * The base tech does not support any type, but source handlers might
10849   * overwrite this.
10850   *
10851   * @param {string} type
10852   *        The media type to check
10853   * @return {string} Returns the native video element's response
10854   */
10855
10856
10857  Tech.canPlayType = function canPlayType() {
10858    return '';
10859  };
10860
10861  /**
10862   * Check if the tech can support the given source
10863   * @param {Object} srcObj
10864   *        The source object
10865   * @param {Object} options
10866   *        The options passed to the tech
10867   * @return {string} 'probably', 'maybe', or '' (empty string)
10868   */
10869
10870
10871  Tech.canPlaySource = function canPlaySource(srcObj, options) {
10872    return Tech.canPlayType(srcObj.type);
10873  };
10874
10875  /*
10876   * Return whether the argument is a Tech or not.
10877   * Can be passed either a Class like `Html5` or a instance like `player.tech_`
10878   *
10879   * @param {Object} component
10880   *        The item to check
10881   *
10882   * @return {boolean}
10883   *         Whether it is a tech or not
10884   *         - True if it is a tech
10885   *         - False if it is not
10886   */
10887
10888
10889  Tech.isTech = function isTech(component) {
10890    return component.prototype instanceof Tech || component instanceof Tech || component === Tech;
10891  };
10892
10893  /**
10894   * Registers a `Tech` into a shared list for videojs.
10895   *
10896   * @param {string} name
10897   *        Name of the `Tech` to register.
10898   *
10899   * @param {Object} tech
10900   *        The `Tech` class to register.
10901   */
10902
10903
10904  Tech.registerTech = function registerTech(name, tech) {
10905    if (!Tech.techs_) {
10906      Tech.techs_ = {};
10907    }
10908
10909    if (!Tech.isTech(tech)) {
10910      throw new Error('Tech ' + name + ' must be a Tech');
10911    }
10912
10913    if (!Tech.canPlayType) {
10914      throw new Error('Techs must have a static canPlayType method on them');
10915    }
10916    if (!Tech.canPlaySource) {
10917      throw new Error('Techs must have a static canPlaySource method on them');
10918    }
10919
10920    name = toTitleCase(name);
10921
10922    Tech.techs_[name] = tech;
10923    if (name !== 'Tech') {
10924      // camel case the techName for use in techOrder
10925      Tech.defaultTechOrder_.push(name);
10926    }
10927    return tech;
10928  };
10929
10930  /**
10931   * Get a `Tech` from the shared list by name.
10932   *
10933   * @param {string} name
10934   *        `camelCase` or `TitleCase` name of the Tech to get
10935   *
10936   * @return {Tech|undefined}
10937   *         The `Tech` or undefined if there was no tech with the name requsted.
10938   */
10939
10940
10941  Tech.getTech = function getTech(name) {
10942    if (!name) {
10943      return;
10944    }
10945
10946    name = toTitleCase(name);
10947
10948    if (Tech.techs_ && Tech.techs_[name]) {
10949      return Tech.techs_[name];
10950    }
10951
10952    if (window_1 && window_1.videojs && window_1.videojs[name]) {
10953      log$1.warn('The ' + name + ' tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)');
10954      return window_1.videojs[name];
10955    }
10956  };
10957
10958  return Tech;
10959}(Component);
10960
10961/**
10962 * Get the {@link VideoTrackList}
10963 *
10964 * @returns {VideoTrackList}
10965 * @method Tech.prototype.videoTracks
10966 */
10967
10968/**
10969 * Get the {@link AudioTrackList}
10970 *
10971 * @returns {AudioTrackList}
10972 * @method Tech.prototype.audioTracks
10973 */
10974
10975/**
10976 * Get the {@link TextTrackList}
10977 *
10978 * @returns {TextTrackList}
10979 * @method Tech.prototype.textTracks
10980 */
10981
10982/**
10983 * Get the remote element {@link TextTrackList}
10984 *
10985 * @returns {TextTrackList}
10986 * @method Tech.prototype.remoteTextTracks
10987 */
10988
10989/**
10990 * Get the remote element {@link HtmlTrackElementList}
10991 *
10992 * @returns {HtmlTrackElementList}
10993 * @method Tech.prototype.remoteTextTrackEls
10994 */
10995
10996ALL.names.forEach(function (name) {
10997  var props = ALL[name];
10998
10999  Tech.prototype[props.getterName] = function () {
11000    this[props.privateName] = this[props.privateName] || new props.ListClass();
11001    return this[props.privateName];
11002  };
11003});
11004
11005/**
11006 * List of associated text tracks
11007 *
11008 * @type {TextTrackList}
11009 * @private
11010 * @property Tech#textTracks_
11011 */
11012
11013/**
11014 * List of associated audio tracks.
11015 *
11016 * @type {AudioTrackList}
11017 * @private
11018 * @property Tech#audioTracks_
11019 */
11020
11021/**
11022 * List of associated video tracks.
11023 *
11024 * @type {VideoTrackList}
11025 * @private
11026 * @property Tech#videoTracks_
11027 */
11028
11029/**
11030 * Boolean indicating wether the `Tech` supports volume control.
11031 *
11032 * @type {boolean}
11033 * @default
11034 */
11035Tech.prototype.featuresVolumeControl = true;
11036
11037/**
11038 * Boolean indicating wether the `Tech` support fullscreen resize control.
11039 * Resizing plugins using request fullscreen reloads the plugin
11040 *
11041 * @type {boolean}
11042 * @default
11043 */
11044Tech.prototype.featuresFullscreenResize = false;
11045
11046/**
11047 * Boolean indicating wether the `Tech` supports changing the speed at which the video
11048 * plays. Examples:
11049 *   - Set player to play 2x (twice) as fast
11050 *   - Set player to play 0.5x (half) as fast
11051 *
11052 * @type {boolean}
11053 * @default
11054 */
11055Tech.prototype.featuresPlaybackRate = false;
11056
11057/**
11058 * Boolean indicating wether the `Tech` supports the `progress` event. This is currently
11059 * not triggered by video-js-swf. This will be used to determine if
11060 * {@link Tech#manualProgressOn} should be called.
11061 *
11062 * @type {boolean}
11063 * @default
11064 */
11065Tech.prototype.featuresProgressEvents = false;
11066
11067/**
11068 * Boolean indicating wether the `Tech` supports the `timeupdate` event. This is currently
11069 * not triggered by video-js-swf. This will be used to determine if
11070 * {@link Tech#manualTimeUpdates} should be called.
11071 *
11072 * @type {boolean}
11073 * @default
11074 */
11075Tech.prototype.featuresTimeupdateEvents = false;
11076
11077/**
11078 * Boolean indicating wether the `Tech` supports the native `TextTrack`s.
11079 * This will help us integrate with native `TextTrack`s if the browser supports them.
11080 *
11081 * @type {boolean}
11082 * @default
11083 */
11084Tech.prototype.featuresNativeTextTracks = false;
11085
11086/**
11087 * A functional mixin for techs that want to use the Source Handler pattern.
11088 * Source handlers are scripts for handling specific formats.
11089 * The source handler pattern is used for adaptive formats (HLS, DASH) that
11090 * manually load video data and feed it into a Source Buffer (Media Source Extensions)
11091 * Example: `Tech.withSourceHandlers.call(MyTech);`
11092 *
11093 * @param {Tech} _Tech
11094 *        The tech to add source handler functions to.
11095 *
11096 * @mixes Tech~SourceHandlerAdditions
11097 */
11098Tech.withSourceHandlers = function (_Tech) {
11099
11100  /**
11101   * Register a source handler
11102   *
11103   * @param {Function} handler
11104   *        The source handler class
11105   *
11106   * @param {number} [index]
11107   *        Register it at the following index
11108   */
11109  _Tech.registerSourceHandler = function (handler, index) {
11110    var handlers = _Tech.sourceHandlers;
11111
11112    if (!handlers) {
11113      handlers = _Tech.sourceHandlers = [];
11114    }
11115
11116    if (index === undefined) {
11117      // add to the end of the list
11118      index = handlers.length;
11119    }
11120
11121    handlers.splice(index, 0, handler);
11122  };
11123
11124  /**
11125   * Check if the tech can support the given type. Also checks the
11126   * Techs sourceHandlers.
11127   *
11128   * @param {string} type
11129   *         The mimetype to check.
11130   *
11131   * @return {string}
11132   *         'probably', 'maybe', or '' (empty string)
11133   */
11134  _Tech.canPlayType = function (type) {
11135    var handlers = _Tech.sourceHandlers || [];
11136    var can = void 0;
11137
11138    for (var i = 0; i < handlers.length; i++) {
11139      can = handlers[i].canPlayType(type);
11140
11141      if (can) {
11142        return can;
11143      }
11144    }
11145
11146    return '';
11147  };
11148
11149  /**
11150   * Returns the first source handler that supports the source.
11151   *
11152   * TODO: Answer question: should 'probably' be prioritized over 'maybe'
11153   *
11154   * @param {Tech~SourceObject} source
11155   *        The source object
11156   *
11157   * @param {Object} options
11158   *        The options passed to the tech
11159   *
11160   * @return {SourceHandler|null}
11161   *          The first source handler that supports the source or null if
11162   *          no SourceHandler supports the source
11163   */
11164  _Tech.selectSourceHandler = function (source, options) {
11165    var handlers = _Tech.sourceHandlers || [];
11166    var can = void 0;
11167
11168    for (var i = 0; i < handlers.length; i++) {
11169      can = handlers[i].canHandleSource(source, options);
11170
11171      if (can) {
11172        return handlers[i];
11173      }
11174    }
11175
11176    return null;
11177  };
11178
11179  /**
11180   * Check if the tech can support the given source.
11181   *
11182   * @param {Tech~SourceObject} srcObj
11183   *        The source object
11184   *
11185   * @param {Object} options
11186   *        The options passed to the tech
11187   *
11188   * @return {string}
11189   *         'probably', 'maybe', or '' (empty string)
11190   */
11191  _Tech.canPlaySource = function (srcObj, options) {
11192    var sh = _Tech.selectSourceHandler(srcObj, options);
11193
11194    if (sh) {
11195      return sh.canHandleSource(srcObj, options);
11196    }
11197
11198    return '';
11199  };
11200
11201  /**
11202   * When using a source handler, prefer its implementation of
11203   * any function normally provided by the tech.
11204   */
11205  var deferrable = ['seekable', 'duration'];
11206
11207  /**
11208   * A wrapper around {@link Tech#seekable} that will call a `SourceHandler`s seekable
11209   * function if it exists, with a fallback to the Techs seekable function.
11210   *
11211   * @method _Tech.seekable
11212   */
11213
11214  /**
11215   * A wrapper around {@link Tech#duration} that will call a `SourceHandler`s duration
11216   * function if it exists, otherwise it will fallback to the techs duration function.
11217   *
11218   * @method _Tech.duration
11219   */
11220
11221  deferrable.forEach(function (fnName) {
11222    var originalFn = this[fnName];
11223
11224    if (typeof originalFn !== 'function') {
11225      return;
11226    }
11227
11228    this[fnName] = function () {
11229      if (this.sourceHandler_ && this.sourceHandler_[fnName]) {
11230        return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments);
11231      }
11232      return originalFn.apply(this, arguments);
11233    };
11234  }, _Tech.prototype);
11235
11236  /**
11237   * Create a function for setting the source using a source object
11238   * and source handlers.
11239   * Should never be called unless a source handler was found.
11240   *
11241   * @param {Tech~SourceObject} source
11242   *        A source object with src and type keys
11243   */
11244  _Tech.prototype.setSource = function (source) {
11245    var sh = _Tech.selectSourceHandler(source, this.options_);
11246
11247    if (!sh) {
11248      // Fall back to a native source hander when unsupported sources are
11249      // deliberately set
11250      if (_Tech.nativeSourceHandler) {
11251        sh = _Tech.nativeSourceHandler;
11252      } else {
11253        log$1.error('No source hander found for the current source.');
11254      }
11255    }
11256
11257    // Dispose any existing source handler
11258    this.disposeSourceHandler();
11259    this.off('dispose', this.disposeSourceHandler);
11260
11261    if (sh !== _Tech.nativeSourceHandler) {
11262      this.currentSource_ = source;
11263    }
11264
11265    this.sourceHandler_ = sh.handleSource(source, this, this.options_);
11266    this.on('dispose', this.disposeSourceHandler);
11267  };
11268
11269  /**
11270   * Clean up any existing SourceHandlers and listeners when the Tech is disposed.
11271   *
11272   * @listens Tech#dispose
11273   */
11274  _Tech.prototype.disposeSourceHandler = function () {
11275    // if we have a source and get another one
11276    // then we are loading something new
11277    // than clear all of our current tracks
11278    if (this.currentSource_) {
11279      this.clearTracks(['audio', 'video']);
11280      this.currentSource_ = null;
11281    }
11282
11283    // always clean up auto-text tracks
11284    this.cleanupAutoTextTracks();
11285
11286    if (this.sourceHandler_) {
11287
11288      if (this.sourceHandler_.dispose) {
11289        this.sourceHandler_.dispose();
11290      }
11291
11292      this.sourceHandler_ = null;
11293    }
11294  };
11295};
11296
11297// The base Tech class needs to be registered as a Component. It is the only
11298// Tech that can be registered as a Component.
11299Component.registerComponent('Tech', Tech);
11300Tech.registerTech('Tech', Tech);
11301
11302/**
11303 * A list of techs that should be added to techOrder on Players
11304 *
11305 * @private
11306 */
11307Tech.defaultTechOrder_ = [];
11308
11309var middlewares = {};
11310
11311function use(type, middleware) {
11312  middlewares[type] = middlewares[type] || [];
11313  middlewares[type].push(middleware);
11314}
11315
11316
11317
11318function setSource(player, src, next) {
11319  player.setTimeout(function () {
11320    return setSourceHelper(src, middlewares[src.type], next, player);
11321  }, 1);
11322}
11323
11324function setTech(middleware, tech) {
11325  middleware.forEach(function (mw) {
11326    return mw.setTech && mw.setTech(tech);
11327  });
11328}
11329
11330function get$1(middleware, tech, method) {
11331  return middleware.reduceRight(middlewareIterator(method), tech[method]());
11332}
11333
11334function set$1(middleware, tech, method, arg) {
11335  return tech[method](middleware.reduce(middlewareIterator(method), arg));
11336}
11337
11338var allowedGetters = {
11339  buffered: 1,
11340  currentTime: 1,
11341  duration: 1,
11342  seekable: 1,
11343  played: 1
11344};
11345
11346var allowedSetters = {
11347  setCurrentTime: 1
11348};
11349
11350function middlewareIterator(method) {
11351  return function (value, mw) {
11352    if (mw[method]) {
11353      return mw[method](value);
11354    }
11355
11356    return value;
11357  };
11358}
11359
11360function setSourceHelper() {
11361  var src = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
11362  var middleware = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
11363  var next = arguments[2];
11364  var player = arguments[3];
11365  var acc = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : [];
11366  var lastRun = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : false;
11367  var mwFactory = middleware[0],
11368      mwrest = middleware.slice(1);
11369
11370  // if mwFactory is a string, then we're at a fork in the road
11371
11372  if (typeof mwFactory === 'string') {
11373    setSourceHelper(src, middlewares[mwFactory], next, player, acc, lastRun);
11374
11375    // if we have an mwFactory, call it with the player to get the mw,
11376    // then call the mw's setSource method
11377  } else if (mwFactory) {
11378    var mw = mwFactory(player);
11379
11380    mw.setSource(assign({}, src), function (err, _src) {
11381
11382      // something happened, try the next middleware on the current level
11383      // make sure to use the old src
11384      if (err) {
11385        return setSourceHelper(src, mwrest, next, player, acc, lastRun);
11386      }
11387
11388      // we've succeeded, now we need to go deeper
11389      acc.push(mw);
11390
11391      // if it's the same type, continue down the current chain
11392      // otherwise, we want to go down the new chain
11393      setSourceHelper(_src, src.type === _src.type ? mwrest : middlewares[_src.type], next, player, acc, lastRun);
11394    });
11395  } else if (mwrest.length) {
11396    setSourceHelper(src, mwrest, next, player, acc, lastRun);
11397  } else if (lastRun) {
11398    next(src, acc);
11399  } else {
11400    setSourceHelper(src, middlewares['*'], next, player, acc, true);
11401  }
11402}
11403
11404/**
11405 * @module filter-source
11406 */
11407/**
11408 * Filter out single bad source objects or multiple source objects in an
11409 * array. Also flattens nested source object arrays into a 1 dimensional
11410 * array of source objects.
11411 *
11412 * @param {Tech~SourceObject|Tech~SourceObject[]} src
11413 *        The src object to filter
11414 *
11415 * @return {Tech~SourceObject[]}
11416 *         An array of sourceobjects containing only valid sources
11417 *
11418 * @private
11419 */
11420var filterSource = function filterSource(src) {
11421  // traverse array
11422  if (Array.isArray(src)) {
11423    var newsrc = [];
11424
11425    src.forEach(function (srcobj) {
11426      srcobj = filterSource(srcobj);
11427
11428      if (Array.isArray(srcobj)) {
11429        newsrc = newsrc.concat(srcobj);
11430      } else if (isObject(srcobj)) {
11431        newsrc.push(srcobj);
11432      }
11433    });
11434
11435    src = newsrc;
11436  } else if (typeof src === 'string' && src.trim()) {
11437    // convert string into object
11438    src = [{ src: src }];
11439  } else if (isObject(src) && typeof src.src === 'string' && src.src && src.src.trim()) {
11440    // src is already valid
11441    src = [src];
11442  } else {
11443    // invalid source, turn it into an empty array
11444    src = [];
11445  }
11446
11447  return src;
11448};
11449
11450/**
11451 * @file loader.js
11452 */
11453/**
11454 * The `MediaLoader` is the `Component` that decides which playback technology to load
11455 * when a player is initialized.
11456 *
11457 * @extends Component
11458 */
11459
11460var MediaLoader = function (_Component) {
11461  inherits(MediaLoader, _Component);
11462
11463  /**
11464   * Create an instance of this class.
11465   *
11466   * @param {Player} player
11467   *        The `Player` that this class should attach to.
11468   *
11469   * @param {Object} [options]
11470   *        The key/value stroe of player options.
11471   *
11472   * @param {Component~ReadyCallback} [ready]
11473   *        The function that is run when this component is ready.
11474   */
11475  function MediaLoader(player, options, ready) {
11476    classCallCheck(this, MediaLoader);
11477
11478    // MediaLoader has no element
11479    var options_ = mergeOptions({ createEl: false }, options);
11480
11481    // If there are no sources when the player is initialized,
11482    // load the first supported playback technology.
11483
11484    var _this = possibleConstructorReturn(this, _Component.call(this, player, options_, ready));
11485
11486    if (!options.playerOptions.sources || options.playerOptions.sources.length === 0) {
11487      for (var i = 0, j = options.playerOptions.techOrder; i < j.length; i++) {
11488        var techName = toTitleCase(j[i]);
11489        var tech = Tech.getTech(techName);
11490
11491        // Support old behavior of techs being registered as components.
11492        // Remove once that deprecated behavior is removed.
11493        if (!techName) {
11494          tech = Component.getComponent(techName);
11495        }
11496
11497        // Check if the browser supports this technology
11498        if (tech && tech.isSupported()) {
11499          player.loadTech_(techName);
11500          break;
11501        }
11502      }
11503    } else {
11504      // Loop through playback technologies (HTML5, Flash) and check for support.
11505      // Then load the best source.
11506      // A few assumptions here:
11507      //   All playback technologies respect preload false.
11508      player.src(options.playerOptions.sources);
11509    }
11510    return _this;
11511  }
11512
11513  return MediaLoader;
11514}(Component);
11515
11516Component.registerComponent('MediaLoader', MediaLoader);
11517
11518/**
11519 * @file button.js
11520 */
11521/**
11522 * Clickable Component which is clickable or keyboard actionable,
11523 * but is not a native HTML button.
11524 *
11525 * @extends Component
11526 */
11527
11528var ClickableComponent = function (_Component) {
11529  inherits(ClickableComponent, _Component);
11530
11531  /**
11532   * Creates an instance of this class.
11533   *
11534   * @param  {Player} player
11535   *         The `Player` that this class should be attached to.
11536   *
11537   * @param  {Object} [options]
11538   *         The key/value store of player options.
11539   */
11540  function ClickableComponent(player, options) {
11541    classCallCheck(this, ClickableComponent);
11542
11543    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
11544
11545    _this.emitTapEvents();
11546
11547    _this.enable();
11548    return _this;
11549  }
11550
11551  /**
11552   * Create the `Component`s DOM element.
11553   *
11554   * @param {string} [tag=div]
11555   *        The element's node type.
11556   *
11557   * @param {Object} [props={}]
11558   *        An object of properties that should be set on the element.
11559   *
11560   * @param {Object} [attributes={}]
11561   *        An object of attributes that should be set on the element.
11562   *
11563   * @return {Element}
11564   *         The element that gets created.
11565   */
11566
11567
11568  ClickableComponent.prototype.createEl = function createEl$$1() {
11569    var tag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
11570    var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
11571    var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
11572
11573    props = assign({
11574      innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
11575      className: this.buildCSSClass(),
11576      tabIndex: 0
11577    }, props);
11578
11579    if (tag === 'button') {
11580      log$1.error('Creating a ClickableComponent with an HTML element of ' + tag + ' is not supported; use a Button instead.');
11581    }
11582
11583    // Add ARIA attributes for clickable element which is not a native HTML button
11584    attributes = assign({
11585      'role': 'button',
11586
11587      // let the screen reader user know that the text of the element may change
11588      'aria-live': 'polite'
11589    }, attributes);
11590
11591    this.tabIndex_ = props.tabIndex;
11592
11593    var el = _Component.prototype.createEl.call(this, tag, props, attributes);
11594
11595    this.createControlTextEl(el);
11596
11597    return el;
11598  };
11599
11600  ClickableComponent.prototype.dispose = function dispose() {
11601    // remove controlTextEl_ on dipose
11602    this.controlTextEl_ = null;
11603
11604    _Component.prototype.dispose.call(this);
11605  };
11606
11607  /**
11608   * Create a control text element on this `Component`
11609   *
11610   * @param {Element} [el]
11611   *        Parent element for the control text.
11612   *
11613   * @return {Element}
11614   *         The control text element that gets created.
11615   */
11616
11617
11618  ClickableComponent.prototype.createControlTextEl = function createControlTextEl(el) {
11619    this.controlTextEl_ = createEl('span', {
11620      className: 'vjs-control-text'
11621    });
11622
11623    if (el) {
11624      el.appendChild(this.controlTextEl_);
11625    }
11626
11627    this.controlText(this.controlText_, el);
11628
11629    return this.controlTextEl_;
11630  };
11631
11632  /**
11633   * Get or set the localize text to use for the controls on the `Component`.
11634   *
11635   * @param {string} [text]
11636   *        Control text for element.
11637   *
11638   * @param {Element} [el=this.el()]
11639   *        Element to set the title on.
11640   *
11641   * @return {string}
11642   *         - The control text when getting
11643   */
11644
11645
11646  ClickableComponent.prototype.controlText = function controlText(text) {
11647    var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.el();
11648
11649    if (text === undefined) {
11650      return this.controlText_ || 'Need Text';
11651    }
11652
11653    var localizedText = this.localize(text);
11654
11655    this.controlText_ = text;
11656    textContent(this.controlTextEl_, localizedText);
11657    if (!this.nonIconControl) {
11658      // Set title attribute if only an icon is shown
11659      el.setAttribute('title', localizedText);
11660    }
11661  };
11662
11663  /**
11664   * Builds the default DOM `className`.
11665   *
11666   * @return {string}
11667   *         The DOM `className` for this object.
11668   */
11669
11670
11671  ClickableComponent.prototype.buildCSSClass = function buildCSSClass() {
11672    return 'vjs-control vjs-button ' + _Component.prototype.buildCSSClass.call(this);
11673  };
11674
11675  /**
11676   * Enable this `Component`s element.
11677   */
11678
11679
11680  ClickableComponent.prototype.enable = function enable() {
11681    if (!this.enabled_) {
11682      this.enabled_ = true;
11683      this.removeClass('vjs-disabled');
11684      this.el_.setAttribute('aria-disabled', 'false');
11685      if (typeof this.tabIndex_ !== 'undefined') {
11686        this.el_.setAttribute('tabIndex', this.tabIndex_);
11687      }
11688      this.on(['tap', 'click'], this.handleClick);
11689      this.on('focus', this.handleFocus);
11690      this.on('blur', this.handleBlur);
11691    }
11692  };
11693
11694  /**
11695   * Disable this `Component`s element.
11696   */
11697
11698
11699  ClickableComponent.prototype.disable = function disable() {
11700    this.enabled_ = false;
11701    this.addClass('vjs-disabled');
11702    this.el_.setAttribute('aria-disabled', 'true');
11703    if (typeof this.tabIndex_ !== 'undefined') {
11704      this.el_.removeAttribute('tabIndex');
11705    }
11706    this.off(['tap', 'click'], this.handleClick);
11707    this.off('focus', this.handleFocus);
11708    this.off('blur', this.handleBlur);
11709  };
11710
11711  /**
11712   * This gets called when a `ClickableComponent` gets:
11713   * - Clicked (via the `click` event, listening starts in the constructor)
11714   * - Tapped (via the `tap` event, listening starts in the constructor)
11715   * - The following things happen in order:
11716   *   1. {@link ClickableComponent#handleFocus} is called via a `focus` event on the
11717   *      `ClickableComponent`.
11718   *   2. {@link ClickableComponent#handleFocus} adds a listener for `keydown` on using
11719   *      {@link ClickableComponent#handleKeyPress}.
11720   *   3. `ClickableComponent` has not had a `blur` event (`blur` means that focus was lost). The user presses
11721   *      the space or enter key.
11722   *   4. {@link ClickableComponent#handleKeyPress} calls this function with the `keydown`
11723   *      event as a parameter.
11724   *
11725   * @param {EventTarget~Event} event
11726   *        The `keydown`, `tap`, or `click` event that caused this function to be
11727   *        called.
11728   *
11729   * @listens tap
11730   * @listens click
11731   * @abstract
11732   */
11733
11734
11735  ClickableComponent.prototype.handleClick = function handleClick(event) {};
11736
11737  /**
11738   * This gets called when a `ClickableComponent` gains focus via a `focus` event.
11739   * Turns on listening for `keydown` events. When they happen it
11740   * calls `this.handleKeyPress`.
11741   *
11742   * @param {EventTarget~Event} event
11743   *        The `focus` event that caused this function to be called.
11744   *
11745   * @listens focus
11746   */
11747
11748
11749  ClickableComponent.prototype.handleFocus = function handleFocus(event) {
11750    on(document_1, 'keydown', bind(this, this.handleKeyPress));
11751  };
11752
11753  /**
11754   * Called when this ClickableComponent has focus and a key gets pressed down. By
11755   * default it will call `this.handleClick` when the key is space or enter.
11756   *
11757   * @param {EventTarget~Event} event
11758   *        The `keydown` event that caused this function to be called.
11759   *
11760   * @listens keydown
11761   */
11762
11763
11764  ClickableComponent.prototype.handleKeyPress = function handleKeyPress(event) {
11765
11766    // Support Space (32) or Enter (13) key operation to fire a click event
11767    if (event.which === 32 || event.which === 13) {
11768      event.preventDefault();
11769      this.trigger('click');
11770    } else if (_Component.prototype.handleKeyPress) {
11771
11772      // Pass keypress handling up for unsupported keys
11773      _Component.prototype.handleKeyPress.call(this, event);
11774    }
11775  };
11776
11777  /**
11778   * Called when a `ClickableComponent` loses focus. Turns off the listener for
11779   * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
11780   *
11781   * @param {EventTarget~Event} event
11782   *        The `blur` event that caused this function to be called.
11783   *
11784   * @listens blur
11785   */
11786
11787
11788  ClickableComponent.prototype.handleBlur = function handleBlur(event) {
11789    off(document_1, 'keydown', bind(this, this.handleKeyPress));
11790  };
11791
11792  return ClickableComponent;
11793}(Component);
11794
11795Component.registerComponent('ClickableComponent', ClickableComponent);
11796
11797/**
11798 * @file poster-image.js
11799 */
11800/**
11801 * A `ClickableComponent` that handles showing the poster image for the player.
11802 *
11803 * @extends ClickableComponent
11804 */
11805
11806var PosterImage = function (_ClickableComponent) {
11807  inherits(PosterImage, _ClickableComponent);
11808
11809  /**
11810   * Create an instance of this class.
11811   *
11812   * @param {Player} player
11813   *        The `Player` that this class should attach to.
11814   *
11815   * @param {Object} [options]
11816   *        The key/value store of player options.
11817   */
11818  function PosterImage(player, options) {
11819    classCallCheck(this, PosterImage);
11820
11821    var _this = possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
11822
11823    _this.update();
11824    player.on('posterchange', bind(_this, _this.update));
11825    return _this;
11826  }
11827
11828  /**
11829   * Clean up and dispose of the `PosterImage`.
11830   */
11831
11832
11833  PosterImage.prototype.dispose = function dispose() {
11834    this.player().off('posterchange', this.update);
11835    _ClickableComponent.prototype.dispose.call(this);
11836  };
11837
11838  /**
11839   * Create the `PosterImage`s DOM element.
11840   *
11841   * @return {Element}
11842   *         The element that gets created.
11843   */
11844
11845
11846  PosterImage.prototype.createEl = function createEl$$1() {
11847    var el = createEl('div', {
11848      className: 'vjs-poster',
11849
11850      // Don't want poster to be tabbable.
11851      tabIndex: -1
11852    });
11853
11854    // To ensure the poster image resizes while maintaining its original aspect
11855    // ratio, use a div with `background-size` when available. For browsers that
11856    // do not support `background-size` (e.g. IE8), fall back on using a regular
11857    // img element.
11858    if (!BACKGROUND_SIZE_SUPPORTED) {
11859      this.fallbackImg_ = createEl('img');
11860      el.appendChild(this.fallbackImg_);
11861    }
11862
11863    return el;
11864  };
11865
11866  /**
11867   * An {@link EventTarget~EventListener} for {@link Player#posterchange} events.
11868   *
11869   * @listens Player#posterchange
11870   *
11871   * @param {EventTarget~Event} [event]
11872   *        The `Player#posterchange` event that triggered this function.
11873   */
11874
11875
11876  PosterImage.prototype.update = function update(event) {
11877    var url = this.player().poster();
11878
11879    this.setSrc(url);
11880
11881    // If there's no poster source we should display:none on this component
11882    // so it's not still clickable or right-clickable
11883    if (url) {
11884      this.show();
11885    } else {
11886      this.hide();
11887    }
11888  };
11889
11890  /**
11891   * Set the source of the `PosterImage` depending on the display method.
11892   *
11893   * @param {string} url
11894   *        The URL to the source for the `PosterImage`.
11895   */
11896
11897
11898  PosterImage.prototype.setSrc = function setSrc(url) {
11899    if (this.fallbackImg_) {
11900      this.fallbackImg_.src = url;
11901    } else {
11902      var backgroundImage = '';
11903
11904      // Any falsey values should stay as an empty string, otherwise
11905      // this will throw an extra error
11906      if (url) {
11907        backgroundImage = 'url("' + url + '")';
11908      }
11909
11910      this.el_.style.backgroundImage = backgroundImage;
11911    }
11912  };
11913
11914  /**
11915   * An {@link EventTarget~EventListener} for clicks on the `PosterImage`. See
11916   * {@link ClickableComponent#handleClick} for instances where this will be triggered.
11917   *
11918   * @listens tap
11919   * @listens click
11920   * @listens keydown
11921   *
11922   * @param {EventTarget~Event} event
11923   +        The `click`, `tap` or `keydown` event that caused this function to be called.
11924   */
11925
11926
11927  PosterImage.prototype.handleClick = function handleClick(event) {
11928    // We don't want a click to trigger playback when controls are disabled
11929    if (!this.player_.controls()) {
11930      return;
11931    }
11932
11933    if (this.player_.paused()) {
11934      this.player_.play();
11935    } else {
11936      this.player_.pause();
11937    }
11938  };
11939
11940  return PosterImage;
11941}(ClickableComponent);
11942
11943Component.registerComponent('PosterImage', PosterImage);
11944
11945/**
11946 * @file text-track-display.js
11947 */
11948var darkGray = '#222';
11949var lightGray = '#ccc';
11950var fontMap = {
11951  monospace: 'monospace',
11952  sansSerif: 'sans-serif',
11953  serif: 'serif',
11954  monospaceSansSerif: '"Andale Mono", "Lucida Console", monospace',
11955  monospaceSerif: '"Courier New", monospace',
11956  proportionalSansSerif: 'sans-serif',
11957  proportionalSerif: 'serif',
11958  casual: '"Comic Sans MS", Impact, fantasy',
11959  script: '"Monotype Corsiva", cursive',
11960  smallcaps: '"Andale Mono", "Lucida Console", monospace, sans-serif'
11961};
11962
11963/**
11964 * Construct an rgba color from a given hex color code.
11965 *
11966 * @param {number} color
11967 *        Hex number for color, like #f0e.
11968 *
11969 * @param {number} opacity
11970 *        Value for opacity, 0.0 - 1.0.
11971 *
11972 * @return {string}
11973 *         The rgba color that was created, like 'rgba(255, 0, 0, 0.3)'.
11974 *
11975 * @private
11976 */
11977function constructColor(color, opacity) {
11978  return 'rgba(' +
11979  // color looks like "#f0e"
11980  parseInt(color[1] + color[1], 16) + ',' + parseInt(color[2] + color[2], 16) + ',' + parseInt(color[3] + color[3], 16) + ',' + opacity + ')';
11981}
11982
11983/**
11984 * Try to update the style of a DOM element. Some style changes will throw an error,
11985 * particularly in IE8. Those should be noops.
11986 *
11987 * @param {Element} el
11988 *        The DOM element to be styled.
11989 *
11990 * @param {string} style
11991 *        The CSS property on the element that should be styled.
11992 *
11993 * @param {string} rule
11994 *        The style rule that should be applied to the property.
11995 *
11996 * @private
11997 */
11998function tryUpdateStyle(el, style, rule) {
11999  try {
12000    el.style[style] = rule;
12001  } catch (e) {
12002
12003    // Satisfies linter.
12004    return;
12005  }
12006}
12007
12008/**
12009 * The component for displaying text track cues.
12010 *
12011 * @extends Component
12012 */
12013
12014var TextTrackDisplay = function (_Component) {
12015  inherits(TextTrackDisplay, _Component);
12016
12017  /**
12018   * Creates an instance of this class.
12019   *
12020   * @param {Player} player
12021   *        The `Player` that this class should be attached to.
12022   *
12023   * @param {Object} [options]
12024   *        The key/value store of player options.
12025   *
12026   * @param {Component~ReadyCallback} [ready]
12027   *        The function to call when `TextTrackDisplay` is ready.
12028   */
12029  function TextTrackDisplay(player, options, ready) {
12030    classCallCheck(this, TextTrackDisplay);
12031
12032    var _this = possibleConstructorReturn(this, _Component.call(this, player, options, ready));
12033
12034    player.on('loadstart', bind(_this, _this.toggleDisplay));
12035    player.on('texttrackchange', bind(_this, _this.updateDisplay));
12036    player.on('loadstart', bind(_this, _this.preselectTrack));
12037
12038    // This used to be called during player init, but was causing an error
12039    // if a track should show by default and the display hadn't loaded yet.
12040    // Should probably be moved to an external track loader when we support
12041    // tracks that don't need a display.
12042    player.ready(bind(_this, function () {
12043      if (player.tech_ && player.tech_.featuresNativeTextTracks) {
12044        this.hide();
12045        return;
12046      }
12047
12048      player.on('fullscreenchange', bind(this, this.updateDisplay));
12049
12050      var tracks = this.options_.playerOptions.tracks || [];
12051
12052      for (var i = 0; i < tracks.length; i++) {
12053        this.player_.addRemoteTextTrack(tracks[i], true);
12054      }
12055
12056      this.preselectTrack();
12057    }));
12058    return _this;
12059  }
12060
12061  /**
12062  * Preselect a track following this precedence:
12063  * - matches the previously selected {@link TextTrack}'s language and kind
12064  * - matches the previously selected {@link TextTrack}'s language only
12065  * - is the first default captions track
12066  * - is the first default descriptions track
12067  *
12068  * @listens Player#loadstart
12069  */
12070
12071
12072  TextTrackDisplay.prototype.preselectTrack = function preselectTrack() {
12073    var modes = { captions: 1, subtitles: 1 };
12074    var trackList = this.player_.textTracks();
12075    var userPref = this.player_.cache_.selectedLanguage;
12076    var firstDesc = void 0;
12077    var firstCaptions = void 0;
12078    var preferredTrack = void 0;
12079
12080    for (var i = 0; i < trackList.length; i++) {
12081      var track = trackList[i];
12082
12083      if (userPref && userPref.enabled && userPref.language === track.language) {
12084        // Always choose the track that matches both language and kind
12085        if (track.kind === userPref.kind) {
12086          preferredTrack = track;
12087          // or choose the first track that matches language
12088        } else if (!preferredTrack) {
12089          preferredTrack = track;
12090        }
12091
12092        // clear everything if offTextTrackMenuItem was clicked
12093      } else if (userPref && !userPref.enabled) {
12094        preferredTrack = null;
12095        firstDesc = null;
12096        firstCaptions = null;
12097      } else if (track['default']) {
12098        if (track.kind === 'descriptions' && !firstDesc) {
12099          firstDesc = track;
12100        } else if (track.kind in modes && !firstCaptions) {
12101          firstCaptions = track;
12102        }
12103      }
12104    }
12105
12106    // The preferredTrack matches the user preference and takes
12107    // precendence over all the other tracks.
12108    // So, display the preferredTrack before the first default track
12109    // and the subtitles/captions track before the descriptions track
12110    if (preferredTrack) {
12111      preferredTrack.mode = 'showing';
12112    } else if (firstCaptions) {
12113      firstCaptions.mode = 'showing';
12114    } else if (firstDesc) {
12115      firstDesc.mode = 'showing';
12116    }
12117  };
12118
12119  /**
12120   * Turn display of {@link TextTrack}'s from the current state into the other state.
12121   * There are only two states:
12122   * - 'shown'
12123   * - 'hidden'
12124   *
12125   * @listens Player#loadstart
12126   */
12127
12128
12129  TextTrackDisplay.prototype.toggleDisplay = function toggleDisplay() {
12130    if (this.player_.tech_ && this.player_.tech_.featuresNativeTextTracks) {
12131      this.hide();
12132    } else {
12133      this.show();
12134    }
12135  };
12136
12137  /**
12138   * Create the {@link Component}'s DOM element.
12139   *
12140   * @return {Element}
12141   *         The element that was created.
12142   */
12143
12144
12145  TextTrackDisplay.prototype.createEl = function createEl() {
12146    return _Component.prototype.createEl.call(this, 'div', {
12147      className: 'vjs-text-track-display'
12148    }, {
12149      'aria-live': 'off',
12150      'aria-atomic': 'true'
12151    });
12152  };
12153
12154  /**
12155   * Clear all displayed {@link TextTrack}s.
12156   */
12157
12158
12159  TextTrackDisplay.prototype.clearDisplay = function clearDisplay() {
12160    if (typeof window_1.WebVTT === 'function') {
12161      window_1.WebVTT.processCues(window_1, [], this.el_);
12162    }
12163  };
12164
12165  /**
12166   * Update the displayed TextTrack when a either a {@link Player#texttrackchange} or
12167   * a {@link Player#fullscreenchange} is fired.
12168   *
12169   * @listens Player#texttrackchange
12170   * @listens Player#fullscreenchange
12171   */
12172
12173
12174  TextTrackDisplay.prototype.updateDisplay = function updateDisplay() {
12175    var tracks = this.player_.textTracks();
12176
12177    this.clearDisplay();
12178
12179    // Track display prioritization model: if multiple tracks are 'showing',
12180    //  display the first 'subtitles' or 'captions' track which is 'showing',
12181    //  otherwise display the first 'descriptions' track which is 'showing'
12182
12183    var descriptionsTrack = null;
12184    var captionsSubtitlesTrack = null;
12185    var i = tracks.length;
12186
12187    while (i--) {
12188      var track = tracks[i];
12189
12190      if (track.mode === 'showing') {
12191        if (track.kind === 'descriptions') {
12192          descriptionsTrack = track;
12193        } else {
12194          captionsSubtitlesTrack = track;
12195        }
12196      }
12197    }
12198
12199    if (captionsSubtitlesTrack) {
12200      if (this.getAttribute('aria-live') !== 'off') {
12201        this.setAttribute('aria-live', 'off');
12202      }
12203      this.updateForTrack(captionsSubtitlesTrack);
12204    } else if (descriptionsTrack) {
12205      if (this.getAttribute('aria-live') !== 'assertive') {
12206        this.setAttribute('aria-live', 'assertive');
12207      }
12208      this.updateForTrack(descriptionsTrack);
12209    }
12210  };
12211
12212  /**
12213   * Add an {@link Texttrack} to to the {@link Tech}s {@link TextTrackList}.
12214   *
12215   * @param {TextTrack} track
12216   *        Text track object to be added to the list.
12217   */
12218
12219
12220  TextTrackDisplay.prototype.updateForTrack = function updateForTrack(track) {
12221    if (typeof window_1.WebVTT !== 'function' || !track.activeCues) {
12222      return;
12223    }
12224
12225    var overrides = this.player_.textTrackSettings.getValues();
12226    var cues = [];
12227
12228    for (var _i = 0; _i < track.activeCues.length; _i++) {
12229      cues.push(track.activeCues[_i]);
12230    }
12231
12232    window_1.WebVTT.processCues(window_1, cues, this.el_);
12233
12234    var i = cues.length;
12235
12236    while (i--) {
12237      var cue = cues[i];
12238
12239      if (!cue) {
12240        continue;
12241      }
12242
12243      var cueDiv = cue.displayState;
12244
12245      if (overrides.color) {
12246        cueDiv.firstChild.style.color = overrides.color;
12247      }
12248      if (overrides.textOpacity) {
12249        tryUpdateStyle(cueDiv.firstChild, 'color', constructColor(overrides.color || '#fff', overrides.textOpacity));
12250      }
12251      if (overrides.backgroundColor) {
12252        cueDiv.firstChild.style.backgroundColor = overrides.backgroundColor;
12253      }
12254      if (overrides.backgroundOpacity) {
12255        tryUpdateStyle(cueDiv.firstChild, 'backgroundColor', constructColor(overrides.backgroundColor || '#000', overrides.backgroundOpacity));
12256      }
12257      if (overrides.windowColor) {
12258        if (overrides.windowOpacity) {
12259          tryUpdateStyle(cueDiv, 'backgroundColor', constructColor(overrides.windowColor, overrides.windowOpacity));
12260        } else {
12261          cueDiv.style.backgroundColor = overrides.windowColor;
12262        }
12263      }
12264      if (overrides.edgeStyle) {
12265        if (overrides.edgeStyle === 'dropshadow') {
12266          cueDiv.firstChild.style.textShadow = '2px 2px 3px ' + darkGray + ', 2px 2px 4px ' + darkGray + ', 2px 2px 5px ' + darkGray;
12267        } else if (overrides.edgeStyle === 'raised') {
12268          cueDiv.firstChild.style.textShadow = '1px 1px ' + darkGray + ', 2px 2px ' + darkGray + ', 3px 3px ' + darkGray;
12269        } else if (overrides.edgeStyle === 'depressed') {
12270          cueDiv.firstChild.style.textShadow = '1px 1px ' + lightGray + ', 0 1px ' + lightGray + ', -1px -1px ' + darkGray + ', 0 -1px ' + darkGray;
12271        } else if (overrides.edgeStyle === 'uniform') {
12272          cueDiv.firstChild.style.textShadow = '0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray + ', 0 0 4px ' + darkGray;
12273        }
12274      }
12275      if (overrides.fontPercent && overrides.fontPercent !== 1) {
12276        var fontSize = window_1.parseFloat(cueDiv.style.fontSize);
12277
12278        cueDiv.style.fontSize = fontSize * overrides.fontPercent + 'px';
12279        cueDiv.style.height = 'auto';
12280        cueDiv.style.top = 'auto';
12281        cueDiv.style.bottom = '2px';
12282      }
12283      if (overrides.fontFamily && overrides.fontFamily !== 'default') {
12284        if (overrides.fontFamily === 'small-caps') {
12285          cueDiv.firstChild.style.fontVariant = 'small-caps';
12286        } else {
12287          cueDiv.firstChild.style.fontFamily = fontMap[overrides.fontFamily];
12288        }
12289      }
12290    }
12291  };
12292
12293  return TextTrackDisplay;
12294}(Component);
12295
12296Component.registerComponent('TextTrackDisplay', TextTrackDisplay);
12297
12298/**
12299 * @file loading-spinner.js
12300 */
12301/**
12302 * A loading spinner for use during waiting/loading events.
12303 *
12304 * @extends Component
12305 */
12306
12307var LoadingSpinner = function (_Component) {
12308  inherits(LoadingSpinner, _Component);
12309
12310  function LoadingSpinner() {
12311    classCallCheck(this, LoadingSpinner);
12312    return possibleConstructorReturn(this, _Component.apply(this, arguments));
12313  }
12314
12315  /**
12316   * Create the `LoadingSpinner`s DOM element.
12317   *
12318   * @return {Element}
12319   *         The dom element that gets created.
12320   */
12321  LoadingSpinner.prototype.createEl = function createEl() {
12322    return _Component.prototype.createEl.call(this, 'div', {
12323      className: 'vjs-loading-spinner',
12324      dir: 'ltr'
12325    });
12326  };
12327
12328  return LoadingSpinner;
12329}(Component);
12330
12331Component.registerComponent('LoadingSpinner', LoadingSpinner);
12332
12333/**
12334 * @file button.js
12335 */
12336/**
12337 * Base class for all buttons.
12338 *
12339 * @extends ClickableComponent
12340 */
12341
12342var Button = function (_ClickableComponent) {
12343  inherits(Button, _ClickableComponent);
12344
12345  function Button() {
12346    classCallCheck(this, Button);
12347    return possibleConstructorReturn(this, _ClickableComponent.apply(this, arguments));
12348  }
12349
12350  /**
12351   * Create the `Button`s DOM element.
12352   *
12353   * @param {string} [tag="button"]
12354   *        The element's node type. This argument is IGNORED: no matter what
12355   *        is passed, it will always create a `button` element.
12356   *
12357   * @param {Object} [props={}]
12358   *        An object of properties that should be set on the element.
12359   *
12360   * @param {Object} [attributes={}]
12361   *        An object of attributes that should be set on the element.
12362   *
12363   * @return {Element}
12364   *         The element that gets created.
12365   */
12366  Button.prototype.createEl = function createEl(tag) {
12367    var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
12368    var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
12369
12370    tag = 'button';
12371
12372    props = assign({
12373      innerHTML: '<span aria-hidden="true" class="vjs-icon-placeholder"></span>',
12374      className: this.buildCSSClass()
12375    }, props);
12376
12377    // Add attributes for button element
12378    attributes = assign({
12379
12380      // Necessary since the default button type is "submit"
12381      'type': 'button',
12382
12383      // let the screen reader user know that the text of the button may change
12384      'aria-live': 'polite'
12385    }, attributes);
12386
12387    var el = Component.prototype.createEl.call(this, tag, props, attributes);
12388
12389    this.createControlTextEl(el);
12390
12391    return el;
12392  };
12393
12394  /**
12395   * Add a child `Component` inside of this `Button`.
12396   *
12397   * @param {string|Component} child
12398   *        The name or instance of a child to add.
12399   *
12400   * @param {Object} [options={}]
12401   *        The key/value store of options that will get passed to children of
12402   *        the child.
12403   *
12404   * @return {Component}
12405   *         The `Component` that gets added as a child. When using a string the
12406   *         `Component` will get created by this process.
12407   *
12408   * @deprecated since version 5
12409   */
12410
12411
12412  Button.prototype.addChild = function addChild(child) {
12413    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
12414
12415    var className = this.constructor.name;
12416
12417    log$1.warn('Adding an actionable (user controllable) child to a Button (' + className + ') is not supported; use a ClickableComponent instead.');
12418
12419    // Avoid the error message generated by ClickableComponent's addChild method
12420    return Component.prototype.addChild.call(this, child, options);
12421  };
12422
12423  /**
12424   * Enable the `Button` element so that it can be activated or clicked. Use this with
12425   * {@link Button#disable}.
12426   */
12427
12428
12429  Button.prototype.enable = function enable() {
12430    _ClickableComponent.prototype.enable.call(this);
12431    this.el_.removeAttribute('disabled');
12432  };
12433
12434  /**
12435   * Disable the `Button` element so that it cannot be activated or clicked. Use this with
12436   * {@link Button#enable}.
12437   */
12438
12439
12440  Button.prototype.disable = function disable() {
12441    _ClickableComponent.prototype.disable.call(this);
12442    this.el_.setAttribute('disabled', 'disabled');
12443  };
12444
12445  /**
12446   * This gets called when a `Button` has focus and `keydown` is triggered via a key
12447   * press.
12448   *
12449   * @param {EventTarget~Event} event
12450   *        The event that caused this function to get called.
12451   *
12452   * @listens keydown
12453   */
12454
12455
12456  Button.prototype.handleKeyPress = function handleKeyPress(event) {
12457
12458    // Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button.
12459    if (event.which === 32 || event.which === 13) {
12460      return;
12461    }
12462
12463    // Pass keypress handling up for unsupported keys
12464    _ClickableComponent.prototype.handleKeyPress.call(this, event);
12465  };
12466
12467  return Button;
12468}(ClickableComponent);
12469
12470Component.registerComponent('Button', Button);
12471
12472/**
12473 * @file big-play-button.js
12474 */
12475/**
12476 * The initial play button that shows before the video has played. The hiding of the
12477 * `BigPlayButton` get done via CSS and `Player` states.
12478 *
12479 * @extends Button
12480 */
12481
12482var BigPlayButton = function (_Button) {
12483  inherits(BigPlayButton, _Button);
12484
12485  function BigPlayButton(player, options) {
12486    classCallCheck(this, BigPlayButton);
12487
12488    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
12489
12490    _this.mouseused_ = false;
12491
12492    _this.on('mousedown', _this.handleMouseDown);
12493    return _this;
12494  }
12495
12496  /**
12497   * Builds the default DOM `className`.
12498   *
12499   * @return {string}
12500   *         The DOM `className` for this object. Always returns 'vjs-big-play-button'.
12501   */
12502
12503
12504  BigPlayButton.prototype.buildCSSClass = function buildCSSClass() {
12505    return 'vjs-big-play-button';
12506  };
12507
12508  /**
12509   * This gets called when a `BigPlayButton` "clicked". See {@link ClickableComponent}
12510   * for more detailed information on what a click can be.
12511   *
12512   * @param {EventTarget~Event} event
12513   *        The `keydown`, `tap`, or `click` event that caused this function to be
12514   *        called.
12515   *
12516   * @listens tap
12517   * @listens click
12518   */
12519
12520
12521  BigPlayButton.prototype.handleClick = function handleClick(event) {
12522    var playPromise = this.player_.play();
12523
12524    // exit early if clicked via the mouse
12525    if (this.mouseused_ && event.clientX && event.clientY) {
12526      return;
12527    }
12528
12529    var cb = this.player_.getChild('controlBar');
12530    var playToggle = cb && cb.getChild('playToggle');
12531
12532    if (!playToggle) {
12533      this.player_.focus();
12534      return;
12535    }
12536
12537    var playFocus = function playFocus() {
12538      return playToggle.focus();
12539    };
12540
12541    if (isPromise(playPromise)) {
12542      playPromise.then(playFocus, function () {});
12543    } else {
12544      this.setTimeout(playFocus, 1);
12545    }
12546  };
12547
12548  BigPlayButton.prototype.handleKeyPress = function handleKeyPress(event) {
12549    this.mouseused_ = false;
12550
12551    _Button.prototype.handleKeyPress.call(this, event);
12552  };
12553
12554  BigPlayButton.prototype.handleMouseDown = function handleMouseDown(event) {
12555    this.mouseused_ = true;
12556  };
12557
12558  return BigPlayButton;
12559}(Button);
12560
12561/**
12562 * The text that should display over the `BigPlayButton`s controls. Added to for localization.
12563 *
12564 * @type {string}
12565 * @private
12566 */
12567
12568
12569BigPlayButton.prototype.controlText_ = 'Play Video';
12570
12571Component.registerComponent('BigPlayButton', BigPlayButton);
12572
12573/**
12574 * @file close-button.js
12575 */
12576/**
12577 * The `CloseButton` is a `{@link Button}` that fires a `close` event when
12578 * it gets clicked.
12579 *
12580 * @extends Button
12581 */
12582
12583var CloseButton = function (_Button) {
12584  inherits(CloseButton, _Button);
12585
12586  /**
12587   * Creates an instance of the this class.
12588   *
12589   * @param  {Player} player
12590   *         The `Player` that this class should be attached to.
12591   *
12592   * @param  {Object} [options]
12593   *         The key/value store of player options.
12594   */
12595  function CloseButton(player, options) {
12596    classCallCheck(this, CloseButton);
12597
12598    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
12599
12600    _this.controlText(options && options.controlText || _this.localize('Close'));
12601    return _this;
12602  }
12603
12604  /**
12605   * Builds the default DOM `className`.
12606   *
12607   * @return {string}
12608   *         The DOM `className` for this object.
12609   */
12610
12611
12612  CloseButton.prototype.buildCSSClass = function buildCSSClass() {
12613    return 'vjs-close-button ' + _Button.prototype.buildCSSClass.call(this);
12614  };
12615
12616  /**
12617   * This gets called when a `CloseButton` gets clicked. See
12618   * {@link ClickableComponent#handleClick} for more information on when this will be
12619   * triggered
12620   *
12621   * @param {EventTarget~Event} event
12622   *        The `keydown`, `tap`, or `click` event that caused this function to be
12623   *        called.
12624   *
12625   * @listens tap
12626   * @listens click
12627   * @fires CloseButton#close
12628   */
12629
12630
12631  CloseButton.prototype.handleClick = function handleClick(event) {
12632
12633    /**
12634     * Triggered when the a `CloseButton` is clicked.
12635     *
12636     * @event CloseButton#close
12637     * @type {EventTarget~Event}
12638     *
12639     * @property {boolean} [bubbles=false]
12640     *           set to false so that the close event does not
12641     *           bubble up to parents if there is no listener
12642     */
12643    this.trigger({ type: 'close', bubbles: false });
12644  };
12645
12646  return CloseButton;
12647}(Button);
12648
12649Component.registerComponent('CloseButton', CloseButton);
12650
12651/**
12652 * @file play-toggle.js
12653 */
12654/**
12655 * Button to toggle between play and pause.
12656 *
12657 * @extends Button
12658 */
12659
12660var PlayToggle = function (_Button) {
12661  inherits(PlayToggle, _Button);
12662
12663  /**
12664   * Creates an instance of this class.
12665   *
12666   * @param {Player} player
12667   *        The `Player` that this class should be attached to.
12668   *
12669   * @param {Object} [options]
12670   *        The key/value store of player options.
12671   */
12672  function PlayToggle(player, options) {
12673    classCallCheck(this, PlayToggle);
12674
12675    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
12676
12677    _this.on(player, 'play', _this.handlePlay);
12678    _this.on(player, 'pause', _this.handlePause);
12679    _this.on(player, 'ended', _this.handleEnded);
12680    return _this;
12681  }
12682
12683  /**
12684   * Builds the default DOM `className`.
12685   *
12686   * @return {string}
12687   *         The DOM `className` for this object.
12688   */
12689
12690
12691  PlayToggle.prototype.buildCSSClass = function buildCSSClass() {
12692    return 'vjs-play-control ' + _Button.prototype.buildCSSClass.call(this);
12693  };
12694
12695  /**
12696   * This gets called when an `PlayToggle` is "clicked". See
12697   * {@link ClickableComponent} for more detailed information on what a click can be.
12698   *
12699   * @param {EventTarget~Event} [event]
12700   *        The `keydown`, `tap`, or `click` event that caused this function to be
12701   *        called.
12702   *
12703   * @listens tap
12704   * @listens click
12705   */
12706
12707
12708  PlayToggle.prototype.handleClick = function handleClick(event) {
12709    if (this.player_.paused()) {
12710      this.player_.play();
12711    } else {
12712      this.player_.pause();
12713    }
12714  };
12715
12716  /**
12717   * This gets called once after the video has ended and the user seeks so that
12718   * we can change the replay button back to a play button.
12719   *
12720   * @param {EventTarget~Event} [event]
12721   *        The event that caused this function to run.
12722   *
12723   * @listens Player#seeked
12724   */
12725
12726
12727  PlayToggle.prototype.handleSeeked = function handleSeeked(event) {
12728    this.removeClass('vjs-ended');
12729
12730    if (this.player_.paused()) {
12731      this.handlePause(event);
12732    } else {
12733      this.handlePlay(event);
12734    }
12735  };
12736
12737  /**
12738   * Add the vjs-playing class to the element so it can change appearance.
12739   *
12740   * @param {EventTarget~Event} [event]
12741   *        The event that caused this function to run.
12742   *
12743   * @listens Player#play
12744   */
12745
12746
12747  PlayToggle.prototype.handlePlay = function handlePlay(event) {
12748    this.removeClass('vjs-ended');
12749    this.removeClass('vjs-paused');
12750    this.addClass('vjs-playing');
12751    // change the button text to "Pause"
12752    this.controlText('Pause');
12753  };
12754
12755  /**
12756   * Add the vjs-paused class to the element so it can change appearance.
12757   *
12758   * @param {EventTarget~Event} [event]
12759   *        The event that caused this function to run.
12760   *
12761   * @listens Player#pause
12762   */
12763
12764
12765  PlayToggle.prototype.handlePause = function handlePause(event) {
12766    this.removeClass('vjs-playing');
12767    this.addClass('vjs-paused');
12768    // change the button text to "Play"
12769    this.controlText('Play');
12770  };
12771
12772  /**
12773   * Add the vjs-ended class to the element so it can change appearance
12774   *
12775   * @param {EventTarget~Event} [event]
12776   *        The event that caused this function to run.
12777   *
12778   * @listens Player#ended
12779   */
12780
12781
12782  PlayToggle.prototype.handleEnded = function handleEnded(event) {
12783    this.removeClass('vjs-playing');
12784    this.addClass('vjs-ended');
12785    // change the button text to "Replay"
12786    this.controlText('Replay');
12787
12788    // on the next seek remove the replay button
12789    this.one(this.player_, 'seeked', this.handleSeeked);
12790  };
12791
12792  return PlayToggle;
12793}(Button);
12794
12795/**
12796 * The text that should display over the `PlayToggle`s controls. Added for localization.
12797 *
12798 * @type {string}
12799 * @private
12800 */
12801
12802
12803PlayToggle.prototype.controlText_ = 'Play';
12804
12805Component.registerComponent('PlayToggle', PlayToggle);
12806
12807/**
12808 * @file format-time.js
12809 * @module Format-time
12810 */
12811
12812/**
12813 * Format seconds as a time string, H:MM:SS or M:SS. Supplying a guide (in seconds)
12814 * will force a number of leading zeros to cover the length of the guide.
12815 *
12816 * @param {number} seconds
12817 *        Number of seconds to be turned into a string
12818 *
12819 * @param {number} guide
12820 *        Number (in seconds) to model the string after
12821 *
12822 * @return {string}
12823 *         Time formatted as H:MM:SS or M:SS
12824 */
12825function formatTime(seconds) {
12826  var guide = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : seconds;
12827
12828  seconds = seconds < 0 ? 0 : seconds;
12829  var s = Math.floor(seconds % 60);
12830  var m = Math.floor(seconds / 60 % 60);
12831  var h = Math.floor(seconds / 3600);
12832  var gm = Math.floor(guide / 60 % 60);
12833  var gh = Math.floor(guide / 3600);
12834
12835  // handle invalid times
12836  if (isNaN(seconds) || seconds === Infinity) {
12837    // '-' is false for all relational operators (e.g. <, >=) so this setting
12838    // will add the minimum number of fields specified by the guide
12839    h = m = s = '-';
12840  }
12841
12842  // Check if we need to show hours
12843  h = h > 0 || gh > 0 ? h + ':' : '';
12844
12845  // If hours are showing, we may need to add a leading zero.
12846  // Always show at least one digit of minutes.
12847  m = ((h || gm >= 10) && m < 10 ? '0' + m : m) + ':';
12848
12849  // Check if leading zero is need for seconds
12850  s = s < 10 ? '0' + s : s;
12851
12852  return h + m + s;
12853}
12854
12855/**
12856 * @file time-display.js
12857 */
12858/**
12859 * Displays the time left in the video
12860 *
12861 * @extends Component
12862 */
12863
12864var TimeDisplay = function (_Component) {
12865  inherits(TimeDisplay, _Component);
12866
12867  /**
12868   * Creates an instance of this class.
12869   *
12870   * @param {Player} player
12871   *        The `Player` that this class should be attached to.
12872   *
12873   * @param {Object} [options]
12874   *        The key/value store of player options.
12875   */
12876  function TimeDisplay(player, options) {
12877    classCallCheck(this, TimeDisplay);
12878
12879    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
12880
12881    _this.throttledUpdateContent = throttle(bind(_this, _this.updateContent), 25);
12882    _this.on(player, 'timeupdate', _this.throttledUpdateContent);
12883    return _this;
12884  }
12885
12886  /**
12887   * Create the `Component`'s DOM element
12888   *
12889   * @return {Element}
12890   *         The element that was created.
12891   */
12892
12893
12894  TimeDisplay.prototype.createEl = function createEl$$1(plainName) {
12895    var className = this.buildCSSClass();
12896    var el = _Component.prototype.createEl.call(this, 'div', {
12897      className: className + ' vjs-time-control vjs-control'
12898    });
12899
12900    this.contentEl_ = createEl('div', {
12901      className: className + '-display'
12902    }, {
12903      // tell screen readers not to automatically read the time as it changes
12904      'aria-live': 'off'
12905    }, createEl('span', {
12906      className: 'vjs-control-text',
12907      textContent: this.localize(this.controlText_)
12908    }));
12909
12910    this.updateTextNode_();
12911    el.appendChild(this.contentEl_);
12912    return el;
12913  };
12914
12915  TimeDisplay.prototype.dispose = function dispose() {
12916    this.contentEl_ = null;
12917    this.textNode_ = null;
12918
12919    _Component.prototype.dispose.call(this);
12920  };
12921
12922  /**
12923   * Updates the "remaining time" text node with new content using the
12924   * contents of the `formattedTime_` property.
12925   *
12926   * @private
12927   */
12928
12929
12930  TimeDisplay.prototype.updateTextNode_ = function updateTextNode_() {
12931    if (!this.contentEl_) {
12932      return;
12933    }
12934
12935    while (this.contentEl_.firstChild) {
12936      this.contentEl_.removeChild(this.contentEl_.firstChild);
12937    }
12938
12939    this.textNode_ = document_1.createTextNode(this.formattedTime_ || '0:00');
12940    this.contentEl_.appendChild(this.textNode_);
12941  };
12942
12943  /**
12944   * Generates a formatted time for this component to use in display.
12945   *
12946   * @param  {number} time
12947   *         A numeric time, in seconds.
12948   *
12949   * @return {string}
12950   *         A formatted time
12951   *
12952   * @private
12953   */
12954
12955
12956  TimeDisplay.prototype.formatTime_ = function formatTime_(time) {
12957    return formatTime(time);
12958  };
12959
12960  /**
12961   * Updates the time display text node if it has what was passed in changed
12962   * the formatted time.
12963   *
12964   * @param {number} time
12965   *        The time to update to
12966   *
12967   * @private
12968   */
12969
12970
12971  TimeDisplay.prototype.updateFormattedTime_ = function updateFormattedTime_(time) {
12972    var formattedTime = this.formatTime_(time);
12973
12974    if (formattedTime === this.formattedTime_) {
12975      return;
12976    }
12977
12978    this.formattedTime_ = formattedTime;
12979    this.requestAnimationFrame(this.updateTextNode_);
12980  };
12981
12982  /**
12983   * To be filled out in the child class, should update the displayed time
12984   * in accordance with the fact that the current time has changed.
12985   *
12986   * @param {EventTarget~Event} [event]
12987   *        The `timeupdate`  event that caused this to run.
12988   *
12989   * @listens Player#timeupdate
12990   */
12991
12992
12993  TimeDisplay.prototype.updateContent = function updateContent(event) {};
12994
12995  return TimeDisplay;
12996}(Component);
12997
12998/**
12999 * The text that should display over the `TimeDisplay`s controls. Added to for localization.
13000 *
13001 * @type {string}
13002 * @private
13003 */
13004
13005
13006TimeDisplay.prototype.controlText_ = 'Time';
13007
13008Component.registerComponent('TimeDisplay', TimeDisplay);
13009
13010/**
13011 * @file current-time-display.js
13012 */
13013/**
13014 * Displays the current time
13015 *
13016 * @extends Component
13017 */
13018
13019var CurrentTimeDisplay = function (_TimeDisplay) {
13020  inherits(CurrentTimeDisplay, _TimeDisplay);
13021
13022  /**
13023   * Creates an instance of this class.
13024   *
13025   * @param {Player} player
13026   *        The `Player` that this class should be attached to.
13027   *
13028   * @param {Object} [options]
13029   *        The key/value store of player options.
13030   */
13031  function CurrentTimeDisplay(player, options) {
13032    classCallCheck(this, CurrentTimeDisplay);
13033
13034    var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
13035
13036    _this.on(player, 'ended', _this.handleEnded);
13037    return _this;
13038  }
13039
13040  /**
13041   * Builds the default DOM `className`.
13042   *
13043   * @return {string}
13044   *         The DOM `className` for this object.
13045   */
13046
13047
13048  CurrentTimeDisplay.prototype.buildCSSClass = function buildCSSClass() {
13049    return 'vjs-current-time';
13050  };
13051
13052  /**
13053   * Update current time display
13054   *
13055   * @param {EventTarget~Event} [event]
13056   *        The `timeupdate` event that caused this function to run.
13057   *
13058   * @listens Player#timeupdate
13059   */
13060
13061
13062  CurrentTimeDisplay.prototype.updateContent = function updateContent(event) {
13063    // Allows for smooth scrubbing, when player can't keep up.
13064    var time = this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
13065
13066    this.updateFormattedTime_(time);
13067  };
13068
13069  /**
13070   * When the player fires ended there should be no time left. Sadly
13071   * this is not always the case, lets make it seem like that is the case
13072   * for users.
13073   *
13074   * @param {EventTarget~Event} [event]
13075   *        The `ended` event that caused this to run.
13076   *
13077   * @listens Player#ended
13078   */
13079
13080
13081  CurrentTimeDisplay.prototype.handleEnded = function handleEnded(event) {
13082    if (!this.player_.duration()) {
13083      return;
13084    }
13085    this.updateFormattedTime_(this.player_.duration());
13086  };
13087
13088  return CurrentTimeDisplay;
13089}(TimeDisplay);
13090
13091/**
13092 * The text that should display over the `CurrentTimeDisplay`s controls. Added to for localization.
13093 *
13094 * @type {string}
13095 * @private
13096 */
13097
13098
13099CurrentTimeDisplay.prototype.controlText_ = 'Current Time';
13100
13101Component.registerComponent('CurrentTimeDisplay', CurrentTimeDisplay);
13102
13103/**
13104 * @file duration-display.js
13105 */
13106/**
13107 * Displays the duration
13108 *
13109 * @extends Component
13110 */
13111
13112var DurationDisplay = function (_TimeDisplay) {
13113  inherits(DurationDisplay, _TimeDisplay);
13114
13115  /**
13116   * Creates an instance of this class.
13117   *
13118   * @param {Player} player
13119   *        The `Player` that this class should be attached to.
13120   *
13121   * @param {Object} [options]
13122   *        The key/value store of player options.
13123   */
13124  function DurationDisplay(player, options) {
13125    classCallCheck(this, DurationDisplay);
13126
13127    // we do not want to/need to throttle duration changes,
13128    // as they should always display the changed duration as
13129    // it has changed
13130    var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
13131
13132    _this.on(player, 'durationchange', _this.updateContent);
13133
13134    // Also listen for timeupdate (in the parent) and loadedmetadata because removing those
13135    // listeners could have broken dependent applications/libraries. These
13136    // can likely be removed for 7.0.
13137    _this.on(player, 'loadedmetadata', _this.throttledUpdateContent);
13138    return _this;
13139  }
13140
13141  /**
13142   * Builds the default DOM `className`.
13143   *
13144   * @return {string}
13145   *         The DOM `className` for this object.
13146   */
13147
13148
13149  DurationDisplay.prototype.buildCSSClass = function buildCSSClass() {
13150    return 'vjs-duration';
13151  };
13152
13153  /**
13154   * Update duration time display.
13155   *
13156   * @param {EventTarget~Event} [event]
13157   *        The `durationchange`, `timeupdate`, or `loadedmetadata` event that caused
13158   *        this function to be called.
13159   *
13160   * @listens Player#durationchange
13161   * @listens Player#timeupdate
13162   * @listens Player#loadedmetadata
13163   */
13164
13165
13166  DurationDisplay.prototype.updateContent = function updateContent(event) {
13167    var duration = this.player_.duration();
13168
13169    if (duration && this.duration_ !== duration) {
13170      this.duration_ = duration;
13171      this.updateFormattedTime_(duration);
13172    }
13173  };
13174
13175  return DurationDisplay;
13176}(TimeDisplay);
13177
13178/**
13179 * The text that should display over the `DurationDisplay`s controls. Added to for localization.
13180 *
13181 * @type {string}
13182 * @private
13183 */
13184
13185
13186DurationDisplay.prototype.controlText_ = 'Duration Time';
13187
13188Component.registerComponent('DurationDisplay', DurationDisplay);
13189
13190/**
13191 * @file time-divider.js
13192 */
13193/**
13194 * The separator between the current time and duration.
13195 * Can be hidden if it's not needed in the design.
13196 *
13197 * @extends Component
13198 */
13199
13200var TimeDivider = function (_Component) {
13201  inherits(TimeDivider, _Component);
13202
13203  function TimeDivider() {
13204    classCallCheck(this, TimeDivider);
13205    return possibleConstructorReturn(this, _Component.apply(this, arguments));
13206  }
13207
13208  /**
13209   * Create the component's DOM element
13210   *
13211   * @return {Element}
13212   *         The element that was created.
13213   */
13214  TimeDivider.prototype.createEl = function createEl() {
13215    return _Component.prototype.createEl.call(this, 'div', {
13216      className: 'vjs-time-control vjs-time-divider',
13217      innerHTML: '<div><span>/</span></div>'
13218    });
13219  };
13220
13221  return TimeDivider;
13222}(Component);
13223
13224Component.registerComponent('TimeDivider', TimeDivider);
13225
13226/**
13227 * @file remaining-time-display.js
13228 */
13229/**
13230 * Displays the time left in the video
13231 *
13232 * @extends Component
13233 */
13234
13235var RemainingTimeDisplay = function (_TimeDisplay) {
13236  inherits(RemainingTimeDisplay, _TimeDisplay);
13237
13238  /**
13239   * Creates an instance of this class.
13240   *
13241   * @param {Player} player
13242   *        The `Player` that this class should be attached to.
13243   *
13244   * @param {Object} [options]
13245   *        The key/value store of player options.
13246   */
13247  function RemainingTimeDisplay(player, options) {
13248    classCallCheck(this, RemainingTimeDisplay);
13249
13250    var _this = possibleConstructorReturn(this, _TimeDisplay.call(this, player, options));
13251
13252    _this.on(player, 'durationchange', _this.throttledUpdateContent);
13253    _this.on(player, 'ended', _this.handleEnded);
13254    return _this;
13255  }
13256
13257  /**
13258   * Builds the default DOM `className`.
13259   *
13260   * @return {string}
13261   *         The DOM `className` for this object.
13262   */
13263
13264
13265  RemainingTimeDisplay.prototype.buildCSSClass = function buildCSSClass() {
13266    return 'vjs-remaining-time';
13267  };
13268
13269  /**
13270   * The remaining time display prefixes numbers with a "minus" character.
13271   *
13272   * @param  {number} time
13273   *         A numeric time, in seconds.
13274   *
13275   * @return {string}
13276   *         A formatted time
13277   *
13278   * @private
13279   */
13280
13281
13282  RemainingTimeDisplay.prototype.formatTime_ = function formatTime_(time) {
13283    return '-' + _TimeDisplay.prototype.formatTime_.call(this, time);
13284  };
13285
13286  /**
13287   * Update remaining time display.
13288   *
13289   * @param {EventTarget~Event} [event]
13290   *        The `timeupdate` or `durationchange` event that caused this to run.
13291   *
13292   * @listens Player#timeupdate
13293   * @listens Player#durationchange
13294   */
13295
13296
13297  RemainingTimeDisplay.prototype.updateContent = function updateContent(event) {
13298    if (!this.player_.duration()) {
13299      return;
13300    }
13301
13302    // @deprecated We should only use remainingTimeDisplay
13303    // as of video.js 7
13304    if (this.player_.remainingTimeDisplay) {
13305      this.updateFormattedTime_(this.player_.remainingTimeDisplay());
13306    } else {
13307      this.updateFormattedTime_(this.player_.remainingTime());
13308    }
13309  };
13310
13311  /**
13312   * When the player fires ended there should be no time left. Sadly
13313   * this is not always the case, lets make it seem like that is the case
13314   * for users.
13315   *
13316   * @param {EventTarget~Event} [event]
13317   *        The `ended` event that caused this to run.
13318   *
13319   * @listens Player#ended
13320   */
13321
13322
13323  RemainingTimeDisplay.prototype.handleEnded = function handleEnded(event) {
13324    if (!this.player_.duration()) {
13325      return;
13326    }
13327    this.updateFormattedTime_(0);
13328  };
13329
13330  return RemainingTimeDisplay;
13331}(TimeDisplay);
13332
13333/**
13334 * The text that should display over the `RemainingTimeDisplay`s controls. Added to for localization.
13335 *
13336 * @type {string}
13337 * @private
13338 */
13339
13340
13341RemainingTimeDisplay.prototype.controlText_ = 'Remaining Time';
13342
13343Component.registerComponent('RemainingTimeDisplay', RemainingTimeDisplay);
13344
13345/**
13346 * @file live-display.js
13347 */
13348// TODO - Future make it click to snap to live
13349
13350/**
13351 * Displays the live indicator when duration is Infinity.
13352 *
13353 * @extends Component
13354 */
13355
13356var LiveDisplay = function (_Component) {
13357  inherits(LiveDisplay, _Component);
13358
13359  /**
13360   * Creates an instance of this class.
13361   *
13362   * @param {Player} player
13363   *        The `Player` that this class should be attached to.
13364   *
13365   * @param {Object} [options]
13366   *        The key/value store of player options.
13367   */
13368  function LiveDisplay(player, options) {
13369    classCallCheck(this, LiveDisplay);
13370
13371    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
13372
13373    _this.updateShowing();
13374    _this.on(_this.player(), 'durationchange', _this.updateShowing);
13375    return _this;
13376  }
13377
13378  /**
13379   * Create the `Component`'s DOM element
13380   *
13381   * @return {Element}
13382   *         The element that was created.
13383   */
13384
13385
13386  LiveDisplay.prototype.createEl = function createEl$$1() {
13387    var el = _Component.prototype.createEl.call(this, 'div', {
13388      className: 'vjs-live-control vjs-control'
13389    });
13390
13391    this.contentEl_ = createEl('div', {
13392      className: 'vjs-live-display',
13393      innerHTML: '<span class="vjs-control-text">' + this.localize('Stream Type') + '</span>' + this.localize('LIVE')
13394    }, {
13395      'aria-live': 'off'
13396    });
13397
13398    el.appendChild(this.contentEl_);
13399    return el;
13400  };
13401
13402  LiveDisplay.prototype.dispose = function dispose() {
13403    this.contentEl_ = null;
13404
13405    _Component.prototype.dispose.call(this);
13406  };
13407
13408  /**
13409   * Check the duration to see if the LiveDisplay should be showing or not. Then show/hide
13410   * it accordingly
13411   *
13412   * @param {EventTarget~Event} [event]
13413   *        The {@link Player#durationchange} event that caused this function to run.
13414   *
13415   * @listens Player#durationchange
13416   */
13417
13418
13419  LiveDisplay.prototype.updateShowing = function updateShowing(event) {
13420    if (this.player().duration() === Infinity) {
13421      this.show();
13422    } else {
13423      this.hide();
13424    }
13425  };
13426
13427  return LiveDisplay;
13428}(Component);
13429
13430Component.registerComponent('LiveDisplay', LiveDisplay);
13431
13432/**
13433 * @file slider.js
13434 */
13435/**
13436 * The base functionality for a slider. Can be vertical or horizontal.
13437 * For instance the volume bar or the seek bar on a video is a slider.
13438 *
13439 * @extends Component
13440 */
13441
13442var Slider = function (_Component) {
13443  inherits(Slider, _Component);
13444
13445  /**
13446   * Create an instance of this class
13447   *
13448   * @param {Player} player
13449   *        The `Player` that this class should be attached to.
13450   *
13451   * @param {Object} [options]
13452   *        The key/value store of player options.
13453   */
13454  function Slider(player, options) {
13455    classCallCheck(this, Slider);
13456
13457    // Set property names to bar to match with the child Slider class is looking for
13458    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
13459
13460    _this.bar = _this.getChild(_this.options_.barName);
13461
13462    // Set a horizontal or vertical class on the slider depending on the slider type
13463    _this.vertical(!!_this.options_.vertical);
13464
13465    _this.enable();
13466    return _this;
13467  }
13468
13469  /**
13470   * Are controls are currently enabled for this slider or not.
13471   *
13472   * @return {boolean}
13473   *         true if controls are enabled, false otherwise
13474   */
13475
13476
13477  Slider.prototype.enabled = function enabled() {
13478    return this.enabled_;
13479  };
13480
13481  /**
13482   * Enable controls for this slider if they are disabled
13483   */
13484
13485
13486  Slider.prototype.enable = function enable() {
13487    if (this.enabled()) {
13488      return;
13489    }
13490
13491    this.on('mousedown', this.handleMouseDown);
13492    this.on('touchstart', this.handleMouseDown);
13493    this.on('focus', this.handleFocus);
13494    this.on('blur', this.handleBlur);
13495    this.on('click', this.handleClick);
13496
13497    this.on(this.player_, 'controlsvisible', this.update);
13498
13499    if (this.playerEvent) {
13500      this.on(this.player_, this.playerEvent, this.update);
13501    }
13502
13503    this.removeClass('disabled');
13504    this.setAttribute('tabindex', 0);
13505
13506    this.enabled_ = true;
13507  };
13508
13509  /**
13510   * Disable controls for this slider if they are enabled
13511   */
13512
13513
13514  Slider.prototype.disable = function disable() {
13515    if (!this.enabled()) {
13516      return;
13517    }
13518    var doc = this.bar.el_.ownerDocument;
13519
13520    this.off('mousedown', this.handleMouseDown);
13521    this.off('touchstart', this.handleMouseDown);
13522    this.off('focus', this.handleFocus);
13523    this.off('blur', this.handleBlur);
13524    this.off('click', this.handleClick);
13525    this.off(this.player_, 'controlsvisible', this.update);
13526    this.off(doc, 'mousemove', this.handleMouseMove);
13527    this.off(doc, 'mouseup', this.handleMouseUp);
13528    this.off(doc, 'touchmove', this.handleMouseMove);
13529    this.off(doc, 'touchend', this.handleMouseUp);
13530    this.removeAttribute('tabindex');
13531
13532    this.addClass('disabled');
13533
13534    if (this.playerEvent) {
13535      this.off(this.player_, this.playerEvent, this.update);
13536    }
13537    this.enabled_ = false;
13538  };
13539
13540  /**
13541   * Create the `Button`s DOM element.
13542   *
13543   * @param {string} type
13544   *        Type of element to create.
13545   *
13546   * @param {Object} [props={}]
13547   *        List of properties in Object form.
13548   *
13549   * @param {Object} [attributes={}]
13550   *        list of attributes in Object form.
13551   *
13552   * @return {Element}
13553   *         The element that gets created.
13554   */
13555
13556
13557  Slider.prototype.createEl = function createEl$$1(type) {
13558    var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
13559    var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
13560
13561    // Add the slider element class to all sub classes
13562    props.className = props.className + ' vjs-slider';
13563    props = assign({
13564      tabIndex: 0
13565    }, props);
13566
13567    attributes = assign({
13568      'role': 'slider',
13569      'aria-valuenow': 0,
13570      'aria-valuemin': 0,
13571      'aria-valuemax': 100,
13572      'tabIndex': 0
13573    }, attributes);
13574
13575    return _Component.prototype.createEl.call(this, type, props, attributes);
13576  };
13577
13578  /**
13579   * Handle `mousedown` or `touchstart` events on the `Slider`.
13580   *
13581   * @param {EventTarget~Event} event
13582   *        `mousedown` or `touchstart` event that triggered this function
13583   *
13584   * @listens mousedown
13585   * @listens touchstart
13586   * @fires Slider#slideractive
13587   */
13588
13589
13590  Slider.prototype.handleMouseDown = function handleMouseDown(event) {
13591    var doc = this.bar.el_.ownerDocument;
13592
13593    event.preventDefault();
13594    blockTextSelection();
13595
13596    this.addClass('vjs-sliding');
13597    /**
13598     * Triggered when the slider is in an active state
13599     *
13600     * @event Slider#slideractive
13601     * @type {EventTarget~Event}
13602     */
13603    this.trigger('slideractive');
13604
13605    this.on(doc, 'mousemove', this.handleMouseMove);
13606    this.on(doc, 'mouseup', this.handleMouseUp);
13607    this.on(doc, 'touchmove', this.handleMouseMove);
13608    this.on(doc, 'touchend', this.handleMouseUp);
13609
13610    this.handleMouseMove(event);
13611  };
13612
13613  /**
13614   * Handle the `mousemove`, `touchmove`, and `mousedown` events on this `Slider`.
13615   * The `mousemove` and `touchmove` events will only only trigger this function during
13616   * `mousedown` and `touchstart`. This is due to {@link Slider#handleMouseDown} and
13617   * {@link Slider#handleMouseUp}.
13618   *
13619   * @param {EventTarget~Event} event
13620   *        `mousedown`, `mousemove`, `touchstart`, or `touchmove` event that triggered
13621   *        this function
13622   *
13623   * @listens mousemove
13624   * @listens touchmove
13625   */
13626
13627
13628  Slider.prototype.handleMouseMove = function handleMouseMove(event) {};
13629
13630  /**
13631   * Handle `mouseup` or `touchend` events on the `Slider`.
13632   *
13633   * @param {EventTarget~Event} event
13634   *        `mouseup` or `touchend` event that triggered this function.
13635   *
13636   * @listens touchend
13637   * @listens mouseup
13638   * @fires Slider#sliderinactive
13639   */
13640
13641
13642  Slider.prototype.handleMouseUp = function handleMouseUp() {
13643    var doc = this.bar.el_.ownerDocument;
13644
13645    unblockTextSelection();
13646
13647    this.removeClass('vjs-sliding');
13648    /**
13649     * Triggered when the slider is no longer in an active state.
13650     *
13651     * @event Slider#sliderinactive
13652     * @type {EventTarget~Event}
13653     */
13654    this.trigger('sliderinactive');
13655
13656    this.off(doc, 'mousemove', this.handleMouseMove);
13657    this.off(doc, 'mouseup', this.handleMouseUp);
13658    this.off(doc, 'touchmove', this.handleMouseMove);
13659    this.off(doc, 'touchend', this.handleMouseUp);
13660
13661    this.update();
13662  };
13663
13664  /**
13665   * Update the progress bar of the `Slider`.
13666   *
13667   * @returns {number}
13668   *          The percentage of progress the progress bar represents as a
13669   *          number from 0 to 1.
13670   */
13671
13672
13673  Slider.prototype.update = function update() {
13674
13675    // In VolumeBar init we have a setTimeout for update that pops and update
13676    // to the end of the execution stack. The player is destroyed before then
13677    // update will cause an error
13678    if (!this.el_) {
13679      return;
13680    }
13681
13682    // If scrubbing, we could use a cached value to make the handle keep up
13683    // with the user's mouse. On HTML5 browsers scrubbing is really smooth, but
13684    // some flash players are slow, so we might want to utilize this later.
13685    // var progress =  (this.player_.scrubbing()) ? this.player_.getCache().currentTime / this.player_.duration() : this.player_.currentTime() / this.player_.duration();
13686    var progress = this.getPercent();
13687    var bar = this.bar;
13688
13689    // If there's no bar...
13690    if (!bar) {
13691      return;
13692    }
13693
13694    // Protect against no duration and other division issues
13695    if (typeof progress !== 'number' || progress !== progress || progress < 0 || progress === Infinity) {
13696      progress = 0;
13697    }
13698
13699    // Convert to a percentage for setting
13700    var percentage = (progress * 100).toFixed(2) + '%';
13701    var style = bar.el().style;
13702
13703    // Set the new bar width or height
13704    if (this.vertical()) {
13705      style.height = percentage;
13706    } else {
13707      style.width = percentage;
13708    }
13709
13710    return progress;
13711  };
13712
13713  /**
13714   * Calculate distance for slider
13715   *
13716   * @param {EventTarget~Event} event
13717   *        The event that caused this function to run.
13718   *
13719   * @return {number}
13720   *         The current position of the Slider.
13721   *         - postition.x for vertical `Slider`s
13722   *         - postition.y for horizontal `Slider`s
13723   */
13724
13725
13726  Slider.prototype.calculateDistance = function calculateDistance(event) {
13727    var position = getPointerPosition(this.el_, event);
13728
13729    if (this.vertical()) {
13730      return position.y;
13731    }
13732    return position.x;
13733  };
13734
13735  /**
13736   * Handle a `focus` event on this `Slider`.
13737   *
13738   * @param {EventTarget~Event} event
13739   *        The `focus` event that caused this function to run.
13740   *
13741   * @listens focus
13742   */
13743
13744
13745  Slider.prototype.handleFocus = function handleFocus() {
13746    this.on(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
13747  };
13748
13749  /**
13750   * Handle a `keydown` event on the `Slider`. Watches for left, rigth, up, and down
13751   * arrow keys. This function will only be called when the slider has focus. See
13752   * {@link Slider#handleFocus} and {@link Slider#handleBlur}.
13753   *
13754   * @param {EventTarget~Event} event
13755   *        the `keydown` event that caused this function to run.
13756   *
13757   * @listens keydown
13758   */
13759
13760
13761  Slider.prototype.handleKeyPress = function handleKeyPress(event) {
13762    // Left and Down Arrows
13763    if (event.which === 37 || event.which === 40) {
13764      event.preventDefault();
13765      this.stepBack();
13766
13767      // Up and Right Arrows
13768    } else if (event.which === 38 || event.which === 39) {
13769      event.preventDefault();
13770      this.stepForward();
13771    }
13772  };
13773
13774  /**
13775   * Handle a `blur` event on this `Slider`.
13776   *
13777   * @param {EventTarget~Event} event
13778   *        The `blur` event that caused this function to run.
13779   *
13780   * @listens blur
13781   */
13782
13783  Slider.prototype.handleBlur = function handleBlur() {
13784    this.off(this.bar.el_.ownerDocument, 'keydown', this.handleKeyPress);
13785  };
13786
13787  /**
13788   * Listener for click events on slider, used to prevent clicks
13789   *   from bubbling up to parent elements like button menus.
13790   *
13791   * @param {Object} event
13792   *        Event that caused this object to run
13793   */
13794
13795
13796  Slider.prototype.handleClick = function handleClick(event) {
13797    event.stopImmediatePropagation();
13798    event.preventDefault();
13799  };
13800
13801  /**
13802   * Get/set if slider is horizontal for vertical
13803   *
13804   * @param {boolean} [bool]
13805   *        - true if slider is vertical,
13806   *        - false is horizontal
13807   *
13808   * @return {boolean}
13809   *         - true if slider is vertical, and getting
13810   *         - false if the slider is horizontal, and getting
13811   */
13812
13813
13814  Slider.prototype.vertical = function vertical(bool) {
13815    if (bool === undefined) {
13816      return this.vertical_ || false;
13817    }
13818
13819    this.vertical_ = !!bool;
13820
13821    if (this.vertical_) {
13822      this.addClass('vjs-slider-vertical');
13823    } else {
13824      this.addClass('vjs-slider-horizontal');
13825    }
13826  };
13827
13828  return Slider;
13829}(Component);
13830
13831Component.registerComponent('Slider', Slider);
13832
13833/**
13834 * @file load-progress-bar.js
13835 */
13836/**
13837 * Shows loading progress
13838 *
13839 * @extends Component
13840 */
13841
13842var LoadProgressBar = function (_Component) {
13843  inherits(LoadProgressBar, _Component);
13844
13845  /**
13846   * Creates an instance of this class.
13847   *
13848   * @param {Player} player
13849   *        The `Player` that this class should be attached to.
13850   *
13851   * @param {Object} [options]
13852   *        The key/value store of player options.
13853   */
13854  function LoadProgressBar(player, options) {
13855    classCallCheck(this, LoadProgressBar);
13856
13857    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
13858
13859    _this.partEls_ = [];
13860    _this.on(player, 'progress', _this.update);
13861    return _this;
13862  }
13863
13864  /**
13865   * Create the `Component`'s DOM element
13866   *
13867   * @return {Element}
13868   *         The element that was created.
13869   */
13870
13871
13872  LoadProgressBar.prototype.createEl = function createEl$$1() {
13873    return _Component.prototype.createEl.call(this, 'div', {
13874      className: 'vjs-load-progress',
13875      innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Loaded') + '</span>: 0%</span>'
13876    });
13877  };
13878
13879  LoadProgressBar.prototype.dispose = function dispose() {
13880    this.partEls_ = null;
13881
13882    _Component.prototype.dispose.call(this);
13883  };
13884
13885  /**
13886   * Update progress bar
13887   *
13888   * @param {EventTarget~Event} [event]
13889   *        The `progress` event that caused this function to run.
13890   *
13891   * @listens Player#progress
13892   */
13893
13894
13895  LoadProgressBar.prototype.update = function update(event) {
13896    var buffered = this.player_.buffered();
13897    var duration = this.player_.duration();
13898    var bufferedEnd = this.player_.bufferedEnd();
13899    var children = this.partEls_;
13900
13901    // get the percent width of a time compared to the total end
13902    var percentify = function percentify(time, end) {
13903      // no NaN
13904      var percent = time / end || 0;
13905
13906      return (percent >= 1 ? 1 : percent) * 100 + '%';
13907    };
13908
13909    // update the width of the progress bar
13910    this.el_.style.width = percentify(bufferedEnd, duration);
13911
13912    // add child elements to represent the individual buffered time ranges
13913    for (var i = 0; i < buffered.length; i++) {
13914      var start = buffered.start(i);
13915      var end = buffered.end(i);
13916      var part = children[i];
13917
13918      if (!part) {
13919        part = this.el_.appendChild(createEl());
13920        children[i] = part;
13921      }
13922
13923      // set the percent based on the width of the progress bar (bufferedEnd)
13924      part.style.left = percentify(start, bufferedEnd);
13925      part.style.width = percentify(end - start, bufferedEnd);
13926    }
13927
13928    // remove unused buffered range elements
13929    for (var _i = children.length; _i > buffered.length; _i--) {
13930      this.el_.removeChild(children[_i - 1]);
13931    }
13932    children.length = buffered.length;
13933  };
13934
13935  return LoadProgressBar;
13936}(Component);
13937
13938Component.registerComponent('LoadProgressBar', LoadProgressBar);
13939
13940/**
13941 * @file time-tooltip.js
13942 */
13943/**
13944 * Time tooltips display a time above the progress bar.
13945 *
13946 * @extends Component
13947 */
13948
13949var TimeTooltip = function (_Component) {
13950  inherits(TimeTooltip, _Component);
13951
13952  function TimeTooltip() {
13953    classCallCheck(this, TimeTooltip);
13954    return possibleConstructorReturn(this, _Component.apply(this, arguments));
13955  }
13956
13957  /**
13958   * Create the time tooltip DOM element
13959   *
13960   * @return {Element}
13961   *         The element that was created.
13962   */
13963  TimeTooltip.prototype.createEl = function createEl$$1() {
13964    return _Component.prototype.createEl.call(this, 'div', {
13965      className: 'vjs-time-tooltip'
13966    });
13967  };
13968
13969  /**
13970   * Updates the position of the time tooltip relative to the `SeekBar`.
13971   *
13972   * @param {Object} seekBarRect
13973   *        The `ClientRect` for the {@link SeekBar} element.
13974   *
13975   * @param {number} seekBarPoint
13976   *        A number from 0 to 1, representing a horizontal reference point
13977   *        from the left edge of the {@link SeekBar}
13978   */
13979
13980
13981  TimeTooltip.prototype.update = function update(seekBarRect, seekBarPoint, content) {
13982    var tooltipRect = getBoundingClientRect(this.el_);
13983    var playerRect = getBoundingClientRect(this.player_.el());
13984    var seekBarPointPx = seekBarRect.width * seekBarPoint;
13985
13986    // do nothing if either rect isn't available
13987    // for example, if the player isn't in the DOM for testing
13988    if (!playerRect || !tooltipRect) {
13989      return;
13990    }
13991
13992    // This is the space left of the `seekBarPoint` available within the bounds
13993    // of the player. We calculate any gap between the left edge of the player
13994    // and the left edge of the `SeekBar` and add the number of pixels in the
13995    // `SeekBar` before hitting the `seekBarPoint`
13996    var spaceLeftOfPoint = seekBarRect.left - playerRect.left + seekBarPointPx;
13997
13998    // This is the space right of the `seekBarPoint` available within the bounds
13999    // of the player. We calculate the number of pixels from the `seekBarPoint`
14000    // to the right edge of the `SeekBar` and add to that any gap between the
14001    // right edge of the `SeekBar` and the player.
14002    var spaceRightOfPoint = seekBarRect.width - seekBarPointPx + (playerRect.right - seekBarRect.right);
14003
14004    // This is the number of pixels by which the tooltip will need to be pulled
14005    // further to the right to center it over the `seekBarPoint`.
14006    var pullTooltipBy = tooltipRect.width / 2;
14007
14008    // Adjust the `pullTooltipBy` distance to the left or right depending on
14009    // the results of the space calculations above.
14010    if (spaceLeftOfPoint < pullTooltipBy) {
14011      pullTooltipBy += pullTooltipBy - spaceLeftOfPoint;
14012    } else if (spaceRightOfPoint < pullTooltipBy) {
14013      pullTooltipBy = spaceRightOfPoint;
14014    }
14015
14016    // Due to the imprecision of decimal/ratio based calculations and varying
14017    // rounding behaviors, there are cases where the spacing adjustment is off
14018    // by a pixel or two. This adds insurance to these calculations.
14019    if (pullTooltipBy < 0) {
14020      pullTooltipBy = 0;
14021    } else if (pullTooltipBy > tooltipRect.width) {
14022      pullTooltipBy = tooltipRect.width;
14023    }
14024
14025    this.el_.style.right = '-' + pullTooltipBy + 'px';
14026    textContent(this.el_, content);
14027  };
14028
14029  return TimeTooltip;
14030}(Component);
14031
14032Component.registerComponent('TimeTooltip', TimeTooltip);
14033
14034/**
14035 * @file play-progress-bar.js
14036 */
14037/**
14038 * Used by {@link SeekBar} to display media playback progress as part of the
14039 * {@link ProgressControl}.
14040 *
14041 * @extends Component
14042 */
14043
14044var PlayProgressBar = function (_Component) {
14045  inherits(PlayProgressBar, _Component);
14046
14047  function PlayProgressBar() {
14048    classCallCheck(this, PlayProgressBar);
14049    return possibleConstructorReturn(this, _Component.apply(this, arguments));
14050  }
14051
14052  /**
14053   * Create the the DOM element for this class.
14054   *
14055   * @return {Element}
14056   *         The element that was created.
14057   */
14058  PlayProgressBar.prototype.createEl = function createEl() {
14059    return _Component.prototype.createEl.call(this, 'div', {
14060      className: 'vjs-play-progress vjs-slider-bar',
14061      innerHTML: '<span class="vjs-control-text"><span>' + this.localize('Progress') + '</span>: 0%</span>'
14062    });
14063  };
14064
14065  /**
14066   * Enqueues updates to its own DOM as well as the DOM of its
14067   * {@link TimeTooltip} child.
14068   *
14069   * @param {Object} seekBarRect
14070   *        The `ClientRect` for the {@link SeekBar} element.
14071   *
14072   * @param {number} seekBarPoint
14073   *        A number from 0 to 1, representing a horizontal reference point
14074   *        from the left edge of the {@link SeekBar}
14075   */
14076
14077
14078  PlayProgressBar.prototype.update = function update(seekBarRect, seekBarPoint) {
14079    var _this2 = this;
14080
14081    // If there is an existing rAF ID, cancel it so we don't over-queue.
14082    if (this.rafId_) {
14083      this.cancelAnimationFrame(this.rafId_);
14084    }
14085
14086    this.rafId_ = this.requestAnimationFrame(function () {
14087      var time = _this2.player_.scrubbing() ? _this2.player_.getCache().currentTime : _this2.player_.currentTime();
14088
14089      var content = formatTime(time, _this2.player_.duration());
14090      var timeTooltip = _this2.getChild('timeTooltip');
14091
14092      if (timeTooltip) {
14093        timeTooltip.update(seekBarRect, seekBarPoint, content);
14094      }
14095    });
14096  };
14097
14098  return PlayProgressBar;
14099}(Component);
14100
14101/**
14102 * Default options for {@link PlayProgressBar}.
14103 *
14104 * @type {Object}
14105 * @private
14106 */
14107
14108
14109PlayProgressBar.prototype.options_ = {
14110  children: []
14111};
14112
14113// Time tooltips should not be added to a player on mobile devices or IE8
14114if ((!IE_VERSION || IE_VERSION > 8) && !IS_IOS && !IS_ANDROID) {
14115  PlayProgressBar.prototype.options_.children.push('timeTooltip');
14116}
14117
14118Component.registerComponent('PlayProgressBar', PlayProgressBar);
14119
14120/**
14121 * @file mouse-time-display.js
14122 */
14123/**
14124 * The {@link MouseTimeDisplay} component tracks mouse movement over the
14125 * {@link ProgressControl}. It displays an indicator and a {@link TimeTooltip}
14126 * indicating the time which is represented by a given point in the
14127 * {@link ProgressControl}.
14128 *
14129 * @extends Component
14130 */
14131
14132var MouseTimeDisplay = function (_Component) {
14133  inherits(MouseTimeDisplay, _Component);
14134
14135  /**
14136   * Creates an instance of this class.
14137   *
14138   * @param {Player} player
14139   *        The {@link Player} that this class should be attached to.
14140   *
14141   * @param {Object} [options]
14142   *        The key/value store of player options.
14143   */
14144  function MouseTimeDisplay(player, options) {
14145    classCallCheck(this, MouseTimeDisplay);
14146
14147    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
14148
14149    _this.update = throttle(bind(_this, _this.update), 25);
14150    return _this;
14151  }
14152
14153  /**
14154   * Create the DOM element for this class.
14155   *
14156   * @return {Element}
14157   *         The element that was created.
14158   */
14159
14160
14161  MouseTimeDisplay.prototype.createEl = function createEl() {
14162    return _Component.prototype.createEl.call(this, 'div', {
14163      className: 'vjs-mouse-display'
14164    });
14165  };
14166
14167  /**
14168   * Enqueues updates to its own DOM as well as the DOM of its
14169   * {@link TimeTooltip} child.
14170   *
14171   * @param {Object} seekBarRect
14172   *        The `ClientRect` for the {@link SeekBar} element.
14173   *
14174   * @param {number} seekBarPoint
14175   *        A number from 0 to 1, representing a horizontal reference point
14176   *        from the left edge of the {@link SeekBar}
14177   */
14178
14179
14180  MouseTimeDisplay.prototype.update = function update(seekBarRect, seekBarPoint) {
14181    var _this2 = this;
14182
14183    // If there is an existing rAF ID, cancel it so we don't over-queue.
14184    if (this.rafId_) {
14185      this.cancelAnimationFrame(this.rafId_);
14186    }
14187
14188    this.rafId_ = this.requestAnimationFrame(function () {
14189      var duration = _this2.player_.duration();
14190      var content = formatTime(seekBarPoint * duration, duration);
14191
14192      _this2.el_.style.left = seekBarRect.width * seekBarPoint + 'px';
14193      _this2.getChild('timeTooltip').update(seekBarRect, seekBarPoint, content);
14194    });
14195  };
14196
14197  return MouseTimeDisplay;
14198}(Component);
14199
14200/**
14201 * Default options for `MouseTimeDisplay`
14202 *
14203 * @type {Object}
14204 * @private
14205 */
14206
14207
14208MouseTimeDisplay.prototype.options_ = {
14209  children: ['timeTooltip']
14210};
14211
14212Component.registerComponent('MouseTimeDisplay', MouseTimeDisplay);
14213
14214/**
14215 * @file seek-bar.js
14216 */
14217// The number of seconds the `step*` functions move the timeline.
14218var STEP_SECONDS = 5;
14219
14220// The interval at which the bar should update as it progresses.
14221var UPDATE_REFRESH_INTERVAL = 30;
14222
14223/**
14224 * Seek bar and container for the progress bars. Uses {@link PlayProgressBar}
14225 * as its `bar`.
14226 *
14227 * @extends Slider
14228 */
14229
14230var SeekBar = function (_Slider) {
14231  inherits(SeekBar, _Slider);
14232
14233  /**
14234   * Creates an instance of this class.
14235   *
14236   * @param {Player} player
14237   *        The `Player` that this class should be attached to.
14238   *
14239   * @param {Object} [options]
14240   *        The key/value store of player options.
14241   */
14242  function SeekBar(player, options) {
14243    classCallCheck(this, SeekBar);
14244
14245    var _this = possibleConstructorReturn(this, _Slider.call(this, player, options));
14246
14247    _this.update = throttle(bind(_this, _this.update), UPDATE_REFRESH_INTERVAL);
14248
14249    _this.on(player, 'timeupdate', _this.update);
14250    _this.on(player, 'ended', _this.handleEnded);
14251
14252    // when playing, let's ensure we smoothly update the play progress bar
14253    // via an interval
14254    _this.updateInterval = null;
14255
14256    _this.on(player, ['playing'], function () {
14257      _this.clearInterval(_this.updateInterval);
14258
14259      _this.updateInterval = _this.setInterval(function () {
14260        _this.requestAnimationFrame(function () {
14261          _this.update();
14262        });
14263      }, UPDATE_REFRESH_INTERVAL);
14264    });
14265
14266    _this.on(player, ['ended', 'pause', 'waiting'], function () {
14267      _this.clearInterval(_this.updateInterval);
14268    });
14269
14270    _this.on(player, ['timeupdate', 'ended'], _this.update);
14271    return _this;
14272  }
14273
14274  /**
14275   * Create the `Component`'s DOM element
14276   *
14277   * @return {Element}
14278   *         The element that was created.
14279   */
14280
14281
14282  SeekBar.prototype.createEl = function createEl$$1() {
14283    return _Slider.prototype.createEl.call(this, 'div', {
14284      className: 'vjs-progress-holder'
14285    }, {
14286      'aria-label': this.localize('Progress Bar')
14287    });
14288  };
14289
14290  /**
14291   * This function updates the play progress bar and accessiblity
14292   * attributes to whatever is passed in.
14293   *
14294   * @param {number} currentTime
14295   *        The currentTime value that should be used for accessiblity
14296   *
14297   * @param {number} percent
14298   *        The percentage as a decimal that the bar should be filled from 0-1.
14299   *
14300   * @private
14301   */
14302
14303
14304  SeekBar.prototype.update_ = function update_(currentTime, percent) {
14305    var duration = this.player_.duration();
14306
14307    // machine readable value of progress bar (percentage complete)
14308    this.el_.setAttribute('aria-valuenow', (percent * 100).toFixed(2));
14309
14310    // human readable value of progress bar (time complete)
14311    this.el_.setAttribute('aria-valuetext', this.localize('progress bar timing: currentTime={1} duration={2}', [formatTime(currentTime, duration), formatTime(duration, duration)], '{1} of {2}'));
14312
14313    // Update the `PlayProgressBar`.
14314    this.bar.update(getBoundingClientRect(this.el_), percent);
14315  };
14316
14317  /**
14318   * Update the seek bar's UI.
14319   *
14320   * @param {EventTarget~Event} [event]
14321   *        The `timeupdate` or `ended` event that caused this to run.
14322   *
14323   * @listens Player#timeupdate
14324   *
14325   * @returns {number}
14326   *          The current percent at a number from 0-1
14327   */
14328
14329
14330  SeekBar.prototype.update = function update(event) {
14331    var percent = _Slider.prototype.update.call(this);
14332
14333    this.update_(this.getCurrentTime_(), percent);
14334    return percent;
14335  };
14336
14337  /**
14338   * Get the value of current time but allows for smooth scrubbing,
14339   * when player can't keep up.
14340   *
14341   * @return {number}
14342   *         The current time value to display
14343   *
14344   * @private
14345   */
14346
14347
14348  SeekBar.prototype.getCurrentTime_ = function getCurrentTime_() {
14349    return this.player_.scrubbing() ? this.player_.getCache().currentTime : this.player_.currentTime();
14350  };
14351
14352  /**
14353   * We want the seek bar to be full on ended
14354   * no matter what the actual internal values are. so we force it.
14355   *
14356   * @param {EventTarget~Event} [event]
14357   *        The `timeupdate` or `ended` event that caused this to run.
14358   *
14359   * @listens Player#ended
14360   */
14361
14362
14363  SeekBar.prototype.handleEnded = function handleEnded(event) {
14364    this.update_(this.player_.duration(), 1);
14365  };
14366
14367  /**
14368   * Get the percentage of media played so far.
14369   *
14370   * @return {number}
14371   *         The percentage of media played so far (0 to 1).
14372   */
14373
14374
14375  SeekBar.prototype.getPercent = function getPercent() {
14376    var percent = this.getCurrentTime_() / this.player_.duration();
14377
14378    return percent >= 1 ? 1 : percent;
14379  };
14380
14381  /**
14382   * Handle mouse down on seek bar
14383   *
14384   * @param {EventTarget~Event} event
14385   *        The `mousedown` event that caused this to run.
14386   *
14387   * @listens mousedown
14388   */
14389
14390
14391  SeekBar.prototype.handleMouseDown = function handleMouseDown(event) {
14392    if (!isSingleLeftClick(event)) {
14393      return;
14394    }
14395
14396    this.player_.scrubbing(true);
14397
14398    this.videoWasPlaying = !this.player_.paused();
14399    this.player_.pause();
14400
14401    _Slider.prototype.handleMouseDown.call(this, event);
14402  };
14403
14404  /**
14405   * Handle mouse move on seek bar
14406   *
14407   * @param {EventTarget~Event} event
14408   *        The `mousemove` event that caused this to run.
14409   *
14410   * @listens mousemove
14411   */
14412
14413
14414  SeekBar.prototype.handleMouseMove = function handleMouseMove(event) {
14415    if (!isSingleLeftClick(event)) {
14416      return;
14417    }
14418
14419    var newTime = this.calculateDistance(event) * this.player_.duration();
14420
14421    // Don't let video end while scrubbing.
14422    if (newTime === this.player_.duration()) {
14423      newTime = newTime - 0.1;
14424    }
14425
14426    // Set new time (tell player to seek to new time)
14427    this.player_.currentTime(newTime);
14428  };
14429
14430  SeekBar.prototype.enable = function enable() {
14431    _Slider.prototype.enable.call(this);
14432    var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
14433
14434    if (!mouseTimeDisplay) {
14435      return;
14436    }
14437
14438    mouseTimeDisplay.show();
14439  };
14440
14441  SeekBar.prototype.disable = function disable() {
14442    _Slider.prototype.disable.call(this);
14443    var mouseTimeDisplay = this.getChild('mouseTimeDisplay');
14444
14445    if (!mouseTimeDisplay) {
14446      return;
14447    }
14448
14449    mouseTimeDisplay.hide();
14450  };
14451
14452  /**
14453   * Handle mouse up on seek bar
14454   *
14455   * @param {EventTarget~Event} event
14456   *        The `mouseup` event that caused this to run.
14457   *
14458   * @listens mouseup
14459   */
14460
14461
14462  SeekBar.prototype.handleMouseUp = function handleMouseUp(event) {
14463    _Slider.prototype.handleMouseUp.call(this, event);
14464
14465    this.player_.scrubbing(false);
14466
14467    /**
14468     * Trigger timeupdate because we're done seeking and the time has changed.
14469     * This is particularly useful for if the player is paused to time the time displays.
14470     *
14471     * @event Tech#timeupdate
14472     * @type {EventTarget~Event}
14473     */
14474    this.player_.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
14475    if (this.videoWasPlaying) {
14476      silencePromise(this.player_.play());
14477    }
14478  };
14479
14480  /**
14481   * Move more quickly fast forward for keyboard-only users
14482   */
14483
14484
14485  SeekBar.prototype.stepForward = function stepForward() {
14486    this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS);
14487  };
14488
14489  /**
14490   * Move more quickly rewind for keyboard-only users
14491   */
14492
14493
14494  SeekBar.prototype.stepBack = function stepBack() {
14495    this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS);
14496  };
14497
14498  /**
14499   * Toggles the playback state of the player
14500   * This gets called when enter or space is used on the seekbar
14501   *
14502   * @param {EventTarget~Event} event
14503   *        The `keydown` event that caused this function to be called
14504   *
14505   */
14506
14507
14508  SeekBar.prototype.handleAction = function handleAction(event) {
14509    if (this.player_.paused()) {
14510      this.player_.play();
14511    } else {
14512      this.player_.pause();
14513    }
14514  };
14515
14516  /**
14517   * Called when this SeekBar has focus and a key gets pressed down. By
14518   * default it will call `this.handleAction` when the key is space or enter.
14519   *
14520   * @param {EventTarget~Event} event
14521   *        The `keydown` event that caused this function to be called.
14522   *
14523   * @listens keydown
14524   */
14525
14526
14527  SeekBar.prototype.handleKeyPress = function handleKeyPress(event) {
14528
14529    // Support Space (32) or Enter (13) key operation to fire a click event
14530    if (event.which === 32 || event.which === 13) {
14531      event.preventDefault();
14532      this.handleAction(event);
14533    } else if (_Slider.prototype.handleKeyPress) {
14534
14535      // Pass keypress handling up for unsupported keys
14536      _Slider.prototype.handleKeyPress.call(this, event);
14537    }
14538  };
14539
14540  return SeekBar;
14541}(Slider);
14542
14543/**
14544 * Default options for the `SeekBar`
14545 *
14546 * @type {Object}
14547 * @private
14548 */
14549
14550
14551SeekBar.prototype.options_ = {
14552  children: ['loadProgressBar', 'playProgressBar'],
14553  barName: 'playProgressBar'
14554};
14555
14556// MouseTimeDisplay tooltips should not be added to a player on mobile devices or IE8
14557if ((!IE_VERSION || IE_VERSION > 8) && !IS_IOS && !IS_ANDROID) {
14558  SeekBar.prototype.options_.children.splice(1, 0, 'mouseTimeDisplay');
14559}
14560
14561/**
14562 * Call the update event for this Slider when this event happens on the player.
14563 *
14564 * @type {string}
14565 */
14566SeekBar.prototype.playerEvent = 'timeupdate';
14567
14568Component.registerComponent('SeekBar', SeekBar);
14569
14570/**
14571 * @file progress-control.js
14572 */
14573/**
14574 * The Progress Control component contains the seek bar, load progress,
14575 * and play progress.
14576 *
14577 * @extends Component
14578 */
14579
14580var ProgressControl = function (_Component) {
14581  inherits(ProgressControl, _Component);
14582
14583  /**
14584   * Creates an instance of this class.
14585   *
14586   * @param {Player} player
14587   *        The `Player` that this class should be attached to.
14588   *
14589   * @param {Object} [options]
14590   *        The key/value store of player options.
14591   */
14592  function ProgressControl(player, options) {
14593    classCallCheck(this, ProgressControl);
14594
14595    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
14596
14597    _this.handleMouseMove = throttle(bind(_this, _this.handleMouseMove), 25);
14598    _this.throttledHandleMouseSeek = throttle(bind(_this, _this.handleMouseSeek), 25);
14599
14600    _this.enable();
14601    return _this;
14602  }
14603
14604  /**
14605   * Create the `Component`'s DOM element
14606   *
14607   * @return {Element}
14608   *         The element that was created.
14609   */
14610
14611
14612  ProgressControl.prototype.createEl = function createEl$$1() {
14613    return _Component.prototype.createEl.call(this, 'div', {
14614      className: 'vjs-progress-control vjs-control'
14615    });
14616  };
14617
14618  /**
14619   * When the mouse moves over the `ProgressControl`, the pointer position
14620   * gets passed down to the `MouseTimeDisplay` component.
14621   *
14622   * @param {EventTarget~Event} event
14623   *        The `mousemove` event that caused this function to run.
14624   *
14625   * @listen mousemove
14626   */
14627
14628
14629  ProgressControl.prototype.handleMouseMove = function handleMouseMove(event) {
14630    var seekBar = this.getChild('seekBar');
14631    var mouseTimeDisplay = seekBar.getChild('mouseTimeDisplay');
14632    var seekBarEl = seekBar.el();
14633    var seekBarRect = getBoundingClientRect(seekBarEl);
14634    var seekBarPoint = getPointerPosition(seekBarEl, event).x;
14635
14636    // The default skin has a gap on either side of the `SeekBar`. This means
14637    // that it's possible to trigger this behavior outside the boundaries of
14638    // the `SeekBar`. This ensures we stay within it at all times.
14639    if (seekBarPoint > 1) {
14640      seekBarPoint = 1;
14641    } else if (seekBarPoint < 0) {
14642      seekBarPoint = 0;
14643    }
14644
14645    if (mouseTimeDisplay) {
14646      mouseTimeDisplay.update(seekBarRect, seekBarPoint);
14647    }
14648  };
14649
14650  /**
14651   * A throttled version of the {@link ProgressControl#handleMouseSeek} listener.
14652   *
14653   * @method ProgressControl#throttledHandleMouseSeek
14654   * @param {EventTarget~Event} event
14655   *        The `mousemove` event that caused this function to run.
14656   *
14657   * @listen mousemove
14658   * @listen touchmove
14659   */
14660
14661  /**
14662   * Handle `mousemove` or `touchmove` events on the `ProgressControl`.
14663   *
14664   * @param {EventTarget~Event} event
14665   *        `mousedown` or `touchstart` event that triggered this function
14666   *
14667   * @listens mousemove
14668   * @listens touchmove
14669   */
14670
14671
14672  ProgressControl.prototype.handleMouseSeek = function handleMouseSeek(event) {
14673    var seekBar = this.getChild('seekBar');
14674
14675    seekBar.handleMouseMove(event);
14676  };
14677
14678  /**
14679   * Are controls are currently enabled for this progress control.
14680   *
14681   * @return {boolean}
14682   *         true if controls are enabled, false otherwise
14683   */
14684
14685
14686  ProgressControl.prototype.enabled = function enabled() {
14687    return this.enabled_;
14688  };
14689
14690  /**
14691   * Disable all controls on the progress control and its children
14692   */
14693
14694
14695  ProgressControl.prototype.disable = function disable() {
14696    this.children().forEach(function (child) {
14697      return child.disable && child.disable();
14698    });
14699
14700    if (!this.enabled()) {
14701      return;
14702    }
14703
14704    this.off(['mousedown', 'touchstart'], this.handleMouseDown);
14705    this.off(this.el_, 'mousemove', this.handleMouseMove);
14706    this.handleMouseUp();
14707
14708    this.addClass('disabled');
14709
14710    this.enabled_ = false;
14711  };
14712
14713  /**
14714   * Enable all controls on the progress control and its children
14715   */
14716
14717
14718  ProgressControl.prototype.enable = function enable() {
14719    this.children().forEach(function (child) {
14720      return child.enable && child.enable();
14721    });
14722
14723    if (this.enabled()) {
14724      return;
14725    }
14726
14727    this.on(['mousedown', 'touchstart'], this.handleMouseDown);
14728    this.on(this.el_, 'mousemove', this.handleMouseMove);
14729    this.removeClass('disabled');
14730
14731    this.enabled_ = true;
14732  };
14733
14734  /**
14735   * Handle `mousedown` or `touchstart` events on the `ProgressControl`.
14736   *
14737   * @param {EventTarget~Event} event
14738   *        `mousedown` or `touchstart` event that triggered this function
14739   *
14740   * @listens mousedown
14741   * @listens touchstart
14742   */
14743
14744
14745  ProgressControl.prototype.handleMouseDown = function handleMouseDown(event) {
14746    var doc = this.el_.ownerDocument;
14747
14748    this.on(doc, 'mousemove', this.throttledHandleMouseSeek);
14749    this.on(doc, 'touchmove', this.throttledHandleMouseSeek);
14750    this.on(doc, 'mouseup', this.handleMouseUp);
14751    this.on(doc, 'touchend', this.handleMouseUp);
14752  };
14753
14754  /**
14755   * Handle `mouseup` or `touchend` events on the `ProgressControl`.
14756   *
14757   * @param {EventTarget~Event} event
14758   *        `mouseup` or `touchend` event that triggered this function.
14759   *
14760   * @listens touchend
14761   * @listens mouseup
14762   */
14763
14764
14765  ProgressControl.prototype.handleMouseUp = function handleMouseUp(event) {
14766    var doc = this.el_.ownerDocument;
14767
14768    this.off(doc, 'mousemove', this.throttledHandleMouseSeek);
14769    this.off(doc, 'touchmove', this.throttledHandleMouseSeek);
14770    this.off(doc, 'mouseup', this.handleMouseUp);
14771    this.off(doc, 'touchend', this.handleMouseUp);
14772  };
14773
14774  return ProgressControl;
14775}(Component);
14776
14777/**
14778 * Default options for `ProgressControl`
14779 *
14780 * @type {Object}
14781 * @private
14782 */
14783
14784
14785ProgressControl.prototype.options_ = {
14786  children: ['seekBar']
14787};
14788
14789Component.registerComponent('ProgressControl', ProgressControl);
14790
14791/**
14792 * @file fullscreen-toggle.js
14793 */
14794/**
14795 * Toggle fullscreen video
14796 *
14797 * @extends Button
14798 */
14799
14800var FullscreenToggle = function (_Button) {
14801  inherits(FullscreenToggle, _Button);
14802
14803  /**
14804   * Creates an instance of this class.
14805   *
14806   * @param {Player} player
14807   *        The `Player` that this class should be attached to.
14808   *
14809   * @param {Object} [options]
14810   *        The key/value store of player options.
14811   */
14812  function FullscreenToggle(player, options) {
14813    classCallCheck(this, FullscreenToggle);
14814
14815    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
14816
14817    _this.on(player, 'fullscreenchange', _this.handleFullscreenChange);
14818    return _this;
14819  }
14820
14821  /**
14822   * Builds the default DOM `className`.
14823   *
14824   * @return {string}
14825   *         The DOM `className` for this object.
14826   */
14827
14828
14829  FullscreenToggle.prototype.buildCSSClass = function buildCSSClass() {
14830    return 'vjs-fullscreen-control ' + _Button.prototype.buildCSSClass.call(this);
14831  };
14832
14833  /**
14834   * Handles fullscreenchange on the player and change control text accordingly.
14835   *
14836   * @param {EventTarget~Event} [event]
14837   *        The {@link Player#fullscreenchange} event that caused this function to be
14838   *        called.
14839   *
14840   * @listens Player#fullscreenchange
14841   */
14842
14843
14844  FullscreenToggle.prototype.handleFullscreenChange = function handleFullscreenChange(event) {
14845    if (this.player_.isFullscreen()) {
14846      this.controlText('Non-Fullscreen');
14847    } else {
14848      this.controlText('Fullscreen');
14849    }
14850  };
14851
14852  /**
14853   * This gets called when an `FullscreenToggle` is "clicked". See
14854   * {@link ClickableComponent} for more detailed information on what a click can be.
14855   *
14856   * @param {EventTarget~Event} [event]
14857   *        The `keydown`, `tap`, or `click` event that caused this function to be
14858   *        called.
14859   *
14860   * @listens tap
14861   * @listens click
14862   */
14863
14864
14865  FullscreenToggle.prototype.handleClick = function handleClick(event) {
14866    if (!this.player_.isFullscreen()) {
14867      this.player_.requestFullscreen();
14868    } else {
14869      this.player_.exitFullscreen();
14870    }
14871  };
14872
14873  return FullscreenToggle;
14874}(Button);
14875
14876/**
14877 * The text that should display over the `FullscreenToggle`s controls. Added for localization.
14878 *
14879 * @type {string}
14880 * @private
14881 */
14882
14883
14884FullscreenToggle.prototype.controlText_ = 'Fullscreen';
14885
14886Component.registerComponent('FullscreenToggle', FullscreenToggle);
14887
14888/**
14889 * Check if volume control is supported and if it isn't hide the
14890 * `Component` that was passed  using the `vjs-hidden` class.
14891 *
14892 * @param {Component} self
14893 *        The component that should be hidden if volume is unsupported
14894 *
14895 * @param {Player} player
14896 *        A reference to the player
14897 *
14898 * @private
14899 */
14900var checkVolumeSupport = function checkVolumeSupport(self, player) {
14901  // hide volume controls when they're not supported by the current tech
14902  if (player.tech_ && !player.tech_.featuresVolumeControl) {
14903    self.addClass('vjs-hidden');
14904  }
14905
14906  self.on(player, 'loadstart', function () {
14907    if (!player.tech_.featuresVolumeControl) {
14908      self.addClass('vjs-hidden');
14909    } else {
14910      self.removeClass('vjs-hidden');
14911    }
14912  });
14913};
14914
14915/**
14916 * @file volume-level.js
14917 */
14918/**
14919 * Shows volume level
14920 *
14921 * @extends Component
14922 */
14923
14924var VolumeLevel = function (_Component) {
14925  inherits(VolumeLevel, _Component);
14926
14927  function VolumeLevel() {
14928    classCallCheck(this, VolumeLevel);
14929    return possibleConstructorReturn(this, _Component.apply(this, arguments));
14930  }
14931
14932  /**
14933   * Create the `Component`'s DOM element
14934   *
14935   * @return {Element}
14936   *         The element that was created.
14937   */
14938  VolumeLevel.prototype.createEl = function createEl() {
14939    return _Component.prototype.createEl.call(this, 'div', {
14940      className: 'vjs-volume-level',
14941      innerHTML: '<span class="vjs-control-text"></span>'
14942    });
14943  };
14944
14945  return VolumeLevel;
14946}(Component);
14947
14948Component.registerComponent('VolumeLevel', VolumeLevel);
14949
14950/**
14951 * @file volume-bar.js
14952 */
14953// Required children
14954/**
14955 * The bar that contains the volume level and can be clicked on to adjust the level
14956 *
14957 * @extends Slider
14958 */
14959
14960var VolumeBar = function (_Slider) {
14961  inherits(VolumeBar, _Slider);
14962
14963  /**
14964   * Creates an instance of this class.
14965   *
14966   * @param {Player} player
14967   *        The `Player` that this class should be attached to.
14968   *
14969   * @param {Object} [options]
14970   *        The key/value store of player options.
14971   */
14972  function VolumeBar(player, options) {
14973    classCallCheck(this, VolumeBar);
14974
14975    var _this = possibleConstructorReturn(this, _Slider.call(this, player, options));
14976
14977    _this.on('slideractive', _this.updateLastVolume_);
14978    _this.on(player, 'volumechange', _this.updateARIAAttributes);
14979    player.ready(function () {
14980      return _this.updateARIAAttributes();
14981    });
14982    return _this;
14983  }
14984
14985  /**
14986   * Create the `Component`'s DOM element
14987   *
14988   * @return {Element}
14989   *         The element that was created.
14990   */
14991
14992
14993  VolumeBar.prototype.createEl = function createEl$$1() {
14994    return _Slider.prototype.createEl.call(this, 'div', {
14995      className: 'vjs-volume-bar vjs-slider-bar'
14996    }, {
14997      'aria-label': this.localize('Volume Level'),
14998      'aria-live': 'polite'
14999    });
15000  };
15001
15002  /**
15003   * Handle mouse down on volume bar
15004   *
15005   * @param {EventTarget~Event} event
15006   *        The `mousedown` event that caused this to run.
15007   *
15008   * @listens mousedown
15009   */
15010
15011
15012  VolumeBar.prototype.handleMouseDown = function handleMouseDown(event) {
15013    if (!isSingleLeftClick(event)) {
15014      return;
15015    }
15016
15017    _Slider.prototype.handleMouseDown.call(this, event);
15018  };
15019
15020  /**
15021   * Handle movement events on the {@link VolumeMenuButton}.
15022   *
15023   * @param {EventTarget~Event} event
15024   *        The event that caused this function to run.
15025   *
15026   * @listens mousemove
15027   */
15028
15029
15030  VolumeBar.prototype.handleMouseMove = function handleMouseMove(event) {
15031    if (!isSingleLeftClick(event)) {
15032      return;
15033    }
15034
15035    this.checkMuted();
15036    this.player_.volume(this.calculateDistance(event));
15037  };
15038
15039  /**
15040   * If the player is muted unmute it.
15041   */
15042
15043
15044  VolumeBar.prototype.checkMuted = function checkMuted() {
15045    if (this.player_.muted()) {
15046      this.player_.muted(false);
15047    }
15048  };
15049
15050  /**
15051   * Get percent of volume level
15052   *
15053   * @return {number}
15054   *         Volume level percent as a decimal number.
15055   */
15056
15057
15058  VolumeBar.prototype.getPercent = function getPercent() {
15059    if (this.player_.muted()) {
15060      return 0;
15061    }
15062    return this.player_.volume();
15063  };
15064
15065  /**
15066   * Increase volume level for keyboard users
15067   */
15068
15069
15070  VolumeBar.prototype.stepForward = function stepForward() {
15071    this.checkMuted();
15072    this.player_.volume(this.player_.volume() + 0.1);
15073  };
15074
15075  /**
15076   * Decrease volume level for keyboard users
15077   */
15078
15079
15080  VolumeBar.prototype.stepBack = function stepBack() {
15081    this.checkMuted();
15082    this.player_.volume(this.player_.volume() - 0.1);
15083  };
15084
15085  /**
15086   * Update ARIA accessibility attributes
15087   *
15088   * @param {EventTarget~Event} [event]
15089   *        The `volumechange` event that caused this function to run.
15090   *
15091   * @listens Player#volumechange
15092   */
15093
15094
15095  VolumeBar.prototype.updateARIAAttributes = function updateARIAAttributes(event) {
15096    var ariaValue = this.player_.muted() ? 0 : this.volumeAsPercentage_();
15097
15098    this.el_.setAttribute('aria-valuenow', ariaValue);
15099    this.el_.setAttribute('aria-valuetext', ariaValue + '%');
15100  };
15101
15102  /**
15103   * Returns the current value of the player volume as a percentage
15104   *
15105   * @private
15106   */
15107
15108
15109  VolumeBar.prototype.volumeAsPercentage_ = function volumeAsPercentage_() {
15110    return Math.round(this.player_.volume() * 100);
15111  };
15112
15113  /**
15114   * When user starts dragging the VolumeBar, store the volume and listen for
15115   * the end of the drag. When the drag ends, if the volume was set to zero,
15116   * set lastVolume to the stored volume.
15117   *
15118   * @listens slideractive
15119   * @private
15120   */
15121
15122
15123  VolumeBar.prototype.updateLastVolume_ = function updateLastVolume_() {
15124    var _this2 = this;
15125
15126    var volumeBeforeDrag = this.player_.volume();
15127
15128    this.one('sliderinactive', function () {
15129      if (_this2.player_.volume() === 0) {
15130        _this2.player_.lastVolume_(volumeBeforeDrag);
15131      }
15132    });
15133  };
15134
15135  return VolumeBar;
15136}(Slider);
15137
15138/**
15139 * Default options for the `VolumeBar`
15140 *
15141 * @type {Object}
15142 * @private
15143 */
15144
15145
15146VolumeBar.prototype.options_ = {
15147  children: ['volumeLevel'],
15148  barName: 'volumeLevel'
15149};
15150
15151/**
15152 * Call the update event for this Slider when this event happens on the player.
15153 *
15154 * @type {string}
15155 */
15156VolumeBar.prototype.playerEvent = 'volumechange';
15157
15158Component.registerComponent('VolumeBar', VolumeBar);
15159
15160/**
15161 * @file volume-control.js
15162 */
15163// Required children
15164/**
15165 * The component for controlling the volume level
15166 *
15167 * @extends Component
15168 */
15169
15170var VolumeControl = function (_Component) {
15171  inherits(VolumeControl, _Component);
15172
15173  /**
15174   * Creates an instance of this class.
15175   *
15176   * @param {Player} player
15177   *        The `Player` that this class should be attached to.
15178   *
15179   * @param {Object} [options={}]
15180   *        The key/value store of player options.
15181   */
15182  function VolumeControl(player) {
15183    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
15184    classCallCheck(this, VolumeControl);
15185
15186    options.vertical = options.vertical || false;
15187
15188    // Pass the vertical option down to the VolumeBar if
15189    // the VolumeBar is turned on.
15190    if (typeof options.volumeBar === 'undefined' || isPlain(options.volumeBar)) {
15191      options.volumeBar = options.volumeBar || {};
15192      options.volumeBar.vertical = options.vertical;
15193    }
15194
15195    // hide this control if volume support is missing
15196    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
15197
15198    checkVolumeSupport(_this, player);
15199
15200    _this.throttledHandleMouseMove = throttle(bind(_this, _this.handleMouseMove), 25);
15201
15202    _this.on('mousedown', _this.handleMouseDown);
15203    _this.on('touchstart', _this.handleMouseDown);
15204
15205    // while the slider is active (the mouse has been pressed down and
15206    // is dragging) or in focus we do not want to hide the VolumeBar
15207    _this.on(_this.volumeBar, ['focus', 'slideractive'], function () {
15208      _this.volumeBar.addClass('vjs-slider-active');
15209      _this.addClass('vjs-slider-active');
15210      _this.trigger('slideractive');
15211    });
15212
15213    _this.on(_this.volumeBar, ['blur', 'sliderinactive'], function () {
15214      _this.volumeBar.removeClass('vjs-slider-active');
15215      _this.removeClass('vjs-slider-active');
15216      _this.trigger('sliderinactive');
15217    });
15218    return _this;
15219  }
15220
15221  /**
15222   * Create the `Component`'s DOM element
15223   *
15224   * @return {Element}
15225   *         The element that was created.
15226   */
15227
15228
15229  VolumeControl.prototype.createEl = function createEl() {
15230    var orientationClass = 'vjs-volume-horizontal';
15231
15232    if (this.options_.vertical) {
15233      orientationClass = 'vjs-volume-vertical';
15234    }
15235
15236    return _Component.prototype.createEl.call(this, 'div', {
15237      className: 'vjs-volume-control vjs-control ' + orientationClass
15238    });
15239  };
15240
15241  /**
15242   * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
15243   *
15244   * @param {EventTarget~Event} event
15245   *        `mousedown` or `touchstart` event that triggered this function
15246   *
15247   * @listens mousedown
15248   * @listens touchstart
15249   */
15250
15251
15252  VolumeControl.prototype.handleMouseDown = function handleMouseDown(event) {
15253    var doc = this.el_.ownerDocument;
15254
15255    this.on(doc, 'mousemove', this.throttledHandleMouseMove);
15256    this.on(doc, 'touchmove', this.throttledHandleMouseMove);
15257    this.on(doc, 'mouseup', this.handleMouseUp);
15258    this.on(doc, 'touchend', this.handleMouseUp);
15259  };
15260
15261  /**
15262   * Handle `mouseup` or `touchend` events on the `VolumeControl`.
15263   *
15264   * @param {EventTarget~Event} event
15265   *        `mouseup` or `touchend` event that triggered this function.
15266   *
15267   * @listens touchend
15268   * @listens mouseup
15269   */
15270
15271
15272  VolumeControl.prototype.handleMouseUp = function handleMouseUp(event) {
15273    var doc = this.el_.ownerDocument;
15274
15275    this.off(doc, 'mousemove', this.throttledHandleMouseMove);
15276    this.off(doc, 'touchmove', this.throttledHandleMouseMove);
15277    this.off(doc, 'mouseup', this.handleMouseUp);
15278    this.off(doc, 'touchend', this.handleMouseUp);
15279  };
15280
15281  /**
15282   * Handle `mousedown` or `touchstart` events on the `VolumeControl`.
15283   *
15284   * @param {EventTarget~Event} event
15285   *        `mousedown` or `touchstart` event that triggered this function
15286   *
15287   * @listens mousedown
15288   * @listens touchstart
15289   */
15290
15291
15292  VolumeControl.prototype.handleMouseMove = function handleMouseMove(event) {
15293    this.volumeBar.handleMouseMove(event);
15294  };
15295
15296  return VolumeControl;
15297}(Component);
15298
15299/**
15300 * Default options for the `VolumeControl`
15301 *
15302 * @type {Object}
15303 * @private
15304 */
15305
15306
15307VolumeControl.prototype.options_ = {
15308  children: ['volumeBar']
15309};
15310
15311Component.registerComponent('VolumeControl', VolumeControl);
15312
15313/**
15314 * @file mute-toggle.js
15315 */
15316/**
15317 * A button component for muting the audio.
15318 *
15319 * @extends Button
15320 */
15321
15322var MuteToggle = function (_Button) {
15323  inherits(MuteToggle, _Button);
15324
15325  /**
15326   * Creates an instance of this class.
15327   *
15328   * @param {Player} player
15329   *        The `Player` that this class should be attached to.
15330   *
15331   * @param {Object} [options]
15332   *        The key/value store of player options.
15333   */
15334  function MuteToggle(player, options) {
15335    classCallCheck(this, MuteToggle);
15336
15337    // hide this control if volume support is missing
15338    var _this = possibleConstructorReturn(this, _Button.call(this, player, options));
15339
15340    checkVolumeSupport(_this, player);
15341
15342    _this.on(player, ['loadstart', 'volumechange'], _this.update);
15343    return _this;
15344  }
15345
15346  /**
15347   * Builds the default DOM `className`.
15348   *
15349   * @return {string}
15350   *         The DOM `className` for this object.
15351   */
15352
15353
15354  MuteToggle.prototype.buildCSSClass = function buildCSSClass() {
15355    return 'vjs-mute-control ' + _Button.prototype.buildCSSClass.call(this);
15356  };
15357
15358  /**
15359   * This gets called when an `MuteToggle` is "clicked". See
15360   * {@link ClickableComponent} for more detailed information on what a click can be.
15361   *
15362   * @param {EventTarget~Event} [event]
15363   *        The `keydown`, `tap`, or `click` event that caused this function to be
15364   *        called.
15365   *
15366   * @listens tap
15367   * @listens click
15368   */
15369
15370
15371  MuteToggle.prototype.handleClick = function handleClick(event) {
15372    var vol = this.player_.volume();
15373    var lastVolume = this.player_.lastVolume_();
15374
15375    if (vol === 0) {
15376      var volumeToSet = lastVolume < 0.1 ? 0.1 : lastVolume;
15377
15378      this.player_.volume(volumeToSet);
15379      this.player_.muted(false);
15380    } else {
15381      this.player_.muted(this.player_.muted() ? false : true);
15382    }
15383  };
15384
15385  /**
15386   * Update the `MuteToggle` button based on the state of `volume` and `muted`
15387   * on the player.
15388   *
15389   * @param {EventTarget~Event} [event]
15390   *        The {@link Player#loadstart} event if this function was called
15391   *        through an event.
15392   *
15393   * @listens Player#loadstart
15394   * @listens Player#volumechange
15395   */
15396
15397
15398  MuteToggle.prototype.update = function update(event) {
15399    this.updateIcon_();
15400    this.updateControlText_();
15401  };
15402
15403  /**
15404   * Update the appearance of the `MuteToggle` icon.
15405   *
15406   * Possible states (given `level` variable below):
15407   * - 0: crossed out
15408   * - 1: zero bars of volume
15409   * - 2: one bar of volume
15410   * - 3: two bars of volume
15411   *
15412   * @private
15413   */
15414
15415
15416  MuteToggle.prototype.updateIcon_ = function updateIcon_() {
15417    var vol = this.player_.volume();
15418    var level = 3;
15419
15420    if (vol === 0 || this.player_.muted()) {
15421      level = 0;
15422    } else if (vol < 0.33) {
15423      level = 1;
15424    } else if (vol < 0.67) {
15425      level = 2;
15426    }
15427
15428    // TODO improve muted icon classes
15429    for (var i = 0; i < 4; i++) {
15430      removeClass(this.el_, 'vjs-vol-' + i);
15431    }
15432    addClass(this.el_, 'vjs-vol-' + level);
15433  };
15434
15435  /**
15436   * If `muted` has changed on the player, update the control text
15437   * (`title` attribute on `vjs-mute-control` element and content of
15438   * `vjs-control-text` element).
15439   *
15440   * @private
15441   */
15442
15443
15444  MuteToggle.prototype.updateControlText_ = function updateControlText_() {
15445    var soundOff = this.player_.muted() || this.player_.volume() === 0;
15446    var text = soundOff ? 'Unmute' : 'Mute';
15447
15448    if (this.controlText() !== text) {
15449      this.controlText(text);
15450    }
15451  };
15452
15453  return MuteToggle;
15454}(Button);
15455
15456/**
15457 * The text that should display over the `MuteToggle`s controls. Added for localization.
15458 *
15459 * @type {string}
15460 * @private
15461 */
15462
15463
15464MuteToggle.prototype.controlText_ = 'Mute';
15465
15466Component.registerComponent('MuteToggle', MuteToggle);
15467
15468/**
15469 * @file volume-control.js
15470 */
15471// Required children
15472/**
15473 * A Component to contain the MuteToggle and VolumeControl so that
15474 * they can work together.
15475 *
15476 * @extends Component
15477 */
15478
15479var VolumePanel = function (_Component) {
15480  inherits(VolumePanel, _Component);
15481
15482  /**
15483   * Creates an instance of this class.
15484   *
15485   * @param {Player} player
15486   *        The `Player` that this class should be attached to.
15487   *
15488   * @param {Object} [options={}]
15489   *        The key/value store of player options.
15490   */
15491  function VolumePanel(player) {
15492    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
15493    classCallCheck(this, VolumePanel);
15494
15495    if (typeof options.inline !== 'undefined') {
15496      options.inline = options.inline;
15497    } else {
15498      options.inline = true;
15499    }
15500
15501    // pass the inline option down to the VolumeControl as vertical if
15502    // the VolumeControl is on.
15503    if (typeof options.volumeControl === 'undefined' || isPlain(options.volumeControl)) {
15504      options.volumeControl = options.volumeControl || {};
15505      options.volumeControl.vertical = !options.inline;
15506    }
15507
15508    // hide this control if volume support is missing
15509    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
15510
15511    checkVolumeSupport(_this, player);
15512
15513    // while the slider is active (the mouse has been pressed down and
15514    // is dragging) we do not want to hide the VolumeBar
15515    _this.on(_this.volumeControl, ['slideractive'], _this.sliderActive_);
15516
15517    _this.on(_this.volumeControl, ['sliderinactive'], _this.sliderInactive_);
15518    return _this;
15519  }
15520
15521  /**
15522   * Add vjs-slider-active class to the VolumePanel
15523   *
15524   * @listens VolumeControl#slideractive
15525   * @private
15526   */
15527
15528
15529  VolumePanel.prototype.sliderActive_ = function sliderActive_() {
15530    this.addClass('vjs-slider-active');
15531  };
15532
15533  /**
15534   * Removes vjs-slider-active class to the VolumePanel
15535   *
15536   * @listens VolumeControl#sliderinactive
15537   * @private
15538   */
15539
15540
15541  VolumePanel.prototype.sliderInactive_ = function sliderInactive_() {
15542    this.removeClass('vjs-slider-active');
15543  };
15544
15545  /**
15546   * Create the `Component`'s DOM element
15547   *
15548   * @return {Element}
15549   *         The element that was created.
15550   */
15551
15552
15553  VolumePanel.prototype.createEl = function createEl() {
15554    var orientationClass = 'vjs-volume-panel-horizontal';
15555
15556    if (!this.options_.inline) {
15557      orientationClass = 'vjs-volume-panel-vertical';
15558    }
15559
15560    return _Component.prototype.createEl.call(this, 'div', {
15561      className: 'vjs-volume-panel vjs-control ' + orientationClass
15562    });
15563  };
15564
15565  return VolumePanel;
15566}(Component);
15567
15568/**
15569 * Default options for the `VolumeControl`
15570 *
15571 * @type {Object}
15572 * @private
15573 */
15574
15575
15576VolumePanel.prototype.options_ = {
15577  children: ['muteToggle', 'volumeControl']
15578};
15579
15580Component.registerComponent('VolumePanel', VolumePanel);
15581
15582/**
15583 * @file menu.js
15584 */
15585/**
15586 * The Menu component is used to build popup menus, including subtitle and
15587 * captions selection menus.
15588 *
15589 * @extends Component
15590 */
15591
15592var Menu = function (_Component) {
15593  inherits(Menu, _Component);
15594
15595  /**
15596   * Create an instance of this class.
15597   *
15598   * @param {Player} player
15599   *        the player that this component should attach to
15600   *
15601   * @param {Object} [options]
15602   *        Object of option names and values
15603   *
15604   */
15605  function Menu(player, options) {
15606    classCallCheck(this, Menu);
15607
15608    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
15609
15610    if (options) {
15611      _this.menuButton_ = options.menuButton;
15612    }
15613
15614    _this.focusedChild_ = -1;
15615
15616    _this.on('keydown', _this.handleKeyPress);
15617    return _this;
15618  }
15619
15620  /**
15621   * Add a {@link MenuItem} to the menu.
15622   *
15623   * @param {Object|string} component
15624   *        The name or instance of the `MenuItem` to add.
15625   *
15626   */
15627
15628
15629  Menu.prototype.addItem = function addItem(component) {
15630    this.addChild(component);
15631    component.on('click', bind(this, function (event) {
15632      // Unpress the associated MenuButton, and move focus back to it
15633      if (this.menuButton_) {
15634        this.menuButton_.unpressButton();
15635
15636        // don't focus menu button if item is a caption settings item
15637        // because focus will move elsewhere and it logs an error on IE8
15638        if (component.name() !== 'CaptionSettingsMenuItem') {
15639          this.menuButton_.focus();
15640        }
15641      }
15642    }));
15643  };
15644
15645  /**
15646   * Create the `Menu`s DOM element.
15647   *
15648   * @return {Element}
15649   *         the element that was created
15650   */
15651
15652
15653  Menu.prototype.createEl = function createEl$$1() {
15654    var contentElType = this.options_.contentElType || 'ul';
15655
15656    this.contentEl_ = createEl(contentElType, {
15657      className: 'vjs-menu-content'
15658    });
15659
15660    this.contentEl_.setAttribute('role', 'menu');
15661
15662    var el = _Component.prototype.createEl.call(this, 'div', {
15663      append: this.contentEl_,
15664      className: 'vjs-menu'
15665    });
15666
15667    el.appendChild(this.contentEl_);
15668
15669    // Prevent clicks from bubbling up. Needed for Menu Buttons,
15670    // where a click on the parent is significant
15671    on(el, 'click', function (event) {
15672      event.preventDefault();
15673      event.stopImmediatePropagation();
15674    });
15675
15676    return el;
15677  };
15678
15679  Menu.prototype.dispose = function dispose() {
15680    this.contentEl_ = null;
15681
15682    _Component.prototype.dispose.call(this);
15683  };
15684
15685  /**
15686   * Handle a `keydown` event on this menu. This listener is added in the constructor.
15687   *
15688   * @param {EventTarget~Event} event
15689   *        A `keydown` event that happened on the menu.
15690   *
15691   * @listens keydown
15692   */
15693
15694
15695  Menu.prototype.handleKeyPress = function handleKeyPress(event) {
15696    // Left and Down Arrows
15697    if (event.which === 37 || event.which === 40) {
15698      event.preventDefault();
15699      this.stepForward();
15700
15701      // Up and Right Arrows
15702    } else if (event.which === 38 || event.which === 39) {
15703      event.preventDefault();
15704      this.stepBack();
15705    }
15706  };
15707
15708  /**
15709   * Move to next (lower) menu item for keyboard users.
15710   */
15711
15712
15713  Menu.prototype.stepForward = function stepForward() {
15714    var stepChild = 0;
15715
15716    if (this.focusedChild_ !== undefined) {
15717      stepChild = this.focusedChild_ + 1;
15718    }
15719    this.focus(stepChild);
15720  };
15721
15722  /**
15723   * Move to previous (higher) menu item for keyboard users.
15724   */
15725
15726
15727  Menu.prototype.stepBack = function stepBack() {
15728    var stepChild = 0;
15729
15730    if (this.focusedChild_ !== undefined) {
15731      stepChild = this.focusedChild_ - 1;
15732    }
15733    this.focus(stepChild);
15734  };
15735
15736  /**
15737   * Set focus on a {@link MenuItem} in the `Menu`.
15738   *
15739   * @param {Object|string} [item=0]
15740   *        Index of child item set focus on.
15741   */
15742
15743
15744  Menu.prototype.focus = function focus() {
15745    var item = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
15746
15747    var children = this.children().slice();
15748    var haveTitle = children.length && children[0].className && /vjs-menu-title/.test(children[0].className);
15749
15750    if (haveTitle) {
15751      children.shift();
15752    }
15753
15754    if (children.length > 0) {
15755      if (item < 0) {
15756        item = 0;
15757      } else if (item >= children.length) {
15758        item = children.length - 1;
15759      }
15760
15761      this.focusedChild_ = item;
15762
15763      children[item].el_.focus();
15764    }
15765  };
15766
15767  return Menu;
15768}(Component);
15769
15770Component.registerComponent('Menu', Menu);
15771
15772/**
15773 * @file menu-button.js
15774 */
15775/**
15776 * A `MenuButton` class for any popup {@link Menu}.
15777 *
15778 * @extends Component
15779 */
15780
15781var MenuButton = function (_Component) {
15782  inherits(MenuButton, _Component);
15783
15784  /**
15785   * Creates an instance of this class.
15786   *
15787   * @param {Player} player
15788   *        The `Player` that this class should be attached to.
15789   *
15790   * @param {Object} [options={}]
15791   *        The key/value store of player options.
15792   */
15793  function MenuButton(player) {
15794    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
15795    classCallCheck(this, MenuButton);
15796
15797    var _this = possibleConstructorReturn(this, _Component.call(this, player, options));
15798
15799    _this.menuButton_ = new Button(player, options);
15800
15801    _this.menuButton_.controlText(_this.controlText_);
15802    _this.menuButton_.el_.setAttribute('aria-haspopup', 'true');
15803
15804    // Add buildCSSClass values to the button, not the wrapper
15805    var buttonClass = Button.prototype.buildCSSClass();
15806
15807    _this.menuButton_.el_.className = _this.buildCSSClass() + ' ' + buttonClass;
15808    _this.menuButton_.removeClass('vjs-control');
15809
15810    _this.addChild(_this.menuButton_);
15811
15812    _this.update();
15813
15814    _this.enabled_ = true;
15815
15816    _this.on(_this.menuButton_, 'tap', _this.handleClick);
15817    _this.on(_this.menuButton_, 'click', _this.handleClick);
15818    _this.on(_this.menuButton_, 'focus', _this.handleFocus);
15819    _this.on(_this.menuButton_, 'blur', _this.handleBlur);
15820
15821    _this.on('keydown', _this.handleSubmenuKeyPress);
15822    return _this;
15823  }
15824
15825  /**
15826   * Update the menu based on the current state of its items.
15827   */
15828
15829
15830  MenuButton.prototype.update = function update() {
15831    var menu = this.createMenu();
15832
15833    if (this.menu) {
15834      this.menu.dispose();
15835      this.removeChild(this.menu);
15836    }
15837
15838    this.menu = menu;
15839    this.addChild(menu);
15840
15841    /**
15842     * Track the state of the menu button
15843     *
15844     * @type {Boolean}
15845     * @private
15846     */
15847    this.buttonPressed_ = false;
15848    this.menuButton_.el_.setAttribute('aria-expanded', 'false');
15849
15850    if (this.items && this.items.length <= this.hideThreshold_) {
15851      this.hide();
15852    } else {
15853      this.show();
15854    }
15855  };
15856
15857  /**
15858   * Create the menu and add all items to it.
15859   *
15860   * @return {Menu}
15861   *         The constructed menu
15862   */
15863
15864
15865  MenuButton.prototype.createMenu = function createMenu() {
15866    var menu = new Menu(this.player_, { menuButton: this });
15867
15868    /**
15869     * Hide the menu if the number of items is less than or equal to this threshold. This defaults
15870     * to 0 and whenever we add items which can be hidden to the menu we'll increment it. We list
15871     * it here because every time we run `createMenu` we need to reset the value.
15872     *
15873     * @protected
15874     * @type {Number}
15875     */
15876    this.hideThreshold_ = 0;
15877
15878    // Add a title list item to the top
15879    if (this.options_.title) {
15880      var title = createEl('li', {
15881        className: 'vjs-menu-title',
15882        innerHTML: toTitleCase(this.options_.title),
15883        tabIndex: -1
15884      });
15885
15886      this.hideThreshold_ += 1;
15887
15888      menu.children_.unshift(title);
15889      prependTo(title, menu.contentEl());
15890    }
15891
15892    this.items = this.createItems();
15893
15894    if (this.items) {
15895      // Add menu items to the menu
15896      for (var i = 0; i < this.items.length; i++) {
15897        menu.addItem(this.items[i]);
15898      }
15899    }
15900
15901    return menu;
15902  };
15903
15904  /**
15905   * Create the list of menu items. Specific to each subclass.
15906   *
15907   * @abstract
15908   */
15909
15910
15911  MenuButton.prototype.createItems = function createItems() {};
15912
15913  /**
15914   * Create the `MenuButtons`s DOM element.
15915   *
15916   * @return {Element}
15917   *         The element that gets created.
15918   */
15919
15920
15921  MenuButton.prototype.createEl = function createEl$$1() {
15922    return _Component.prototype.createEl.call(this, 'div', {
15923      className: this.buildWrapperCSSClass()
15924    }, {});
15925  };
15926
15927  /**
15928   * Allow sub components to stack CSS class names for the wrapper element
15929   *
15930   * @return {string}
15931   *         The constructed wrapper DOM `className`
15932   */
15933
15934
15935  MenuButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
15936    var menuButtonClass = 'vjs-menu-button';
15937
15938    // If the inline option is passed, we want to use different styles altogether.
15939    if (this.options_.inline === true) {
15940      menuButtonClass += '-inline';
15941    } else {
15942      menuButtonClass += '-popup';
15943    }
15944
15945    // TODO: Fix the CSS so that this isn't necessary
15946    var buttonClass = Button.prototype.buildCSSClass();
15947
15948    return 'vjs-menu-button ' + menuButtonClass + ' ' + buttonClass + ' ' + _Component.prototype.buildCSSClass.call(this);
15949  };
15950
15951  /**
15952   * Builds the default DOM `className`.
15953   *
15954   * @return {string}
15955   *         The DOM `className` for this object.
15956   */
15957
15958
15959  MenuButton.prototype.buildCSSClass = function buildCSSClass() {
15960    var menuButtonClass = 'vjs-menu-button';
15961
15962    // If the inline option is passed, we want to use different styles altogether.
15963    if (this.options_.inline === true) {
15964      menuButtonClass += '-inline';
15965    } else {
15966      menuButtonClass += '-popup';
15967    }
15968
15969    return 'vjs-menu-button ' + menuButtonClass + ' ' + _Component.prototype.buildCSSClass.call(this);
15970  };
15971
15972  /**
15973   * Get or set the localized control text that will be used for accessibility.
15974   *
15975   * > NOTE: This will come from the internal `menuButton_` element.
15976   *
15977   * @param {string} [text]
15978   *        Control text for element.
15979   *
15980   * @param {Element} [el=this.menuButton_.el()]
15981   *        Element to set the title on.
15982   *
15983   * @return {string}
15984   *         - The control text when getting
15985   */
15986
15987
15988  MenuButton.prototype.controlText = function controlText(text) {
15989    var el = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.menuButton_.el();
15990
15991    return this.menuButton_.controlText(text, el);
15992  };
15993
15994  /**
15995   * Handle a click on a `MenuButton`.
15996   * See {@link ClickableComponent#handleClick} for instances where this is called.
15997   *
15998   * @param {EventTarget~Event} event
15999   *        The `keydown`, `tap`, or `click` event that caused this function to be
16000   *        called.
16001   *
16002   * @listens tap
16003   * @listens click
16004   */
16005
16006
16007  MenuButton.prototype.handleClick = function handleClick(event) {
16008    // When you click the button it adds focus, which will show the menu.
16009    // So we'll remove focus when the mouse leaves the button. Focus is needed
16010    // for tab navigation.
16011
16012    this.one(this.menu.contentEl(), 'mouseleave', bind(this, function (e) {
16013      this.unpressButton();
16014      this.el_.blur();
16015    }));
16016    if (this.buttonPressed_) {
16017      this.unpressButton();
16018    } else {
16019      this.pressButton();
16020    }
16021  };
16022
16023  /**
16024   * Set the focus to the actual button, not to this element
16025   */
16026
16027
16028  MenuButton.prototype.focus = function focus() {
16029    this.menuButton_.focus();
16030  };
16031
16032  /**
16033   * Remove the focus from the actual button, not this element
16034   */
16035
16036
16037  MenuButton.prototype.blur = function blur() {
16038    this.menuButton_.blur();
16039  };
16040
16041  /**
16042   * This gets called when a `MenuButton` gains focus via a `focus` event.
16043   * Turns on listening for `keydown` events. When they happen it
16044   * calls `this.handleKeyPress`.
16045   *
16046   * @param {EventTarget~Event} event
16047   *        The `focus` event that caused this function to be called.
16048   *
16049   * @listens focus
16050   */
16051
16052
16053  MenuButton.prototype.handleFocus = function handleFocus() {
16054    on(document_1, 'keydown', bind(this, this.handleKeyPress));
16055  };
16056
16057  /**
16058   * Called when a `MenuButton` loses focus. Turns off the listener for
16059   * `keydown` events. Which Stops `this.handleKeyPress` from getting called.
16060   *
16061   * @param {EventTarget~Event} event
16062   *        The `blur` event that caused this function to be called.
16063   *
16064   * @listens blur
16065   */
16066
16067
16068  MenuButton.prototype.handleBlur = function handleBlur() {
16069    off(document_1, 'keydown', bind(this, this.handleKeyPress));
16070  };
16071
16072  /**
16073   * Handle tab, escape, down arrow, and up arrow keys for `MenuButton`. See
16074   * {@link ClickableComponent#handleKeyPress} for instances where this is called.
16075   *
16076   * @param {EventTarget~Event} event
16077   *        The `keydown` event that caused this function to be called.
16078   *
16079   * @listens keydown
16080   */
16081
16082
16083  MenuButton.prototype.handleKeyPress = function handleKeyPress(event) {
16084
16085    // Escape (27) key or Tab (9) key unpress the 'button'
16086    if (event.which === 27 || event.which === 9) {
16087      if (this.buttonPressed_) {
16088        this.unpressButton();
16089      }
16090      // Don't preventDefault for Tab key - we still want to lose focus
16091      if (event.which !== 9) {
16092        event.preventDefault();
16093        // Set focus back to the menu button's button
16094        this.menuButton_.el_.focus();
16095      }
16096      // Up (38) key or Down (40) key press the 'button'
16097    } else if (event.which === 38 || event.which === 40) {
16098      if (!this.buttonPressed_) {
16099        this.pressButton();
16100        event.preventDefault();
16101      }
16102    }
16103  };
16104
16105  /**
16106   * Handle a `keydown` event on a sub-menu. The listener for this is added in
16107   * the constructor.
16108   *
16109   * @param {EventTarget~Event} event
16110   *        Key press event
16111   *
16112   * @listens keydown
16113   */
16114
16115
16116  MenuButton.prototype.handleSubmenuKeyPress = function handleSubmenuKeyPress(event) {
16117
16118    // Escape (27) key or Tab (9) key unpress the 'button'
16119    if (event.which === 27 || event.which === 9) {
16120      if (this.buttonPressed_) {
16121        this.unpressButton();
16122      }
16123      // Don't preventDefault for Tab key - we still want to lose focus
16124      if (event.which !== 9) {
16125        event.preventDefault();
16126        // Set focus back to the menu button's button
16127        this.menuButton_.el_.focus();
16128      }
16129    }
16130  };
16131
16132  /**
16133   * Put the current `MenuButton` into a pressed state.
16134   */
16135
16136
16137  MenuButton.prototype.pressButton = function pressButton() {
16138    if (this.enabled_) {
16139      this.buttonPressed_ = true;
16140      this.menu.lockShowing();
16141      this.menuButton_.el_.setAttribute('aria-expanded', 'true');
16142
16143      // set the focus into the submenu, except on iOS where it is resulting in
16144      // undesired scrolling behavior when the player is in an iframe
16145      if (IS_IOS && isInFrame()) {
16146        // Return early so that the menu isn't focused
16147        return;
16148      }
16149
16150      this.menu.focus();
16151    }
16152  };
16153
16154  /**
16155   * Take the current `MenuButton` out of a pressed state.
16156   */
16157
16158
16159  MenuButton.prototype.unpressButton = function unpressButton() {
16160    if (this.enabled_) {
16161      this.buttonPressed_ = false;
16162      this.menu.unlockShowing();
16163      this.menuButton_.el_.setAttribute('aria-expanded', 'false');
16164    }
16165  };
16166
16167  /**
16168   * Disable the `MenuButton`. Don't allow it to be clicked.
16169   */
16170
16171
16172  MenuButton.prototype.disable = function disable() {
16173    this.unpressButton();
16174
16175    this.enabled_ = false;
16176    this.addClass('vjs-disabled');
16177
16178    this.menuButton_.disable();
16179  };
16180
16181  /**
16182   * Enable the `MenuButton`. Allow it to be clicked.
16183   */
16184
16185
16186  MenuButton.prototype.enable = function enable() {
16187    this.enabled_ = true;
16188    this.removeClass('vjs-disabled');
16189
16190    this.menuButton_.enable();
16191  };
16192
16193  return MenuButton;
16194}(Component);
16195
16196Component.registerComponent('MenuButton', MenuButton);
16197
16198/**
16199 * @file track-button.js
16200 */
16201/**
16202 * The base class for buttons that toggle specific  track types (e.g. subtitles).
16203 *
16204 * @extends MenuButton
16205 */
16206
16207var TrackButton = function (_MenuButton) {
16208  inherits(TrackButton, _MenuButton);
16209
16210  /**
16211   * Creates an instance of this class.
16212   *
16213   * @param {Player} player
16214   *        The `Player` that this class should be attached to.
16215   *
16216   * @param {Object} [options]
16217   *        The key/value store of player options.
16218   */
16219  function TrackButton(player, options) {
16220    classCallCheck(this, TrackButton);
16221
16222    var tracks = options.tracks;
16223
16224    var _this = possibleConstructorReturn(this, _MenuButton.call(this, player, options));
16225
16226    if (_this.items.length <= 1) {
16227      _this.hide();
16228    }
16229
16230    if (!tracks) {
16231      return possibleConstructorReturn(_this);
16232    }
16233
16234    var updateHandler = bind(_this, _this.update);
16235
16236    tracks.addEventListener('removetrack', updateHandler);
16237    tracks.addEventListener('addtrack', updateHandler);
16238    _this.player_.on('ready', updateHandler);
16239
16240    _this.player_.on('dispose', function () {
16241      tracks.removeEventListener('removetrack', updateHandler);
16242      tracks.removeEventListener('addtrack', updateHandler);
16243    });
16244    return _this;
16245  }
16246
16247  return TrackButton;
16248}(MenuButton);
16249
16250Component.registerComponent('TrackButton', TrackButton);
16251
16252/**
16253 * @file menu-item.js
16254 */
16255/**
16256 * The component for a menu item. `<li>`
16257 *
16258 * @extends ClickableComponent
16259 */
16260
16261var MenuItem = function (_ClickableComponent) {
16262  inherits(MenuItem, _ClickableComponent);
16263
16264  /**
16265   * Creates an instance of the this class.
16266   *
16267   * @param {Player} player
16268   *        The `Player` that this class should be attached to.
16269   *
16270   * @param {Object} [options={}]
16271   *        The key/value store of player options.
16272   *
16273   */
16274  function MenuItem(player, options) {
16275    classCallCheck(this, MenuItem);
16276
16277    var _this = possibleConstructorReturn(this, _ClickableComponent.call(this, player, options));
16278
16279    _this.selectable = options.selectable;
16280
16281    _this.selected(options.selected);
16282
16283    if (_this.selectable) {
16284      // TODO: May need to be either menuitemcheckbox or menuitemradio,
16285      //       and may need logical grouping of menu items.
16286      _this.el_.setAttribute('role', 'menuitemcheckbox');
16287    } else {
16288      _this.el_.setAttribute('role', 'menuitem');
16289    }
16290    return _this;
16291  }
16292
16293  /**
16294   * Create the `MenuItem's DOM element
16295   *
16296   * @param {string} [type=li]
16297   *        Element's node type, not actually used, always set to `li`.
16298   *
16299   * @param {Object} [props={}]
16300   *        An object of properties that should be set on the element
16301   *
16302   * @param {Object} [attrs={}]
16303   *        An object of attributes that should be set on the element
16304   *
16305   * @return {Element}
16306   *         The element that gets created.
16307   */
16308
16309
16310  MenuItem.prototype.createEl = function createEl(type, props, attrs) {
16311    // The control is textual, not just an icon
16312    this.nonIconControl = true;
16313
16314    return _ClickableComponent.prototype.createEl.call(this, 'li', assign({
16315      className: 'vjs-menu-item',
16316      innerHTML: '<span class="vjs-menu-item-text">' + this.localize(this.options_.label) + '</span>',
16317      tabIndex: -1
16318    }, props), attrs);
16319  };
16320
16321  /**
16322   * Any click on a `MenuItem` puts int into the selected state.
16323   * See {@link ClickableComponent#handleClick} for instances where this is called.
16324   *
16325   * @param {EventTarget~Event} event
16326   *        The `keydown`, `tap`, or `click` event that caused this function to be
16327   *        called.
16328   *
16329   * @listens tap
16330   * @listens click
16331   */
16332
16333
16334  MenuItem.prototype.handleClick = function handleClick(event) {
16335    this.selected(true);
16336  };
16337
16338  /**
16339   * Set the state for this menu item as selected or not.
16340   *
16341   * @param {boolean} selected
16342   *        if the menu item is selected or not
16343   */
16344
16345
16346  MenuItem.prototype.selected = function selected(_selected) {
16347    if (this.selectable) {
16348      if (_selected) {
16349        this.addClass('vjs-selected');
16350        this.el_.setAttribute('aria-checked', 'true');
16351        // aria-checked isn't fully supported by browsers/screen readers,
16352        // so indicate selected state to screen reader in the control text.
16353        this.controlText(', selected');
16354      } else {
16355        this.removeClass('vjs-selected');
16356        this.el_.setAttribute('aria-checked', 'false');
16357        // Indicate un-selected state to screen reader
16358        this.controlText('');
16359      }
16360    }
16361  };
16362
16363  return MenuItem;
16364}(ClickableComponent);
16365
16366Component.registerComponent('MenuItem', MenuItem);
16367
16368/**
16369 * @file text-track-menu-item.js
16370 */
16371/**
16372 * The specific menu item type for selecting a language within a text track kind
16373 *
16374 * @extends MenuItem
16375 */
16376
16377var TextTrackMenuItem = function (_MenuItem) {
16378  inherits(TextTrackMenuItem, _MenuItem);
16379
16380  /**
16381   * Creates an instance of this class.
16382   *
16383   * @param {Player} player
16384   *        The `Player` that this class should be attached to.
16385   *
16386   * @param {Object} [options]
16387   *        The key/value store of player options.
16388   */
16389  function TextTrackMenuItem(player, options) {
16390    classCallCheck(this, TextTrackMenuItem);
16391
16392    var track = options.track;
16393    var tracks = player.textTracks();
16394
16395    // Modify options for parent MenuItem class's init.
16396    options.label = track.label || track.language || 'Unknown';
16397    options.selected = track.mode === 'showing';
16398
16399    var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
16400
16401    _this.track = track;
16402    var changeHandler = function changeHandler() {
16403      for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
16404        args[_key] = arguments[_key];
16405      }
16406
16407      _this.handleTracksChange.apply(_this, args);
16408    };
16409    var selectedLanguageChangeHandler = function selectedLanguageChangeHandler() {
16410      for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
16411        args[_key2] = arguments[_key2];
16412      }
16413
16414      _this.handleSelectedLanguageChange.apply(_this, args);
16415    };
16416
16417    player.on(['loadstart', 'texttrackchange'], changeHandler);
16418    tracks.addEventListener('change', changeHandler);
16419    tracks.addEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
16420    _this.on('dispose', function () {
16421      player.off(['loadstart', 'texttrackchange'], changeHandler);
16422      tracks.removeEventListener('change', changeHandler);
16423      tracks.removeEventListener('selectedlanguagechange', selectedLanguageChangeHandler);
16424    });
16425
16426    // iOS7 doesn't dispatch change events to TextTrackLists when an
16427    // associated track's mode changes. Without something like
16428    // Object.observe() (also not present on iOS7), it's not
16429    // possible to detect changes to the mode attribute and polyfill
16430    // the change event. As a poor substitute, we manually dispatch
16431    // change events whenever the controls modify the mode.
16432    if (tracks.onchange === undefined) {
16433      var event = void 0;
16434
16435      _this.on(['tap', 'click'], function () {
16436        if (_typeof(window_1.Event) !== 'object') {
16437          // Android 2.3 throws an Illegal Constructor error for window.Event
16438          try {
16439            event = new window_1.Event('change');
16440          } catch (err) {
16441            // continue regardless of error
16442          }
16443        }
16444
16445        if (!event) {
16446          event = document_1.createEvent('Event');
16447          event.initEvent('change', true, true);
16448        }
16449
16450        tracks.dispatchEvent(event);
16451      });
16452    }
16453
16454    // set the default state based on current tracks
16455    _this.handleTracksChange();
16456    return _this;
16457  }
16458
16459  /**
16460   * This gets called when an `TextTrackMenuItem` is "clicked". See
16461   * {@link ClickableComponent} for more detailed information on what a click can be.
16462   *
16463   * @param {EventTarget~Event} event
16464   *        The `keydown`, `tap`, or `click` event that caused this function to be
16465   *        called.
16466   *
16467   * @listens tap
16468   * @listens click
16469   */
16470
16471
16472  TextTrackMenuItem.prototype.handleClick = function handleClick(event) {
16473    var kind = this.track.kind;
16474    var kinds = this.track.kinds;
16475    var tracks = this.player_.textTracks();
16476
16477    if (!kinds) {
16478      kinds = [kind];
16479    }
16480
16481    _MenuItem.prototype.handleClick.call(this, event);
16482
16483    if (!tracks) {
16484      return;
16485    }
16486
16487    for (var i = 0; i < tracks.length; i++) {
16488      var track = tracks[i];
16489
16490      if (track === this.track && kinds.indexOf(track.kind) > -1) {
16491        if (track.mode !== 'showing') {
16492          track.mode = 'showing';
16493        }
16494      } else if (track.mode !== 'disabled') {
16495        track.mode = 'disabled';
16496      }
16497    }
16498  };
16499
16500  /**
16501   * Handle text track list change
16502   *
16503   * @param {EventTarget~Event} event
16504   *        The `change` event that caused this function to be called.
16505   *
16506   * @listens TextTrackList#change
16507   */
16508
16509
16510  TextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
16511    this.selected(this.track.mode === 'showing');
16512  };
16513
16514  TextTrackMenuItem.prototype.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
16515    if (this.track.mode === 'showing') {
16516      var selectedLanguage = this.player_.cache_.selectedLanguage;
16517
16518      // Don't replace the kind of track across the same language
16519      if (selectedLanguage && selectedLanguage.enabled && selectedLanguage.language === this.track.language && selectedLanguage.kind !== this.track.kind) {
16520        return;
16521      }
16522
16523      this.player_.cache_.selectedLanguage = {
16524        enabled: true,
16525        language: this.track.language,
16526        kind: this.track.kind
16527      };
16528    }
16529  };
16530
16531  TextTrackMenuItem.prototype.dispose = function dispose() {
16532    // remove reference to track object on dispose
16533    this.track = null;
16534
16535    _MenuItem.prototype.dispose.call(this);
16536  };
16537
16538  return TextTrackMenuItem;
16539}(MenuItem);
16540
16541Component.registerComponent('TextTrackMenuItem', TextTrackMenuItem);
16542
16543/**
16544 * @file off-text-track-menu-item.js
16545 */
16546/**
16547 * A special menu item for turning of a specific type of text track
16548 *
16549 * @extends TextTrackMenuItem
16550 */
16551
16552var OffTextTrackMenuItem = function (_TextTrackMenuItem) {
16553  inherits(OffTextTrackMenuItem, _TextTrackMenuItem);
16554
16555  /**
16556   * Creates an instance of this class.
16557   *
16558   * @param {Player} player
16559   *        The `Player` that this class should be attached to.
16560   *
16561   * @param {Object} [options]
16562   *        The key/value store of player options.
16563   */
16564  function OffTextTrackMenuItem(player, options) {
16565    classCallCheck(this, OffTextTrackMenuItem);
16566
16567    // Create pseudo track info
16568    // Requires options['kind']
16569    options.track = {
16570      player: player,
16571      kind: options.kind,
16572      kinds: options.kinds,
16573      'default': false,
16574      mode: 'disabled'
16575    };
16576
16577    if (!options.kinds) {
16578      options.kinds = [options.kind];
16579    }
16580
16581    if (options.label) {
16582      options.track.label = options.label;
16583    } else {
16584      options.track.label = options.kinds.join(' and ') + ' off';
16585    }
16586
16587    // MenuItem is selectable
16588    options.selectable = true;
16589
16590    return possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options));
16591  }
16592
16593  /**
16594   * Handle text track change
16595   *
16596   * @param {EventTarget~Event} event
16597   *        The event that caused this function to run
16598   */
16599
16600
16601  OffTextTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
16602    var tracks = this.player().textTracks();
16603    var selected = true;
16604
16605    for (var i = 0, l = tracks.length; i < l; i++) {
16606      var track = tracks[i];
16607
16608      if (this.options_.kinds.indexOf(track.kind) > -1 && track.mode === 'showing') {
16609        selected = false;
16610        break;
16611      }
16612    }
16613
16614    this.selected(selected);
16615  };
16616
16617  OffTextTrackMenuItem.prototype.handleSelectedLanguageChange = function handleSelectedLanguageChange(event) {
16618    var tracks = this.player().textTracks();
16619    var allHidden = true;
16620
16621    for (var i = 0, l = tracks.length; i < l; i++) {
16622      var track = tracks[i];
16623
16624      if (['captions', 'descriptions', 'subtitles'].indexOf(track.kind) > -1 && track.mode === 'showing') {
16625        allHidden = false;
16626        break;
16627      }
16628    }
16629
16630    if (allHidden) {
16631      this.player_.cache_.selectedLanguage = {
16632        enabled: false
16633      };
16634    }
16635  };
16636
16637  return OffTextTrackMenuItem;
16638}(TextTrackMenuItem);
16639
16640Component.registerComponent('OffTextTrackMenuItem', OffTextTrackMenuItem);
16641
16642/**
16643 * @file text-track-button.js
16644 */
16645/**
16646 * The base class for buttons that toggle specific text track types (e.g. subtitles)
16647 *
16648 * @extends MenuButton
16649 */
16650
16651var TextTrackButton = function (_TrackButton) {
16652  inherits(TextTrackButton, _TrackButton);
16653
16654  /**
16655   * Creates an instance of this class.
16656   *
16657   * @param {Player} player
16658   *        The `Player` that this class should be attached to.
16659   *
16660   * @param {Object} [options={}]
16661   *        The key/value store of player options.
16662   */
16663  function TextTrackButton(player) {
16664    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
16665    classCallCheck(this, TextTrackButton);
16666
16667    options.tracks = player.textTracks();
16668
16669    return possibleConstructorReturn(this, _TrackButton.call(this, player, options));
16670  }
16671
16672  /**
16673   * Create a menu item for each text track
16674   *
16675   * @param {TextTrackMenuItem[]} [items=[]]
16676   *        Existing array of items to use during creation
16677   *
16678   * @return {TextTrackMenuItem[]}
16679   *         Array of menu items that were created
16680   */
16681
16682
16683  TextTrackButton.prototype.createItems = function createItems() {
16684    var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
16685    var TrackMenuItem = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : TextTrackMenuItem;
16686
16687
16688    // Label is an overide for the [track] off label
16689    // USed to localise captions/subtitles
16690    var label = void 0;
16691
16692    if (this.label_) {
16693      label = this.label_ + ' off';
16694    }
16695    // Add an OFF menu item to turn all tracks off
16696    items.push(new OffTextTrackMenuItem(this.player_, {
16697      kinds: this.kinds_,
16698      kind: this.kind_,
16699      label: label
16700    }));
16701
16702    this.hideThreshold_ += 1;
16703
16704    var tracks = this.player_.textTracks();
16705
16706    if (!Array.isArray(this.kinds_)) {
16707      this.kinds_ = [this.kind_];
16708    }
16709
16710    for (var i = 0; i < tracks.length; i++) {
16711      var track = tracks[i];
16712
16713      // only add tracks that are of an appropriate kind and have a label
16714      if (this.kinds_.indexOf(track.kind) > -1) {
16715
16716        var item = new TrackMenuItem(this.player_, {
16717          track: track,
16718          // MenuItem is selectable
16719          selectable: true
16720        });
16721
16722        item.addClass('vjs-' + track.kind + '-menu-item');
16723        items.push(item);
16724      }
16725    }
16726
16727    return items;
16728  };
16729
16730  return TextTrackButton;
16731}(TrackButton);
16732
16733Component.registerComponent('TextTrackButton', TextTrackButton);
16734
16735/**
16736 * @file chapters-track-menu-item.js
16737 */
16738/**
16739 * The chapter track menu item
16740 *
16741 * @extends MenuItem
16742 */
16743
16744var ChaptersTrackMenuItem = function (_MenuItem) {
16745  inherits(ChaptersTrackMenuItem, _MenuItem);
16746
16747  /**
16748   * Creates an instance of this class.
16749   *
16750   * @param {Player} player
16751   *        The `Player` that this class should be attached to.
16752   *
16753   * @param {Object} [options]
16754   *        The key/value store of player options.
16755   */
16756  function ChaptersTrackMenuItem(player, options) {
16757    classCallCheck(this, ChaptersTrackMenuItem);
16758
16759    var track = options.track;
16760    var cue = options.cue;
16761    var currentTime = player.currentTime();
16762
16763    // Modify options for parent MenuItem class's init.
16764    options.selectable = true;
16765    options.label = cue.text;
16766    options.selected = cue.startTime <= currentTime && currentTime < cue.endTime;
16767
16768    var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
16769
16770    _this.track = track;
16771    _this.cue = cue;
16772    track.addEventListener('cuechange', bind(_this, _this.update));
16773    return _this;
16774  }
16775
16776  /**
16777   * This gets called when an `ChaptersTrackMenuItem` is "clicked". See
16778   * {@link ClickableComponent} for more detailed information on what a click can be.
16779   *
16780   * @param {EventTarget~Event} [event]
16781   *        The `keydown`, `tap`, or `click` event that caused this function to be
16782   *        called.
16783   *
16784   * @listens tap
16785   * @listens click
16786   */
16787
16788
16789  ChaptersTrackMenuItem.prototype.handleClick = function handleClick(event) {
16790    _MenuItem.prototype.handleClick.call(this);
16791    this.player_.currentTime(this.cue.startTime);
16792    this.update(this.cue.startTime);
16793  };
16794
16795  /**
16796   * Update chapter menu item
16797   *
16798   * @param {EventTarget~Event} [event]
16799   *        The `cuechange` event that caused this function to run.
16800   *
16801   * @listens TextTrack#cuechange
16802   */
16803
16804
16805  ChaptersTrackMenuItem.prototype.update = function update(event) {
16806    var cue = this.cue;
16807    var currentTime = this.player_.currentTime();
16808
16809    // vjs.log(currentTime, cue.startTime);
16810    this.selected(cue.startTime <= currentTime && currentTime < cue.endTime);
16811  };
16812
16813  return ChaptersTrackMenuItem;
16814}(MenuItem);
16815
16816Component.registerComponent('ChaptersTrackMenuItem', ChaptersTrackMenuItem);
16817
16818/**
16819 * @file chapters-button.js
16820 */
16821/**
16822 * The button component for toggling and selecting chapters
16823 * Chapters act much differently than other text tracks
16824 * Cues are navigation vs. other tracks of alternative languages
16825 *
16826 * @extends TextTrackButton
16827 */
16828
16829var ChaptersButton = function (_TextTrackButton) {
16830  inherits(ChaptersButton, _TextTrackButton);
16831
16832  /**
16833   * Creates an instance of this class.
16834   *
16835   * @param {Player} player
16836   *        The `Player` that this class should be attached to.
16837   *
16838   * @param {Object} [options]
16839   *        The key/value store of player options.
16840   *
16841   * @param {Component~ReadyCallback} [ready]
16842   *        The function to call when this function is ready.
16843   */
16844  function ChaptersButton(player, options, ready) {
16845    classCallCheck(this, ChaptersButton);
16846    return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
16847  }
16848
16849  /**
16850   * Builds the default DOM `className`.
16851   *
16852   * @return {string}
16853   *         The DOM `className` for this object.
16854   */
16855
16856
16857  ChaptersButton.prototype.buildCSSClass = function buildCSSClass() {
16858    return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
16859  };
16860
16861  ChaptersButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
16862    return 'vjs-chapters-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
16863  };
16864
16865  /**
16866   * Update the menu based on the current state of its items.
16867   *
16868   * @param {EventTarget~Event} [event]
16869   *        An event that triggered this function to run.
16870   *
16871   * @listens TextTrackList#addtrack
16872   * @listens TextTrackList#removetrack
16873   * @listens TextTrackList#change
16874   */
16875
16876
16877  ChaptersButton.prototype.update = function update(event) {
16878    if (!this.track_ || event && (event.type === 'addtrack' || event.type === 'removetrack')) {
16879      this.setTrack(this.findChaptersTrack());
16880    }
16881    _TextTrackButton.prototype.update.call(this);
16882  };
16883
16884  /**
16885   * Set the currently selected track for the chapters button.
16886   *
16887   * @param {TextTrack} track
16888   *        The new track to select. Nothing will change if this is the currently selected
16889   *        track.
16890   */
16891
16892
16893  ChaptersButton.prototype.setTrack = function setTrack(track) {
16894    if (this.track_ === track) {
16895      return;
16896    }
16897
16898    if (!this.updateHandler_) {
16899      this.updateHandler_ = this.update.bind(this);
16900    }
16901
16902    // here this.track_ refers to the old track instance
16903    if (this.track_) {
16904      var remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
16905
16906      if (remoteTextTrackEl) {
16907        remoteTextTrackEl.removeEventListener('load', this.updateHandler_);
16908      }
16909
16910      this.track_ = null;
16911    }
16912
16913    this.track_ = track;
16914
16915    // here this.track_ refers to the new track instance
16916    if (this.track_) {
16917      this.track_.mode = 'hidden';
16918
16919      var _remoteTextTrackEl = this.player_.remoteTextTrackEls().getTrackElementByTrack_(this.track_);
16920
16921      if (_remoteTextTrackEl) {
16922        _remoteTextTrackEl.addEventListener('load', this.updateHandler_);
16923      }
16924    }
16925  };
16926
16927  /**
16928   * Find the track object that is currently in use by this ChaptersButton
16929   *
16930   * @return {TextTrack|undefined}
16931   *         The current track or undefined if none was found.
16932   */
16933
16934
16935  ChaptersButton.prototype.findChaptersTrack = function findChaptersTrack() {
16936    var tracks = this.player_.textTracks() || [];
16937
16938    for (var i = tracks.length - 1; i >= 0; i--) {
16939      // We will always choose the last track as our chaptersTrack
16940      var track = tracks[i];
16941
16942      if (track.kind === this.kind_) {
16943        return track;
16944      }
16945    }
16946  };
16947
16948  /**
16949   * Get the caption for the ChaptersButton based on the track label. This will also
16950   * use the current tracks localized kind as a fallback if a label does not exist.
16951   *
16952   * @return {string}
16953   *         The tracks current label or the localized track kind.
16954   */
16955
16956
16957  ChaptersButton.prototype.getMenuCaption = function getMenuCaption() {
16958    if (this.track_ && this.track_.label) {
16959      return this.track_.label;
16960    }
16961    return this.localize(toTitleCase(this.kind_));
16962  };
16963
16964  /**
16965   * Create menu from chapter track
16966   *
16967   * @return {Menu}
16968   *         New menu for the chapter buttons
16969   */
16970
16971
16972  ChaptersButton.prototype.createMenu = function createMenu() {
16973    this.options_.title = this.getMenuCaption();
16974    return _TextTrackButton.prototype.createMenu.call(this);
16975  };
16976
16977  /**
16978   * Create a menu item for each text track
16979   *
16980   * @return {TextTrackMenuItem[]}
16981   *         Array of menu items
16982   */
16983
16984
16985  ChaptersButton.prototype.createItems = function createItems() {
16986    var items = [];
16987
16988    if (!this.track_) {
16989      return items;
16990    }
16991
16992    var cues = this.track_.cues;
16993
16994    if (!cues) {
16995      return items;
16996    }
16997
16998    for (var i = 0, l = cues.length; i < l; i++) {
16999      var cue = cues[i];
17000      var mi = new ChaptersTrackMenuItem(this.player_, { track: this.track_, cue: cue });
17001
17002      items.push(mi);
17003    }
17004
17005    return items;
17006  };
17007
17008  return ChaptersButton;
17009}(TextTrackButton);
17010
17011/**
17012 * `kind` of TextTrack to look for to associate it with this menu.
17013 *
17014 * @type {string}
17015 * @private
17016 */
17017
17018
17019ChaptersButton.prototype.kind_ = 'chapters';
17020
17021/**
17022 * The text that should display over the `ChaptersButton`s controls. Added for localization.
17023 *
17024 * @type {string}
17025 * @private
17026 */
17027ChaptersButton.prototype.controlText_ = 'Chapters';
17028
17029Component.registerComponent('ChaptersButton', ChaptersButton);
17030
17031/**
17032 * @file descriptions-button.js
17033 */
17034/**
17035 * The button component for toggling and selecting descriptions
17036 *
17037 * @extends TextTrackButton
17038 */
17039
17040var DescriptionsButton = function (_TextTrackButton) {
17041  inherits(DescriptionsButton, _TextTrackButton);
17042
17043  /**
17044   * Creates an instance of this class.
17045   *
17046   * @param {Player} player
17047   *        The `Player` that this class should be attached to.
17048   *
17049   * @param {Object} [options]
17050   *        The key/value store of player options.
17051   *
17052   * @param {Component~ReadyCallback} [ready]
17053   *        The function to call when this component is ready.
17054   */
17055  function DescriptionsButton(player, options, ready) {
17056    classCallCheck(this, DescriptionsButton);
17057
17058    var _this = possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
17059
17060    var tracks = player.textTracks();
17061    var changeHandler = bind(_this, _this.handleTracksChange);
17062
17063    tracks.addEventListener('change', changeHandler);
17064    _this.on('dispose', function () {
17065      tracks.removeEventListener('change', changeHandler);
17066    });
17067    return _this;
17068  }
17069
17070  /**
17071   * Handle text track change
17072   *
17073   * @param {EventTarget~Event} event
17074   *        The event that caused this function to run
17075   *
17076   * @listens TextTrackList#change
17077   */
17078
17079
17080  DescriptionsButton.prototype.handleTracksChange = function handleTracksChange(event) {
17081    var tracks = this.player().textTracks();
17082    var disabled = false;
17083
17084    // Check whether a track of a different kind is showing
17085    for (var i = 0, l = tracks.length; i < l; i++) {
17086      var track = tracks[i];
17087
17088      if (track.kind !== this.kind_ && track.mode === 'showing') {
17089        disabled = true;
17090        break;
17091      }
17092    }
17093
17094    // If another track is showing, disable this menu button
17095    if (disabled) {
17096      this.disable();
17097    } else {
17098      this.enable();
17099    }
17100  };
17101
17102  /**
17103   * Builds the default DOM `className`.
17104   *
17105   * @return {string}
17106   *         The DOM `className` for this object.
17107   */
17108
17109
17110  DescriptionsButton.prototype.buildCSSClass = function buildCSSClass() {
17111    return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
17112  };
17113
17114  DescriptionsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
17115    return 'vjs-descriptions-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
17116  };
17117
17118  return DescriptionsButton;
17119}(TextTrackButton);
17120
17121/**
17122 * `kind` of TextTrack to look for to associate it with this menu.
17123 *
17124 * @type {string}
17125 * @private
17126 */
17127
17128
17129DescriptionsButton.prototype.kind_ = 'descriptions';
17130
17131/**
17132 * The text that should display over the `DescriptionsButton`s controls. Added for localization.
17133 *
17134 * @type {string}
17135 * @private
17136 */
17137DescriptionsButton.prototype.controlText_ = 'Descriptions';
17138
17139Component.registerComponent('DescriptionsButton', DescriptionsButton);
17140
17141/**
17142 * @file subtitles-button.js
17143 */
17144/**
17145 * The button component for toggling and selecting subtitles
17146 *
17147 * @extends TextTrackButton
17148 */
17149
17150var SubtitlesButton = function (_TextTrackButton) {
17151  inherits(SubtitlesButton, _TextTrackButton);
17152
17153  /**
17154   * Creates an instance of this class.
17155   *
17156   * @param {Player} player
17157   *        The `Player` that this class should be attached to.
17158   *
17159   * @param {Object} [options]
17160   *        The key/value store of player options.
17161   *
17162   * @param {Component~ReadyCallback} [ready]
17163   *        The function to call when this component is ready.
17164   */
17165  function SubtitlesButton(player, options, ready) {
17166    classCallCheck(this, SubtitlesButton);
17167    return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
17168  }
17169
17170  /**
17171   * Builds the default DOM `className`.
17172   *
17173   * @return {string}
17174   *         The DOM `className` for this object.
17175   */
17176
17177
17178  SubtitlesButton.prototype.buildCSSClass = function buildCSSClass() {
17179    return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
17180  };
17181
17182  SubtitlesButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
17183    return 'vjs-subtitles-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
17184  };
17185
17186  return SubtitlesButton;
17187}(TextTrackButton);
17188
17189/**
17190 * `kind` of TextTrack to look for to associate it with this menu.
17191 *
17192 * @type {string}
17193 * @private
17194 */
17195
17196
17197SubtitlesButton.prototype.kind_ = 'subtitles';
17198
17199/**
17200 * The text that should display over the `SubtitlesButton`s controls. Added for localization.
17201 *
17202 * @type {string}
17203 * @private
17204 */
17205SubtitlesButton.prototype.controlText_ = 'Subtitles';
17206
17207Component.registerComponent('SubtitlesButton', SubtitlesButton);
17208
17209/**
17210 * @file caption-settings-menu-item.js
17211 */
17212/**
17213 * The menu item for caption track settings menu
17214 *
17215 * @extends TextTrackMenuItem
17216 */
17217
17218var CaptionSettingsMenuItem = function (_TextTrackMenuItem) {
17219  inherits(CaptionSettingsMenuItem, _TextTrackMenuItem);
17220
17221  /**
17222   * Creates an instance of this class.
17223   *
17224   * @param {Player} player
17225   *        The `Player` that this class should be attached to.
17226   *
17227   * @param {Object} [options]
17228   *        The key/value store of player options.
17229   */
17230  function CaptionSettingsMenuItem(player, options) {
17231    classCallCheck(this, CaptionSettingsMenuItem);
17232
17233    options.track = {
17234      player: player,
17235      kind: options.kind,
17236      label: options.kind + ' settings',
17237      selectable: false,
17238      'default': false,
17239      mode: 'disabled'
17240    };
17241
17242    // CaptionSettingsMenuItem has no concept of 'selected'
17243    options.selectable = false;
17244
17245    options.name = 'CaptionSettingsMenuItem';
17246
17247    var _this = possibleConstructorReturn(this, _TextTrackMenuItem.call(this, player, options));
17248
17249    _this.addClass('vjs-texttrack-settings');
17250    _this.controlText(', opens ' + options.kind + ' settings dialog');
17251    return _this;
17252  }
17253
17254  /**
17255   * This gets called when an `CaptionSettingsMenuItem` is "clicked". See
17256   * {@link ClickableComponent} for more detailed information on what a click can be.
17257   *
17258   * @param {EventTarget~Event} [event]
17259   *        The `keydown`, `tap`, or `click` event that caused this function to be
17260   *        called.
17261   *
17262   * @listens tap
17263   * @listens click
17264   */
17265
17266
17267  CaptionSettingsMenuItem.prototype.handleClick = function handleClick(event) {
17268    this.player().getChild('textTrackSettings').open();
17269  };
17270
17271  return CaptionSettingsMenuItem;
17272}(TextTrackMenuItem);
17273
17274Component.registerComponent('CaptionSettingsMenuItem', CaptionSettingsMenuItem);
17275
17276/**
17277 * @file captions-button.js
17278 */
17279/**
17280 * The button component for toggling and selecting captions
17281 *
17282 * @extends TextTrackButton
17283 */
17284
17285var CaptionsButton = function (_TextTrackButton) {
17286  inherits(CaptionsButton, _TextTrackButton);
17287
17288  /**
17289   * Creates an instance of this class.
17290   *
17291   * @param {Player} player
17292   *        The `Player` that this class should be attached to.
17293   *
17294   * @param {Object} [options]
17295   *        The key/value store of player options.
17296   *
17297   * @param {Component~ReadyCallback} [ready]
17298   *        The function to call when this component is ready.
17299   */
17300  function CaptionsButton(player, options, ready) {
17301    classCallCheck(this, CaptionsButton);
17302    return possibleConstructorReturn(this, _TextTrackButton.call(this, player, options, ready));
17303  }
17304
17305  /**
17306   * Builds the default DOM `className`.
17307   *
17308   * @return {string}
17309   *         The DOM `className` for this object.
17310   */
17311
17312
17313  CaptionsButton.prototype.buildCSSClass = function buildCSSClass() {
17314    return 'vjs-captions-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
17315  };
17316
17317  CaptionsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
17318    return 'vjs-captions-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
17319  };
17320
17321  /**
17322   * Create caption menu items
17323   *
17324   * @return {CaptionSettingsMenuItem[]}
17325   *         The array of current menu items.
17326   */
17327
17328
17329  CaptionsButton.prototype.createItems = function createItems() {
17330    var items = [];
17331
17332    if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks)) {
17333      items.push(new CaptionSettingsMenuItem(this.player_, { kind: this.kind_ }));
17334
17335      this.hideThreshold_ += 1;
17336    }
17337
17338    return _TextTrackButton.prototype.createItems.call(this, items);
17339  };
17340
17341  return CaptionsButton;
17342}(TextTrackButton);
17343
17344/**
17345 * `kind` of TextTrack to look for to associate it with this menu.
17346 *
17347 * @type {string}
17348 * @private
17349 */
17350
17351
17352CaptionsButton.prototype.kind_ = 'captions';
17353
17354/**
17355 * The text that should display over the `CaptionsButton`s controls. Added for localization.
17356 *
17357 * @type {string}
17358 * @private
17359 */
17360CaptionsButton.prototype.controlText_ = 'Captions';
17361
17362Component.registerComponent('CaptionsButton', CaptionsButton);
17363
17364/**
17365 * @file subs-caps-menu-item.js
17366 */
17367/**
17368 * SubsCapsMenuItem has an [cc] icon to distinguish captions from subtitles
17369 * in the SubsCapsMenu.
17370 *
17371 * @extends TextTrackMenuItem
17372 */
17373
17374var SubsCapsMenuItem = function (_TextTrackMenuItem) {
17375  inherits(SubsCapsMenuItem, _TextTrackMenuItem);
17376
17377  function SubsCapsMenuItem() {
17378    classCallCheck(this, SubsCapsMenuItem);
17379    return possibleConstructorReturn(this, _TextTrackMenuItem.apply(this, arguments));
17380  }
17381
17382  SubsCapsMenuItem.prototype.createEl = function createEl(type, props, attrs) {
17383    var innerHTML = '<span class="vjs-menu-item-text">' + this.localize(this.options_.label);
17384
17385    if (this.options_.track.kind === 'captions') {
17386      innerHTML += '\n        <span aria-hidden="true" class="vjs-icon-placeholder"></span>\n        <span class="vjs-control-text"> ' + this.localize('Captions') + '</span>\n      ';
17387    }
17388
17389    innerHTML += '</span>';
17390
17391    var el = _TextTrackMenuItem.prototype.createEl.call(this, type, assign({
17392      innerHTML: innerHTML
17393    }, props), attrs);
17394
17395    return el;
17396  };
17397
17398  return SubsCapsMenuItem;
17399}(TextTrackMenuItem);
17400
17401Component.registerComponent('SubsCapsMenuItem', SubsCapsMenuItem);
17402
17403/**
17404 * @file sub-caps-button.js
17405 */
17406/**
17407 * The button component for toggling and selecting captions and/or subtitles
17408 *
17409 * @extends TextTrackButton
17410 */
17411
17412var SubsCapsButton = function (_TextTrackButton) {
17413  inherits(SubsCapsButton, _TextTrackButton);
17414
17415  function SubsCapsButton(player) {
17416    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
17417    classCallCheck(this, SubsCapsButton);
17418
17419    // Although North America uses "captions" in most cases for
17420    // "captions and subtitles" other locales use "subtitles"
17421    var _this = possibleConstructorReturn(this, _TextTrackButton.call(this, player, options));
17422
17423    _this.label_ = 'subtitles';
17424    if (['en', 'en-us', 'en-ca', 'fr-ca'].indexOf(_this.player_.language_) > -1) {
17425      _this.label_ = 'captions';
17426    }
17427    _this.menuButton_.controlText(toTitleCase(_this.label_));
17428    return _this;
17429  }
17430
17431  /**
17432   * Builds the default DOM `className`.
17433   *
17434   * @return {string}
17435   *         The DOM `className` for this object.
17436   */
17437
17438
17439  SubsCapsButton.prototype.buildCSSClass = function buildCSSClass() {
17440    return 'vjs-subs-caps-button ' + _TextTrackButton.prototype.buildCSSClass.call(this);
17441  };
17442
17443  SubsCapsButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
17444    return 'vjs-subs-caps-button ' + _TextTrackButton.prototype.buildWrapperCSSClass.call(this);
17445  };
17446
17447  /**
17448   * Create caption/subtitles menu items
17449   *
17450   * @return {CaptionSettingsMenuItem[]}
17451   *         The array of current menu items.
17452   */
17453
17454
17455  SubsCapsButton.prototype.createItems = function createItems() {
17456    var items = [];
17457
17458    if (!(this.player().tech_ && this.player().tech_.featuresNativeTextTracks)) {
17459      items.push(new CaptionSettingsMenuItem(this.player_, { kind: this.label_ }));
17460
17461      this.hideThreshold_ += 1;
17462    }
17463
17464    items = _TextTrackButton.prototype.createItems.call(this, items, SubsCapsMenuItem);
17465    return items;
17466  };
17467
17468  return SubsCapsButton;
17469}(TextTrackButton);
17470
17471/**
17472 * `kind`s of TextTrack to look for to associate it with this menu.
17473 *
17474 * @type {array}
17475 * @private
17476 */
17477
17478
17479SubsCapsButton.prototype.kinds_ = ['captions', 'subtitles'];
17480
17481/**
17482 * The text that should display over the `SubsCapsButton`s controls.
17483 *
17484 *
17485 * @type {string}
17486 * @private
17487 */
17488SubsCapsButton.prototype.controlText_ = 'Subtitles';
17489
17490Component.registerComponent('SubsCapsButton', SubsCapsButton);
17491
17492/**
17493 * @file audio-track-menu-item.js
17494 */
17495/**
17496 * An {@link AudioTrack} {@link MenuItem}
17497 *
17498 * @extends MenuItem
17499 */
17500
17501var AudioTrackMenuItem = function (_MenuItem) {
17502  inherits(AudioTrackMenuItem, _MenuItem);
17503
17504  /**
17505   * Creates an instance of this class.
17506   *
17507   * @param {Player} player
17508   *        The `Player` that this class should be attached to.
17509   *
17510   * @param {Object} [options]
17511   *        The key/value store of player options.
17512   */
17513  function AudioTrackMenuItem(player, options) {
17514    classCallCheck(this, AudioTrackMenuItem);
17515
17516    var track = options.track;
17517    var tracks = player.audioTracks();
17518
17519    // Modify options for parent MenuItem class's init.
17520    options.label = track.label || track.language || 'Unknown';
17521    options.selected = track.enabled;
17522
17523    var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
17524
17525    _this.track = track;
17526
17527    var changeHandler = function changeHandler() {
17528      for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
17529        args[_key] = arguments[_key];
17530      }
17531
17532      _this.handleTracksChange.apply(_this, args);
17533    };
17534
17535    tracks.addEventListener('change', changeHandler);
17536    _this.on('dispose', function () {
17537      tracks.removeEventListener('change', changeHandler);
17538    });
17539    return _this;
17540  }
17541
17542  /**
17543   * This gets called when an `AudioTrackMenuItem is "clicked". See {@link ClickableComponent}
17544   * for more detailed information on what a click can be.
17545   *
17546   * @param {EventTarget~Event} [event]
17547   *        The `keydown`, `tap`, or `click` event that caused this function to be
17548   *        called.
17549   *
17550   * @listens tap
17551   * @listens click
17552   */
17553
17554
17555  AudioTrackMenuItem.prototype.handleClick = function handleClick(event) {
17556    var tracks = this.player_.audioTracks();
17557
17558    _MenuItem.prototype.handleClick.call(this, event);
17559
17560    for (var i = 0; i < tracks.length; i++) {
17561      var track = tracks[i];
17562
17563      track.enabled = track === this.track;
17564    }
17565  };
17566
17567  /**
17568   * Handle any {@link AudioTrack} change.
17569   *
17570   * @param {EventTarget~Event} [event]
17571   *        The {@link AudioTrackList#change} event that caused this to run.
17572   *
17573   * @listens AudioTrackList#change
17574   */
17575
17576
17577  AudioTrackMenuItem.prototype.handleTracksChange = function handleTracksChange(event) {
17578    this.selected(this.track.enabled);
17579  };
17580
17581  return AudioTrackMenuItem;
17582}(MenuItem);
17583
17584Component.registerComponent('AudioTrackMenuItem', AudioTrackMenuItem);
17585
17586/**
17587 * @file audio-track-button.js
17588 */
17589/**
17590 * The base class for buttons that toggle specific {@link AudioTrack} types.
17591 *
17592 * @extends TrackButton
17593 */
17594
17595var AudioTrackButton = function (_TrackButton) {
17596  inherits(AudioTrackButton, _TrackButton);
17597
17598  /**
17599   * Creates an instance of this class.
17600   *
17601   * @param {Player} player
17602   *        The `Player` that this class should be attached to.
17603   *
17604   * @param {Object} [options={}]
17605   *        The key/value store of player options.
17606   */
17607  function AudioTrackButton(player) {
17608    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
17609    classCallCheck(this, AudioTrackButton);
17610
17611    options.tracks = player.audioTracks();
17612
17613    return possibleConstructorReturn(this, _TrackButton.call(this, player, options));
17614  }
17615
17616  /**
17617   * Builds the default DOM `className`.
17618   *
17619   * @return {string}
17620   *         The DOM `className` for this object.
17621   */
17622
17623
17624  AudioTrackButton.prototype.buildCSSClass = function buildCSSClass() {
17625    return 'vjs-audio-button ' + _TrackButton.prototype.buildCSSClass.call(this);
17626  };
17627
17628  AudioTrackButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
17629    return 'vjs-audio-button ' + _TrackButton.prototype.buildWrapperCSSClass.call(this);
17630  };
17631
17632  /**
17633   * Create a menu item for each audio track
17634   *
17635   * @param {AudioTrackMenuItem[]} [items=[]]
17636   *        An array of existing menu items to use.
17637   *
17638   * @return {AudioTrackMenuItem[]}
17639   *         An array of menu items
17640   */
17641
17642
17643  AudioTrackButton.prototype.createItems = function createItems() {
17644    var items = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
17645
17646    // if there's only one audio track, there no point in showing it
17647    this.hideThreshold_ = 1;
17648
17649    var tracks = this.player_.audioTracks();
17650
17651    for (var i = 0; i < tracks.length; i++) {
17652      var track = tracks[i];
17653
17654      items.push(new AudioTrackMenuItem(this.player_, {
17655        track: track,
17656        // MenuItem is selectable
17657        selectable: true
17658      }));
17659    }
17660
17661    return items;
17662  };
17663
17664  return AudioTrackButton;
17665}(TrackButton);
17666
17667/**
17668 * The text that should display over the `AudioTrackButton`s controls. Added for localization.
17669 *
17670 * @type {string}
17671 * @private
17672 */
17673
17674
17675AudioTrackButton.prototype.controlText_ = 'Audio Track';
17676Component.registerComponent('AudioTrackButton', AudioTrackButton);
17677
17678/**
17679 * @file playback-rate-menu-item.js
17680 */
17681/**
17682 * The specific menu item type for selecting a playback rate.
17683 *
17684 * @extends MenuItem
17685 */
17686
17687var PlaybackRateMenuItem = function (_MenuItem) {
17688  inherits(PlaybackRateMenuItem, _MenuItem);
17689
17690  /**
17691   * Creates an instance of this class.
17692   *
17693   * @param {Player} player
17694   *        The `Player` that this class should be attached to.
17695   *
17696   * @param {Object} [options]
17697   *        The key/value store of player options.
17698   */
17699  function PlaybackRateMenuItem(player, options) {
17700    classCallCheck(this, PlaybackRateMenuItem);
17701
17702    var label = options.rate;
17703    var rate = parseFloat(label, 10);
17704
17705    // Modify options for parent MenuItem class's init.
17706    options.label = label;
17707    options.selected = rate === 1;
17708    options.selectable = true;
17709
17710    var _this = possibleConstructorReturn(this, _MenuItem.call(this, player, options));
17711
17712    _this.label = label;
17713    _this.rate = rate;
17714
17715    _this.on(player, 'ratechange', _this.update);
17716    return _this;
17717  }
17718
17719  /**
17720   * This gets called when an `PlaybackRateMenuItem` is "clicked". See
17721   * {@link ClickableComponent} for more detailed information on what a click can be.
17722   *
17723   * @param {EventTarget~Event} [event]
17724   *        The `keydown`, `tap`, or `click` event that caused this function to be
17725   *        called.
17726   *
17727   * @listens tap
17728   * @listens click
17729   */
17730
17731
17732  PlaybackRateMenuItem.prototype.handleClick = function handleClick(event) {
17733    _MenuItem.prototype.handleClick.call(this);
17734    this.player().playbackRate(this.rate);
17735  };
17736
17737  /**
17738   * Update the PlaybackRateMenuItem when the playbackrate changes.
17739   *
17740   * @param {EventTarget~Event} [event]
17741   *        The `ratechange` event that caused this function to run.
17742   *
17743   * @listens Player#ratechange
17744   */
17745
17746
17747  PlaybackRateMenuItem.prototype.update = function update(event) {
17748    this.selected(this.player().playbackRate() === this.rate);
17749  };
17750
17751  return PlaybackRateMenuItem;
17752}(MenuItem);
17753
17754/**
17755 * The text that should display over the `PlaybackRateMenuItem`s controls. Added for localization.
17756 *
17757 * @type {string}
17758 * @private
17759 */
17760
17761
17762PlaybackRateMenuItem.prototype.contentElType = 'button';
17763
17764Component.registerComponent('PlaybackRateMenuItem', PlaybackRateMenuItem);
17765
17766/**
17767 * @file playback-rate-menu-button.js
17768 */
17769/**
17770 * The component for controlling the playback rate.
17771 *
17772 * @extends MenuButton
17773 */
17774
17775var PlaybackRateMenuButton = function (_MenuButton) {
17776  inherits(PlaybackRateMenuButton, _MenuButton);
17777
17778  /**
17779   * Creates an instance of this class.
17780   *
17781   * @param {Player} player
17782   *        The `Player` that this class should be attached to.
17783   *
17784   * @param {Object} [options]
17785   *        The key/value store of player options.
17786   */
17787  function PlaybackRateMenuButton(player, options) {
17788    classCallCheck(this, PlaybackRateMenuButton);
17789
17790    var _this = possibleConstructorReturn(this, _MenuButton.call(this, player, options));
17791
17792    _this.updateVisibility();
17793    _this.updateLabel();
17794
17795    _this.on(player, 'loadstart', _this.updateVisibility);
17796    _this.on(player, 'ratechange', _this.updateLabel);
17797    return _this;
17798  }
17799
17800  /**
17801   * Create the `Component`'s DOM element
17802   *
17803   * @return {Element}
17804   *         The element that was created.
17805   */
17806
17807
17808  PlaybackRateMenuButton.prototype.createEl = function createEl$$1() {
17809    var el = _MenuButton.prototype.createEl.call(this);
17810
17811    this.labelEl_ = createEl('div', {
17812      className: 'vjs-playback-rate-value',
17813      innerHTML: '1x'
17814    });
17815
17816    el.appendChild(this.labelEl_);
17817
17818    return el;
17819  };
17820
17821  PlaybackRateMenuButton.prototype.dispose = function dispose() {
17822    this.labelEl_ = null;
17823
17824    _MenuButton.prototype.dispose.call(this);
17825  };
17826
17827  /**
17828   * Builds the default DOM `className`.
17829   *
17830   * @return {string}
17831   *         The DOM `className` for this object.
17832   */
17833
17834
17835  PlaybackRateMenuButton.prototype.buildCSSClass = function buildCSSClass() {
17836    return 'vjs-playback-rate ' + _MenuButton.prototype.buildCSSClass.call(this);
17837  };
17838
17839  PlaybackRateMenuButton.prototype.buildWrapperCSSClass = function buildWrapperCSSClass() {
17840    return 'vjs-playback-rate ' + _MenuButton.prototype.buildWrapperCSSClass.call(this);
17841  };
17842
17843  /**
17844   * Create the playback rate menu
17845   *
17846   * @return {Menu}
17847   *         Menu object populated with {@link PlaybackRateMenuItem}s
17848   */
17849
17850
17851  PlaybackRateMenuButton.prototype.createMenu = function createMenu() {
17852    var menu = new Menu(this.player());
17853    var rates = this.playbackRates();
17854
17855    if (rates) {
17856      for (var i = rates.length - 1; i >= 0; i--) {
17857        menu.addChild(new PlaybackRateMenuItem(this.player(), { rate: rates[i] + 'x' }));
17858      }
17859    }
17860
17861    return menu;
17862  };
17863
17864  /**
17865   * Updates ARIA accessibility attributes
17866   */
17867
17868
17869  PlaybackRateMenuButton.prototype.updateARIAAttributes = function updateARIAAttributes() {
17870    // Current playback rate
17871    this.el().setAttribute('aria-valuenow', this.player().playbackRate());
17872  };
17873
17874  /**
17875   * This gets called when an `PlaybackRateMenuButton` is "clicked". See
17876   * {@link ClickableComponent} for more detailed information on what a click can be.
17877   *
17878   * @param {EventTarget~Event} [event]
17879   *        The `keydown`, `tap`, or `click` event that caused this function to be
17880   *        called.
17881   *
17882   * @listens tap
17883   * @listens click
17884   */
17885
17886
17887  PlaybackRateMenuButton.prototype.handleClick = function handleClick(event) {
17888    // select next rate option
17889    var currentRate = this.player().playbackRate();
17890    var rates = this.playbackRates();
17891
17892    // this will select first one if the last one currently selected
17893    var newRate = rates[0];
17894
17895    for (var i = 0; i < rates.length; i++) {
17896      if (rates[i] > currentRate) {
17897        newRate = rates[i];
17898        break;
17899      }
17900    }
17901    this.player().playbackRate(newRate);
17902  };
17903
17904  /**
17905   * Get possible playback rates
17906   *
17907   * @return {Array}
17908   *         All possible playback rates
17909   */
17910
17911
17912  PlaybackRateMenuButton.prototype.playbackRates = function playbackRates() {
17913    return this.options_.playbackRates || this.options_.playerOptions && this.options_.playerOptions.playbackRates;
17914  };
17915
17916  /**
17917   * Get whether playback rates is supported by the tech
17918   * and an array of playback rates exists
17919   *
17920   * @return {boolean}
17921   *         Whether changing playback rate is supported
17922   */
17923
17924
17925  PlaybackRateMenuButton.prototype.playbackRateSupported = function playbackRateSupported() {
17926    return this.player().tech_ && this.player().tech_.featuresPlaybackRate && this.playbackRates() && this.playbackRates().length > 0;
17927  };
17928
17929  /**
17930   * Hide playback rate controls when they're no playback rate options to select
17931   *
17932   * @param {EventTarget~Event} [event]
17933   *        The event that caused this function to run.
17934   *
17935   * @listens Player#loadstart
17936   */
17937
17938
17939  PlaybackRateMenuButton.prototype.updateVisibility = function updateVisibility(event) {
17940    if (this.playbackRateSupported()) {
17941      this.removeClass('vjs-hidden');
17942    } else {
17943      this.addClass('vjs-hidden');
17944    }
17945  };
17946
17947  /**
17948   * Update button label when rate changed
17949   *
17950   * @param {EventTarget~Event} [event]
17951   *        The event that caused this function to run.
17952   *
17953   * @listens Player#ratechange
17954   */
17955
17956
17957  PlaybackRateMenuButton.prototype.updateLabel = function updateLabel(event) {
17958    if (this.playbackRateSupported()) {
17959      this.labelEl_.innerHTML = this.player().playbackRate() + 'x';
17960    }
17961  };
17962
17963  return PlaybackRateMenuButton;
17964}(MenuButton);
17965
17966/**
17967 * The text that should display over the `FullscreenToggle`s controls. Added for localization.
17968 *
17969 * @type {string}
17970 * @private
17971 */
17972
17973
17974PlaybackRateMenuButton.prototype.controlText_ = 'Playback Rate';
17975
17976Component.registerComponent('PlaybackRateMenuButton', PlaybackRateMenuButton);
17977
17978/**
17979 * @file spacer.js
17980 */
17981/**
17982 * Just an empty spacer element that can be used as an append point for plugins, etc.
17983 * Also can be used to create space between elements when necessary.
17984 *
17985 * @extends Component
17986 */
17987
17988var Spacer = function (_Component) {
17989  inherits(Spacer, _Component);
17990
17991  function Spacer() {
17992    classCallCheck(this, Spacer);
17993    return possibleConstructorReturn(this, _Component.apply(this, arguments));
17994  }
17995
17996  /**
17997   * Builds the default DOM `className`.
17998   *
17999   * @return {string}
18000   *         The DOM `className` for this object.
18001   */
18002  Spacer.prototype.buildCSSClass = function buildCSSClass() {
18003    return 'vjs-spacer ' + _Component.prototype.buildCSSClass.call(this);
18004  };
18005
18006  /**
18007   * Create the `Component`'s DOM element
18008   *
18009   * @return {Element}
18010   *         The element that was created.
18011   */
18012
18013
18014  Spacer.prototype.createEl = function createEl() {
18015    return _Component.prototype.createEl.call(this, 'div', {
18016      className: this.buildCSSClass()
18017    });
18018  };
18019
18020  return Spacer;
18021}(Component);
18022
18023Component.registerComponent('Spacer', Spacer);
18024
18025/**
18026 * @file custom-control-spacer.js
18027 */
18028/**
18029 * Spacer specifically meant to be used as an insertion point for new plugins, etc.
18030 *
18031 * @extends Spacer
18032 */
18033
18034var CustomControlSpacer = function (_Spacer) {
18035  inherits(CustomControlSpacer, _Spacer);
18036
18037  function CustomControlSpacer() {
18038    classCallCheck(this, CustomControlSpacer);
18039    return possibleConstructorReturn(this, _Spacer.apply(this, arguments));
18040  }
18041
18042  /**
18043   * Builds the default DOM `className`.
18044   *
18045   * @return {string}
18046   *         The DOM `className` for this object.
18047   */
18048  CustomControlSpacer.prototype.buildCSSClass = function buildCSSClass() {
18049    return 'vjs-custom-control-spacer ' + _Spacer.prototype.buildCSSClass.call(this);
18050  };
18051
18052  /**
18053   * Create the `Component`'s DOM element
18054   *
18055   * @return {Element}
18056   *         The element that was created.
18057   */
18058
18059
18060  CustomControlSpacer.prototype.createEl = function createEl() {
18061    var el = _Spacer.prototype.createEl.call(this, {
18062      className: this.buildCSSClass()
18063    });
18064
18065    // No-flex/table-cell mode requires there be some content
18066    // in the cell to fill the remaining space of the table.
18067    el.innerHTML = '\xA0';
18068    return el;
18069  };
18070
18071  return CustomControlSpacer;
18072}(Spacer);
18073
18074Component.registerComponent('CustomControlSpacer', CustomControlSpacer);
18075
18076/**
18077 * @file control-bar.js
18078 */
18079// Required children
18080/**
18081 * Container of main controls.
18082 *
18083 * @extends Component
18084 */
18085
18086var ControlBar = function (_Component) {
18087  inherits(ControlBar, _Component);
18088
18089  function ControlBar() {
18090    classCallCheck(this, ControlBar);
18091    return possibleConstructorReturn(this, _Component.apply(this, arguments));
18092  }
18093
18094  /**
18095   * Create the `Component`'s DOM element
18096   *
18097   * @return {Element}
18098   *         The element that was created.
18099   */
18100  ControlBar.prototype.createEl = function createEl() {
18101    return _Component.prototype.createEl.call(this, 'div', {
18102      className: 'vjs-control-bar',
18103      dir: 'ltr'
18104    }, {
18105      // The control bar is a group, but we don't aria-label it to avoid
18106      //  over-announcing by JAWS
18107      role: 'group'
18108    });
18109  };
18110
18111  return ControlBar;
18112}(Component);
18113
18114/**
18115 * Default options for `ControlBar`
18116 *
18117 * @type {Object}
18118 * @private
18119 */
18120
18121
18122ControlBar.prototype.options_ = {
18123  children: ['playToggle', 'volumePanel', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subsCapsButton', 'audioTrackButton', 'fullscreenToggle']
18124};
18125
18126Component.registerComponent('ControlBar', ControlBar);
18127
18128/**
18129 * @file error-display.js
18130 */
18131/**
18132 * A display that indicates an error has occurred. This means that the video
18133 * is unplayable.
18134 *
18135 * @extends ModalDialog
18136 */
18137
18138var ErrorDisplay = function (_ModalDialog) {
18139  inherits(ErrorDisplay, _ModalDialog);
18140
18141  /**
18142   * Creates an instance of this class.
18143   *
18144   * @param  {Player} player
18145   *         The `Player` that this class should be attached to.
18146   *
18147   * @param  {Object} [options]
18148   *         The key/value store of player options.
18149   */
18150  function ErrorDisplay(player, options) {
18151    classCallCheck(this, ErrorDisplay);
18152
18153    var _this = possibleConstructorReturn(this, _ModalDialog.call(this, player, options));
18154
18155    _this.on(player, 'error', _this.open);
18156    return _this;
18157  }
18158
18159  /**
18160   * Builds the default DOM `className`.
18161   *
18162   * @return {string}
18163   *         The DOM `className` for this object.
18164   *
18165   * @deprecated Since version 5.
18166   */
18167
18168
18169  ErrorDisplay.prototype.buildCSSClass = function buildCSSClass() {
18170    return 'vjs-error-display ' + _ModalDialog.prototype.buildCSSClass.call(this);
18171  };
18172
18173  /**
18174   * Gets the localized error message based on the `Player`s error.
18175   *
18176   * @return {string}
18177   *         The `Player`s error message localized or an empty string.
18178   */
18179
18180
18181  ErrorDisplay.prototype.content = function content() {
18182    var error = this.player().error();
18183
18184    return error ? this.localize(error.message) : '';
18185  };
18186
18187  return ErrorDisplay;
18188}(ModalDialog);
18189
18190/**
18191 * The default options for an `ErrorDisplay`.
18192 *
18193 * @private
18194 */
18195
18196
18197ErrorDisplay.prototype.options_ = mergeOptions(ModalDialog.prototype.options_, {
18198  pauseOnOpen: false,
18199  fillAlways: true,
18200  temporary: false,
18201  uncloseable: true
18202});
18203
18204Component.registerComponent('ErrorDisplay', ErrorDisplay);
18205
18206/**
18207 * @file text-track-settings.js
18208 */
18209var LOCAL_STORAGE_KEY = 'vjs-text-track-settings';
18210
18211var COLOR_BLACK = ['#000', 'Black'];
18212var COLOR_BLUE = ['#00F', 'Blue'];
18213var COLOR_CYAN = ['#0FF', 'Cyan'];
18214var COLOR_GREEN = ['#0F0', 'Green'];
18215var COLOR_MAGENTA = ['#F0F', 'Magenta'];
18216var COLOR_RED = ['#F00', 'Red'];
18217var COLOR_WHITE = ['#FFF', 'White'];
18218var COLOR_YELLOW = ['#FF0', 'Yellow'];
18219
18220var OPACITY_OPAQUE = ['1', 'Opaque'];
18221var OPACITY_SEMI = ['0.5', 'Semi-Transparent'];
18222var OPACITY_TRANS = ['0', 'Transparent'];
18223
18224// Configuration for the various <select> elements in the DOM of this component.
18225//
18226// Possible keys include:
18227//
18228// `default`:
18229//   The default option index. Only needs to be provided if not zero.
18230// `parser`:
18231//   A function which is used to parse the value from the selected option in
18232//   a customized way.
18233// `selector`:
18234//   The selector used to find the associated <select> element.
18235var selectConfigs = {
18236  backgroundColor: {
18237    selector: '.vjs-bg-color > select',
18238    id: 'captions-background-color-%s',
18239    label: 'Color',
18240    options: [COLOR_BLACK, COLOR_WHITE, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
18241  },
18242
18243  backgroundOpacity: {
18244    selector: '.vjs-bg-opacity > select',
18245    id: 'captions-background-opacity-%s',
18246    label: 'Transparency',
18247    options: [OPACITY_OPAQUE, OPACITY_SEMI, OPACITY_TRANS]
18248  },
18249
18250  color: {
18251    selector: '.vjs-fg-color > select',
18252    id: 'captions-foreground-color-%s',
18253    label: 'Color',
18254    options: [COLOR_WHITE, COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE, COLOR_YELLOW, COLOR_MAGENTA, COLOR_CYAN]
18255  },
18256
18257  edgeStyle: {
18258    selector: '.vjs-edge-style > select',
18259    id: '%s',
18260    label: 'Text Edge Style',
18261    options: [['none', 'None'], ['raised', 'Raised'], ['depressed', 'Depressed'], ['uniform', 'Uniform'], ['dropshadow', 'Dropshadow']]
18262  },
18263
18264  fontFamily: {
18265    selector: '.vjs-font-family > select',
18266    id: 'captions-font-family-%s',
18267    label: 'Font Family',
18268    options: [['proportionalSansSerif', 'Proportional Sans-Serif'], ['monospaceSansSerif', 'Monospace Sans-Serif'], ['proportionalSerif', 'Proportional Serif'], ['monospaceSerif', 'Monospace Serif'], ['casual', 'Casual'], ['script', 'Script'], ['small-caps', 'Small Caps']]
18269  },
18270
18271  fontPercent: {
18272    selector: '.vjs-font-percent > select',
18273    id: 'captions-font-size-%s',
18274    label: 'Font Size',
18275    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%']],
18276    'default': 2,
18277    parser: function parser(v) {
18278      return v === '1.00' ? null : Number(v);
18279    }
18280  },
18281
18282  textOpacity: {
18283    selector: '.vjs-text-opacity > select',
18284    id: 'captions-foreground-opacity-%s',
18285    label: 'Transparency',
18286    options: [OPACITY_OPAQUE, OPACITY_SEMI]
18287  },
18288
18289  // Options for this object are defined below.
18290  windowColor: {
18291    selector: '.vjs-window-color > select',
18292    id: 'captions-window-color-%s',
18293    label: 'Color'
18294  },
18295
18296  // Options for this object are defined below.
18297  windowOpacity: {
18298    selector: '.vjs-window-opacity > select',
18299    id: 'captions-window-opacity-%s',
18300    label: 'Transparency',
18301    options: [OPACITY_TRANS, OPACITY_SEMI, OPACITY_OPAQUE]
18302  }
18303};
18304
18305selectConfigs.windowColor.options = selectConfigs.backgroundColor.options;
18306
18307/**
18308 * Get the actual value of an option.
18309 *
18310 * @param  {string} value
18311 *         The value to get
18312 *
18313 * @param  {Function} [parser]
18314 *         Optional function to adjust the value.
18315 *
18316 * @return {Mixed}
18317 *         - Will be `undefined` if no value exists
18318 *         - Will be `undefined` if the given value is "none".
18319 *         - Will be the actual value otherwise.
18320 *
18321 * @private
18322 */
18323function parseOptionValue(value, parser) {
18324  if (parser) {
18325    value = parser(value);
18326  }
18327
18328  if (value && value !== 'none') {
18329    return value;
18330  }
18331}
18332
18333/**
18334 * Gets the value of the selected <option> element within a <select> element.
18335 *
18336 * @param  {Element} el
18337 *         the element to look in
18338 *
18339 * @param  {Function} [parser]
18340 *         Optional function to adjust the value.
18341 *
18342 * @return {Mixed}
18343 *         - Will be `undefined` if no value exists
18344 *         - Will be `undefined` if the given value is "none".
18345 *         - Will be the actual value otherwise.
18346 *
18347 * @private
18348 */
18349function getSelectedOptionValue(el, parser) {
18350  var value = el.options[el.options.selectedIndex].value;
18351
18352  return parseOptionValue(value, parser);
18353}
18354
18355/**
18356 * Sets the selected <option> element within a <select> element based on a
18357 * given value.
18358 *
18359 * @param {Element} el
18360 *        The element to look in.
18361 *
18362 * @param {string} value
18363 *        the property to look on.
18364 *
18365 * @param {Function} [parser]
18366 *        Optional function to adjust the value before comparing.
18367 *
18368 * @private
18369 */
18370function setSelectedOption(el, value, parser) {
18371  if (!value) {
18372    return;
18373  }
18374
18375  for (var i = 0; i < el.options.length; i++) {
18376    if (parseOptionValue(el.options[i].value, parser) === value) {
18377      el.selectedIndex = i;
18378      break;
18379    }
18380  }
18381}
18382
18383/**
18384 * Manipulate Text Tracks settings.
18385 *
18386 * @extends ModalDialog
18387 */
18388
18389var TextTrackSettings = function (_ModalDialog) {
18390  inherits(TextTrackSettings, _ModalDialog);
18391
18392  /**
18393   * Creates an instance of this class.
18394   *
18395   * @param {Player} player
18396   *         The `Player` that this class should be attached to.
18397   *
18398   * @param {Object} [options]
18399   *         The key/value store of player options.
18400   */
18401  function TextTrackSettings(player, options) {
18402    classCallCheck(this, TextTrackSettings);
18403
18404    options.temporary = false;
18405
18406    var _this = possibleConstructorReturn(this, _ModalDialog.call(this, player, options));
18407
18408    _this.updateDisplay = bind(_this, _this.updateDisplay);
18409
18410    // fill the modal and pretend we have opened it
18411    _this.fill();
18412    _this.hasBeenOpened_ = _this.hasBeenFilled_ = true;
18413
18414    _this.endDialog = createEl('p', {
18415      className: 'vjs-control-text',
18416      textContent: _this.localize('End of dialog window.')
18417    });
18418    _this.el().appendChild(_this.endDialog);
18419
18420    _this.setDefaults();
18421
18422    // Grab `persistTextTrackSettings` from the player options if not passed in child options
18423    if (options.persistTextTrackSettings === undefined) {
18424      _this.options_.persistTextTrackSettings = _this.options_.playerOptions.persistTextTrackSettings;
18425    }
18426
18427    _this.on(_this.$('.vjs-done-button'), 'click', function () {
18428      _this.saveSettings();
18429      _this.close();
18430    });
18431
18432    _this.on(_this.$('.vjs-default-button'), 'click', function () {
18433      _this.setDefaults();
18434      _this.updateDisplay();
18435    });
18436
18437    each(selectConfigs, function (config) {
18438      _this.on(_this.$(config.selector), 'change', _this.updateDisplay);
18439    });
18440
18441    if (_this.options_.persistTextTrackSettings) {
18442      _this.restoreSettings();
18443    }
18444    return _this;
18445  }
18446
18447  TextTrackSettings.prototype.dispose = function dispose() {
18448    this.endDialog = null;
18449
18450    _ModalDialog.prototype.dispose.call(this);
18451  };
18452
18453  /**
18454   * Create a <select> element with configured options.
18455   *
18456   * @param {string} key
18457   *        Configuration key to use during creation.
18458   *
18459   * @return {string}
18460   *         An HTML string.
18461   *
18462   * @private
18463   */
18464
18465
18466  TextTrackSettings.prototype.createElSelect_ = function createElSelect_(key) {
18467    var _this2 = this;
18468
18469    var legendId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '';
18470    var type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'label';
18471
18472    var config = selectConfigs[key];
18473    var id = config.id.replace('%s', this.id_);
18474
18475    return ['<' + type + ' id="' + id + '" class="' + (type === 'label' ? 'vjs-label' : '') + '">', this.localize(config.label), '</' + type + '>', '<select aria-labelledby="' + (legendId !== '' ? legendId + ' ' : '') + id + '">'].concat(config.options.map(function (o) {
18476      var optionId = id + '-' + o[1];
18477
18478      return ['<option id="' + optionId + '" value="' + o[0] + '" ', 'aria-labelledby="' + (legendId !== '' ? legendId + ' ' : '') + id + ' ' + optionId + '">', _this2.localize(o[1]), '</option>'].join('');
18479    })).concat('</select>').join('');
18480  };
18481
18482  /**
18483   * Create foreground color element for the component
18484   *
18485   * @return {string}
18486   *         An HTML string.
18487   *
18488   * @private
18489   */
18490
18491
18492  TextTrackSettings.prototype.createElFgColor_ = function createElFgColor_() {
18493    var legendId = 'captions-text-legend-' + this.id_;
18494
18495    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('');
18496  };
18497
18498  /**
18499   * Create background color element for the component
18500   *
18501   * @return {string}
18502   *         An HTML string.
18503   *
18504   * @private
18505   */
18506
18507
18508  TextTrackSettings.prototype.createElBgColor_ = function createElBgColor_() {
18509    var legendId = 'captions-background-' + this.id_;
18510
18511    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('');
18512  };
18513
18514  /**
18515   * Create window color element for the component
18516   *
18517   * @return {string}
18518   *         An HTML string.
18519   *
18520   * @private
18521   */
18522
18523
18524  TextTrackSettings.prototype.createElWinColor_ = function createElWinColor_() {
18525    var legendId = 'captions-window-' + this.id_;
18526
18527    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('');
18528  };
18529
18530  /**
18531   * Create color elements for the component
18532   *
18533   * @return {Element}
18534   *         The element that was created
18535   *
18536   * @private
18537   */
18538
18539
18540  TextTrackSettings.prototype.createElColors_ = function createElColors_() {
18541    return createEl('div', {
18542      className: 'vjs-track-settings-colors',
18543      innerHTML: [this.createElFgColor_(), this.createElBgColor_(), this.createElWinColor_()].join('')
18544    });
18545  };
18546
18547  /**
18548   * Create font elements for the component
18549   *
18550   * @return {Element}
18551   *         The element that was created.
18552   *
18553   * @private
18554   */
18555
18556
18557  TextTrackSettings.prototype.createElFont_ = function createElFont_() {
18558    return createEl('div', {
18559      className: 'vjs-track-settings-font">',
18560      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('')
18561    });
18562  };
18563
18564  /**
18565   * Create controls for the component
18566   *
18567   * @return {Element}
18568   *         The element that was created.
18569   *
18570   * @private
18571   */
18572
18573
18574  TextTrackSettings.prototype.createElControls_ = function createElControls_() {
18575    var defaultsDescription = this.localize('restore all settings to the default values');
18576
18577    return createEl('div', {
18578      className: 'vjs-track-settings-controls',
18579      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('')
18580    });
18581  };
18582
18583  TextTrackSettings.prototype.content = function content() {
18584    return [this.createElColors_(), this.createElFont_(), this.createElControls_()];
18585  };
18586
18587  TextTrackSettings.prototype.label = function label() {
18588    return this.localize('Caption Settings Dialog');
18589  };
18590
18591  TextTrackSettings.prototype.description = function description() {
18592    return this.localize('Beginning of dialog window. Escape will cancel and close the window.');
18593  };
18594
18595  TextTrackSettings.prototype.buildCSSClass = function buildCSSClass() {
18596    return _ModalDialog.prototype.buildCSSClass.call(this) + ' vjs-text-track-settings';
18597  };
18598
18599  /**
18600   * Gets an object of text track settings (or null).
18601   *
18602   * @return {Object}
18603   *         An object with config values parsed from the DOM or localStorage.
18604   */
18605
18606
18607  TextTrackSettings.prototype.getValues = function getValues() {
18608    var _this3 = this;
18609
18610    return reduce(selectConfigs, function (accum, config, key) {
18611      var value = getSelectedOptionValue(_this3.$(config.selector), config.parser);
18612
18613      if (value !== undefined) {
18614        accum[key] = value;
18615      }
18616
18617      return accum;
18618    }, {});
18619  };
18620
18621  /**
18622   * Sets text track settings from an object of values.
18623   *
18624   * @param {Object} values
18625   *        An object with config values parsed from the DOM or localStorage.
18626   */
18627
18628
18629  TextTrackSettings.prototype.setValues = function setValues(values) {
18630    var _this4 = this;
18631
18632    each(selectConfigs, function (config, key) {
18633      setSelectedOption(_this4.$(config.selector), values[key], config.parser);
18634    });
18635  };
18636
18637  /**
18638   * Sets all `<select>` elements to their default values.
18639   */
18640
18641
18642  TextTrackSettings.prototype.setDefaults = function setDefaults() {
18643    var _this5 = this;
18644
18645    each(selectConfigs, function (config) {
18646      var index = config.hasOwnProperty('default') ? config['default'] : 0;
18647
18648      _this5.$(config.selector).selectedIndex = index;
18649    });
18650  };
18651
18652  /**
18653   * Restore texttrack settings from localStorage
18654   */
18655
18656
18657  TextTrackSettings.prototype.restoreSettings = function restoreSettings() {
18658    var values = void 0;
18659
18660    try {
18661      values = JSON.parse(window_1.localStorage.getItem(LOCAL_STORAGE_KEY));
18662    } catch (err) {
18663      log$1.warn(err);
18664    }
18665
18666    if (values) {
18667      this.setValues(values);
18668    }
18669  };
18670
18671  /**
18672   * Save text track settings to localStorage
18673   */
18674
18675
18676  TextTrackSettings.prototype.saveSettings = function saveSettings() {
18677    if (!this.options_.persistTextTrackSettings) {
18678      return;
18679    }
18680
18681    var values = this.getValues();
18682
18683    try {
18684      if (Object.keys(values).length) {
18685        window_1.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(values));
18686      } else {
18687        window_1.localStorage.removeItem(LOCAL_STORAGE_KEY);
18688      }
18689    } catch (err) {
18690      log$1.warn(err);
18691    }
18692  };
18693
18694  /**
18695   * Update display of text track settings
18696   */
18697
18698
18699  TextTrackSettings.prototype.updateDisplay = function updateDisplay() {
18700    var ttDisplay = this.player_.getChild('textTrackDisplay');
18701
18702    if (ttDisplay) {
18703      ttDisplay.updateDisplay();
18704    }
18705  };
18706
18707  /**
18708   * conditionally blur the element and refocus the captions button
18709   *
18710   * @private
18711   */
18712
18713
18714  TextTrackSettings.prototype.conditionalBlur_ = function conditionalBlur_() {
18715    this.previouslyActiveEl_ = null;
18716    this.off(document_1, 'keydown', this.handleKeyDown);
18717
18718    var cb = this.player_.controlBar;
18719    var subsCapsBtn = cb && cb.subsCapsButton;
18720    var ccBtn = cb && cb.captionsButton;
18721
18722    if (subsCapsBtn) {
18723      subsCapsBtn.focus();
18724    } else if (ccBtn) {
18725      ccBtn.focus();
18726    }
18727  };
18728
18729  return TextTrackSettings;
18730}(ModalDialog);
18731
18732Component.registerComponent('TextTrackSettings', TextTrackSettings);
18733
18734var _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.']);
18735
18736/**
18737 * @file html5.js
18738 */
18739/**
18740 * HTML5 Media Controller - Wrapper for HTML5 Media API
18741 *
18742 * @mixes Tech~SouceHandlerAdditions
18743 * @extends Tech
18744 */
18745
18746var Html5 = function (_Tech) {
18747  inherits(Html5, _Tech);
18748
18749  /**
18750   * Create an instance of this Tech.
18751   *
18752   * @param {Object} [options]
18753   *        The key/value store of player options.
18754   *
18755   * @param {Component~ReadyCallback} ready
18756   *        Callback function to call when the `HTML5` Tech is ready.
18757   */
18758  function Html5(options, ready) {
18759    classCallCheck(this, Html5);
18760
18761    var _this = possibleConstructorReturn(this, _Tech.call(this, options, ready));
18762
18763    var source = options.source;
18764    var crossoriginTracks = false;
18765
18766    // Set the source if one is provided
18767    // 1) Check if the source is new (if not, we want to keep the original so playback isn't interrupted)
18768    // 2) Check to see if the network state of the tag was failed at init, and if so, reset the source
18769    // anyway so the error gets fired.
18770    if (source && (_this.el_.currentSrc !== source.src || options.tag && options.tag.initNetworkState_ === 3)) {
18771      _this.setSource(source);
18772    } else {
18773      _this.handleLateInit_(_this.el_);
18774    }
18775
18776    if (_this.el_.hasChildNodes()) {
18777
18778      var nodes = _this.el_.childNodes;
18779      var nodesLength = nodes.length;
18780      var removeNodes = [];
18781
18782      while (nodesLength--) {
18783        var node = nodes[nodesLength];
18784        var nodeName = node.nodeName.toLowerCase();
18785
18786        if (nodeName === 'track') {
18787          if (!_this.featuresNativeTextTracks) {
18788            // Empty video tag tracks so the built-in player doesn't use them also.
18789            // This may not be fast enough to stop HTML5 browsers from reading the tags
18790            // so we'll need to turn off any default tracks if we're manually doing
18791            // captions and subtitles. videoElement.textTracks
18792            removeNodes.push(node);
18793          } else {
18794            // store HTMLTrackElement and TextTrack to remote list
18795            _this.remoteTextTrackEls().addTrackElement_(node);
18796            _this.remoteTextTracks().addTrack(node.track);
18797            _this.textTracks().addTrack(node.track);
18798            if (!crossoriginTracks && !_this.el_.hasAttribute('crossorigin') && isCrossOrigin(node.src)) {
18799              crossoriginTracks = true;
18800            }
18801          }
18802        }
18803      }
18804
18805      for (var i = 0; i < removeNodes.length; i++) {
18806        _this.el_.removeChild(removeNodes[i]);
18807      }
18808    }
18809
18810    _this.proxyNativeTracks_();
18811    if (_this.featuresNativeTextTracks && crossoriginTracks) {
18812      log$1.warn(tsml(_templateObject$2));
18813    }
18814
18815    // prevent iOS Safari from disabling metadata text tracks during native playback
18816    _this.restoreMetadataTracksInIOSNativePlayer_();
18817
18818    // Determine if native controls should be used
18819    // Our goal should be to get the custom controls on mobile solid everywhere
18820    // so we can remove this all together. Right now this will block custom
18821    // controls on touch enabled laptops like the Chrome Pixel
18822    if ((TOUCH_ENABLED || IS_IPHONE || IS_NATIVE_ANDROID) && options.nativeControlsForTouch === true) {
18823      _this.setControls(true);
18824    }
18825
18826    // on iOS, we want to proxy `webkitbeginfullscreen` and `webkitendfullscreen`
18827    // into a `fullscreenchange` event
18828    _this.proxyWebkitFullscreen_();
18829
18830    _this.triggerReady();
18831    return _this;
18832  }
18833
18834  /**
18835   * Dispose of `HTML5` media element and remove all tracks.
18836   */
18837
18838
18839  Html5.prototype.dispose = function dispose() {
18840    Html5.disposeMediaElement(this.el_);
18841    this.options_ = null;
18842
18843    // tech will handle clearing of the emulated track list
18844    _Tech.prototype.dispose.call(this);
18845  };
18846
18847  /**
18848   * When a captions track is enabled in the iOS Safari native player, all other
18849   * tracks are disabled (including metadata tracks), which nulls all of their
18850   * associated cue points. This will restore metadata tracks to their pre-fullscreen
18851   * state in those cases so that cue points are not needlessly lost.
18852   *
18853   * @private
18854   */
18855
18856
18857  Html5.prototype.restoreMetadataTracksInIOSNativePlayer_ = function restoreMetadataTracksInIOSNativePlayer_() {
18858    var textTracks = this.textTracks();
18859    var metadataTracksPreFullscreenState = void 0;
18860
18861    // captures a snapshot of every metadata track's current state
18862    var takeMetadataTrackSnapshot = function takeMetadataTrackSnapshot() {
18863      metadataTracksPreFullscreenState = [];
18864
18865      for (var i = 0; i < textTracks.length; i++) {
18866        var track = textTracks[i];
18867
18868        if (track.kind === 'metadata') {
18869          metadataTracksPreFullscreenState.push({
18870            track: track,
18871            storedMode: track.mode
18872          });
18873        }
18874      }
18875    };
18876
18877    // snapshot each metadata track's initial state, and update the snapshot
18878    // each time there is a track 'change' event
18879    takeMetadataTrackSnapshot();
18880    textTracks.addEventListener('change', takeMetadataTrackSnapshot);
18881
18882    this.on('dispose', function () {
18883      return textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
18884    });
18885
18886    var restoreTrackMode = function restoreTrackMode() {
18887      for (var i = 0; i < metadataTracksPreFullscreenState.length; i++) {
18888        var storedTrack = metadataTracksPreFullscreenState[i];
18889
18890        if (storedTrack.track.mode === 'disabled' && storedTrack.track.mode !== storedTrack.storedMode) {
18891          storedTrack.track.mode = storedTrack.storedMode;
18892        }
18893      }
18894      // we only want this handler to be executed on the first 'change' event
18895      textTracks.removeEventListener('change', restoreTrackMode);
18896    };
18897
18898    // when we enter fullscreen playback, stop updating the snapshot and
18899    // restore all track modes to their pre-fullscreen state
18900    this.on('webkitbeginfullscreen', function () {
18901      textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
18902
18903      // remove the listener before adding it just in case it wasn't previously removed
18904      textTracks.removeEventListener('change', restoreTrackMode);
18905      textTracks.addEventListener('change', restoreTrackMode);
18906    });
18907
18908    // start updating the snapshot again after leaving fullscreen
18909    this.on('webkitendfullscreen', function () {
18910      // remove the listener before adding it just in case it wasn't previously removed
18911      textTracks.removeEventListener('change', takeMetadataTrackSnapshot);
18912      textTracks.addEventListener('change', takeMetadataTrackSnapshot);
18913
18914      // remove the restoreTrackMode handler in case it wasn't triggered during fullscreen playback
18915      textTracks.removeEventListener('change', restoreTrackMode);
18916    });
18917  };
18918
18919  /**
18920   * Proxy all native track list events to our track lists if the browser we are playing
18921   * in supports that type of track list.
18922   *
18923   * @private
18924   */
18925
18926
18927  Html5.prototype.proxyNativeTracks_ = function proxyNativeTracks_() {
18928    var _this2 = this;
18929
18930    NORMAL.names.forEach(function (name) {
18931      var props = NORMAL[name];
18932      var elTracks = _this2.el()[props.getterName];
18933      var techTracks = _this2[props.getterName]();
18934
18935      if (!_this2['featuresNative' + props.capitalName + 'Tracks'] || !elTracks || !elTracks.addEventListener) {
18936        return;
18937      }
18938      var listeners = {
18939        change: function change(e) {
18940          techTracks.trigger({
18941            type: 'change',
18942            target: techTracks,
18943            currentTarget: techTracks,
18944            srcElement: techTracks
18945          });
18946        },
18947        addtrack: function addtrack(e) {
18948          techTracks.addTrack(e.track);
18949        },
18950        removetrack: function removetrack(e) {
18951          techTracks.removeTrack(e.track);
18952        }
18953      };
18954      var removeOldTracks = function removeOldTracks() {
18955        var removeTracks = [];
18956
18957        for (var i = 0; i < techTracks.length; i++) {
18958          var found = false;
18959
18960          for (var j = 0; j < elTracks.length; j++) {
18961            if (elTracks[j] === techTracks[i]) {
18962              found = true;
18963              break;
18964            }
18965          }
18966
18967          if (!found) {
18968            removeTracks.push(techTracks[i]);
18969          }
18970        }
18971
18972        while (removeTracks.length) {
18973          techTracks.removeTrack(removeTracks.shift());
18974        }
18975      };
18976
18977      Object.keys(listeners).forEach(function (eventName) {
18978        var listener = listeners[eventName];
18979
18980        elTracks.addEventListener(eventName, listener);
18981        _this2.on('dispose', function (e) {
18982          return elTracks.removeEventListener(eventName, listener);
18983        });
18984      });
18985
18986      // Remove (native) tracks that are not used anymore
18987      _this2.on('loadstart', removeOldTracks);
18988      _this2.on('dispose', function (e) {
18989        return _this2.off('loadstart', removeOldTracks);
18990      });
18991    });
18992  };
18993
18994  /**
18995   * Create the `Html5` Tech's DOM element.
18996   *
18997   * @return {Element}
18998   *         The element that gets created.
18999   */
19000
19001
19002  Html5.prototype.createEl = function createEl$$1() {
19003    var el = this.options_.tag;
19004
19005    // Check if this browser supports moving the element into the box.
19006    // On the iPhone video will break if you move the element,
19007    // So we have to create a brand new element.
19008    // If we ingested the player div, we do not need to move the media element.
19009    if (!el || !(this.options_.playerElIngest || this.movingMediaElementInDOM)) {
19010
19011      // If the original tag is still there, clone and remove it.
19012      if (el) {
19013        var clone = el.cloneNode(true);
19014
19015        if (el.parentNode) {
19016          el.parentNode.insertBefore(clone, el);
19017        }
19018        Html5.disposeMediaElement(el);
19019        el = clone;
19020      } else {
19021        el = document_1.createElement('video');
19022
19023        // determine if native controls should be used
19024        var tagAttributes = this.options_.tag && getAttributes(this.options_.tag);
19025        var attributes = mergeOptions({}, tagAttributes);
19026
19027        if (!TOUCH_ENABLED || this.options_.nativeControlsForTouch !== true) {
19028          delete attributes.controls;
19029        }
19030
19031        setAttributes(el, assign(attributes, {
19032          id: this.options_.techId,
19033          'class': 'vjs-tech'
19034        }));
19035      }
19036
19037      el.playerId = this.options_.playerId;
19038    }
19039
19040    if (typeof this.options_.preload !== 'undefined') {
19041      setAttribute(el, 'preload', this.options_.preload);
19042    }
19043
19044    // Update specific tag settings, in case they were overridden
19045    // `autoplay` has to be *last* so that `muted` and `playsinline` are present
19046    // when iOS/Safari or other browsers attempt to autoplay.
19047    var settingsAttrs = ['loop', 'muted', 'playsinline', 'autoplay'];
19048
19049    for (var i = 0; i < settingsAttrs.length; i++) {
19050      var attr = settingsAttrs[i];
19051      var value = this.options_[attr];
19052
19053      if (typeof value !== 'undefined') {
19054        if (value) {
19055          setAttribute(el, attr, attr);
19056        } else {
19057          removeAttribute(el, attr);
19058        }
19059        el[attr] = value;
19060      }
19061    }
19062
19063    return el;
19064  };
19065
19066  /**
19067   * This will be triggered if the loadstart event has already fired, before videojs was
19068   * ready. Two known examples of when this can happen are:
19069   * 1. If we're loading the playback object after it has started loading
19070   * 2. The media is already playing the (often with autoplay on) then
19071   *
19072   * This function will fire another loadstart so that videojs can catchup.
19073   *
19074   * @fires Tech#loadstart
19075   *
19076   * @return {undefined}
19077   *         returns nothing.
19078   */
19079
19080
19081  Html5.prototype.handleLateInit_ = function handleLateInit_(el) {
19082    if (el.networkState === 0 || el.networkState === 3) {
19083      // The video element hasn't started loading the source yet
19084      // or didn't find a source
19085      return;
19086    }
19087
19088    if (el.readyState === 0) {
19089      // NetworkState is set synchronously BUT loadstart is fired at the
19090      // end of the current stack, usually before setInterval(fn, 0).
19091      // So at this point we know loadstart may have already fired or is
19092      // about to fire, and either way the player hasn't seen it yet.
19093      // We don't want to fire loadstart prematurely here and cause a
19094      // double loadstart so we'll wait and see if it happens between now
19095      // and the next loop, and fire it if not.
19096      // HOWEVER, we also want to make sure it fires before loadedmetadata
19097      // which could also happen between now and the next loop, so we'll
19098      // watch for that also.
19099      var loadstartFired = false;
19100      var setLoadstartFired = function setLoadstartFired() {
19101        loadstartFired = true;
19102      };
19103
19104      this.on('loadstart', setLoadstartFired);
19105
19106      var triggerLoadstart = function triggerLoadstart() {
19107        // We did miss the original loadstart. Make sure the player
19108        // sees loadstart before loadedmetadata
19109        if (!loadstartFired) {
19110          this.trigger('loadstart');
19111        }
19112      };
19113
19114      this.on('loadedmetadata', triggerLoadstart);
19115
19116      this.ready(function () {
19117        this.off('loadstart', setLoadstartFired);
19118        this.off('loadedmetadata', triggerLoadstart);
19119
19120        if (!loadstartFired) {
19121          // We did miss the original native loadstart. Fire it now.
19122          this.trigger('loadstart');
19123        }
19124      });
19125
19126      return;
19127    }
19128
19129    // From here on we know that loadstart already fired and we missed it.
19130    // The other readyState events aren't as much of a problem if we double
19131    // them, so not going to go to as much trouble as loadstart to prevent
19132    // that unless we find reason to.
19133    var eventsToTrigger = ['loadstart'];
19134
19135    // loadedmetadata: newly equal to HAVE_METADATA (1) or greater
19136    eventsToTrigger.push('loadedmetadata');
19137
19138    // loadeddata: newly increased to HAVE_CURRENT_DATA (2) or greater
19139    if (el.readyState >= 2) {
19140      eventsToTrigger.push('loadeddata');
19141    }
19142
19143    // canplay: newly increased to HAVE_FUTURE_DATA (3) or greater
19144    if (el.readyState >= 3) {
19145      eventsToTrigger.push('canplay');
19146    }
19147
19148    // canplaythrough: newly equal to HAVE_ENOUGH_DATA (4)
19149    if (el.readyState >= 4) {
19150      eventsToTrigger.push('canplaythrough');
19151    }
19152
19153    // We still need to give the player time to add event listeners
19154    this.ready(function () {
19155      eventsToTrigger.forEach(function (type) {
19156        this.trigger(type);
19157      }, this);
19158    });
19159  };
19160
19161  /**
19162   * Set current time for the `HTML5` tech.
19163   *
19164   * @param {number} seconds
19165   *        Set the current time of the media to this.
19166   */
19167
19168
19169  Html5.prototype.setCurrentTime = function setCurrentTime(seconds) {
19170    try {
19171      this.el_.currentTime = seconds;
19172    } catch (e) {
19173      log$1(e, 'Video is not ready. (Video.js)');
19174      // this.warning(VideoJS.warnings.videoNotReady);
19175    }
19176  };
19177
19178  /**
19179   * Get the current duration of the HTML5 media element.
19180   *
19181   * @return {number}
19182   *         The duration of the media or 0 if there is no duration.
19183   */
19184
19185
19186  Html5.prototype.duration = function duration() {
19187    var _this3 = this;
19188
19189    // Android Chrome will report duration as Infinity for VOD HLS until after
19190    // playback has started, which triggers the live display erroneously.
19191    // Return NaN if playback has not started and trigger a durationupdate once
19192    // the duration can be reliably known.
19193    if (this.el_.duration === Infinity && IS_ANDROID && IS_CHROME && this.el_.currentTime === 0) {
19194      // Wait for the first `timeupdate` with currentTime > 0 - there may be
19195      // several with 0
19196      var checkProgress = function checkProgress() {
19197        if (_this3.el_.currentTime > 0) {
19198          // Trigger durationchange for genuinely live video
19199          if (_this3.el_.duration === Infinity) {
19200            _this3.trigger('durationchange');
19201          }
19202          _this3.off('timeupdate', checkProgress);
19203        }
19204      };
19205
19206      this.on('timeupdate', checkProgress);
19207      return NaN;
19208    }
19209    return this.el_.duration || NaN;
19210  };
19211
19212  /**
19213   * Get the current width of the HTML5 media element.
19214   *
19215   * @return {number}
19216   *         The width of the HTML5 media element.
19217   */
19218
19219
19220  Html5.prototype.width = function width() {
19221    return this.el_.offsetWidth;
19222  };
19223
19224  /**
19225   * Get the current height of the HTML5 media element.
19226   *
19227   * @return {number}
19228   *         The heigth of the HTML5 media element.
19229   */
19230
19231
19232  Html5.prototype.height = function height() {
19233    return this.el_.offsetHeight;
19234  };
19235
19236  /**
19237   * Proxy iOS `webkitbeginfullscreen` and `webkitendfullscreen` into
19238   * `fullscreenchange` event.
19239   *
19240   * @private
19241   * @fires fullscreenchange
19242   * @listens webkitendfullscreen
19243   * @listens webkitbeginfullscreen
19244   * @listens webkitbeginfullscreen
19245   */
19246
19247
19248  Html5.prototype.proxyWebkitFullscreen_ = function proxyWebkitFullscreen_() {
19249    var _this4 = this;
19250
19251    if (!('webkitDisplayingFullscreen' in this.el_)) {
19252      return;
19253    }
19254
19255    var endFn = function endFn() {
19256      this.trigger('fullscreenchange', { isFullscreen: false });
19257    };
19258
19259    var beginFn = function beginFn() {
19260      if ('webkitPresentationMode' in this.el_ && this.el_.webkitPresentationMode !== 'picture-in-picture') {
19261        this.one('webkitendfullscreen', endFn);
19262
19263        this.trigger('fullscreenchange', { isFullscreen: true });
19264      }
19265    };
19266
19267    this.on('webkitbeginfullscreen', beginFn);
19268    this.on('dispose', function () {
19269      _this4.off('webkitbeginfullscreen', beginFn);
19270      _this4.off('webkitendfullscreen', endFn);
19271    });
19272  };
19273
19274  /**
19275   * Check if fullscreen is supported on the current playback device.
19276   *
19277   * @return {boolean}
19278   *         - True if fullscreen is supported.
19279   *         - False if fullscreen is not supported.
19280   */
19281
19282
19283  Html5.prototype.supportsFullScreen = function supportsFullScreen() {
19284    if (typeof this.el_.webkitEnterFullScreen === 'function') {
19285      var userAgent = window_1.navigator && window_1.navigator.userAgent || '';
19286
19287      // Seems to be broken in Chromium/Chrome && Safari in Leopard
19288      if (/Android/.test(userAgent) || !/Chrome|Mac OS X 10.5/.test(userAgent)) {
19289        return true;
19290      }
19291    }
19292    return false;
19293  };
19294
19295  /**
19296   * Request that the `HTML5` Tech enter fullscreen.
19297   */
19298
19299
19300  Html5.prototype.enterFullScreen = function enterFullScreen() {
19301    var video = this.el_;
19302
19303    if (video.paused && video.networkState <= video.HAVE_METADATA) {
19304      // attempt to prime the video element for programmatic access
19305      // this isn't necessary on the desktop but shouldn't hurt
19306      this.el_.play();
19307
19308      // playing and pausing synchronously during the transition to fullscreen
19309      // can get iOS ~6.1 devices into a play/pause loop
19310      this.setTimeout(function () {
19311        video.pause();
19312        video.webkitEnterFullScreen();
19313      }, 0);
19314    } else {
19315      video.webkitEnterFullScreen();
19316    }
19317  };
19318
19319  /**
19320   * Request that the `HTML5` Tech exit fullscreen.
19321   */
19322
19323
19324  Html5.prototype.exitFullScreen = function exitFullScreen() {
19325    this.el_.webkitExitFullScreen();
19326  };
19327
19328  /**
19329   * A getter/setter for the `Html5` Tech's source object.
19330   * > Note: Please use {@link Html5#setSource}
19331   *
19332   * @param {Tech~SourceObject} [src]
19333   *        The source object you want to set on the `HTML5` techs element.
19334   *
19335   * @return {Tech~SourceObject|undefined}
19336   *         - The current source object when a source is not passed in.
19337   *         - undefined when setting
19338   *
19339   * @deprecated Since version 5.
19340   */
19341
19342
19343  Html5.prototype.src = function src(_src) {
19344    if (_src === undefined) {
19345      return this.el_.src;
19346    }
19347
19348    // Setting src through `src` instead of `setSrc` will be deprecated
19349    this.setSrc(_src);
19350  };
19351
19352  /**
19353   * Reset the tech by removing all sources and then calling
19354   * {@link Html5.resetMediaElement}.
19355   */
19356
19357
19358  Html5.prototype.reset = function reset() {
19359    Html5.resetMediaElement(this.el_);
19360  };
19361
19362  /**
19363   * Get the current source on the `HTML5` Tech. Falls back to returning the source from
19364   * the HTML5 media element.
19365   *
19366   * @return {Tech~SourceObject}
19367   *         The current source object from the HTML5 tech. With a fallback to the
19368   *         elements source.
19369   */
19370
19371
19372  Html5.prototype.currentSrc = function currentSrc() {
19373    if (this.currentSource_) {
19374      return this.currentSource_.src;
19375    }
19376    return this.el_.currentSrc;
19377  };
19378
19379  /**
19380   * Set controls attribute for the HTML5 media Element.
19381   *
19382   * @param {string} val
19383   *        Value to set the controls attribute to
19384   */
19385
19386
19387  Html5.prototype.setControls = function setControls(val) {
19388    this.el_.controls = !!val;
19389  };
19390
19391  /**
19392   * Create and returns a remote {@link TextTrack} object.
19393   *
19394   * @param {string} kind
19395   *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
19396   *
19397   * @param {string} [label]
19398   *        Label to identify the text track
19399   *
19400   * @param {string} [language]
19401   *        Two letter language abbreviation
19402   *
19403   * @return {TextTrack}
19404   *         The TextTrack that gets created.
19405   */
19406
19407
19408  Html5.prototype.addTextTrack = function addTextTrack(kind, label, language) {
19409    if (!this.featuresNativeTextTracks) {
19410      return _Tech.prototype.addTextTrack.call(this, kind, label, language);
19411    }
19412
19413    return this.el_.addTextTrack(kind, label, language);
19414  };
19415
19416  /**
19417   * Creates either native TextTrack or an emulated TextTrack depending
19418   * on the value of `featuresNativeTextTracks`
19419   *
19420   * @param {Object} options
19421   *        The object should contain the options to intialize the TextTrack with.
19422   *
19423   * @param {string} [options.kind]
19424   *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
19425   *
19426   * @param {string} [options.label].
19427   *        Label to identify the text track
19428   *
19429   * @param {string} [options.language]
19430   *        Two letter language abbreviation.
19431   *
19432   * @param {boolean} [options.default]
19433   *        Default this track to on.
19434   *
19435   * @param {string} [options.id]
19436   *        The internal id to assign this track.
19437   *
19438   * @param {string} [options.src]
19439   *        A source url for the track.
19440   *
19441   * @return {HTMLTrackElement}
19442   *         The track element that gets created.
19443   */
19444
19445
19446  Html5.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
19447    if (!this.featuresNativeTextTracks) {
19448      return _Tech.prototype.createRemoteTextTrack.call(this, options);
19449    }
19450    var htmlTrackElement = document_1.createElement('track');
19451
19452    if (options.kind) {
19453      htmlTrackElement.kind = options.kind;
19454    }
19455    if (options.label) {
19456      htmlTrackElement.label = options.label;
19457    }
19458    if (options.language || options.srclang) {
19459      htmlTrackElement.srclang = options.language || options.srclang;
19460    }
19461    if (options['default']) {
19462      htmlTrackElement['default'] = options['default'];
19463    }
19464    if (options.id) {
19465      htmlTrackElement.id = options.id;
19466    }
19467    if (options.src) {
19468      htmlTrackElement.src = options.src;
19469    }
19470
19471    return htmlTrackElement;
19472  };
19473
19474  /**
19475   * Creates a remote text track object and returns an html track element.
19476   *
19477   * @param {Object} options The object should contain values for
19478   * kind, language, label, and src (location of the WebVTT file)
19479   * @param {Boolean} [manualCleanup=true] if set to false, the TextTrack will be
19480   * automatically removed from the video element whenever the source changes
19481   * @return {HTMLTrackElement} An Html Track Element.
19482   * This can be an emulated {@link HTMLTrackElement} or a native one.
19483   * @deprecated The default value of the "manualCleanup" parameter will default
19484   * to "false" in upcoming versions of Video.js
19485   */
19486
19487
19488  Html5.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
19489    var htmlTrackElement = _Tech.prototype.addRemoteTextTrack.call(this, options, manualCleanup);
19490
19491    if (this.featuresNativeTextTracks) {
19492      this.el().appendChild(htmlTrackElement);
19493    }
19494
19495    return htmlTrackElement;
19496  };
19497
19498  /**
19499   * Remove remote `TextTrack` from `TextTrackList` object
19500   *
19501   * @param {TextTrack} track
19502   *        `TextTrack` object to remove
19503   */
19504
19505
19506  Html5.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
19507    _Tech.prototype.removeRemoteTextTrack.call(this, track);
19508
19509    if (this.featuresNativeTextTracks) {
19510      var tracks = this.$$('track');
19511
19512      var i = tracks.length;
19513
19514      while (i--) {
19515        if (track === tracks[i] || track === tracks[i].track) {
19516          this.el().removeChild(tracks[i]);
19517        }
19518      }
19519    }
19520  };
19521
19522  /**
19523   * Gets available media playback quality metrics as specified by the W3C's Media
19524   * Playback Quality API.
19525   *
19526   * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
19527   *
19528   * @return {Object}
19529   *         An object with supported media playback quality metrics
19530   */
19531
19532
19533  Html5.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
19534    if (typeof this.el().getVideoPlaybackQuality === 'function') {
19535      return this.el().getVideoPlaybackQuality();
19536    }
19537
19538    var videoPlaybackQuality = {};
19539
19540    if (typeof this.el().webkitDroppedFrameCount !== 'undefined' && typeof this.el().webkitDecodedFrameCount !== 'undefined') {
19541      videoPlaybackQuality.droppedVideoFrames = this.el().webkitDroppedFrameCount;
19542      videoPlaybackQuality.totalVideoFrames = this.el().webkitDecodedFrameCount;
19543    }
19544
19545    if (window_1.performance && typeof window_1.performance.now === 'function') {
19546      videoPlaybackQuality.creationTime = window_1.performance.now();
19547    } else if (window_1.performance && window_1.performance.timing && typeof window_1.performance.timing.navigationStart === 'number') {
19548      videoPlaybackQuality.creationTime = window_1.Date.now() - window_1.performance.timing.navigationStart;
19549    }
19550
19551    return videoPlaybackQuality;
19552  };
19553
19554  return Html5;
19555}(Tech);
19556
19557/* HTML5 Support Testing ---------------------------------------------------- */
19558
19559if (isReal()) {
19560
19561  /**
19562   * Element for testing browser HTML5 media capabilities
19563   *
19564   * @type {Element}
19565   * @constant
19566   * @private
19567   */
19568  Html5.TEST_VID = document_1.createElement('video');
19569  var track = document_1.createElement('track');
19570
19571  track.kind = 'captions';
19572  track.srclang = 'en';
19573  track.label = 'English';
19574  Html5.TEST_VID.appendChild(track);
19575}
19576
19577/**
19578 * Check if HTML5 media is supported by this browser/device.
19579 *
19580 * @return {boolean}
19581 *         - True if HTML5 media is supported.
19582 *         - False if HTML5 media is not supported.
19583 */
19584Html5.isSupported = function () {
19585  // IE9 with no Media Player is a LIAR! (#984)
19586  try {
19587    Html5.TEST_VID.volume = 0.5;
19588  } catch (e) {
19589    return false;
19590  }
19591
19592  return !!(Html5.TEST_VID && Html5.TEST_VID.canPlayType);
19593};
19594
19595/**
19596 * Check if the tech can support the given type
19597 *
19598 * @param {string} type
19599 *        The mimetype to check
19600 * @return {string} 'probably', 'maybe', or '' (empty string)
19601 */
19602Html5.canPlayType = function (type) {
19603  return Html5.TEST_VID.canPlayType(type);
19604};
19605
19606/**
19607 * Check if the tech can support the given source
19608 * @param {Object} srcObj
19609 *        The source object
19610 * @param {Object} options
19611 *        The options passed to the tech
19612 * @return {string} 'probably', 'maybe', or '' (empty string)
19613 */
19614Html5.canPlaySource = function (srcObj, options) {
19615  return Html5.canPlayType(srcObj.type);
19616};
19617
19618/**
19619 * Check if the volume can be changed in this browser/device.
19620 * Volume cannot be changed in a lot of mobile devices.
19621 * Specifically, it can't be changed from 1 on iOS.
19622 *
19623 * @return {boolean}
19624 *         - True if volume can be controlled
19625 *         - False otherwise
19626 */
19627Html5.canControlVolume = function () {
19628  // IE will error if Windows Media Player not installed #3315
19629  try {
19630    var volume = Html5.TEST_VID.volume;
19631
19632    Html5.TEST_VID.volume = volume / 2 + 0.1;
19633    return volume !== Html5.TEST_VID.volume;
19634  } catch (e) {
19635    return false;
19636  }
19637};
19638
19639/**
19640 * Check if the playback rate can be changed in this browser/device.
19641 *
19642 * @return {boolean}
19643 *         - True if playback rate can be controlled
19644 *         - False otherwise
19645 */
19646Html5.canControlPlaybackRate = function () {
19647  // Playback rate API is implemented in Android Chrome, but doesn't do anything
19648  // https://github.com/videojs/video.js/issues/3180
19649  if (IS_ANDROID && IS_CHROME && CHROME_VERSION < 58) {
19650    return false;
19651  }
19652  // IE will error if Windows Media Player not installed #3315
19653  try {
19654    var playbackRate = Html5.TEST_VID.playbackRate;
19655
19656    Html5.TEST_VID.playbackRate = playbackRate / 2 + 0.1;
19657    return playbackRate !== Html5.TEST_VID.playbackRate;
19658  } catch (e) {
19659    return false;
19660  }
19661};
19662
19663/**
19664 * Check to see if native `TextTrack`s are supported by this browser/device.
19665 *
19666 * @return {boolean}
19667 *         - True if native `TextTrack`s are supported.
19668 *         - False otherwise
19669 */
19670Html5.supportsNativeTextTracks = function () {
19671  return IS_ANY_SAFARI;
19672};
19673
19674/**
19675 * Check to see if native `VideoTrack`s are supported by this browser/device
19676 *
19677 * @return {boolean}
19678 *        - True if native `VideoTrack`s are supported.
19679 *        - False otherwise
19680 */
19681Html5.supportsNativeVideoTracks = function () {
19682  return !!(Html5.TEST_VID && Html5.TEST_VID.videoTracks);
19683};
19684
19685/**
19686 * Check to see if native `AudioTrack`s are supported by this browser/device
19687 *
19688 * @return {boolean}
19689 *        - True if native `AudioTrack`s are supported.
19690 *        - False otherwise
19691 */
19692Html5.supportsNativeAudioTracks = function () {
19693  return !!(Html5.TEST_VID && Html5.TEST_VID.audioTracks);
19694};
19695
19696/**
19697 * An array of events available on the Html5 tech.
19698 *
19699 * @private
19700 * @type {Array}
19701 */
19702Html5.Events = ['loadstart', 'suspend', 'abort', 'error', 'emptied', 'stalled', 'loadedmetadata', 'loadeddata', 'canplay', 'canplaythrough', 'playing', 'waiting', 'seeking', 'seeked', 'ended', 'durationchange', 'timeupdate', 'progress', 'play', 'pause', 'ratechange', 'resize', 'volumechange'];
19703
19704/**
19705 * Boolean indicating whether the `Tech` supports volume control.
19706 *
19707 * @type {boolean}
19708 * @default {@link Html5.canControlVolume}
19709 */
19710Html5.prototype.featuresVolumeControl = Html5.canControlVolume();
19711
19712/**
19713 * Boolean indicating whether the `Tech` supports changing the speed at which the media
19714 * plays. Examples:
19715 *   - Set player to play 2x (twice) as fast
19716 *   - Set player to play 0.5x (half) as fast
19717 *
19718 * @type {boolean}
19719 * @default {@link Html5.canControlPlaybackRate}
19720 */
19721Html5.prototype.featuresPlaybackRate = Html5.canControlPlaybackRate();
19722
19723/**
19724 * Boolean indicating whether the `HTML5` tech currently supports the media element
19725 * moving in the DOM. iOS breaks if you move the media element, so this is set this to
19726 * false there. Everywhere else this should be true.
19727 *
19728 * @type {boolean}
19729 * @default
19730 */
19731Html5.prototype.movingMediaElementInDOM = !IS_IOS;
19732
19733// TODO: Previous comment: No longer appears to be used. Can probably be removed.
19734//       Is this true?
19735/**
19736 * Boolean indicating whether the `HTML5` tech currently supports automatic media resize
19737 * when going into fullscreen.
19738 *
19739 * @type {boolean}
19740 * @default
19741 */
19742Html5.prototype.featuresFullscreenResize = true;
19743
19744/**
19745 * Boolean indicating whether the `HTML5` tech currently supports the progress event.
19746 * If this is false, manual `progress` events will be triggred instead.
19747 *
19748 * @type {boolean}
19749 * @default
19750 */
19751Html5.prototype.featuresProgressEvents = true;
19752
19753/**
19754 * Boolean indicating whether the `HTML5` tech currently supports the timeupdate event.
19755 * If this is false, manual `timeupdate` events will be triggred instead.
19756 *
19757 * @default
19758 */
19759Html5.prototype.featuresTimeupdateEvents = true;
19760
19761/**
19762 * Boolean indicating whether the `HTML5` tech currently supports native `TextTrack`s.
19763 *
19764 * @type {boolean}
19765 * @default {@link Html5.supportsNativeTextTracks}
19766 */
19767Html5.prototype.featuresNativeTextTracks = Html5.supportsNativeTextTracks();
19768
19769/**
19770 * Boolean indicating whether the `HTML5` tech currently supports native `VideoTrack`s.
19771 *
19772 * @type {boolean}
19773 * @default {@link Html5.supportsNativeVideoTracks}
19774 */
19775Html5.prototype.featuresNativeVideoTracks = Html5.supportsNativeVideoTracks();
19776
19777/**
19778 * Boolean indicating whether the `HTML5` tech currently supports native `AudioTrack`s.
19779 *
19780 * @type {boolean}
19781 * @default {@link Html5.supportsNativeAudioTracks}
19782 */
19783Html5.prototype.featuresNativeAudioTracks = Html5.supportsNativeAudioTracks();
19784
19785// HTML5 Feature detection and Device Fixes --------------------------------- //
19786var canPlayType = Html5.TEST_VID && Html5.TEST_VID.constructor.prototype.canPlayType;
19787var mpegurlRE = /^application\/(?:x-|vnd\.apple\.)mpegurl/i;
19788var mp4RE = /^video\/mp4/i;
19789
19790Html5.patchCanPlayType = function () {
19791
19792  // Android 4.0 and above can play HLS to some extent but it reports being unable to do so
19793  if (ANDROID_VERSION >= 4.0 && !IS_FIREFOX) {
19794    Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
19795      if (type && mpegurlRE.test(type)) {
19796        return 'maybe';
19797      }
19798      return canPlayType.call(this, type);
19799    };
19800
19801    // Override Android 2.2 and less canPlayType method which is broken
19802  } else if (IS_OLD_ANDROID) {
19803    Html5.TEST_VID.constructor.prototype.canPlayType = function (type) {
19804      if (type && mp4RE.test(type)) {
19805        return 'maybe';
19806      }
19807      return canPlayType.call(this, type);
19808    };
19809  }
19810};
19811
19812Html5.unpatchCanPlayType = function () {
19813  var r = Html5.TEST_VID.constructor.prototype.canPlayType;
19814
19815  Html5.TEST_VID.constructor.prototype.canPlayType = canPlayType;
19816  return r;
19817};
19818
19819// by default, patch the media element
19820Html5.patchCanPlayType();
19821
19822Html5.disposeMediaElement = function (el) {
19823  if (!el) {
19824    return;
19825  }
19826
19827  if (el.parentNode) {
19828    el.parentNode.removeChild(el);
19829  }
19830
19831  // remove any child track or source nodes to prevent their loading
19832  while (el.hasChildNodes()) {
19833    el.removeChild(el.firstChild);
19834  }
19835
19836  // remove any src reference. not setting `src=''` because that causes a warning
19837  // in firefox
19838  el.removeAttribute('src');
19839
19840  // force the media element to update its loading state by calling load()
19841  // however IE on Windows 7N has a bug that throws an error so need a try/catch (#793)
19842  if (typeof el.load === 'function') {
19843    // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
19844    (function () {
19845      try {
19846        el.load();
19847      } catch (e) {
19848        // not supported
19849      }
19850    })();
19851  }
19852};
19853
19854Html5.resetMediaElement = function (el) {
19855  if (!el) {
19856    return;
19857  }
19858
19859  var sources = el.querySelectorAll('source');
19860  var i = sources.length;
19861
19862  while (i--) {
19863    el.removeChild(sources[i]);
19864  }
19865
19866  // remove any src reference.
19867  // not setting `src=''` because that throws an error
19868  el.removeAttribute('src');
19869
19870  if (typeof el.load === 'function') {
19871    // wrapping in an iife so it's not deoptimized (#1060#discussion_r10324473)
19872    (function () {
19873      try {
19874        el.load();
19875      } catch (e) {
19876        // satisfy linter
19877      }
19878    })();
19879  }
19880};
19881
19882/* Native HTML5 element property wrapping ----------------------------------- */
19883// Wrap native boolean attributes with getters that check both property and attribute
19884// The list is as followed:
19885// muted, defaultMuted, autoplay, controls, loop, playsinline
19886[
19887/**
19888 * Get the value of `muted` from the media element. `muted` indicates
19889 * that the volume for the media should be set to silent. This does not actually change
19890 * the `volume` attribute.
19891 *
19892 * @method Html5#muted
19893 * @return {boolean}
19894 *         - True if the value of `volume` should be ignored and the audio set to silent.
19895 *         - False if the value of `volume` should be used.
19896 *
19897 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
19898 */
19899'muted',
19900
19901/**
19902 * Get the value of `defaultMuted` from the media element. `defaultMuted` indicates
19903 * whether the media should start muted or not. Only changes the default state of the
19904 * media. `muted` and `defaultMuted` can have different values. {@link Html5#muted} indicates the
19905 * current state.
19906 *
19907 * @method Html5#defaultMuted
19908 * @return {boolean}
19909 *         - The value of `defaultMuted` from the media element.
19910 *         - True indicates that the media should start muted.
19911 *         - False indicates that the media should not start muted
19912 *
19913 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
19914 */
19915'defaultMuted',
19916
19917/**
19918 * Get the value of `autoplay` from the media element. `autoplay` indicates
19919 * that the media should start to play as soon as the page is ready.
19920 *
19921 * @method Html5#autoplay
19922 * @return {boolean}
19923 *         - The value of `autoplay` from the media element.
19924 *         - True indicates that the media should start as soon as the page loads.
19925 *         - False indicates that the media should not start as soon as the page loads.
19926 *
19927 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
19928 */
19929'autoplay',
19930
19931/**
19932 * Get the value of `controls` from the media element. `controls` indicates
19933 * whether the native media controls should be shown or hidden.
19934 *
19935 * @method Html5#controls
19936 * @return {boolean}
19937 *         - The value of `controls` from the media element.
19938 *         - True indicates that native controls should be showing.
19939 *         - False indicates that native controls should be hidden.
19940 *
19941 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-controls}
19942 */
19943'controls',
19944
19945/**
19946 * Get the value of `loop` from the media element. `loop` indicates
19947 * that the media should return to the start of the media and continue playing once
19948 * it reaches the end.
19949 *
19950 * @method Html5#loop
19951 * @return {boolean}
19952 *         - The value of `loop` from the media element.
19953 *         - True indicates that playback should seek back to start once
19954 *           the end of a media is reached.
19955 *         - False indicates that playback should not loop back to the start when the
19956 *           end of the media is reached.
19957 *
19958 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
19959 */
19960'loop',
19961
19962/**
19963 * Get the value of `playsinline` from the media element. `playsinline` indicates
19964 * to the browser that non-fullscreen playback is preferred when fullscreen
19965 * playback is the native default, such as in iOS Safari.
19966 *
19967 * @method Html5#playsinline
19968 * @return {boolean}
19969 *         - The value of `playsinline` from the media element.
19970 *         - True indicates that the media should play inline.
19971 *         - False indicates that the media should not play inline.
19972 *
19973 * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
19974 */
19975'playsinline'].forEach(function (prop) {
19976  Html5.prototype[prop] = function () {
19977    return this.el_[prop] || this.el_.hasAttribute(prop);
19978  };
19979});
19980
19981// Wrap native boolean attributes with setters that set both property and attribute
19982// The list is as followed:
19983// setMuted, setDefaultMuted, setAutoplay, setLoop, setPlaysinline
19984// setControls is special-cased above
19985[
19986/**
19987 * Set the value of `muted` on the media element. `muted` indicates that the current
19988 * audio level should be silent.
19989 *
19990 * @method Html5#setMuted
19991 * @param {boolean} muted
19992 *        - True if the audio should be set to silent
19993 *        - False otherwise
19994 *
19995 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-muted}
19996 */
19997'muted',
19998
19999/**
20000 * Set the value of `defaultMuted` on the media element. `defaultMuted` indicates that the current
20001 * audio level should be silent, but will only effect the muted level on intial playback..
20002 *
20003 * @method Html5.prototype.setDefaultMuted
20004 * @param {boolean} defaultMuted
20005 *        - True if the audio should be set to silent
20006 *        - False otherwise
20007 *
20008 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultmuted}
20009 */
20010'defaultMuted',
20011
20012/**
20013 * Set the value of `autoplay` on the media element. `autoplay` indicates
20014 * that the media should start to play as soon as the page is ready.
20015 *
20016 * @method Html5#setAutoplay
20017 * @param {boolean} autoplay
20018 *         - True indicates that the media should start as soon as the page loads.
20019 *         - False indicates that the media should not start as soon as the page loads.
20020 *
20021 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-autoplay}
20022 */
20023'autoplay',
20024
20025/**
20026 * Set the value of `loop` on the media element. `loop` indicates
20027 * that the media should return to the start of the media and continue playing once
20028 * it reaches the end.
20029 *
20030 * @method Html5#setLoop
20031 * @param {boolean} loop
20032 *         - True indicates that playback should seek back to start once
20033 *           the end of a media is reached.
20034 *         - False indicates that playback should not loop back to the start when the
20035 *           end of the media is reached.
20036 *
20037 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-loop}
20038 */
20039'loop',
20040
20041/**
20042 * Set the value of `playsinline` from the media element. `playsinline` indicates
20043 * to the browser that non-fullscreen playback is preferred when fullscreen
20044 * playback is the native default, such as in iOS Safari.
20045 *
20046 * @method Html5#setPlaysinline
20047 * @param {boolean} playsinline
20048 *         - True indicates that the media should play inline.
20049 *         - False indicates that the media should not play inline.
20050 *
20051 * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
20052 */
20053'playsinline'].forEach(function (prop) {
20054  Html5.prototype['set' + toTitleCase(prop)] = function (v) {
20055    this.el_[prop] = v;
20056
20057    if (v) {
20058      this.el_.setAttribute(prop, prop);
20059    } else {
20060      this.el_.removeAttribute(prop);
20061    }
20062  };
20063});
20064
20065// Wrap native properties with a getter
20066// The list is as followed
20067// paused, currentTime, buffered, volume, poster, preload, error, seeking
20068// seekable, ended, playbackRate, defaultPlaybackRate, played, networkState
20069// readyState, videoWidth, videoHeight
20070[
20071/**
20072 * Get the value of `paused` from the media element. `paused` indicates whether the media element
20073 * is currently paused or not.
20074 *
20075 * @method Html5#paused
20076 * @return {boolean}
20077 *         The value of `paused` from the media element.
20078 *
20079 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-paused}
20080 */
20081'paused',
20082
20083/**
20084 * Get the value of `currentTime` from the media element. `currentTime` indicates
20085 * the current second that the media is at in playback.
20086 *
20087 * @method Html5#currentTime
20088 * @return {number}
20089 *         The value of `currentTime` from the media element.
20090 *
20091 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-currenttime}
20092 */
20093'currentTime',
20094
20095/**
20096 * Get the value of `buffered` from the media element. `buffered` is a `TimeRange`
20097 * object that represents the parts of the media that are already downloaded and
20098 * available for playback.
20099 *
20100 * @method Html5#buffered
20101 * @return {TimeRange}
20102 *         The value of `buffered` from the media element.
20103 *
20104 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-buffered}
20105 */
20106'buffered',
20107
20108/**
20109 * Get the value of `volume` from the media element. `volume` indicates
20110 * the current playback volume of audio for a media. `volume` will be a value from 0
20111 * (silent) to 1 (loudest and default).
20112 *
20113 * @method Html5#volume
20114 * @return {number}
20115 *         The value of `volume` from the media element. Value will be between 0-1.
20116 *
20117 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
20118 */
20119'volume',
20120
20121/**
20122 * Get the value of `poster` from the media element. `poster` indicates
20123 * that the url of an image file that can/will be shown when no media data is available.
20124 *
20125 * @method Html5#poster
20126 * @return {string}
20127 *         The value of `poster` from the media element. Value will be a url to an
20128 *         image.
20129 *
20130 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-video-poster}
20131 */
20132'poster',
20133
20134/**
20135 * Get the value of `preload` from the media element. `preload` indicates
20136 * what should download before the media is interacted with. It can have the following
20137 * values:
20138 * - none: nothing should be downloaded
20139 * - metadata: poster and the first few frames of the media may be downloaded to get
20140 *   media dimensions and other metadata
20141 * - auto: allow the media and metadata for the media to be downloaded before
20142 *    interaction
20143 *
20144 * @method Html5#preload
20145 * @return {string}
20146 *         The value of `preload` from the media element. Will be 'none', 'metadata',
20147 *         or 'auto'.
20148 *
20149 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
20150 */
20151'preload',
20152
20153/**
20154 * Get the value of the `error` from the media element. `error` indicates any
20155 * MediaError that may have occured during playback. If error returns null there is no
20156 * current error.
20157 *
20158 * @method Html5#error
20159 * @return {MediaError|null}
20160 *         The value of `error` from the media element. Will be `MediaError` if there
20161 *         is a current error and null otherwise.
20162 *
20163 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-error}
20164 */
20165'error',
20166
20167/**
20168 * Get the value of `seeking` from the media element. `seeking` indicates whether the
20169 * media is currently seeking to a new position or not.
20170 *
20171 * @method Html5#seeking
20172 * @return {boolean}
20173 *         - The value of `seeking` from the media element.
20174 *         - True indicates that the media is currently seeking to a new position.
20175 *         - Flase indicates that the media is not seeking to a new position at this time.
20176 *
20177 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seeking}
20178 */
20179'seeking',
20180
20181/**
20182 * Get the value of `seekable` from the media element. `seekable` returns a
20183 * `TimeRange` object indicating ranges of time that can currently be `seeked` to.
20184 *
20185 * @method Html5#seekable
20186 * @return {TimeRange}
20187 *         The value of `seekable` from the media element. A `TimeRange` object
20188 *         indicating the current ranges of time that can be seeked to.
20189 *
20190 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-seekable}
20191 */
20192'seekable',
20193
20194/**
20195 * Get the value of `ended` from the media element. `ended` indicates whether
20196 * the media has reached the end or not.
20197 *
20198 * @method Html5#ended
20199 * @return {boolean}
20200 *         - The value of `ended` from the media element.
20201 *         - True indicates that the media has ended.
20202 *         - False indicates that the media has not ended.
20203 *
20204 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-ended}
20205 */
20206'ended',
20207
20208/**
20209 * Get the value of `playbackRate` from the media element. `playbackRate` indicates
20210 * the rate at which the media is currently playing back. Examples:
20211 *   - if playbackRate is set to 2, media will play twice as fast.
20212 *   - if playbackRate is set to 0.5, media will play half as fast.
20213 *
20214 * @method Html5#playbackRate
20215 * @return {number}
20216 *         The value of `playbackRate` from the media element. A number indicating
20217 *         the current playback speed of the media, where 1 is normal speed.
20218 *
20219 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
20220 */
20221'playbackRate',
20222
20223/**
20224 * Get the value of `defaultPlaybackRate` from the media element. `defaultPlaybackRate` indicates
20225 * the rate at which the media is currently playing back. This value will not indicate the current
20226 * `playbackRate` after playback has started, use {@link Html5#playbackRate} for that.
20227 *
20228 * Examples:
20229 *   - if defaultPlaybackRate is set to 2, media will play twice as fast.
20230 *   - if defaultPlaybackRate is set to 0.5, media will play half as fast.
20231 *
20232 * @method Html5.prototype.defaultPlaybackRate
20233 * @return {number}
20234 *         The value of `defaultPlaybackRate` from the media element. A number indicating
20235 *         the current playback speed of the media, where 1 is normal speed.
20236 *
20237 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
20238 */
20239'defaultPlaybackRate',
20240
20241/**
20242 * Get the value of `played` from the media element. `played` returns a `TimeRange`
20243 * object representing points in the media timeline that have been played.
20244 *
20245 * @method Html5#played
20246 * @return {TimeRange}
20247 *         The value of `played` from the media element. A `TimeRange` object indicating
20248 *         the ranges of time that have been played.
20249 *
20250 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-played}
20251 */
20252'played',
20253
20254/**
20255 * Get the value of `networkState` from the media element. `networkState` indicates
20256 * the current network state. It returns an enumeration from the following list:
20257 * - 0: NETWORK_EMPTY
20258 * - 1: NEWORK_IDLE
20259 * - 2: NETWORK_LOADING
20260 * - 3: NETWORK_NO_SOURCE
20261 *
20262 * @method Html5#networkState
20263 * @return {number}
20264 *         The value of `networkState` from the media element. This will be a number
20265 *         from the list in the description.
20266 *
20267 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-networkstate}
20268 */
20269'networkState',
20270
20271/**
20272 * Get the value of `readyState` from the media element. `readyState` indicates
20273 * the current state of the media element. It returns an enumeration from the
20274 * following list:
20275 * - 0: HAVE_NOTHING
20276 * - 1: HAVE_METADATA
20277 * - 2: HAVE_CURRENT_DATA
20278 * - 3: HAVE_FUTURE_DATA
20279 * - 4: HAVE_ENOUGH_DATA
20280 *
20281 * @method Html5#readyState
20282 * @return {number}
20283 *         The value of `readyState` from the media element. This will be a number
20284 *         from the list in the description.
20285 *
20286 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#ready-states}
20287 */
20288'readyState',
20289
20290/**
20291 * Get the value of `videoWidth` from the video element. `videoWidth` indicates
20292 * the current width of the video in css pixels.
20293 *
20294 * @method Html5#videoWidth
20295 * @return {number}
20296 *         The value of `videoWidth` from the video element. This will be a number
20297 *         in css pixels.
20298 *
20299 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
20300 */
20301'videoWidth',
20302
20303/**
20304 * Get the value of `videoHeight` from the video element. `videoHeigth` indicates
20305 * the current height of the video in css pixels.
20306 *
20307 * @method Html5#videoHeight
20308 * @return {number}
20309 *         The value of `videoHeight` from the video element. This will be a number
20310 *         in css pixels.
20311 *
20312 * @see [Spec] {@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-video-videowidth}
20313 */
20314'videoHeight'].forEach(function (prop) {
20315  Html5.prototype[prop] = function () {
20316    return this.el_[prop];
20317  };
20318});
20319
20320// Wrap native properties with a setter in this format:
20321// set + toTitleCase(name)
20322// The list is as follows:
20323// setVolume, setSrc, setPoster, setPreload, setPlaybackRate, setDefaultPlaybackRate
20324[
20325/**
20326 * Set the value of `volume` on the media element. `volume` indicates the current
20327 * audio level as a percentage in decimal form. This means that 1 is 100%, 0.5 is 50%, and
20328 * so on.
20329 *
20330 * @method Html5#setVolume
20331 * @param {number} percentAsDecimal
20332 *        The volume percent as a decimal. Valid range is from 0-1.
20333 *
20334 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-a-volume}
20335 */
20336'volume',
20337
20338/**
20339 * Set the value of `src` on the media element. `src` indicates the current
20340 * {@link Tech~SourceObject} for the media.
20341 *
20342 * @method Html5#setSrc
20343 * @param {Tech~SourceObject} src
20344 *        The source object to set as the current source.
20345 *
20346 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-src}
20347 */
20348'src',
20349
20350/**
20351 * Set the value of `poster` on the media element. `poster` is the url to
20352 * an image file that can/will be shown when no media data is available.
20353 *
20354 * @method Html5#setPoster
20355 * @param {string} poster
20356 *        The url to an image that should be used as the `poster` for the media
20357 *        element.
20358 *
20359 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-poster}
20360 */
20361'poster',
20362
20363/**
20364 * Set the value of `preload` on the media element. `preload` indicates
20365 * what should download before the media is interacted with. It can have the following
20366 * values:
20367 * - none: nothing should be downloaded
20368 * - metadata: poster and the first few frames of the media may be downloaded to get
20369 *   media dimensions and other metadata
20370 * - auto: allow the media and metadata for the media to be downloaded before
20371 *    interaction
20372 *
20373 * @method Html5#setPreload
20374 * @param {string} preload
20375 *         The value of `preload` to set on the media element. Must be 'none', 'metadata',
20376 *         or 'auto'.
20377 *
20378 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#attr-media-preload}
20379 */
20380'preload',
20381
20382/**
20383 * Set the value of `playbackRate` on the media element. `playbackRate` indicates
20384 * the rate at which the media should play back. Examples:
20385 *   - if playbackRate is set to 2, media will play twice as fast.
20386 *   - if playbackRate is set to 0.5, media will play half as fast.
20387 *
20388 * @method Html5#setPlaybackRate
20389 * @return {number}
20390 *         The value of `playbackRate` from the media element. A number indicating
20391 *         the current playback speed of the media, where 1 is normal speed.
20392 *
20393 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-playbackrate}
20394 */
20395'playbackRate',
20396
20397/**
20398 * Set the value of `defaultPlaybackRate` on the media element. `defaultPlaybackRate` indicates
20399 * the rate at which the media should play back upon initial startup. Changing this value
20400 * after a video has started will do nothing. Instead you should used {@link Html5#setPlaybackRate}.
20401 *
20402 * Example Values:
20403 *   - if playbackRate is set to 2, media will play twice as fast.
20404 *   - if playbackRate is set to 0.5, media will play half as fast.
20405 *
20406 * @method Html5.prototype.setDefaultPlaybackRate
20407 * @return {number}
20408 *         The value of `defaultPlaybackRate` from the media element. A number indicating
20409 *         the current playback speed of the media, where 1 is normal speed.
20410 *
20411 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-defaultplaybackrate}
20412 */
20413'defaultPlaybackRate'].forEach(function (prop) {
20414  Html5.prototype['set' + toTitleCase(prop)] = function (v) {
20415    this.el_[prop] = v;
20416  };
20417});
20418
20419// wrap native functions with a function
20420// The list is as follows:
20421// pause, load play
20422[
20423/**
20424 * A wrapper around the media elements `pause` function. This will call the `HTML5`
20425 * media elements `pause` function.
20426 *
20427 * @method Html5#pause
20428 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-pause}
20429 */
20430'pause',
20431
20432/**
20433 * A wrapper around the media elements `load` function. This will call the `HTML5`s
20434 * media element `load` function.
20435 *
20436 * @method Html5#load
20437 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-load}
20438 */
20439'load',
20440
20441/**
20442 * A wrapper around the media elements `play` function. This will call the `HTML5`s
20443 * media element `play` function.
20444 *
20445 * @method Html5#play
20446 * @see [Spec]{@link https://www.w3.org/TR/html5/embedded-content-0.html#dom-media-play}
20447 */
20448'play'].forEach(function (prop) {
20449  Html5.prototype[prop] = function () {
20450    return this.el_[prop]();
20451  };
20452});
20453
20454Tech.withSourceHandlers(Html5);
20455
20456/**
20457 * Native source handler for Html5, simply passes the source to the media element.
20458 *
20459 * @proprety {Tech~SourceObject} source
20460 *        The source object
20461 *
20462 * @proprety {Html5} tech
20463 *        The instance of the HTML5 tech.
20464 */
20465Html5.nativeSourceHandler = {};
20466
20467/**
20468 * Check if the media element can play the given mime type.
20469 *
20470 * @param {string} type
20471 *        The mimetype to check
20472 *
20473 * @return {string}
20474 *         'probably', 'maybe', or '' (empty string)
20475 */
20476Html5.nativeSourceHandler.canPlayType = function (type) {
20477  // IE9 on Windows 7 without MediaPlayer throws an error here
20478  // https://github.com/videojs/video.js/issues/519
20479  try {
20480    return Html5.TEST_VID.canPlayType(type);
20481  } catch (e) {
20482    return '';
20483  }
20484};
20485
20486/**
20487 * Check if the media element can handle a source natively.
20488 *
20489 * @param {Tech~SourceObject} source
20490 *         The source object
20491 *
20492 * @param {Object} [options]
20493 *         Options to be passed to the tech.
20494 *
20495 * @return {string}
20496 *         'probably', 'maybe', or '' (empty string).
20497 */
20498Html5.nativeSourceHandler.canHandleSource = function (source, options) {
20499
20500  // If a type was provided we should rely on that
20501  if (source.type) {
20502    return Html5.nativeSourceHandler.canPlayType(source.type);
20503
20504    // If no type, fall back to checking 'video/[EXTENSION]'
20505  } else if (source.src) {
20506    var ext = getFileExtension(source.src);
20507
20508    return Html5.nativeSourceHandler.canPlayType('video/' + ext);
20509  }
20510
20511  return '';
20512};
20513
20514/**
20515 * Pass the source to the native media element.
20516 *
20517 * @param {Tech~SourceObject} source
20518 *        The source object
20519 *
20520 * @param {Html5} tech
20521 *        The instance of the Html5 tech
20522 *
20523 * @param {Object} [options]
20524 *        The options to pass to the source
20525 */
20526Html5.nativeSourceHandler.handleSource = function (source, tech, options) {
20527  tech.setSrc(source.src);
20528};
20529
20530/**
20531 * A noop for the native dispose function, as cleanup is not needed.
20532 */
20533Html5.nativeSourceHandler.dispose = function () {};
20534
20535// Register the native source handler
20536Html5.registerSourceHandler(Html5.nativeSourceHandler);
20537
20538Tech.registerTech('Html5', Html5);
20539
20540var _templateObject$1 = taggedTemplateLiteralLoose(['\n        Using the tech directly can be dangerous. I hope you know 
20540what 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      ']);
20541
20542/**
20543 * @file player.js
20544 */
20545// Subclasses Component
20546// The following imports are used only to ensure that the corresponding modules
20547// are always included in the video.js package. Importing the modules will
20548// execute them and they will register themselves with video.js.
20549// Import Html5 tech, at least for disposing the original video tag.
20550// The following tech events are simply re-triggered
20551// on the player when they happen
20552var TECH_EVENTS_RETRIGGER = [
20553/**
20554 * Fired while the user agent is downloading media data.
20555 *
20556 * @event Player#progress
20557 * @type {EventTarget~Event}
20558 */
20559/**
20560 * Retrigger the `progress` event that was triggered by the {@link Tech}.
20561 *
20562 * @private
20563 * @method Player#handleTechProgress_
20564 * @fires Player#progress
20565 * @listens Tech#progress
20566 */
20567'progress',
20568
20569/**
20570 * Fires when the loading of an audio/video is aborted.
20571 *
20572 * @event Player#abort
20573 * @type {EventTarget~Event}
20574 */
20575/**
20576 * Retrigger the `abort` event that was triggered by the {@link Tech}.
20577 *
20578 * @private
20579 * @method Player#handleTechAbort_
20580 * @fires Player#abort
20581 * @listens Tech#abort
20582 */
20583'abort',
20584
20585/**
20586 * Fires when the browser is intentionally not getting media data.
20587 *
20588 * @event Player#suspend
20589 * @type {EventTarget~Event}
20590 */
20591/**
20592 * Retrigger the `suspend` event that was triggered by the {@link Tech}.
20593 *
20594 * @private
20595 * @method Player#handleTechSuspend_
20596 * @fires Player#suspend
20597 * @listens Tech#suspend
20598 */
20599'suspend',
20600
20601/**
20602 * Fires when the current playlist is empty.
20603 *
20604 * @event Player#emptied
20605 * @type {EventTarget~Event}
20606 */
20607/**
20608 * Retrigger the `emptied` event that was triggered by the {@link Tech}.
20609 *
20610 * @private
20611 * @method Player#handleTechEmptied_
20612 * @fires Player#emptied
20613 * @listens Tech#emptied
20614 */
20615'emptied',
20616/**
20617 * Fires when the browser is trying to get media data, but data is not available.
20618 *
20619 * @event Player#stalled
20620 * @type {EventTarget~Event}
20621 */
20622/**
20623 * Retrigger the `stalled` event that was triggered by the {@link Tech}.
20624 *
20625 * @private
20626 * @method Player#handleTechStalled_
20627 * @fires Player#stalled
20628 * @listens Tech#stalled
20629 */
20630'stalled',
20631
20632/**
20633 * Fires when the browser has loaded meta data for the audio/video.
20634 *
20635 * @event Player#loadedmetadata
20636 * @type {EventTarget~Event}
20637 */
20638/**
20639 * Retrigger the `stalled` event that was triggered by the {@link Tech}.
20640 *
20641 * @private
20642 * @method Player#handleTechLoadedmetadata_
20643 * @fires Player#loadedmetadata
20644 * @listens Tech#loadedmetadata
20645 */
20646'loadedmetadata',
20647
20648/**
20649 * Fires when the browser has loaded the current frame of the audio/video.
20650 *
20651 * @event Player#loadeddata
20652 * @type {event}
20653 */
20654/**
20655 * Retrigger the `loadeddata` event that was triggered by the {@link Tech}.
20656 *
20657 * @private
20658 * @method Player#handleTechLoaddeddata_
20659 * @fires Player#loadeddata
20660 * @listens Tech#loadeddata
20661 */
20662'loadeddata',
20663
20664/**
20665 * Fires when the current playback position has changed.
20666 *
20667 * @event Player#timeupdate
20668 * @type {event}
20669 */
20670/**
20671 * Retrigger the `timeupdate` event that was triggered by the {@link Tech}.
20672 *
20673 * @private
20674 * @method Player#handleTechTimeUpdate_
20675 * @fires Player#timeupdate
20676 * @listens Tech#timeupdate
20677 */
20678'timeupdate',
20679
20680/**
20681 * Fires when the playing speed of the audio/video is changed
20682 *
20683 * @event Player#ratechange
20684 * @type {event}
20685 */
20686/**
20687 * Retrigger the `ratechange` event that was triggered by the {@link Tech}.
20688 *
20689 * @private
20690 * @method Player#handleTechRatechange_
20691 * @fires Player#ratechange
20692 * @listens Tech#ratechange
20693 */
20694'ratechange',
20695
20696/**
20697 * Fires when the video's intrinsic dimensions change
20698 *
20699 * @event Player#resize
20700 * @type {event}
20701 */
20702/**
20703 * Retrigger the `resize` event that was triggered by the {@link Tech}.
20704 *
20705 * @private
20706 * @method Player#handleTechResize_
20707 * @fires Player#resize
20708 * @listens Tech#resize
20709 */
20710'resize',
20711
20712/**
20713 * Fires when the volume has been changed
20714 *
20715 * @event Player#volumechange
20716 * @type {event}
20717 */
20718/**
20719 * Retrigger the `volumechange` event that was triggered by the {@link Tech}.
20720 *
20721 * @private
20722 * @method Player#handleTechVolumechange_
20723 * @fires Player#volumechange
20724 * @listens Tech#volumechange
20725 */
20726'volumechange',
20727
20728/**
20729 * Fires when the text track has been changed
20730 *
20731 * @event Player#texttrackchange
20732 * @type {event}
20733 */
20734/**
20735 * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}.
20736 *
20737 * @private
20738 * @method Player#handleTechTexttrackchange_
20739 * @fires Player#texttrackchange
20740 * @listens Tech#texttrackchange
20741 */
20742'texttrackchange'];
20743
20744/**
20745 * An instance of the `Player` class is created when any of the Video.js setup methods
20746 * are used to initialize a video.
20747 *
20748 * After an instance has been created it can be accessed globally in two ways:
20749 * 1. By calling `videojs('example_video_1');`
20750 * 2. By using it directly via  `videojs.players.example_video_1;`
20751 *
20752 * @extends Component
20753 */
20754
20755var Player = function (_Component) {
20756  inherits(Player, _Component);
20757
20758  /**
20759   * Create an instance of this class.
20760   *
20761   * @param {Element} tag
vendor: 24,713 bytes, lines 20762-21554
20762   *        The original video DOM element used for configuring options.
20763   *
20764   * @param {Object} [options]
20765   *        Object of option names and values.
20766   *
20767   * @param {Component~ReadyCallback} [ready]
20768   *        Ready callback function.
20769   */
20770  function Player(tag, options, ready) {
20771    classCallCheck(this, Player);
20772
20773    // Make sure tag ID exists
20774    tag.id = tag.id || 'vjs_video_' + newGUID();
20775
20776    // Set Options
20777    // The options argument overrides options set in the video tag
20778    // which overrides globally set options.
20779    // This latter part coincides with the load order
20780    // (tag must exist before Player)
20781    options = assign(Player.getTagSettings(tag), options);
20782
20783    // Delay the initialization of children because we need to set up
20784    // player properties first, and can't use `this` before `super()`
20785    options.initChildren = false;
20786
20787    // Same with creating the element
20788    options.createEl = false;
20789
20790    // don't auto mixin the evented mixin
20791    options.evented = false;
20792
20793    // we don't want the player to report touch activity on itself
20794    // see enableTouchActivity in Component
20795    options.reportTouchActivity = false;
20796
20797    // If language is not set, get the closest lang attribute
20798    if (!options.language) {
20799      if (typeof tag.closest === 'function') {
20800        var closest = tag.closest('[lang]');
20801
20802        if (closest && closest.getAttribute) {
20803          options.language = closest.getAttribute('lang');
20804        }
20805      } else {
20806        var element = tag;
20807
20808        while (element && element.nodeType === 1) {
20809          if (getAttributes(element).hasOwnProperty('lang')) {
20810            options.language = element.getAttribute('lang');
20811            break;
20812          }
20813          element = element.parentNode;
20814        }
20815      }
20816    }
20817
20818    // Run base component initializing with new options
20819
20820    // Turn off API access because we're loading a new tech that might load asynchronously
20821    var _this = possibleConstructorReturn(this, _Component.call(this, null, options, ready));
20822
20823    _this.isReady_ = false;
20824
20825    // Init state hasStarted_
20826    _this.hasStarted_ = false;
20827
20828    // Init state userActive_
20829    _this.userActive_ = false;
20830
20831    // if the global option object was accidentally blown away by
20832    // someone, bail early with an informative error
20833    if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) {
20834      throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?');
20835    }
20836
20837    // Store the original tag used to set options
20838    _this.tag = tag;
20839
20840    // Store the tag attributes used to restore html5 element
20841    _this.tagAttributes = tag && getAttributes(tag);
20842
20843    // Update current language
20844    _this.language(_this.options_.language);
20845
20846    // Update Supported Languages
20847    if (options.languages) {
20848      // Normalise player option languages to lowercase
20849      var languagesToLower = {};
20850
20851      Object.getOwnPropertyNames(options.languages).forEach(function (name$$1) {
20852        languagesToLower[name$$1.toLowerCase()] = options.languages[name$$1];
20853      });
20854      _this.languages_ = languagesToLower;
20855    } else {
20856      _this.languages_ = Player.prototype.options_.languages;
20857    }
20858
20859    // Cache for video property values.
20860    _this.cache_ = {};
20861
20862    // Set poster
20863    _this.poster_ = options.poster || '';
20864
20865    // Set controls
20866    _this.controls_ = !!options.controls;
20867
20868    // Set default values for lastVolume
20869    _this.cache_.lastVolume = 1;
20870
20871    // Original tag settings stored in options
20872    // now remove immediately so native controls don't flash.
20873    // May be turned back on by HTML5 tech if nativeControlsForTouch is true
20874    tag.controls = false;
20875    tag.removeAttribute('controls');
20876
20877    /*
20878     * Store the internal state of scrubbing
20879     *
20880     * @private
20881     * @return {Boolean} True if the user is scrubbing
20882     */
20883    _this.scrubbing_ = false;
20884
20885    _this.el_ = _this.createEl();
20886
20887    // Make this an evented object and use `el_` as its event bus.
20888    evented(_this, { eventBusKey: 'el_' });
20889
20890    // We also want to pass the original player options to each component and plugin
20891    // as well so they don't need to reach back into the player for options later.
20892    // We also need to do another copy of this.options_ so we don't end up with
20893    // an infinite loop.
20894    var playerOptionsCopy = mergeOptions(_this.options_);
20895
20896    // Load plugins
20897    if (options.plugins) {
20898      var plugins = options.plugins;
20899
20900      Object.keys(plugins).forEach(function (name$$1) {
20901        if (typeof this[name$$1] === 'function') {
20902          this[name$$1](plugins[name$$1]);
20903        } else {
20904          throw new Error('plugin "' + name$$1 + '" does not exist');
20905        }
20906      }, _this);
20907    }
20908
20909    _this.options_.playerOptions = playerOptionsCopy;
20910
20911    _this.middleware_ = [];
20912
20913    _this.initChildren();
20914
20915    // Set isAudio based on whether or not an audio tag was used
20916    _this.isAudio(tag.nodeName.toLowerCase() === 'audio');
20917
20918    // Update controls className. Can't do this when the controls are initially
20919    // set because the element doesn't exist yet.
20920    if (_this.controls()) {
20921      _this.addClass('vjs-controls-enabled');
20922    } else {
20923      _this.addClass('vjs-controls-disabled');
20924    }
20925
20926    // Set ARIA label and region role depending on player type
20927    _this.el_.setAttribute('role', 'region');
20928    if (_this.isAudio()) {
20929      _this.el_.setAttribute('aria-label', _this.localize('Audio Player'));
20930    } else {
20931      _this.el_.setAttribute('aria-label', _this.localize('Video Player'));
20932    }
20933
20934    if (_this.isAudio()) {
20935      _this.addClass('vjs-audio');
20936    }
20937
20938    if (_this.flexNotSupported_()) {
20939      _this.addClass('vjs-no-flex');
20940    }
20941
20942    // TODO: Make this smarter. Toggle user state between touching/mousing
20943    // using events, since devices can have both touch and mouse events.
20944    // if (browser.TOUCH_ENABLED) {
20945    //   this.addClass('vjs-touch-enabled');
20946    // }
20947
20948    // iOS Safari has broken hover handling
20949    if (!IS_IOS) {
20950      _this.addClass('vjs-workinghover');
20951    }
20952
20953    // Make player easily findable by ID
20954    Player.players[_this.id_] = _this;
20955
20956    // Add a major version class to aid css in plugins
20957    var majorVersion = version.split('.')[0];
20958
20959    _this.addClass('vjs-v' + majorVersion);
20960
20961    // When the player is first initialized, trigger activity so components
20962    // like the control bar show themselves if needed
20963    _this.userActive(true);
20964    _this.reportUserActivity();
20965    _this.listenForUserActivity_();
20966
20967    _this.on('fullscreenchange', _this.handleFullscreenChange_);
20968    _this.on('stageclick', _this.handleStageClick_);
20969
20970    _this.changingSrc_ = false;
20971    _this.playWaitingForReady_ = false;
20972    _this.playOnLoadstart_ = null;
20973
20974    _this.forceAutoplayInChrome_();
20975    return _this;
20976  }
20977
20978  /**
20979   * Destroys the video player and does any necessary cleanup.
20980   *
20981   * This is especially helpful if you are dynamically adding and removing videos
20982   * to/from the DOM.
20983   *
20984   * @fires Player#dispose
20985   */
20986
20987
20988  Player.prototype.dispose = function dispose() {
20989    /**
20990     * Called when the player is being disposed of.
20991     *
20992     * @event Player#dispose
20993     * @type {EventTarget~Event}
20994     */
20995    this.trigger('dispose');
20996    // prevent dispose from being called twice
20997    this.off('dispose');
20998
20999    if (this.styleEl_ && this.styleEl_.parentNode) {
21000      this.styleEl_.parentNode.removeChild(this.styleEl_);
21001      this.styleEl_ = null;
21002    }
21003
21004    // Kill reference to this player
21005    Player.players[this.id_] = null;
21006
21007    if (this.tag && this.tag.player) {
21008      this.tag.player = null;
21009    }
21010
21011    if (this.el_ && this.el_.player) {
21012      this.el_.player = null;
21013    }
21014
21015    if (this.tech_) {
21016      this.tech_.dispose();
21017    }
21018
21019    if (this.playerElIngest_) {
21020      this.playerElIngest_ = null;
21021    }
21022
21023    if (this.tag) {
21024      this.tag = null;
21025    }
21026
21027    // the actual .el_ is removed here
21028    _Component.prototype.dispose.call(this);
21029  };
21030
21031  /**
21032   * Create the `Player`'s DOM element.
21033   *
21034   * @return {Element}
21035   *         The DOM element that gets created.
21036   */
21037
21038
21039  Player.prototype.createEl = function createEl$$1() {
21040    var tag = this.tag;
21041    var el = void 0;
21042    var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player');
21043    var divEmbed = this.tag.tagName.toLowerCase() === 'video-js';
21044
21045    if (playerElIngest) {
21046      el = this.el_ = tag.parentNode;
21047    } else if (!divEmbed) {
21048      el = this.el_ = _Component.prototype.createEl.call(this, 'div');
21049    }
21050
21051    // Copy over all the attributes from the tag, including ID and class
21052    // ID will now reference player box, not the video tag
21053    var attrs = getAttributes(tag);
21054
21055    if (divEmbed) {
21056      el = this.el_ = tag;
21057      tag = this.tag = document_1.createElement('video');
21058      while (el.children.length) {
21059        tag.appendChild(el.firstChild);
21060      }
21061
21062      if (!hasClass(el, 'video-js')) {
21063        addClass(el, 'video-js');
21064      }
21065
21066      el.appendChild(tag);
21067
21068      playerElIngest = this.playerElIngest_ = el;
21069    }
21070
21071    // set tabindex to -1 so we could focus on the player element
21072    tag.setAttribute('tabindex', '-1');
21073
21074    // Remove width/height attrs from tag so CSS can make it 100% width/height
21075    tag.removeAttribute('width');
21076    tag.removeAttribute('height');
21077
21078    Object.getOwnPropertyNames(attrs).forEach(function (attr) {
21079      // workaround so we don't totally break IE7
21080      // http://stackoverflow.com/questions/3653444/css-styles-not-applied-on-dynamic-elements-in-internet-explorer-7
21081      if (attr === 'class') {
21082        el.className += ' ' + attrs[attr];
21083
21084        if (divEmbed) {
21085          tag.className += ' ' + attrs[attr];
21086        }
21087      } else {
21088        el.setAttribute(attr, attrs[attr]);
21089
21090        if (divEmbed) {
21091          tag.setAttribute(attr, attrs[attr]);
21092        }
21093      }
21094    });
21095
21096    // Update tag id/class for use as HTML5 playback tech
21097    // Might think we should do this after embedding in container so .vjs-tech class
21098    // doesn't flash 100% width/height, but class only applies with .video-js parent
21099    tag.playerId = tag.id;
21100    tag.id += '_html5_api';
21101    tag.className = 'vjs-tech';
21102
21103    // Make player findable on elements
21104    tag.player = el.player = this;
21105    // Default state of video is paused
21106    this.addClass('vjs-paused');
21107
21108    // Add a style element in the player that we'll use to set the width/height
21109    // of the player in a way that's still overrideable by CSS, just like the
21110    // video element
21111    if (window_1.VIDEOJS_NO_DYNAMIC_STYLE !== true) {
21112      this.styleEl_ = createStyleElement('vjs-styles-dimensions');
21113      var defaultsStyleEl = $('.vjs-styles-defaults');
21114      var head = $('head');
21115
21116      head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
21117    }
21118
21119    // Pass in the width/height/aspectRatio options which will update the style el
21120    this.width(this.options_.width);
21121    this.height(this.options_.height);
21122    this.fluid(this.options_.fluid);
21123    this.aspectRatio(this.options_.aspectRatio);
21124
21125    // Hide any links within the video/audio tag, because IE doesn't hide them completely.
21126    var links = tag.getElementsByTagName('a');
21127
21128    for (var i = 0; i < links.length; i++) {
21129      var linkEl = links.item(i);
21130
21131      addClass(linkEl, 'vjs-hidden');
21132      linkEl.setAttribute('hidden', 'hidden');
21133    }
21134
21135    // insertElFirst seems to cause the networkState to flicker from 3 to 2, so
21136    // keep track of the original for later so we can know if the source originally failed
21137    tag.initNetworkState_ = tag.networkState;
21138
21139    // Wrap video tag in div (el/box) container
21140    if (tag.parentNode && !playerElIngest) {
21141      tag.parentNode.insertBefore(el, tag);
21142    }
21143
21144    // insert the tag as the first child of the player element
21145    // then manually add it to the children array so that this.addChild
21146    // will work properly for other components
21147    //
21148    // Breaks iPhone, fixed in HTML5 setup.
21149    prependTo(tag, el);
21150    this.children_.unshift(tag);
21151
21152    // Set lang attr on player to ensure CSS :lang() in consistent with player
21153    // if it's been set to something different to the doc
21154    this.el_.setAttribute('lang', this.language_);
21155
21156    this.el_ = el;
21157
21158    return el;
21159  };
21160
21161  /**
21162   * A getter/setter for the `Player`'s width. Returns the player's configured value.
21163   * To get the current width use `currentWidth()`.
21164   *
21165   * @param {number} [value]
21166   *        The value to set the `Player`'s width to.
21167   *
21168   * @param {boolean} [skipListeners]
21169   *        Skip the playerresize event trigger
21170   *
21171   * @return {number}
21172   *         The current width of the `Player` when getting.
21173   */
21174
21175
21176  Player.prototype.width = function width(value, skipListeners) {
21177    return this.dimension('width', value, skipListeners);
21178  };
21179
21180  /**
21181   * A getter/setter for the `Player`'s height. Returns the player's configured value.
21182   * To get the current height use `currentheight()`.
21183   *
21184   * @param {number} [value]
21185   *        The value to set the `Player`'s heigth to.
21186   *
21187   * @param {boolean} [skipListeners]
21188   *        Skip the playerresize event trigger
21189   *
21190   * @return {number}
21191   *         The current height of the `Player` when getting.
21192   */
21193
21194
21195  Player.prototype.height = function height(value, skipListeners) {
21196    return this.dimension('height', value, skipListeners);
21197  };
21198
21199  /**
21200   * A getter/setter for the `Player`'s width & height.
21201   *
21202   * @fires Player#playerresize
21203   *
21204   * @param {string} dimension
21205   *        This string can be:
21206   *        - 'width'
21207   *        - 'height'
21208   *
21209   * @param {number} [value]
21210   *        Value for dimension specified in the first argument.
21211   *
21212   * @param {boolean} [skipListeners]
21213   *        Skip the playerresize event trigger
21214   *
21215   * @return {number}
21216   *         The dimension arguments value when getting (width/height).
21217   */
21218
21219
21220  Player.prototype.dimension = function dimension(_dimension, value, skipListeners) {
21221    var privDimension = _dimension + '_';
21222
21223    if (value === undefined) {
21224      return this[privDimension] || 0;
21225    }
21226
21227    if (value === '') {
21228      // If an empty string is given, reset the dimension to be automatic
21229      this[privDimension] = undefined;
21230      this.updateStyleEl_();
21231      return;
21232    }
21233
21234    var parsedVal = parseFloat(value);
21235
21236    if (isNaN(parsedVal)) {
21237      log$1.error('Improper value "' + value + '" supplied for for ' + _dimension);
21238      return;
21239    }
21240
21241    this[privDimension] = parsedVal;
21242    this.updateStyleEl_();
21243
21244    // skipListeners allows us to avoid triggering the resize event when setting both width and height
21245    if (this.isReady_ && !skipListeners) {
21246      /**
21247       * Triggered when the player is resized.
21248       *
21249       * @event Player#playerresize
21250       * @type {EventTarget~Event}
21251       */
21252      this.trigger('playerresize');
21253    }
21254  };
21255
21256  /**
21257   * A getter/setter/toggler for the vjs-fluid `className` on the `Player`.
21258   *
21259   * @param {boolean} [bool]
21260   *        - A value of true adds the class.
21261   *        - A value of false removes the class.
21262   *        - No value will toggle the fluid class.
21263   *
21264   * @return {boolean|undefined}
21265   *         - The value of fluid when getting.
21266   *         - `undefined` when setting.
21267   */
21268
21269
21270  Player.prototype.fluid = function fluid(bool) {
21271    if (bool === undefined) {
21272      return !!this.fluid_;
21273    }
21274
21275    this.fluid_ = !!bool;
21276
21277    if (bool) {
21278      this.addClass('vjs-fluid');
21279    } else {
21280      this.removeClass('vjs-fluid');
21281    }
21282
21283    this.updateStyleEl_();
21284  };
21285
21286  /**
21287   * Get/Set the aspect ratio
21288   *
21289   * @param {string} [ratio]
21290   *        Aspect ratio for player
21291   *
21292   * @return {string|undefined}
21293   *         returns the current aspect ratio when getting
21294   */
21295
21296  /**
21297   * A getter/setter for the `Player`'s aspect ratio.
21298   *
21299   * @param {string} [ratio]
21300   *        The value to set the `Player's aspect ratio to.
21301   *
21302   * @return {string|undefined}
21303   *         - The current aspect ratio of the `Player` when getting.
21304   *         - undefined when setting
21305   */
21306
21307
21308  Player.prototype.aspectRatio = function aspectRatio(ratio) {
21309    if (ratio === undefined) {
21310      return this.aspectRatio_;
21311    }
21312
21313    // Check for width:height format
21314    if (!/^\d+\:\d+$/.test(ratio)) {
21315      throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
21316    }
21317    this.aspectRatio_ = ratio;
21318
21319    // We're assuming if you set an aspect ratio you want fluid mode,
21320    // because in fixed mode you could calculate width and height yourself.
21321    this.fluid(true);
21322
21323    this.updateStyleEl_();
21324  };
21325
21326  /**
21327   * Update styles of the `Player` element (height, width and aspect ratio).
21328   *
21329   * @private
21330   * @listens Tech#loadedmetadata
21331   */
21332
21333
21334  Player.prototype.updateStyleEl_ = function updateStyleEl_() {
21335    if (window_1.VIDEOJS_NO_DYNAMIC_STYLE === true) {
21336      var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width;
21337      var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height;
21338      var techEl = this.tech_ && this.tech_.el();
21339
21340      if (techEl) {
21341        if (_width >= 0) {
21342          techEl.width = _width;
21343        }
21344        if (_height >= 0) {
21345          techEl.height = _height;
21346        }
21347      }
21348
21349      return;
21350    }
21351
21352    var width = void 0;
21353    var height = void 0;
21354    var aspectRatio = void 0;
21355    var idClass = void 0;
21356
21357    // The aspect ratio is either used directly or to calculate width and height.
21358    if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') {
21359      // Use any aspectRatio that's been specifically set
21360      aspectRatio = this.aspectRatio_;
21361    } else if (this.videoWidth() > 0) {
21362      // Otherwise try to get the aspect ratio from the video metadata
21363      aspectRatio = this.videoWidth() + ':' + this.videoHeight();
21364    } else {
21365      // Or use a default. The video element's is 2:1, but 16:9 is more common.
21366      aspectRatio = '16:9';
21367    }
21368
21369    // Get the ratio as a decimal we can use to calculate dimensions
21370    var ratioParts = aspectRatio.split(':');
21371    var ratioMultiplier = ratioParts[1] / ratioParts[0];
21372
21373    if (this.width_ !== undefined) {
21374      // Use any width that's been specifically set
21375      width = this.width_;
21376    } else if (this.height_ !== undefined) {
21377      // Or calulate the width from the aspect ratio if a height has been set
21378      width = this.height_ / ratioMultiplier;
21379    } else {
21380      // Or use the video's metadata, or use the video el's default of 300
21381      width = this.videoWidth() || 300;
21382    }
21383
21384    if (this.height_ !== undefined) {
21385      // Use any height that's been specifically set
21386      height = this.height_;
21387    } else {
21388      // Otherwise calculate the height from the ratio and the width
21389      height = width * ratioMultiplier;
21390    }
21391
21392    // Ensure the CSS class is valid by starting with an alpha character
21393    if (/^[^a-zA-Z]/.test(this.id())) {
21394      idClass = 'dimensions-' + this.id();
21395    } else {
21396      idClass = this.id() + '-dimensions';
21397    }
21398
21399    // Ensure the right class is still on the player for the style element
21400    this.addClass(idClass);
21401
21402    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    ');
21403  };
21404
21405  /**
21406   * Load/Create an instance of playback {@link Tech} including element
21407   * and API methods. Then append the `Tech` element in `Player` as a child.
21408   *
21409   * @param {string} techName
21410   *        name of the playback technology
21411   *
21412   * @param {string} source
21413   *        video source
21414   *
21415   * @private
21416   */
21417
21418
21419  Player.prototype.loadTech_ = function loadTech_(techName, source) {
21420    var _this2 = this;
21421
21422    // Pause and remove current playback technology
21423    if (this.tech_) {
21424      this.unloadTech_();
21425    }
21426
21427    var titleTechName = toTitleCase(techName);
21428    var camelTechName = techName.charAt(0).toLowerCase() + techName.slice(1);
21429
21430    // get rid of the HTML5 video tag as soon as we are using another tech
21431    if (titleTechName !== 'Html5' && this.tag) {
21432      Tech.getTech('Html5').disposeMediaElement(this.tag);
21433      this.tag.player = null;
21434      this.tag = null;
21435    }
21436
21437    this.techName_ = titleTechName;
21438
21439    // Turn off API access because we're loading a new tech that might load asynchronously
21440    this.isReady_ = false;
21441
21442    // Grab tech-specific options from player options and add source and parent element to use.
21443    var techOptions = {
21444      source: source,
21445      'nativeControlsForTouch': this.options_.nativeControlsForTouch,
21446      'playerId': this.id(),
21447      'techId': this.id() + '_' + titleTechName + '_api',
21448      'autoplay': this.options_.autoplay,
21449      'playsinline': this.options_.playsinline,
21450      'preload': this.options_.preload,
21451      'loop': this.options_.loop,
21452      'muted': this.options_.muted,
21453      'poster': this.poster(),
21454      'language': this.language(),
21455      'playerElIngest': this.playerElIngest_ || false,
21456      'vtt.js': this.options_['vtt.js']
21457    };
21458
21459    ALL.names.forEach(function (name$$1) {
21460      var props = ALL[name$$1];
21461
21462      techOptions[props.getterName] = _this2[props.privateName];
21463    });
21464
21465    assign(techOptions, this.options_[titleTechName]);
21466    assign(techOptions, this.options_[camelTechName]);
21467    assign(techOptions, this.options_[techName.toLowerCase()]);
21468
21469    if (this.tag) {
21470      techOptions.tag = this.tag;
21471    }
21472
21473    if (source && source.src === this.cache_.src && this.cache_.currentTime > 0) {
21474      techOptions.startTime = this.cache_.currentTime;
21475    }
21476
21477    // Initialize tech instance
21478    var TechClass = Tech.getTech(techName);
21479
21480    if (!TechClass) {
21481      throw new Error('No Tech named \'' + titleTechName + '\' exists! \'' + titleTechName + '\' should be registered using videojs.registerTech()\'');
21482    }
21483
21484    this.tech_ = new TechClass(techOptions);
21485
21486    // player.triggerReady is always async, so don't need this to be async
21487    this.tech_.ready(bind(this, this.handleTechReady_), true);
21488
21489    textTrackConverter.jsonToTextTracks(this.textTracksJson_ || [], this.tech_);
21490
21491    // Listen to all HTML5-defined events and trigger them on the player
21492    TECH_EVENTS_RETRIGGER.forEach(function (event) {
21493      _this2.on(_this2.tech_, event, _this2['handleTech' + toTitleCase(event) + '_']);
21494    });
21495    this.on(this.tech_, 'loadstart', this.handleTechLoadStart_);
21496    this.on(this.tech_, 'waiting', this.handleTechWaiting_);
21497    this.on(this.tech_, 'canplay', this.handleTechCanPlay_);
21498    this.on(this.tech_, 'canplaythrough', this.handleTechCanPlayThrough_);
21499    this.on(this.tech_, 'playing', this.handleTechPlaying_);
21500    this.on(this.tech_, 'ended', this.handleTechEnded_);
21501    this.on(this.tech_, 'seeking', this.handleTechSeeking_);
21502    this.on(this.tech_, 'seeked', this.handleTechSeeked_);
21503    this.on(this.tech_, 'play', this.handleTechPlay_);
21504    this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_);
21505    this.on(this.tech_, 'pause', this.handleTechPause_);
21506    this.on(this.tech_, 'durationchange', this.handleTechDurationChange_);
21507    this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_);
21508    this.on(this.tech_, 'error', this.handleTechError_);
21509    this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_);
21510    this.on(this.tech_, 'posterchange', this.handleTechPosterChange_);
21511    this.on(this.tech_, 'textdata', this.handleTechTextData_);
21512
21513    this.usingNativeControls(this.techGet_('controls'));
21514
21515    if (this.controls() && !this.usingNativeControls()) {
21516      this.addTechControlsListeners_();
21517    }
21518
21519    // Add the tech element in the DOM if it was not already there
21520    // Make sure to not insert the original video element if using Html5
21521    if (this.tech_.el().parentNode !== this.el() && (titleTechName !== 'Html5' || !this.tag)) {
21522      prependTo(this.tech_.el(), this.el());
21523    }
21524
21525    // Get rid of the original video tag reference after the first tech is loaded
21526    if (this.tag) {
21527      this.tag.player = null;
21528      this.tag = null;
21529    }
21530  };
21531
21532  /**
21533   * Unload and dispose of the current playback {@link Tech}.
21534   *
21535   * @private
21536   */
21537
21538
21539  Player.prototype.unloadTech_ = function unloadTech_() {
21540    var _this3 = this;
21541
21542    // Save the current text tracks so that we can reuse the same text tracks with the next tech
21543    ALL.names.forEach(function (name$$1) {
21544      var props = ALL[name$$1];
21545
21546      _this3[props.privateName] = _this3[props.getterName]();
21547    });
21548    this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_);
21549
21550    this.isReady_ = false;
21551
21552    this.tech_.dispose();
21553
21554    this.tech_ = false;
21555  };
21556
21557  /**
21558   * Return a reference to the current {@link Tech}.
21559   * It will print a warning by default about the danger of using the tech directly
21560   * but any argument that is passed in will silence the warning.
21561   *
21562   * @param {*} [safety]
21563   *        Anything passed in to silence the warning
21564   *
21565   * @return {Tech}
21566   *         The Tech
21567   */
21568
21569
21570  Player.prototype.tech = function tech(safety) {
21571    if (safety === undefined) {
21572      log$1.warn(tsml(_templateObject$1));
21573    }
21574
21575    return this.tech_;
21576  };
21577
21578  /**
21579   * Set up click and touch listeners for the playback element
21580   *
21581   * - On desktops: a click on the video itself will toggle playback
21582   * - On mobile devices: a click on the video toggles controls
21583   *   which is done by toggling the user state between active and
21584   *   inactive
21585   * - A tap can signal that a user has become active or has become inactive
21586   *   e.g. a quick tap on an iPhone movie should reveal the controls. Another
21587   *   quick tap should hide them again (signaling the user is in an inactive
21588   *   viewing state)
21589   * - In addition to this, we still want the user to be considered inactive after
21590   *   a few seconds of inactivity.
21591   *
21592   * > Note: the only part of iOS interaction we can't mimic with this setup
21593   * is a touch and hold on the video element counting as activity in order to
21594   * keep the controls showing, but that shouldn't be an issue. A touch and hold
21595   * on any controls will still keep the user active
21596   *
21597   * @private
21598   */
21599
21600
21601  Player.prototype.addTechControlsListeners_ = function addTechControlsListeners_() {
21602    // Make sure to remove all the previous listeners in case we are called multiple times.
21603    this.removeTechControlsListeners_();
21604
21605    // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
21606    // trigger mousedown/up.
21607    // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
21608    // Any touch events are set to block the mousedown event from happening
21609    this.on(this.tech_, 'mousedown', this.handleTechClick_);
21610
21611    // If the controls were hidden we don't want that to change without a tap event
21612    // so we'll check if the controls were already showing before reporting user
21613    // activity
21614    this.on(this.tech_, 'touchstart', this.handleTechTouchStart_);
21615    this.on(this.tech_, 'touchmove', this.handleTechTouchMove_);
21616    this.on(this.tech_, 'touchend', this.handleTechTouchEnd_);
21617
21618    // The tap listener needs to come after the touchend listener because the tap
21619    // listener cancels out any reportedUserActivity when setting userActive(false)
21620    this.on(this.tech_, 'tap', this.handleTechTap_);
21621  };
21622
21623  /**
21624   * Remove the listeners used for click and tap controls. This is needed for
21625   * toggling to controls disabled, where a tap/touch should do nothing.
21626   *
21627   * @private
21628   */
21629
21630
21631  Player.prototype.removeTechControlsListeners_ = function removeTechControlsListeners_() {
21632    // We don't want to just use `this.off()` because there might be other needed
21633    // listeners added by techs that extend this.
21634    this.off(this.tech_, 'tap', this.handleTechTap_);
21635    this.off(this.tech_, 'touchstart', this.handleTechTouchStart_);
21636    this.off(this.tech_, 'touchmove', this.handleTechTouchMove_);
21637    this.off(this.tech_, 'touchend', this.handleTechTouchEnd_);
21638    this.off(this.tech_, 'mousedown', this.handleTechClick_);
21639  };
21640
21641  /**
21642   * Player waits for the tech to be ready
21643   *
21644   * @private
21645   */
21646
21647
21648  Player.prototype.handleTechReady_ = function handleTechReady_() {
21649    this.triggerReady();
21650
21651    // Keep the same volume as before
21652    if (this.cache_.volume) {
21653      this.techCall_('setVolume', this.cache_.volume);
21654    }
21655
21656    // Look if the tech found a higher resolution poster while loading
21657    this.handleTechPosterChange_();
21658
21659    // Update the duration if available
21660    this.handleTechDurationChange_();
21661
21662    // Chrome and Safari both have issues with autoplay.
21663    // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work.
21664    // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays)
21665    // This fixes both issues. Need to wait for API, so it updates displays correctly
21666    if ((this.src() || this.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) {
vendor: 24,027 bytes, lines 21667-22615
21667      try {
21668        // Chrome Fix. Fixed in Chrome v16.
21669        delete this.tag.poster;
21670      } catch (e) {
21671        log$1('deleting tag.poster throws in some browsers', e);
21672      }
21673    }
21674  };
21675
21676  /**
21677   * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This
21678   * function will also trigger {@link Player#firstplay} if it is the first loadstart
21679   * for a video.
21680   *
21681   * @fires Player#loadstart
21682   * @fires Player#firstplay
21683   * @listens Tech#loadstart
21684   * @private
21685   */
21686
21687
21688  Player.prototype.handleTechLoadStart_ = function handleTechLoadStart_() {
21689    // TODO: Update to use `emptied` event instead. See #1277.
21690
21691    this.removeClass('vjs-ended');
21692    this.removeClass('vjs-seeking');
21693
21694    // reset the error state
21695    this.error(null);
21696
21697    // If it's already playing we want to trigger a firstplay event now.
21698    // The firstplay event relies on both the play and loadstart events
21699    // which can happen in any order for a new source
21700    if (!this.paused()) {
21701      /**
21702       * Fired when the user agent begins looking for media data
21703       *
21704       * @event Player#loadstart
21705       * @type {EventTarget~Event}
21706       */
21707      this.trigger('loadstart');
21708      this.trigger('firstplay');
21709    } else {
21710      // reset the hasStarted state
21711      this.hasStarted(false);
21712      this.trigger('loadstart');
21713    }
21714  };
21715
21716  /**
21717   * Add/remove the vjs-has-started class
21718   *
21719   * @fires Player#firstplay
21720   *
21721   * @param {boolean} request
21722   *        - true: adds the class
21723   *        - false: remove the class
21724   *
21725   * @return {boolean}
21726   *         the boolean value of hasStarted_
21727   */
21728
21729
21730  Player.prototype.hasStarted = function hasStarted(request) {
21731    if (request === undefined) {
21732      // act as getter, if we have no request to change
21733      return this.hasStarted_;
21734    }
21735
21736    if (request === this.hasStarted_) {
21737      return;
21738    }
21739
21740    this.hasStarted_ = request;
21741
21742    if (this.hasStarted_) {
21743      this.addClass('vjs-has-started');
21744      this.trigger('firstplay');
21745    } else {
21746      this.removeClass('vjs-has-started');
21747    }
21748  };
21749
21750  /**
21751   * Fired whenever the media begins or resumes playback
21752   *
21753   * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play}
21754   * @fires Player#play
21755   * @listens Tech#play
21756   * @private
21757   */
21758
21759
21760  Player.prototype.handleTechPlay_ = function handleTechPlay_() {
21761    this.removeClass('vjs-ended');
21762    this.removeClass('vjs-paused');
21763    this.addClass('vjs-playing');
21764
21765    // hide the poster when the user hits play
21766    this.hasStarted(true);
21767    /**
21768     * Triggered whenever an {@link Tech#play} event happens. Indicates that
21769     * playback has started or resumed.
21770     *
21771     * @event Player#play
21772     * @type {EventTarget~Event}
21773     */
21774    this.trigger('play');
21775  };
21776
21777  /**
21778   * Retrigger the `waiting` event that was triggered by the {@link Tech}.
21779   *
21780   * @fires Player#waiting
21781   * @listens Tech#waiting
21782   * @private
21783   */
21784
21785
21786  Player.prototype.handleTechWaiting_ = function handleTechWaiting_() {
21787    var _this4 = this;
21788
21789    this.addClass('vjs-waiting');
21790    /**
21791     * A readyState change on the DOM element has caused playback to stop.
21792     *
21793     * @event Player#waiting
21794     * @type {EventTarget~Event}
21795     */
21796    this.trigger('waiting');
21797    this.one('timeupdate', function () {
21798      return _this4.removeClass('vjs-waiting');
21799    });
21800  };
21801
21802  /**
21803   * Retrigger the `canplay` event that was triggered by the {@link Tech}.
21804   * > Note: This is not consistent between browsers. See #1351
21805   *
21806   * @fires Player#canplay
21807   * @listens Tech#canplay
21808   * @private
21809   */
21810
21811
21812  Player.prototype.handleTechCanPlay_ = function handleTechCanPlay_() {
21813    this.removeClass('vjs-waiting');
21814    /**
21815     * The media has a readyState of HAVE_FUTURE_DATA or greater.
21816     *
21817     * @event Player#canplay
21818     * @type {EventTarget~Event}
21819     */
21820    this.trigger('canplay');
21821  };
21822
21823  /**
21824   * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}.
21825   *
21826   * @fires Player#canplaythrough
21827   * @listens Tech#canplaythrough
21828   * @private
21829   */
21830
21831
21832  Player.prototype.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() {
21833    this.removeClass('vjs-waiting');
21834    /**
21835     * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the
21836     * entire media file can be played without buffering.
21837     *
21838     * @event Player#canplaythrough
21839     * @type {EventTarget~Event}
21840     */
21841    this.trigger('canplaythrough');
21842  };
21843
21844  /**
21845   * Retrigger the `playing` event that was triggered by the {@link Tech}.
21846   *
21847   * @fires Player#playing
21848   * @listens Tech#playing
21849   * @private
21850   */
21851
21852
21853  Player.prototype.handleTechPlaying_ = function handleTechPlaying_() {
21854    this.removeClass('vjs-waiting');
21855    /**
21856     * The media is no longer blocked from playback, and has started playing.
21857     *
21858     * @event Player#playing
21859     * @type {EventTarget~Event}
21860     */
21861    this.trigger('playing');
21862  };
21863
21864  /**
21865   * Retrigger the `seeking` event that was triggered by the {@link Tech}.
21866   *
21867   * @fires Player#seeking
21868   * @listens Tech#seeking
21869   * @private
21870   */
21871
21872
21873  Player.prototype.handleTechSeeking_ = function handleTechSeeking_() {
21874    this.addClass('vjs-seeking');
21875    /**
21876     * Fired whenever the player is jumping to a new time
21877     *
21878     * @event Player#seeking
21879     * @type {EventTarget~Event}
21880     */
21881    this.trigger('seeking');
21882  };
21883
21884  /**
21885   * Retrigger the `seeked` event that was triggered by the {@link Tech}.
21886   *
21887   * @fires Player#seeked
21888   * @listens Tech#seeked
21889   * @private
21890   */
21891
21892
21893  Player.prototype.handleTechSeeked_ = function handleTechSeeked_() {
21894    this.removeClass('vjs-seeking');
21895    /**
21896     * Fired when the player has finished jumping to a new time
21897     *
21898     * @event Player#seeked
21899     * @type {EventTarget~Event}
21900     */
21901    this.trigger('seeked');
21902  };
21903
21904  /**
21905   * Retrigger the `firstplay` event that was triggered by the {@link Tech}.
21906   *
21907   * @fires Player#firstplay
21908   * @listens Tech#firstplay
21909   * @deprecated As of 6.0 firstplay event is deprecated.
21910   * @deprecated As of 6.0 passing the `starttime` option to the player and the firstplay event are deprecated.
21911   * @private
21912   */
21913
21914
21915  Player.prototype.handleTechFirstPlay_ = function handleTechFirstPlay_() {
21916    // If the first starttime attribute is specified
21917    // then we will start at the given offset in seconds
21918    if (this.options_.starttime) {
21919      log$1.warn('Passing the `starttime` option to the player will be deprecated in 6.0');
21920      this.currentTime(this.options_.starttime);
21921    }
21922
21923    this.addClass('vjs-has-started');
21924    /**
21925     * Fired the first time a video is played. Not part of the HLS spec, and this is
21926     * probably not the best implementation yet, so use sparingly. If you don't have a
21927     * reason to prevent playback, use `myPlayer.one('play');` instead.
21928     *
21929     * @event Player#firstplay
21930     * @deprecated As of 6.0 firstplay event is deprecated.
21931     * @type {EventTarget~Event}
21932     */
21933    this.trigger('firstplay');
21934  };
21935
21936  /**
21937   * Retrigger the `pause` event that was triggered by the {@link Tech}.
21938   *
21939   * @fires Player#pause
21940   * @listens Tech#pause
21941   * @private
21942   */
21943
21944
21945  Player.prototype.handleTechPause_ = function handleTechPause_() {
21946    this.removeClass('vjs-playing');
21947    this.addClass('vjs-paused');
21948    /**
21949     * Fired whenever the media has been paused
21950     *
21951     * @event Player#pause
21952     * @type {EventTarget~Event}
21953     */
21954    this.trigger('pause');
21955  };
21956
21957  /**
21958   * Retrigger the `ended` event that was triggered by the {@link Tech}.
21959   *
21960   * @fires Player#ended
21961   * @listens Tech#ended
21962   * @private
21963   */
21964
21965
21966  Player.prototype.handleTechEnded_ = function handleTechEnded_() {
21967    this.addClass('vjs-ended');
21968    if (this.options_.loop) {
21969      this.currentTime(0);
21970      this.play();
21971    } else if (!this.paused()) {
21972      this.pause();
21973    }
21974
21975    /**
21976     * Fired when the end of the media resource is reached (currentTime == duration)
21977     *
21978     * @event Player#ended
21979     * @type {EventTarget~Event}
21980     */
21981    this.trigger('ended');
21982  };
21983
21984  /**
21985   * Fired when the duration of the media resource is first known or changed
21986   *
21987   * @listens Tech#durationchange
21988   * @private
21989   */
21990
21991
21992  Player.prototype.handleTechDurationChange_ = function handleTechDurationChange_() {
21993    this.duration(this.techGet_('duration'));
21994  };
21995
21996  /**
21997   * Handle a click on the media element to play/pause
21998   *
21999   * @param {EventTarget~Event} event
22000   *        the event that caused this function to trigger
22001   *
22002   * @listens Tech#mousedown
22003   * @private
22004   */
22005
22006
22007  Player.prototype.handleTechClick_ = function handleTechClick_(event) {
22008    if (!isSingleLeftClick(event)) {
22009      return;
22010    }
22011
22012    // When controls are disabled a click should not toggle playback because
22013    // the click is considered a control
22014    if (!this.controls_) {
22015      return;
22016    }
22017
22018    if (this.paused()) {
22019      this.play();
22020    } else {
22021      this.pause();
22022    }
22023  };
22024
22025  /**
22026   * Handle a tap on the media element. It will toggle the user
22027   * activity state, which hides and shows the controls.
22028   *
22029   * @listens Tech#tap
22030   * @private
22031   */
22032
22033
22034  Player.prototype.handleTechTap_ = function handleTechTap_() {
22035    this.userActive(!this.userActive());
22036  };
22037
22038  /**
22039   * Handle touch to start
22040   *
22041   * @listens Tech#touchstart
22042   * @private
22043   */
22044
22045
22046  Player.prototype.handleTechTouchStart_ = function handleTechTouchStart_() {
22047    this.userWasActive = this.userActive();
22048  };
22049
22050  /**
22051   * Handle touch to move
22052   *
22053   * @listens Tech#touchmove
22054   * @private
22055   */
22056
22057
22058  Player.prototype.handleTechTouchMove_ = function handleTechTouchMove_() {
22059    if (this.userWasActive) {
22060      this.reportUserActivity();
22061    }
22062  };
22063
22064  /**
22065   * Handle touch to end
22066   *
22067   * @param {EventTarget~Event} event
22068   *        the touchend event that triggered
22069   *        this function
22070   *
22071   * @listens Tech#touchend
22072   * @private
22073   */
22074
22075
22076  Player.prototype.handleTechTouchEnd_ = function handleTechTouchEnd_(event) {
22077    // Stop the mouse events from also happening
22078    event.preventDefault();
22079  };
22080
22081  /**
22082   * Fired when the player switches in or out of fullscreen mode
22083   *
22084   * @private
22085   * @listens Player#fullscreenchange
22086   */
22087
22088
22089  Player.prototype.handleFullscreenChange_ = function handleFullscreenChange_() {
22090    if (this.isFullscreen()) {
22091      this.addClass('vjs-fullscreen');
22092    } else {
22093      this.removeClass('vjs-fullscreen');
22094    }
22095  };
22096
22097  /**
22098   * native click events on the SWF aren't triggered on IE11, Win8.1RT
22099   * use stageclick events triggered from inside the SWF instead
22100   *
22101   * @private
22102   * @listens stageclick
22103   */
22104
22105
22106  Player.prototype.handleStageClick_ = function handleStageClick_() {
22107    this.reportUserActivity();
22108  };
22109
22110  /**
22111   * Handle Tech Fullscreen Change
22112   *
22113   * @param {EventTarget~Event} event
22114   *        the fullscreenchange event that triggered this function
22115   *
22116   * @param {Object} data
22117   *        the data that was sent with the event
22118   *
22119   * @private
22120   * @listens Tech#fullscreenchange
22121   * @fires Player#fullscreenchange
22122   */
22123
22124
22125  Player.prototype.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) {
22126    if (data) {
22127      this.isFullscreen(data.isFullscreen);
22128    }
22129    /**
22130     * Fired when going in and out of fullscreen.
22131     *
22132     * @event Player#fullscreenchange
22133     * @type {EventTarget~Event}
22134     */
22135    this.trigger('fullscreenchange');
22136  };
22137
22138  /**
22139   * Fires when an error occurred during the loading of an audio/video.
22140   *
22141   * @private
22142   * @listens Tech#error
22143   */
22144
22145
22146  Player.prototype.handleTechError_ = function handleTechError_() {
22147    var error = this.tech_.error();
22148
22149    this.error(error);
22150  };
22151
22152  /**
22153   * Retrigger the `textdata` event that was triggered by the {@link Tech}.
22154   *
22155   * @fires Player#textdata
22156   * @listens Tech#textdata
22157   * @private
22158   */
22159
22160
22161  Player.prototype.handleTechTextData_ = function handleTechTextData_() {
22162    var data = null;
22163
22164    if (arguments.length > 1) {
22165      data = arguments[1];
22166    }
22167
22168    /**
22169     * Fires when we get a textdata event from tech
22170     *
22171     * @event Player#textdata
22172     * @type {EventTarget~Event}
22173     */
22174    this.trigger('textdata', data);
22175  };
22176
22177  /**
22178   * Get object for cached values.
22179   *
22180   * @return {Object}
22181   *         get the current object cache
22182   */
22183
22184
22185  Player.prototype.getCache = function getCache() {
22186    return this.cache_;
22187  };
22188
22189  /**
22190   * Pass values to the playback tech
22191   *
22192   * @param {string} [method]
22193   *        the method to call
22194   *
22195   * @param {Object} arg
22196   *        the argument to pass
22197   *
22198   * @private
22199   */
22200
22201
22202  Player.prototype.techCall_ = function techCall_(method, arg) {
22203    // If it's not ready yet, call method when it is
22204
22205    this.ready(function () {
22206      if (method in allowedSetters) {
22207        return set$1(this.middleware_, this.tech_, method, arg);
22208      }
22209
22210      try {
22211        if (this.tech_) {
22212          this.tech_[method](arg);
22213        }
22214      } catch (e) {
22215        log$1(e);
22216        throw e;
22217      }
22218    }, true);
22219  };
22220
22221  /**
22222   * Get calls can't wait for the tech, and sometimes don't need to.
22223   *
22224   * @param {string} method
22225   *        Tech method
22226   *
22227   * @return {Function|undefined}
22228   *         the method or undefined
22229   *
22230   * @private
22231   */
22232
22233
22234  Player.prototype.techGet_ = function techGet_(method) {
22235    if (!this.tech_ || !this.tech_.isReady_) {
22236      return;
22237    }
22238
22239    if (method in allowedGetters) {
22240      return get$1(this.middleware_, this.tech_, method);
22241    }
22242
22243    // Flash likes to die and reload when you hide or reposition it.
22244    // In these cases the object methods go away and we get errors.
22245    // When that happens we'll catch the errors and inform tech that it's not ready any more.
22246    try {
22247      return this.tech_[method]();
22248    } catch (e) {
22249
22250      // When building additional tech libs, an expected method may not be defined yet
22251      if (this.tech_[method] === undefined) {
22252        log$1('Video.js: ' + method + ' method not defined for ' + this.techName_ + ' playback technology.', e);
22253        throw e;
22254      }
22255
22256      // When a method isn't available on the object it throws a TypeError
22257      if (e.name === 'TypeError') {
22258        log$1('Video.js: ' + method + ' unavailable on ' + this.techName_ + ' playback technology element.', e);
22259        this.tech_.isReady_ = false;
22260        throw e;
22261      }
22262
22263      // If error unknown, just log and throw
22264      log$1(e);
22265      throw e;
22266    }
22267  };
22268
22269  /**
22270   * Attempt to begin playback at the first opportunity.
22271   *
22272   * @return {Promise|undefined}
22273   *         Returns a `Promise` only if the browser returns one and the player
22274   *         is ready to begin playback. For some browsers and all non-ready
22275   *         situations, this will return `undefined`.
22276   */
22277
22278
22279  Player.prototype.play = function play() {
22280    var _this5 = this;
22281
22282    // If this is called while we have a play queued up on a loadstart, remove
22283    // that listener to avoid getting in a potentially bad state.
22284    if (this.playOnLoadstart_) {
22285      this.off('loadstart', this.playOnLoadstart_);
22286    }
22287
22288    // If the player/tech is not ready, queue up another call to `play()` for
22289    // when it is. This will loop back into this method for another attempt at
22290    // playback when the tech is ready.
22291    if (!this.isReady_) {
22292
22293      // Bail out if we're already waiting for `ready`!
22294      if (this.playWaitingForReady_) {
22295        return;
22296      }
22297
22298      this.playWaitingForReady_ = true;
22299      this.ready(function () {
22300        _this5.playWaitingForReady_ = false;
22301        silencePromise(_this5.play());
22302      });
22303
22304      // If the player/tech is ready and we have a source, we can attempt playback.
22305    } else if (!this.changingSrc_ && (this.src() || this.currentSrc())) {
22306      return this.techGet_('play');
22307
22308      // If the tech is ready, but we do not have a source, we'll need to wait
22309      // for both the `ready` and a `loadstart` when the source is finally
22310      // resolved by middleware and set on the player.
22311      //
22312      // This can happen if `play()` is called while changing sources or before
22313      // one has been set on the player.
22314    } else {
22315
22316      this.playOnLoadstart_ = function () {
22317        _this5.playOnLoadstart_ = null;
22318        silencePromise(_this5.play());
22319      };
22320
22321      this.one('loadstart', this.playOnLoadstart_);
22322    }
22323  };
22324
22325  /**
22326   * Pause the video playback
22327   *
22328   * @return {Player}
22329   *         A reference to the player object this function was called on
22330   */
22331
22332
22333  Player.prototype.pause = function pause() {
22334    this.techCall_('pause');
22335  };
22336
22337  /**
22338   * Check if the player is paused or has yet to play
22339   *
22340   * @return {boolean}
22341   *         - false: if the media is currently playing
22342   *         - true: if media is not currently playing
22343   */
22344
22345
22346  Player.prototype.paused = function paused() {
22347    // The initial state of paused should be true (in Safari it's actually false)
22348    return this.techGet_('paused') === false ? false : true;
22349  };
22350
22351  /**
22352   * Get a TimeRange object representing the current ranges of time that the user
22353   * has played.
22354   *
22355   * @return {TimeRange}
22356   *         A time range object that represents all the increments of time that have
22357   *         been played.
22358   */
22359
22360
22361  Player.prototype.played = function played() {
22362    return this.techGet_('played') || createTimeRanges(0, 0);
22363  };
22364
22365  /**
22366   * Returns whether or not the user is "scrubbing". Scrubbing is
22367   * when the user has clicked the progress bar handle and is
22368   * dragging it along the progress bar.
22369   *
22370   * @param {boolean} [isScrubbing]
22371   *        wether the user is or is not scrubbing
22372   *
22373   * @return {boolean}
22374   *         The value of scrubbing when getting
22375   */
22376
22377
22378  Player.prototype.scrubbing = function scrubbing(isScrubbing) {
22379    if (typeof isScrubbing === 'undefined') {
22380      return this.scrubbing_;
22381    }
22382    this.scrubbing_ = !!isScrubbing;
22383
22384    if (isScrubbing) {
22385      this.addClass('vjs-scrubbing');
22386    } else {
22387      this.removeClass('vjs-scrubbing');
22388    }
22389  };
22390
22391  /**
22392   * Get or set the current time (in seconds)
22393   *
22394   * @param {number|string} [seconds]
22395   *        The time to seek to in seconds
22396   *
22397   * @return {number}
22398   *         - the current time in seconds when getting
22399   */
22400
22401
22402  Player.prototype.currentTime = function currentTime(seconds) {
22403    if (typeof seconds !== 'undefined') {
22404      if (seconds < 0) {
22405        seconds = 0;
22406      }
22407      this.techCall_('setCurrentTime', seconds);
22408      return;
22409    }
22410
22411    // cache last currentTime and return. default to 0 seconds
22412    //
22413    // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
22414    // currentTime when scrubbing, but may not provide much performance benefit afterall.
22415    // Should be tested. Also something has to read the actual current time or the cache will
22416    // never get updated.
22417    this.cache_.currentTime = this.techGet_('currentTime') || 0;
22418    return this.cache_.currentTime;
22419  };
22420
22421  /**
22422   * Normally gets the length in time of the video in seconds;
22423   * in all but the rarest use cases an argument will NOT be passed to the method
22424   *
22425   * > **NOTE**: The video must have started loading before the duration can be
22426   * known, and in the case of Flash, may not be known until the video starts
22427   * playing.
22428   *
22429   * @fires Player#durationchange
22430   *
22431   * @param {number} [seconds]
22432   *        The duration of the video to set in seconds
22433   *
22434   * @return {number}
22435   *         - The duration of the video in seconds when getting
22436   */
22437
22438
22439  Player.prototype.duration = function duration(seconds) {
22440    if (seconds === undefined) {
22441      // return NaN if the duration is not known
22442      return this.cache_.duration !== undefined ? this.cache_.duration : NaN;
22443    }
22444
22445    seconds = parseFloat(seconds);
22446
22447    // Standardize on Inifity for signaling video is live
22448    if (seconds < 0) {
22449      seconds = Infinity;
22450    }
22451
22452    if (seconds !== this.cache_.duration) {
22453      // Cache the last set value for optimized scrubbing (esp. Flash)
22454      this.cache_.duration = seconds;
22455
22456      if (seconds === Infinity) {
22457        this.addClass('vjs-live');
22458      } else {
22459        this.removeClass('vjs-live');
22460      }
22461      /**
22462       * @event Player#durationchange
22463       * @type {EventTarget~Event}
22464       */
22465      this.trigger('durationchange');
22466    }
22467  };
22468
22469  /**
22470   * Calculates how much time is left in the video. Not part
22471   * of the native video API.
22472   *
22473   * @return {number}
22474   *         The time remaining in seconds
22475   */
22476
22477
22478  Player.prototype.remainingTime = function remainingTime() {
22479    return this.duration() - this.currentTime();
22480  };
22481
22482  /**
22483   * A remaining time function that is intented to be used when
22484   * the time is to be displayed directly to the user.
22485   *
22486   * @return {number}
22487   *         The rounded time remaining in seconds
22488   */
22489
22490
22491  Player.prototype.remainingTimeDisplay = function remainingTimeDisplay() {
22492    return Math.floor(this.duration()) - Math.floor(this.currentTime());
22493  };
22494
22495  //
22496  // Kind of like an array of portions of the video that have been downloaded.
22497
22498  /**
22499   * Get a TimeRange object with an array of the times of the video
22500   * that have been downloaded. If you just want the percent of the
22501   * video that's been downloaded, use bufferedPercent.
22502   *
22503   * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered}
22504   *
22505   * @return {TimeRange}
22506   *         A mock TimeRange object (following HTML spec)
22507   */
22508
22509
22510  Player.prototype.buffered = function buffered() {
22511    var buffered = this.techGet_('buffered');
22512
22513    if (!buffered || !buffered.length) {
22514      buffered = createTimeRanges(0, 0);
22515    }
22516
22517    return buffered;
22518  };
22519
22520  /**
22521   * Get the percent (as a decimal) of the video that's been downloaded.
22522   * This method is not a part of the native HTML video API.
22523   *
22524   * @return {number}
22525   *         A decimal between 0 and 1 representing the percent
22526   *         that is bufferred 0 being 0% and 1 being 100%
22527   */
22528
22529
22530  Player.prototype.bufferedPercent = function bufferedPercent$$1() {
22531    return bufferedPercent(this.buffered(), this.duration());
22532  };
22533
22534  /**
22535   * Get the ending time of the last buffered time range
22536   * This is used in the progress bar to encapsulate all time ranges.
22537   *
22538   * @return {number}
22539   *         The end of the last buffered time range
22540   */
22541
22542
22543  Player.prototype.bufferedEnd = function bufferedEnd() {
22544    var buffered = this.buffered();
22545    var duration = this.duration();
22546    var end = buffered.end(buffered.length - 1);
22547
22548    if (end > duration) {
22549      end = duration;
22550    }
22551
22552    return end;
22553  };
22554
22555  /**
22556   * Get or set the current volume of the media
22557   *
22558   * @param  {number} [percentAsDecimal]
22559   *         The new volume as a decimal percent:
22560   *         - 0 is muted/0%/off
22561   *         - 1.0 is 100%/full
22562   *         - 0.5 is half volume or 50%
22563   *
22564   * @return {number}
22565   *         The current volume as a percent when getting
22566   */
22567
22568
22569  Player.prototype.volume = function volume(percentAsDecimal) {
22570    var vol = void 0;
22571
22572    if (percentAsDecimal !== undefined) {
22573      // Force value to between 0 and 1
22574      vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
22575      this.cache_.volume = vol;
22576      this.techCall_('setVolume', vol);
22577
22578      if (vol > 0) {
22579        this.lastVolume_(vol);
22580      }
22581
22582      return;
22583    }
22584
22585    // Default to 1 when returning current volume.
22586    vol = parseFloat(this.techGet_('volume'));
22587    return isNaN(vol) ? 1 : vol;
22588  };
22589
22590  /**
22591   * Get the current muted state, or turn mute on or off
22592   *
22593   * @param {boolean} [muted]
22594   *        - true to mute
22595   *        - false to unmute
22596   *
22597   * @return {boolean}
22598   *         - true if mute is on and getting
22599   *         - false if mute is off and getting
22600   */
22601
22602
22603  Player.prototype.muted = function muted(_muted) {
22604    if (_muted !== undefined) {
22605      this.techCall_('setMuted', _muted);
22606      return;
22607    }
22608    return this.techGet_('muted') || false;
22609  };
22610
22611  /**
22612   * Get the current defaultMuted state, or turn defaultMuted on or off. defaultMuted
22613   * indicates the state of muted on intial playback.
22614   *
22615   * ```js
22616   *   var myPlayer = videojs('some-player-id');
22617   *
22618   *   myPlayer.src("http://www.example.com/path/to/video.mp4");
22619   *
22620   *   // get, should be false
22621   *   console.log(myPlayer.defaultMuted());
22622   *   // set to true
22623   *   myPlayer.defaultMuted(true);
22624   *   // get should be true
22625   *   console.log(myPlayer.defaultMuted());
22626   * ```
22627   *
22628   * @param {boolean} [defaultMuted]
22629   *        - true to mute
22630   *        - false to unmute
22631   *
22632   * @return {boolean|Player}
22633   *         - true if defaultMuted is on and getting
22634   *         - false if defaultMuted is off and getting
22635   *         - A reference to the current player when setting
22636   */
22637
22638
22639  Player.prototype.defaultMuted = function defaultMuted(_defaultMuted) {
22640    if (_defaultMuted !== undefined) {
22641      return this.techCall_('setDefaultMuted', _defaultMuted);
22642    }
22643    return this.techGet_('defaultMuted') || false;
22644  };
22645
22646  /**
22647   * Get the last volume, or set it
22648   *
22649   * @param  {number} [percentAsDecimal]
22650   *         The new last volume as a decimal percent:
22651   *         - 0 is muted/0%/off
22652   *         - 1.0 is 100%/full
22653   *         - 0.5 is half volume or 50%
22654   *
22655   * @return {number}
22656   *         the current value of lastVolume as a percent when getting
22657   *
22658   * @private
22659   */
22660
22661
22662  Player.prototype.lastVolume_ = function lastVolume_(percentAsDecimal) {
22663    if (percentAsDecimal !== undefined && percentAsDecimal !== 0) {
22664      this.cache_.lastVolume = percentAsDecimal;
22665      return;
22666    }
22667    return this.cache_.lastVolume;
22668  };
22669
22670  /**
22671   * Check if current tech can support native fullscreen
22672   * (e.g. with built in controls like iOS, so not our flash swf)
22673   *
22674   * @return {boolean}
22675   *         if native fullscreen is supported
22676   */
22677
22678
22679  Player.prototype.supportsFullScreen = function supportsFullScreen() {
22680    return this.techGet_('supportsFullScreen') || false;
22681  };
22682
22683  /**
22684   * Check if the player is in fullscreen mode or tell the player that it
22685   * is or is not in fullscreen mode.
22686   *
22687   * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official
22688   * property and instead document.fullscreenElement is used. But isFullscreen is
22689   * still a valuable property for internal player workings.
22690   *
22691   * @param  {boolean} [isFS]
22692   *         Set the players current fullscreen state
22693   *
22694   * @return {boolean}
22695   *         - true if fullscreen is on and getting
22696   *         - false if fullscreen is off and getting
22697   */
22698
22699
22700  Player.prototype.isFullscreen = function isFullscreen(isFS) {
22701    if (isFS !== undefined) {
22702      this.isFullscreen_ = !!isFS;
22703      return;
22704    }
22705    return !!this.isFullscreen_;
22706  };
22707
22708  /**
22709   * Increase the size of the video to full screen
22710   * In some browsers, full screen is not supported natively, so it enters
22711   * "full window mode", where the video fills the browser window.
22712   * In browsers and devices that support native full screen, sometimes the
22713   * browser's default controls will be shown, and not the Video.js custom skin.
22714   * This includes most mobile devices (iOS, Android) and older versions of
22715   * Safari.
22716   *
22717   * @fires Player#fullscreenchange
22718   */
22719
22720
22721  Player.prototype.requestFullscreen = function requestFullscreen() {
22722    var fsApi = FullscreenApi;
22723
22724    this.isFullscreen(true);
22725
22726    if (fsApi.requestFullscreen) {
22727      // the browser supports going fullscreen at the element level so we can
22728      // take the controls fullscreen as well as the video
22729
22730      // Trigger fullscreenchange event after change
22731      // We have to specifically add this each time, and remove
22732      // when canceling fullscreen. Otherwise if there's multiple
22733      // players on a page, they would all be reacting to the same fullscreen
22734      // events
22735      on(document_1, fsApi.fullscreenchange, bind(this, function documentFullscreenChange(e) {
22736        this.isFullscreen(document_1[fsApi.fullscreenElement]);
22737
22738        // If cancelling fullscreen, remove event listener.
22739        if (this.isFullscreen() === false) {
22740          off(document_1, fsApi.fullscreenchange, documentFullscreenChange);
22741        }
22742        /**
22743         * @event Player#fullscreenchange
22744         * @type {EventTarget~Event}
22745         */
22746        this.trigger('fullscreenchange');
22747      }));
22748
22749      this.el_[fsApi.requestFullscreen]();
22750    } else if (this.tech_.supportsFullScreen()) {
22751      // we can't take the video.js controls fullscreen but we can go fullscreen
22752      // with native controls
22753      this.techCall_('enterFullScreen');
22754    } else {
22755      // fullscreen isn't supported so we'll just stretch the video element to
22756      // fill the viewport
22757      this.enterFullWindow();
22758      /**
22759       * @event Player#fullscreenchange
22760       * @type {EventTarget~Event}
22761       */
22762      this.trigger('fullscreenchange');
22763    }
22764  };
22765
22766  /**
22767   * Return the video to its normal size after having been in full screen mode
22768   *
22769   * @fires Player#fullscreenchange
22770   */
22771
22772
22773  Player.prototype.exitFullscreen = function exitFullscreen() {
22774    var fsApi = FullscreenApi;
22775
22776    this.isFullscreen(false);
22777
22778    // Check for browser element fullscreen support
22779    if (fsApi.requestFullscreen) {
22780      document_1[fsApi.exitFullscreen]();
22781    } else if (this.tech_.supportsFullScreen()) {
22782      this.techCall_('exitFullScreen');
22783    } else {
22784      this.exitFullWindow();
22785      /**
22786       * @event Player#fullscreenchange
22787       * @type {EventTarget~Event}
22788       */
22789      this.trigger('fullscreenchange');
22790    }
22791  };
22792
22793  /**
22794   * When fullscreen isn't supported we can stretch the
22795   * video container to as wide as the browser will let us.
22796   *
22797   * @fires Player#enterFullWindow
22798   */
22799
22800
22801  Player.prototype.enterFullWindow = function enterFullWindow() {
22802    this.isFullWindow = true;
22803
22804    // Storing original doc overflow value to return to when fullscreen is off
22805    this.docOrigOverflow = document_1.documentElement.style.overflow;
22806
22807    // Add listener for esc key to exit fullscreen
22808    on(document_1, 'keydown', bind(this, this.fullWindowOnEscKey));
22809
22810    // Hide any scroll bars
22811    document_1.documentElement.style.overflow = 'hidden';
22812
22813    // Apply fullscreen styles
22814    addClass(document_1.body, 'vjs-full-window');
22815
22816    /**
22817     * @event Player#enterFullWindow
22818     * @type {EventTarget~Event}
22819     */
22820    this.trigger('enterFullWindow');
22821  };
22822
22823  /**
22824   * Check for call to either exit full window or
22825   * full screen on ESC key
22826   *
22827   * @param {string} event
22828   *        Event to check for key press
22829   */
22830
22831
22832  Player.prototype.fullWindowOnEscKey = function fullWindowOnEscKey(event) {
22833    if (event.keyCode === 27) {
22834      if (this.isFullscreen() === true) {
22835        this.exitFullscreen();
22836      } else {
22837        this.exitFullWindow();
22838      }
22839    }
22840  };
22841
22842  /**
22843   * Exit full window
22844   *
22845   * @fires Player#exitFullWindow
22846   */
22847
22848
22849  Player.prototype.exitFullWindow = function exitFullWindow() {
22850    this.isFullWindow = false;
vendor: 4,466 bytes, lines 22851-22999
22851    off(document_1, 'keydown', this.fullWindowOnEscKey);
22852
22853    // Unhide scroll bars.
22854    document_1.documentElement.style.overflow = this.docOrigOverflow;
22855
22856    // Remove fullscreen styles
22857    removeClass(document_1.body, 'vjs-full-window');
22858
22859    // Resize the box, controller, and poster to original sizes
22860    // this.positionAll();
22861    /**
22862     * @event Player#exitFullWindow
22863     * @type {EventTarget~Event}
22864     */
22865    this.trigger('exitFullWindow');
22866  };
22867
22868  /**
22869   * Check whether the player can play a given mimetype
22870   *
22871   * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
22872   *
22873   * @param {string} type
22874   *        The mimetype to check
22875   *
22876   * @return {string}
22877   *         'probably', 'maybe', or '' (empty string)
22878   */
22879
22880
22881  Player.prototype.canPlayType = function canPlayType(type) {
22882    var can = void 0;
22883
22884    // Loop through each playback technology in the options order
22885    for (var i = 0, j = this.options_.techOrder; i < j.length; i++) {
22886      var techName = j[i];
22887      var tech = Tech.getTech(techName);
22888
22889      // Support old behavior of techs being registered as components.
22890      // Remove once that deprecated behavior is removed.
22891      if (!tech) {
22892        tech = Component.getComponent(techName);
22893      }
22894
22895      // Check if the current tech is defined before continuing
22896      if (!tech) {
22897        log$1.error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
22898        continue;
22899      }
22900
22901      // Check if the browser supports this technology
22902      if (tech.isSupported()) {
22903        can = tech.canPlayType(type);
22904
22905        if (can) {
22906          return can;
22907        }
22908      }
22909    }
22910
22911    return '';
22912  };
22913
22914  /**
22915   * Select source based on tech-order or source-order
22916   * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
22917   * defaults to tech-order selection
22918   *
22919   * @param {Array} sources
22920   *        The sources for a media asset
22921   *
22922   * @return {Object|boolean}
22923   *         Object of source and tech order or false
22924   */
22925
22926
22927  Player.prototype.selectSource = function selectSource(sources) {
22928    var _this6 = this;
22929
22930    // Get only the techs specified in `techOrder` that exist and are supported by the
22931    // current platform
22932    var techs = this.options_.techOrder.map(function (techName) {
22933      return [techName, Tech.getTech(techName)];
22934    }).filter(function (_ref) {
22935      var techName = _ref[0],
22936          tech = _ref[1];
22937
22938      // Check if the current tech is defined before continuing
22939      if (tech) {
22940        // Check if the browser supports this technology
22941        return tech.isSupported();
22942      }
22943
22944      log$1.error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
22945      return false;
22946    });
22947
22948    // Iterate over each `innerArray` element once per `outerArray` element and execute
22949    // `tester` with both. If `tester` returns a non-falsy value, exit early and return
22950    // that value.
22951    var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) {
22952      var found = void 0;
22953
22954      outerArray.some(function (outerChoice) {
22955        return innerArray.some(function (innerChoice) {
22956          found = tester(outerChoice, innerChoice);
22957
22958          if (found) {
22959            return true;
22960          }
22961        });
22962      });
22963
22964      return found;
22965    };
22966
22967    var foundSourceAndTech = void 0;
22968    var flip = function flip(fn) {
22969      return function (a, b) {
22970        return fn(b, a);
22971      };
22972    };
22973    var finder = function finder(_ref2, source) {
22974      var techName = _ref2[0],
22975          tech = _ref2[1];
22976
22977      if (tech.canPlaySource(source, _this6.options_[techName.toLowerCase()])) {
22978        return { source: source, tech: techName };
22979      }
22980    };
22981
22982    // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
22983    // to select from them based on their priority.
22984    if (this.options_.sourceOrder) {
22985      // Source-first ordering
22986      foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
22987    } else {
22988      // Tech-first ordering
22989      foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
22990    }
22991
22992    return foundSourceAndTech || false;
22993  };
22994
22995  /**
22996   * Get or set the video source.
22997   *
22998   * @param {Tech~SourceObject|Tech~SourceObject[]|string} [source]
22999   *        A SourceObject, an array of SourceObjects, or a string referencing
23000   *        a URL to a media source. It is _highly recommended_ that an object
23001   *        or array of objects is used here, so that source selection
23002   *        algorithms can take the `type` into account.
23003   *
23004   *        If not provided, this method acts as a getter.
23005   *
23006   * @return {string|undefined}
23007   *         If the `source` argument is missing, returns the current source
23008   *         URL. Otherwise, returns nothing/undefined.
23009   */
23010
23011
23012  Player.prototype.src = function src(source) {
23013    var _this7 = this;
23014
23015    // getter usage
23016    if (typeof source === 'undefined') {
23017      return this.cache_.src || '';
23018    }
23019    // filter out invalid sources and turn our source into
23020    // an array of source objects
23021    var sources = filterSource(source);
23022
23023    // if a source was passed in then it is invalid because
23024    // it was filtered to a zero length Array. So we have to
23025    // show an error
23026    if (!sources.length) {
23027      this.setTimeout(function () {
23028        this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
23029      }, 0);
23030      return;
23031    }
23032
23033    // intial sources
23034    this.cache_.sources = sources;
23035    this.changingSrc_ = true;
23036
23037    // intial source
23038    this.cache_.source = sources[0];
23039
23040    // middlewareSource is the source after it has been changed by middleware
23041    setSource(this, sources[0], function (middlewareSource, mws) {
23042      _this7.middleware_ = mws;
23043
23044      var err = _this7.src_(middlewareSource);
23045
23046      if (err) {
23047        if (sources.length > 1) {
23048          return _this7.src(sources.slice(1));
23049        }
23050
23051        // We need to wrap this in a timeout to give folks a chance to add error event handlers
23052        _this7.setTimeout(function () {
23053          this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
23054        }, 0);
23055
23056        // we could not find an appropriate tech, but let's still notify the delegate that this is it
23057        // this needs a better comment about why this is needed
23058        _this7.triggerReady();
23059
23060        return;
23061      }
23062
23063      _this7.changingSrc_ = false;
23064      // video element listed source
23065      _this7.cache_.src = middlewareSource.src;
23066
23067      setTech(mws, _this7.tech_);
23068    });
23069  };
23070
23071  /**
23072   * Set the source object on the tech, returns a boolean that indicates wether
23073   * there is a tech that can play the source or not
23074   *
23075   * @param {Tech~SourceObject} source
23076   *        The source object to set on the Tech
23077   *
23078   * @return {Boolean}
23079   *         - True if there is no Tech to playback this source
23080   *         - False otherwise
23081   *
23082   * @private
23083   */
23084
23085
23086  Player.prototype.src_ = function src_(source) {
23087    var sourceTech = this.selectSource([source]);
23088
23089    if (!sourceTech) {
23090      return true;
23091    }
23092
23093    if (!titleCaseEquals(sourceTech.tech, this.techName_)) {
23094      this.changingSrc_ = true;
23095
23096      // load this technology with the chosen source
23097      this.loadTech_(sourceTech.tech, sourceTech.source);
23098      return false;
23099    }
23100
23101    // wait until the tech is ready to set the source
23102    this.ready(function () {
23103
23104      // The setSource tech method was added with source handlers
23105      // so older techs won't support it
23106      // We need to check the direct prototype for the case where subclasses
23107      // of the tech do not support source handlers
23108      if (this.tech_.constructor.prototype.hasOwnProperty('setSource')) {
23109        this.techCall_('setSource', source);
23110      } else {
23111        this.techCall_('src', source.src);
23112      }
23113
23114      if (this.options_.preload === 'auto') {
23115        this.load();
23116      }
23117
23118      // Set the source synchronously if possible (#2326)
23119    }, true);
23120
23121    return false;
23122  };
23123
23124  /**
23125   * Begin loading the src data.
23126   */
23127
23128
23129  Player.prototype.load = function load() {
23130    this.techCall_('load');
23131  };
23132
23133  /**
23134   * Reset the player. Loads the first tech in the techOrder,
23135   * and calls `reset` on the tech`.
23136   */
23137
23138
23139  Player.prototype.reset = function reset() {
23140    this.loadTech_(this.options_.techOrder[0], null);
23141    this.techCall_('reset');
23142  };
23143
23144  /**
23145   * Returns all of the current source objects.
23146   *
23147   * @return {Tech~SourceObject[]}
23148   *         The current source objects
23149   */
23150
23151
23152  Player.prototype.currentSources = function currentSources() {
23153    var source = this.currentSource();
23154    var sources = [];
23155
23156    // assume `{}` or `{ src }`
23157    if (Object.keys(source).length !== 0) {
23158      sources.push(source);
23159    }
23160
23161    return this.cache_.sources || sources;
23162  };
23163
23164  /**
23165   * Returns the current source object.
23166   *
23167   * @return {Tech~SourceObject}
23168   *         The current source object
23169   */
23170
23171
23172  Player.prototype.currentSource = function currentSource() {
23173    return this.cache_.source || {};
23174  };
23175
23176  /**
23177   * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
23178   * Can be used in conjuction with `currentType` to assist in rebuilding the current source object.
23179   *
23180   * @return {string}
23181   *         The current source
23182   */
23183
23184
23185  Player.prototype.currentSrc = function currentSrc() {
23186    return this.currentSource() && this.currentSource().src || '';
23187  };
23188
23189  /**
23190   * Get the current source type e.g. video/mp4
23191   * This can allow you rebuild the current source object so that you could load the same
23192   * source and tech later
23193   *
23194   * @return {string}
23195   *         The source MIME type
23196   */
23197
23198
23199  Player.prototype.currentType = function currentType() {
23200    return this.currentSource() && this.currentSource().type || '';
23201  };
23202
23203  /**
23204   * Get or set the preload attribute
23205   *
23206   * @param {boolean} [value]
23207   *        - true means that we should preload
23208   *        - false maens that we should not preload
23209   *
23210   * @return {string}
23211   *         The preload attribute value when getting
23212   */
23213
23214
23215  Player.prototype.preload = function preload(value) {
23216    if (value !== undefined) {
23217      this.techCall_('setPreload', value);
23218      this.options_.preload = value;
23219      return;
23220    }
23221    return this.techGet_('preload');
23222  };
23223
23224  /**
23225   * Get or set the autoplay attribute.
23226   *
23227   * @param {boolean} [value]
23228   *        - true means that we should autoplay
23229   *        - false means that we should not autoplay
23230   *
23231   * @return {string}
23232   *         The current value of autoplay when getting
23233   */
23234
23235
23236  Player.prototype.autoplay = function autoplay(value) {
23237    if (value !== undefined) {
23238      this.techCall_('setAutoplay', value);
23239      this.options_.autoplay = value;
23240      this.ready(this.forceAutoplayInChrome_);
23241      return;
23242    }
23243    return this.techGet_('autoplay', value);
23244  };
23245
23246  /**
23247   * chrome started pausing the video when moving in the DOM
23248   * causing autoplay to not continue due to how Video.js functions.
23249   * See #4720 for more info.
23250   *
23251   * @private
23252   */
23253
23254
23255  Player.prototype.forceAutoplayInChrome_ = function forceAutoplayInChrome_() {
23256    if (this.paused() && (
23257    // read from the video element or options
23258    this.autoplay() || this.options_.autoplay) &&
23259    // only target desktop chrome
23260    IS_CHROME && !IS_ANDROID) {
23261      this.play();
23262    }
23263  };
23264
23265  /**
23266   * Set or unset the playsinline attribute.
23267   * Playsinline tells the browser that non-fullscreen playback is preferred.
23268   *
23269   * @param {boolean} [value]
23270   *        - true means that we should try to play inline by default
23271   *        - false means that we should use the browser's default playback mode,
23272   *          which in most cases is inline. iOS Safari is a notable exception
23273   *          and plays fullscreen by default.
23274   *
23275   * @return {string|Player}
23276   *         - the current value of playsinline
23277   *         - the player when setting
23278   *
23279   * @see [Spec]{@link https://html.spec.whatwg.org/#attr-video-playsinline}
23280   */
23281
23282
23283  Player.prototype.playsinline = function playsinline(value) {
23284    if (value !== undefined) {
23285      this.techCall_('setPlaysinline', value);
23286      this.options_.playsinline = value;
23287      return this;
23288    }
23289    return this.techGet_('playsinline');
23290  };
23291
23292  /**
23293   * Get or set the loop attribute on the video element.
23294   *
23295   * @param {boolean} [value]
23296   *        - true means that we should loop the video
23297   *        - false means that we should not loop the video
23298   *
23299   * @return {string}
23300   *         The current value of loop when getting
23301   */
23302
23303
23304  Player.prototype.loop = function loop(value) {
23305    if (value !== undefined) {
23306      this.techCall_('setLoop', value);
23307      this.options_.loop = value;
23308      return;
23309    }
23310    return this.techGet_('loop');
23311  };
23312
23313  /**
23314   * Get or set the poster image source url
23315   *
23316   * @fires Player#posterchange
23317   *
23318   * @param {string} [src]
23319   *        Poster image source URL
23320   *
23321   * @return {string}
23322   *         The current value of poster when getting
23323   */
23324
23325
23326  Player.prototype.poster = function poster(src) {
23327    if (src === undefined) {
23328      return this.poster_;
23329    }
23330
23331    // The correct way to remove a poster is to set as an empty string
23332    // other falsey values will throw errors
23333    if (!src) {
23334      src = '';
23335    }
23336
23337    // update the internal poster variable
23338    this.poster_ = src;
23339
23340    // update the tech's poster
23341    this.techCall_('setPoster', src);
23342
23343    // alert components that the poster has been set
23344    /**
23345     * This event fires when the poster image is changed on the player.
23346     *
23347     * @event Player#posterchange
23348     * @type {EventTarget~Event}
23349     */
23350    this.trigger('posterchange');
23351  };
23352
23353  /**
23354   * Some techs (e.g. YouTube) can provide a poster source in an
23355   * asynchronous way. We want the poster component to use this
23356   * poster source so that it covers up the tech's controls.
23357   * (YouTube's play button). However we only want to use this
23358   * source if the player user hasn't set a poster through
23359   * the normal APIs.
23360   *
23361   * @fires Player#posterchange
23362   * @listens Tech#posterchange
23363   * @private
23364   */
23365
23366
23367  Player.prototype.handleTechPosterChange_ = function handleTechPosterChange_() {
23368    if (!this.poster_ && this.tech_ && this.tech_.poster) {
23369      this.poster_ = this.tech_.poster() || '';
23370
23371      // Let components know the poster has changed
23372      this.trigger('posterchange');
23373    }
23374  };
23375
23376  /**
23377   * Get or set whether or not the controls are showing.
23378   *
23379   * @fires Player#controlsenabled
23380   *
23381   * @param {boolean} [bool]
23382   *        - true to turn controls on
23383   *        - false to turn controls off
23384   *
23385   * @return {boolean}
23386   *         The current value of controls when getting
23387   */
23388
23389
23390  Player.prototype.controls = function controls(bool) {
23391    if (bool === undefined) {
23392      return !!this.controls_;
23393    }
23394
23395    bool = !!bool;
23396
23397    // Don't trigger a change event unless it actually changed
23398    if (this.controls_ === bool) {
23399      return;
23400    }
23401
23402    this.controls_ = bool;
23403
23404    if (this.usingNativeControls()) {
23405      this.techCall_('setControls', bool);
23406    }
23407
23408    if (this.controls_) {
23409      this.removeClass('vjs-controls-disabled');
23410      this.addClass('vjs-controls-enabled');
23411      /**
23412       * @event Player#controlsenabled
23413       * @type {EventTarget~Event}
23414       */
23415      this.trigger('controlsenabled');
23416      if (!this.usingNativeControls()) {
vendor: 16,856 bytes, lines 23417-24006
23417        this.addTechControlsListeners_();
23418      }
23419    } else {
23420      this.removeClass('vjs-controls-enabled');
23421      this.addClass('vjs-controls-disabled');
23422      /**
23423       * @event Player#controlsdisabled
23424       * @type {EventTarget~Event}
23425       */
23426      this.trigger('controlsdisabled');
23427      if (!this.usingNativeControls()) {
23428        this.removeTechControlsListeners_();
23429      }
23430    }
23431  };
23432
23433  /**
23434   * Toggle native controls on/off. Native controls are the controls built into
23435   * devices (e.g. default iPhone controls), Flash, or other techs
23436   * (e.g. Vimeo Controls)
23437   * **This should only be set by the current tech, because only the tech knows
23438   * if it can support native controls**
23439   *
23440   * @fires Player#usingnativecontrols
23441   * @fires Player#usingcustomcontrols
23442   *
23443   * @param {boolean} [bool]
23444   *        - true to turn native controls on
23445   *        - false to turn native controls off
23446   *
23447   * @return {boolean}
23448   *         The current value of native controls when getting
23449   */
23450
23451
23452  Player.prototype.usingNativeControls = function usingNativeControls(bool) {
23453    if (bool === undefined) {
23454      return !!this.usingNativeControls_;
23455    }
23456
23457    bool = !!bool;
23458
23459    // Don't trigger a change event unless it actually changed
23460    if (this.usingNativeControls_ === bool) {
23461      return;
23462    }
23463
23464    this.usingNativeControls_ = bool;
23465
23466    if (this.usingNativeControls_) {
23467      this.addClass('vjs-using-native-controls');
23468
23469      /**
23470       * player is using the native device controls
23471       *
23472       * @event Player#usingnativecontrols
23473       * @type {EventTarget~Event}
23474       */
23475      this.trigger('usingnativecontrols');
23476    } else {
23477      this.removeClass('vjs-using-native-controls');
23478
23479      /**
23480       * player is using the custom HTML controls
23481       *
23482       * @event Player#usingcustomcontrols
23483       * @type {EventTarget~Event}
23484       */
23485      this.trigger('usingcustomcontrols');
23486    }
23487  };
23488
23489  /**
23490   * Set or get the current MediaError
23491   *
23492   * @fires Player#error
23493   *
23494   * @param  {MediaError|string|number} [err]
23495   *         A MediaError or a string/number to be turned
23496   *         into a MediaError
23497   *
23498   * @return {MediaError|null}
23499   *         The current MediaError when getting (or null)
23500   */
23501
23502
23503  Player.prototype.error = function error(err) {
23504    if (err === undefined) {
23505      return this.error_ || null;
23506    }
23507
23508    // restoring to default
23509    if (err === null) {
23510      this.error_ = err;
23511      this.removeClass('vjs-error');
23512      if (this.errorDisplay) {
23513        this.errorDisplay.close();
23514      }
23515      return;
23516    }
23517
23518    this.error_ = new MediaError(err);
23519
23520    // add the vjs-error classname to the player
23521    this.addClass('vjs-error');
23522
23523    // log the name of the error type and any message
23524    // ie8 just logs "[object object]" if you just log the error object
23525    log$1.error('(CODE:' + this.error_.code + ' ' + MediaError.errorTypes[this.error_.code] + ')', this.error_.message, this.error_);
23526
23527    /**
23528     * @event Player#error
23529     * @type {EventTarget~Event}
23530     */
23531    this.trigger('error');
23532
23533    return;
23534  };
23535
23536  /**
23537   * Report user activity
23538   *
23539   * @param {Object} event
23540   *        Event object
23541   */
23542
23543
23544  Player.prototype.reportUserActivity = function reportUserActivity(event) {
23545    this.userActivity_ = true;
23546  };
23547
23548  /**
23549   * Get/set if user is active
23550   *
23551   * @fires Player#useractive
23552   * @fires Player#userinactive
23553   *
23554   * @param {boolean} [bool]
23555   *        - true if the user is active
23556   *        - false if the user is inactive
23557   *
23558   * @return {boolean}
23559   *         The current value of userActive when getting
23560   */
23561
23562
23563  Player.prototype.userActive = function userActive(bool) {
23564    if (bool === undefined) {
23565      return this.userActive_;
23566    }
23567
23568    bool = !!bool;
23569
23570    if (bool === this.userActive_) {
23571      return;
23572    }
23573
23574    this.userActive_ = bool;
23575
23576    if (this.userActive_) {
23577      this.userActivity_ = true;
23578      this.removeClass('vjs-user-inactive');
23579      this.addClass('vjs-user-active');
23580      /**
23581       * @event Player#useractive
23582       * @type {EventTarget~Event}
23583       */
23584      this.trigger('useractive');
23585      return;
23586    }
23587
23588    // Chrome/Safari/IE have bugs where when you change the cursor it can
23589    // trigger a mousemove event. This causes an issue when you're hiding
23590    // the cursor when the user is inactive, and a mousemove signals user
23591    // activity. Making it impossible to go into inactive mode. Specifically
23592    // this happens in fullscreen when we really need to hide the cursor.
23593    //
23594    // When this gets resolved in ALL browsers it can be removed
23595    // https://code.google.com/p/chromium/issues/detail?id=103041
23596    if (this.tech_) {
23597      this.tech_.one('mousemove', function (e) {
23598        e.stopPropagation();
23599        e.preventDefault();
23600      });
23601    }
23602
23603    this.userActivity_ = false;
23604    this.removeClass('vjs-user-active');
23605    this.addClass('vjs-user-inactive');
23606    /**
23607     * @event Player#userinactive
23608     * @type {EventTarget~Event}
23609     */
23610    this.trigger('userinactive');
23611  };
23612
23613  /**
23614   * Listen for user activity based on timeout value
23615   *
23616   * @private
23617   */
23618
23619
23620  Player.prototype.listenForUserActivity_ = function listenForUserActivity_() {
23621    var mouseInProgress = void 0;
23622    var lastMoveX = void 0;
23623    var lastMoveY = void 0;
23624    var handleActivity = bind(this, this.reportUserActivity);
23625
23626    var handleMouseMove = function handleMouseMove(e) {
23627      // #1068 - Prevent mousemove spamming
23628      // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
23629      if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
23630        lastMoveX = e.screenX;
23631        lastMoveY = e.screenY;
23632        handleActivity();
23633      }
23634    };
23635
23636    var handleMouseDown = function handleMouseDown() {
23637      handleActivity();
23638      // For as long as the they are touching the device or have their mouse down,
23639      // we consider them active even if they're not moving their finger or mouse.
23640      // So we want to continue to update that they are active
23641      this.clearInterval(mouseInProgress);
23642      // Setting userActivity=true now and setting the interval to the same time
23643      // as the activityCheck interval (250) should ensure we never miss the
23644      // next activityCheck
23645      mouseInProgress = this.setInterval(handleActivity, 250);
23646    };
23647
23648    var handleMouseUp = function handleMouseUp(event) {
23649      handleActivity();
23650      // Stop the interval that maintains activity if the mouse/touch is down
23651      this.clearInterval(mouseInProgress);
23652    };
23653
23654    // Any mouse movement will be considered user activity
23655    this.on('mousedown', handleMouseDown);
23656    this.on('mousemove', handleMouseMove);
23657    this.on('mouseup', handleMouseUp);
23658
23659    // Listen for keyboard navigation
23660    // Shouldn't need to use inProgress interval because of key repeat
23661    this.on('keydown', handleActivity);
23662    this.on('keyup', handleActivity);
23663
23664    // Run an interval every 250 milliseconds instead of stuffing everything into
23665    // the mousemove/touchmove function itself, to prevent performance degradation.
23666    // `this.reportUserActivity` simply sets this.userActivity_ to true, which
23667    // then gets picked up by this loop
23668    // http://ejohn.org/blog/learning-from-twitter/
23669    var inactivityTimeout = void 0;
23670
23671    this.setInterval(function () {
23672      // Check to see if mouse/touch activity has happened
23673      if (!this.userActivity_) {
23674        return;
23675      }
23676
23677      // Reset the activity tracker
23678      this.userActivity_ = false;
23679
23680      // If the user state was inactive, set the state to active
23681      this.userActive(true);
23682
23683      // Clear any existing inactivity timeout to start the timer over
23684      this.clearTimeout(inactivityTimeout);
23685
23686      var timeout = this.options_.inactivityTimeout;
23687
23688      if (timeout <= 0) {
23689        return;
23690      }
23691
23692      // In <timeout> milliseconds, if no more activity has occurred the
23693      // user will be considered inactive
23694      inactivityTimeout = this.setTimeout(function () {
23695        // Protect against the case where the inactivityTimeout can trigger just
23696        // before the next user activity is picked up by the activity check loop
23697        // causing a flicker
23698        if (!this.userActivity_) {
23699          this.userActive(false);
23700        }
23701      }, timeout);
23702    }, 250);
23703  };
23704
23705  /**
23706   * Gets or sets the current playback rate. A playback rate of
23707   * 1.0 represents normal speed and 0.5 would indicate half-speed
23708   * playback, for instance.
23709   *
23710   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
23711   *
23712   * @param {number} [rate]
23713   *       New playback rate to set.
23714   *
23715   * @return {number}
23716   *         The current playback rate when getting or 1.0
23717   */
23718
23719
23720  Player.prototype.playbackRate = function playbackRate(rate) {
23721    if (rate !== undefined) {
23722      this.techCall_('setPlaybackRate', rate);
23723      return;
23724    }
23725
23726    if (this.tech_ && this.tech_.featuresPlaybackRate) {
23727      return this.techGet_('playbackRate');
23728    }
23729    return 1.0;
23730  };
23731
23732  /**
23733   * Gets or sets the current default playback rate. A default playback rate of
23734   * 1.0 represents normal speed and 0.5 would indicate half-speed playback, for instance.
23735   * defaultPlaybackRate will only represent what the intial playbackRate of a video was, not
23736   * not the current playbackRate.
23737   *
23738   * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-defaultplaybackrate
23739   *
23740   * @param {number} [rate]
23741   *       New default playback rate to set.
23742   *
23743   * @return {number|Player}
23744   *         - The default playback rate when getting or 1.0
23745   *         - the player when setting
23746   */
23747
23748
23749  Player.prototype.defaultPlaybackRate = function defaultPlaybackRate(rate) {
23750    if (rate !== undefined) {
23751      return this.techCall_('setDefaultPlaybackRate', rate);
23752    }
23753
23754    if (this.tech_ && this.tech_.featuresPlaybackRate) {
23755      return this.techGet_('defaultPlaybackRate');
23756    }
23757    return 1.0;
23758  };
23759
23760  /**
23761   * Gets or sets the audio flag
23762   *
23763   * @param {boolean} bool
23764   *        - true signals that this is an audio player
23765   *        - false signals that this is not an audio player
23766   *
23767   * @return {boolean}
23768   *         The current value of isAudio when getting
23769   */
23770
23771
23772  Player.prototype.isAudio = function isAudio(bool) {
23773    if (bool !== undefined) {
23774      this.isAudio_ = !!bool;
23775      return;
23776    }
23777
23778    return !!this.isAudio_;
23779  };
23780
23781  /**
23782   * A helper method for adding a {@link TextTrack} to our
23783   * {@link TextTrackList}.
23784   *
23785   * In addition to the W3C settings we allow adding additional info through options.
23786   *
23787   * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
23788   *
23789   * @param {string} [kind]
23790   *        the kind of TextTrack you are adding
23791   *
23792   * @param {string} [label]
23793   *        the label to give the TextTrack label
23794   *
23795   * @param {string} [language]
23796   *        the language to set on the TextTrack
23797   *
23798   * @return {TextTrack|undefined}
23799   *         the TextTrack that was added or undefined
23800   *         if there is no tech
23801   */
23802
23803
23804  Player.prototype.addTextTrack = function addTextTrack(kind, label, language) {
23805    if (this.tech_) {
23806      return this.tech_.addTextTrack(kind, label, language);
23807    }
23808  };
23809
23810  /**
23811   * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will
23812   * automatically removed from the video element whenever the source changes, unless
23813   * manualCleanup is set to false.
23814   *
23815   * @param {Object} options
23816   *        Options to pass to {@link HTMLTrackElement} during creation. See
23817   *        {@link HTMLTrackElement} for object properties that you should use.
23818   *
23819   * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
23820   *
23821   * @return {HtmlTrackElement}
23822   *         the HTMLTrackElement that was created and added
23823   *         to the HtmlTrackElementList and the remote
23824   *         TextTrackList
23825   *
23826   * @deprecated The default value of the "manualCleanup" parameter will default
23827   *             to "false" in upcoming versions of Video.js
23828   */
23829
23830
23831  Player.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
23832    if (this.tech_) {
23833      return this.tech_.addRemoteTextTrack(options, manualCleanup);
23834    }
23835  };
23836
23837  /**
23838   * Remove a remote {@link TextTrack} from the respective
23839   * {@link TextTrackList} and {@link HtmlTrackElementList}.
23840   *
23841   * @param {Object} track
23842   *        Remote {@link TextTrack} to remove
23843   *
23844   * @return {undefined}
23845   *         does not return anything
23846   */
23847
23848
23849  Player.prototype.removeRemoteTextTrack = function removeRemoteTextTrack() {
23850    var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
23851        _ref3$track = _ref3.track,
23852        track = _ref3$track === undefined ? arguments[0] : _ref3$track;
23853
23854    // destructure the input into an object with a track argument, defaulting to arguments[0]
23855    // default the whole argument to an empty object if nothing was passed in
23856
23857    if (this.tech_) {
23858      return this.tech_.removeRemoteTextTrack(track);
23859    }
23860  };
23861
23862  /**
23863   * Gets available media playback quality metrics as specified by the W3C's Media
23864   * Playback Quality API.
23865   *
23866   * @see [Spec]{@link https://wicg.github.io/media-playback-quality}
23867   *
23868   * @return {Object|undefined}
23869   *         An object with supported media playback quality metrics or undefined if there
23870   *         is no tech or the tech does not support it.
23871   */
23872
23873
23874  Player.prototype.getVideoPlaybackQuality = function getVideoPlaybackQuality() {
23875    return this.techGet_('getVideoPlaybackQuality');
23876  };
23877
23878  /**
23879   * Get video width
23880   *
23881   * @return {number}
23882   *         current video width
23883   */
23884
23885
23886  Player.prototype.videoWidth = function videoWidth() {
23887    return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0;
23888  };
23889
23890  /**
23891   * Get video height
23892   *
23893   * @return {number}
23894   *         current video height
23895   */
23896
23897
23898  Player.prototype.videoHeight = function videoHeight() {
23899    return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0;
23900  };
23901
23902  /**
23903   * The player's language code
23904   * NOTE: The language should be set in the player options if you want the
23905   * the controls to be built with a specific language. Changing the lanugage
23906   * later will not update controls text.
23907   *
23908   * @param {string} [code]
23909   *        the language code to set the player to
23910   *
23911   * @return {string}
23912   *         The current language code when getting
23913   */
23914
23915
23916  Player.prototype.language = function language(code) {
23917    if (code === undefined) {
23918      return this.language_;
23919    }
23920
23921    this.language_ = String(code).toLowerCase();
23922  };
23923
23924  /**
23925   * Get the player's language dictionary
23926   * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time
23927   * Languages specified directly in the player options have precedence
23928   *
23929   * @return {Array}
23930   *         An array of of supported languages
23931   */
23932
23933
23934  Player.prototype.languages = function languages() {
23935    return mergeOptions(Player.prototype.options_.languages, this.languages_);
23936  };
23937
23938  /**
23939   * returns a JavaScript object reperesenting the current track
23940   * information. **DOES not return it as JSON**
23941   *
23942   * @return {Object}
23943   *         Object representing the current of track info
23944   */
23945
23946
23947  Player.prototype.toJSON = function toJSON() {
23948    var options = mergeOptions(this.options_);
23949    var tracks = options.tracks;
23950
23951    options.tracks = [];
23952
23953    for (var i = 0; i < tracks.length; i++) {
23954      var track = tracks[i];
23955
23956      // deep merge tracks and null out player so no circular references
23957      track = mergeOptions(track);
23958      track.player = undefined;
23959      options.tracks[i] = track;
23960    }
23961
23962    return options;
23963  };
23964
23965  /**
23966   * Creates a simple modal dialog (an instance of the {@link ModalDialog}
23967   * component) that immediately overlays the player with arbitrary
23968   * content and removes itself when closed.
23969   *
23970   * @param {string|Function|Element|Array|null} content
23971   *        Same as {@link ModalDialog#content}'s param of the same name.
23972   *        The most straight-forward usage is to provide a string or DOM
23973   *        element.
23974   *
23975   * @param {Object} [options]
23976   *        Extra options which will be passed on to the {@link ModalDialog}.
23977   *
23978   * @return {ModalDialog}
23979   *         the {@link ModalDialog} that was created
23980   */
23981
23982
23983  Player.prototype.createModal = function createModal(content, options) {
23984    var _this8 = this;
23985
23986    options = options || {};
23987    options.content = content || '';
23988
23989    var modal = new ModalDialog(this, options);
23990
23991    this.addChild(modal);
23992    modal.on('dispose', function () {
23993      _this8.removeChild(modal);
23994    });
23995
23996    modal.open();
23997    return modal;
23998  };
23999
24000  /**
24001   * Gets tag settings
24002   *
24003   * @param {Element} tag
24004   *        The player tag
24005   *
24006   * @return {Object}
vendor: 4,271 bytes, lines 24007-24177
24007   *         An object containing all of the settings
24008   *         for a player tag
24009   */
24010
24011
24012  Player.getTagSettings = function getTagSettings(tag) {
24013    var baseOptions = {
24014      sources: [],
24015      tracks: []
24016    };
24017
24018    var tagOptions = getAttributes(tag);
24019    var dataSetup = tagOptions['data-setup'];
24020
24021    if (hasClass(tag, 'vjs-fluid')) {
24022      tagOptions.fluid = true;
24023    }
24024
24025    // Check if data-setup attr exists.
24026    if (dataSetup !== null) {
24027      // Parse options JSON
24028      // If empty string, make it a parsable json object.
24029      var _safeParseTuple = tuple(dataSetup || '{}'),
24030          err = _safeParseTuple[0],
24031          data = _safeParseTuple[1];
24032
24033      if (err) {
24034        log$1.error(err);
24035      }
24036      assign(tagOptions, data);
24037    }
24038
24039    assign(baseOptions, tagOptions);
24040
24041    // Get tag children settings
24042    if (tag.hasChildNodes()) {
24043      var children = tag.childNodes;
24044
24045      for (var i = 0, j = children.length; i < j; i++) {
24046        var child = children[i];
24047        // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
24048        var childName = child.nodeName.toLowerCase();
24049
24050        if (childName === 'source') {
24051          baseOptions.sources.push(getAttributes(child));
24052        } else if (childName === 'track') {
24053          baseOptions.tracks.push(getAttributes(child));
24054        }
24055      }
24056    }
24057
24058    return baseOptions;
24059  };
24060
24061  /**
24062   * Determine wether or not flexbox is supported
24063   *
24064   * @return {boolean}
24065   *         - true if flexbox is supported
24066   *         - false if flexbox is not supported
24067   */
24068
24069
24070  Player.prototype.flexNotSupported_ = function flexNotSupported_() {
24071    var elem = document_1.createElement('i');
24072
24073    // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
24074    // common flex features that we can rely on when checking for flex support.
24075    return !('flexBasis' in elem.style || 'webkitFlexBasis' in elem.style || 'mozFlexBasis' in elem.style || 'msFlexBasis' in elem.style ||
24076    // IE10-specific (2012 flex spec)
24077    'msFlexOrder' in elem.style);
24078  };
24079
24080  return Player;
24081}(Component);
24082
24083/**
24084 * Get the {@link VideoTrackList}
24085 * @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
24086 *
24087 * @return {VideoTrackList}
24088 *         the current video track list
24089 *
24090 * @method Player.prototype.videoTracks
24091 */
24092
24093/**
24094 * Get the {@link AudioTrackList}
24095 * @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
24096 *
24097 * @return {AudioTrackList}
24098 *         the current audio track list
24099 *
24100 * @method Player.prototype.audioTracks
24101 */
24102
24103/**
24104 * Get the {@link TextTrackList}
24105 *
24106 * @link http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
24107 *
24108 * @return {TextTrackList}
24109 *         the current text track list
24110 *
24111 * @method Player.prototype.textTracks
24112 */
24113
24114/**
24115 * Get the remote {@link TextTrackList}
24116 *
24117 * @return {TextTrackList}
24118 *         The current remote text track list
24119 *
24120 * @method Player.prototype.remoteTextTracks
24121 */
24122
24123/**
24124 * Get the remote {@link HtmlTrackElementList} tracks.
24125 *
24126 * @return {HtmlTrackElementList}
24127 *         The current remote text track element list
24128 *
24129 * @method Player.prototype.remoteTextTrackEls
24130 */
24131
24132ALL.names.forEach(function (name$$1) {
24133  var props = ALL[name$$1];
24134
24135  Player.prototype[props.getterName] = function () {
24136    if (this.tech_) {
24137      return this.tech_[props.getterName]();
24138    }
24139
24140    // if we have not yet loadTech_, we create {video,audio,text}Tracks_
24141    // these will be passed to the tech during loading
24142    this[props.privateName] = this[props.privateName] || new props.ListClass();
24143    return this[props.privateName];
24144  };
24145});
24146
24147/**
24148 * Global player list
24149 *
24150 * @type {Object}
24151 */
24152Player.players = {};
24153
24154var navigator$1 = window_1.navigator;
24155
24156/*
24157 * Player instance options, surfaced using options
24158 * options = Player.prototype.options_
24159 * Make changes in options, not here.
24160 *
24161 * @type {Object}
24162 * @private
24163 */
24164Player.prototype.options_ = {
24165  // Default order of fallback technology
24166  techOrder: Tech.defaultTechOrder_,
24167
24168  html5: {},
24169  flash: {},
24170
24171  // default inactivity timeout
24172  inactivityTimeout: 2000,
24173
24174  // default playback rates
24175  playbackRates: [],
24176  // Add playback rate selection by adding rates
24177  // 'playbackRates': [0.5, 1, 1.5, 2],
vendor: 4,795 bytes, lines 24178-24332
24178
24179  // Included control sets
24180  children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'controlBar', 'errorDisplay', 'textTrackSettings'],
24181
24182  language: navigator$1 && (navigator$1.languages && navigator$1.languages[0] || navigator$1.userLanguage || navigator$1.language) || 'en',
24183
24184  // locales and their language translations
24185  languages: {},
24186
24187  // Default message to show when a video cannot be played.
24188  notSupportedMessage: 'No compatible source was found for this media.'
24189};
24190
24191[
24192/**
24193 * Returns whether or not the player is in the "ended" state.
24194 *
24195 * @return {Boolean} True if the player is in the ended state, false if not.
24196 * @method Player#ended
24197 */
24198'ended',
24199/**
24200 * Returns whether or not the player is in the "seeking" state.
24201 *
24202 * @return {Boolean} True if the player is in the seeking state, false if not.
24203 * @method Player#seeking
24204 */
24205'seeking',
24206/**
24207 * Returns the TimeRanges of the media that are currently available
24208 * for seeking to.
24209 *
24210 * @return {TimeRanges} the seekable intervals of the media timeline
24211 * @method Player#seekable
24212 */
24213'seekable',
24214/**
24215 * Returns the current state of network activity for the element, from
24216 * the codes in the list below.
24217 * - NETWORK_EMPTY (numeric value 0)
24218 *   The element has not yet been initialised. All attributes are in
24219 *   their initial states.
24220 * - NETWORK_IDLE (numeric value 1)
24221 *   The element's resource selection algorithm is active and has
24222 *   selected a resource, but it is not actually using the network at
24223 *   this time.
24224 * - NETWORK_LOADING (numeric value 2)
24225 *   The user agent is actively trying to download data.
24226 * - NETWORK_NO_SOURCE (numeric value 3)
24227 *   The element's resource selection algorithm is active, but it has
24228 *   not yet found a resource to use.
24229 *
24230 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
24231 * @return {number} the current network activity state
24232 * @method Player#networkState
24233 */
24234'networkState',
24235/**
24236 * Returns a value that expresses the current state of the element
24237 * with respect to rendering the current playback position, from the
24238 * codes in the list below.
24239 * - HAVE_NOTHING (numeric value 0)
24240 *   No information regarding the media resource is available.
24241 * - HAVE_METADATA (numeric value 1)
24242 *   Enough of the resource has been obtained that the duration of the
24243 *   resource is available.
24244 * - HAVE_CURRENT_DATA (numeric value 2)
24245 *   Data for the immediate current playback position is available.
24246 * - HAVE_FUTURE_DATA (numeric value 3)
24247 *   Data for the immediate current playback position is available, as
24248 *   well as enough data for the user agent to advance the current
24249 *   playback position in the direction of playback.
24250 * - HAVE_ENOUGH_DATA (numeric value 4)
24251 *   The user agent estimates that enough data is available for
24252 *   playback to proceed uninterrupted.
24253 *
24254 * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
24255 * @return {number} the current playback rendering state
24256 * @method Player#readyState
24257 */
24258'readyState'].forEach(function (fn) {
24259  Player.prototype[fn] = function () {
24260    return this.techGet_(fn);
24261  };
24262});
24263
24264TECH_EVENTS_RETRIGGER.forEach(function (event) {
24265  Player.prototype['handleTech' + toTitleCase(event) + '_'] = function () {
24266    return this.trigger(event);
24267  };
24268});
24269
24270/**
24271 * Fired when the player has initial duration and dimension information
24272 *
24273 * @event Player#loadedmetadata
24274 * @type {EventTarget~Event}
24275 */
24276
24277/**
24278 * Fired when the player has downloaded data at the current playback position
24279 *
24280 * @event Player#loadeddata
24281 * @type {EventTarget~Event}
24282 */
24283
24284/**
24285 * Fired when the current playback position has changed *
24286 * During playback this is fired every 15-250 milliseconds, depending on the
24287 * playback technology in use.
24288 *
24289 * @event Player#timeupdate
24290 * @type {EventTarget~Event}
24291 */
24292
24293/**
24294 * Fired when the volume changes
24295 *
24296 * @event Player#volumechange
24297 * @type {EventTarget~Event}
24298 */
24299
24300/**
24301 * Reports whether or not a player has a plugin available.
24302 *
24303 * This does not report whether or not the plugin has ever been initialized
24304 * on this player. For that, [usingPlugin]{@link Player#usingPlugin}.
24305 *
24306 * @method Player#hasPlugin
24307 * @param  {string}  name
24308 *         The name of a plugin.
24309 *
24310 * @return {boolean}
24311 *         Whether or not this player has the requested plugin available.
24312 */
24313
24314/**
24315 * Reports whether or not a player is using a plugin by name.
24316 *
24317 * For basic plugins, this only reports whether the plugin has _ever_ been
24318 * initialized on this player.
24319 *
24320 * @method Player#usingPlugin
24321 * @param  {string} name
24322 *         The name of a plugin.
24323 *
24324 * @return {boolean}
24325 *         Whether or not this player is using the requested plugin.
24326 */
24327
24328Component.registerComponent('Player', Player);
24329
24330/**
24331 * @file plugin.js
24332 */
vendor: 13,630 bytes, lines 24332-24840
24332
24333/**
24334 * The base plugin name.
24335 *
24336 * @private
24337 * @constant
24338 * @type {string}
24339 */
24340var BASE_PLUGIN_NAME = 'plugin';
24341
24342/**
24343 * The key on which a player's active plugins cache is stored.
24344 *
24345 * @private
24346 * @constant
24347 * @type     {string}
24348 */
24349var PLUGIN_CACHE_KEY = 'activePlugins_';
24350
24351/**
24352 * Stores registered plugins in a private space.
24353 *
24354 * @private
24355 * @type    {Object}
24356 */
24357var pluginStorage = {};
24358
24359/**
24360 * Reports whether or not a plugin has been registered.
24361 *
24362 * @private
24363 * @param   {string} name
24364 *          The name of a plugin.
24365 *
24366 * @returns {boolean}
24367 *          Whether or not the plugin has been registered.
24368 */
24369var pluginExists = function pluginExists(name) {
24370  return pluginStorage.hasOwnProperty(name);
24371};
24372
24373/**
24374 * Get a single registered plugin by name.
24375 *
24376 * @private
24377 * @param   {string} name
24378 *          The name of a plugin.
24379 *
24380 * @returns {Function|undefined}
24381 *          The plugin (or undefined).
24382 */
24383var getPlugin = function getPlugin(name) {
24384  return pluginExists(name) ? pluginStorage[name] : undefined;
24385};
24386
24387/**
24388 * Marks a plugin as "active" on a player.
24389 *
24390 * Also, ensures that the player has an object for tracking active plugins.
24391 *
24392 * @private
24393 * @param   {Player} player
24394 *          A Video.js player instance.
24395 *
24396 * @param   {string} name
24397 *          The name of a plugin.
24398 */
24399var markPluginAsActive = function markPluginAsActive(player, name) {
24400  player[PLUGIN_CACHE_KEY] = player[PLUGIN_CACHE_KEY] || {};
24401  player[PLUGIN_CACHE_KEY][name] = true;
24402};
24403
24404/**
24405 * Triggers a pair of plugin setup events.
24406 *
24407 * @private
24408 * @param  {Player} player
24409 *         A Video.js player instance.
24410 *
24411 * @param  {Plugin~PluginEventHash} hash
24412 *         A plugin event hash.
24413 *
24414 * @param  {Boolean} [before]
24415 *         If true, prefixes the event name with "before". In other words,
24416 *         use this to trigger "beforepluginsetup" instead of "pluginsetup".
24417 */
24418var triggerSetupEvent = function triggerSetupEvent(player, hash, before) {
24419  var eventName = (before ? 'before' : '') + 'pluginsetup';
24420
24421  player.trigger(eventName, hash);
24422  player.trigger(eventName + ':' + hash.name, hash);
24423};
24424
24425/**
24426 * Takes a basic plugin function and returns a wrapper function which marks
24427 * on the player that the plugin has been activated.
24428 *
24429 * @private
24430 * @param   {string} name
24431 *          The name of the plugin.
24432 *
24433 * @param   {Function} plugin
24434 *          The basic plugin.
24435 *
24436 * @returns {Function}
24437 *          A wrapper function for the given plugin.
24438 */
24439var createBasicPlugin = function createBasicPlugin(name, plugin) {
24440  var basicPluginWrapper = function basicPluginWrapper() {
24441
24442    // We trigger the "beforepluginsetup" and "pluginsetup" events on the player
24443    // regardless, but we want the hash to be consistent with the hash provided
24444    // for advanced plugins.
24445    //
24446    // The only potentially counter-intuitive thing here is the `instance` in
24447    // the "pluginsetup" event is the value returned by the `plugin` function.
24448    triggerSetupEvent(this, { name: name, plugin: plugin, instance: null }, true);
24449
24450    var instance = plugin.apply(this, arguments);
24451
24452    markPluginAsActive(this, name);
24453    triggerSetupEvent(this, { name: name, plugin: plugin, instance: instance });
24454
24455    return instance;
24456  };
24457
24458  Object.keys(plugin).forEach(function (prop) {
24459    basicPluginWrapper[prop] = plugin[prop];
24460  });
24461
24462  return basicPluginWrapper;
24463};
24464
24465/**
24466 * Takes a plugin sub-class and returns a factory function for generating
24467 * instances of it.
24468 *
24469 * This factory function will replace itself with an instance of the requested
24470 * sub-class of Plugin.
24471 *
24472 * @private
24473 * @param   {string} name
24474 *          The name of the plugin.
24475 *
24476 * @param   {Plugin} PluginSubClass
24477 *          The advanced plugin.
24478 *
24479 * @returns {Function}
24480 */
24481var createPluginFactory = function createPluginFactory(name, PluginSubClass) {
24482
24483  // Add a `name` property to the plugin prototype so that each plugin can
24484  // refer to itself by name.
24485  PluginSubClass.prototype.name = name;
24486
24487  return function () {
24488    triggerSetupEvent(this, { name: name, plugin: PluginSubClass, instance: null }, true);
24489
24490    for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
24491      args[_key] = arguments[_key];
24492    }
24493
24494    var instance = new (Function.prototype.bind.apply(PluginSubClass, [null].concat([this].concat(args))))();
24495
24496    // The plugin is replaced by a function that returns the current instance.
24497    this[name] = function () {
24498      return instance;
24499    };
24500
24501    triggerSetupEvent(this, instance.getEventHash());
24502
24503    return instance;
24504  };
24505};
24506
24507/**
24508 * Parent class for all advanced plugins.
24509 *
24510 * @mixes   module:evented~EventedMixin
24511 * @mixes   module:stateful~StatefulMixin
24512 * @fires   Player#beforepluginsetup
24513 * @fires   Player#beforepluginsetup:$name
24514 * @fires   Player#pluginsetup
24515 * @fires   Player#pluginsetup:$name
24516 * @listens Player#dispose
24517 * @throws  {Error}
24518 *          If attempting to instantiate the base {@link Plugin} class
24519 *          directly instead of via a sub-class.
24520 */
24521
24522var Plugin = function () {
24523
24524  /**
24525   * Creates an instance of this class.
24526   *
24527   * Sub-classes should call `super` to ensure plugins are properly initialized.
24528   *
24529   * @param {Player} player
24530   *        A Video.js player instance.
24531   */
24532  function Plugin(player) {
24533    classCallCheck(this, Plugin);
24534
24535    if (this.constructor === Plugin) {
24536      throw new Error('Plugin must be sub-classed; not directly instantiated.');
24537    }
24538
24539    this.player = player;
24540
24541    // Make this object evented, but remove the added `trigger` method so we
24542    // use the prototype version instead.
24543    evented(this);
24544    delete this.trigger;
24545
24546    stateful(this, this.constructor.defaultState);
24547    markPluginAsActive(player, this.name);
24548
24549    // Auto-bind the dispose method so we can use it as a listener and unbind
24550    // it later easily.
24551    this.dispose = bind(this, this.dispose);
24552
24553    // If the player is disposed, dispose the plugin.
24554    player.on('dispose', this.dispose);
24555  }
24556
24557  /**
24558   * Get the version of the plugin that was set on <pluginName>.VERSION
24559   */
24560
24561
24562  Plugin.prototype.version = function version() {
24563    return this.constructor.VERSION;
24564  };
24565
24566  /**
24567   * Each event triggered by plugins includes a hash of additional data with
24568   * conventional properties.
24569   *
24570   * This returns that object or mutates an existing hash.
24571   *
24572   * @param   {Object} [hash={}]
24573   *          An object to be used as event an event hash.
24574   *
24575   * @returns {Plugin~PluginEventHash}
24576   *          An event hash object with provided properties mixed-in.
24577   */
24578
24579
24580  Plugin.prototype.getEventHash = function getEventHash() {
24581    var hash = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
24582
24583    hash.name = this.name;
24584    hash.plugin = this.constructor;
24585    hash.instance = this;
24586    return hash;
24587  };
24588
24589  /**
24590   * Triggers an event on the plugin object and overrides
24591   * {@link module:evented~EventedMixin.trigger|EventedMixin.trigger}.
24592   *
24593   * @param   {string|Object} event
24594   *          An event type or an object with a type property.
24595   *
24596   * @param   {Object} [hash={}]
24597   *          Additional data hash to merge with a
24598   *          {@link Plugin~PluginEventHash|PluginEventHash}.
24599   *
24600   * @returns {boolean}
24601   *          Whether or not default was prevented.
24602   */
24603
24604
24605  Plugin.prototype.trigger = function trigger$$1(event) {
24606    var hash = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
24607
24608    return trigger(this.eventBusEl_, event, this.getEventHash(hash));
24609  };
24610
24611  /**
24612   * Handles "statechanged" events on the plugin. No-op by default, override by
24613   * subclassing.
24614   *
24615   * @abstract
24616   * @param    {Event} e
24617   *           An event object provided by a "statechanged" event.
24618   *
24619   * @param    {Object} e.changes
24620   *           An object describing changes that occurred with the "statechanged"
24621   *           event.
24622   */
24623
24624
24625  Plugin.prototype.handleStateChanged = function handleStateChanged(e) {};
24626
24627  /**
24628   * Disposes a plugin.
24629   *
24630   * Subclasses can override this if they want, but for the sake of safety,
24631   * it's probably best to subscribe the "dispose" event.
24632   *
24633   * @fires Plugin#dispose
24634   */
24635
24636
24637  Plugin.prototype.dispose = function dispose() {
24638    var name = this.name,
24639        player = this.player;
24640
24641    /**
24642     * Signals that a advanced plugin is about to be disposed.
24643     *
24644     * @event Plugin#dispose
24645     * @type  {EventTarget~Event}
24646     */
24647
24648    this.trigger('dispose');
24649    this.off();
24650    player.off('dispose', this.dispose);
24651
24652    // Eliminate any possible sources of leaking memory by clearing up
24653    // references between the player and the plugin instance and nulling out
24654    // the plugin's state and replacing methods with a function that throws.
24655    player[PLUGIN_CACHE_KEY][name] = false;
24656    this.player = this.state = null;
24657
24658    // Finally, replace the plugin name on the player with a new factory
24659    // function, so that the plugin is ready to be set up again.
24660    player[name] = createPluginFactory(name, pluginStorage[name]);
24661  };
24662
24663  /**
24664   * Determines if a plugin is a basic plugin (i.e. not a sub-class of `Plugin`).
24665   *
24666   * @param   {string|Function} plugin
24667   *          If a string, matches the name of a plugin. If a function, will be
24668   *          tested directly.
24669   *
24670   * @returns {boolean}
24671   *          Whether or not a plugin is a basic plugin.
24672   */
24673
24674
24675  Plugin.isBasic = function isBasic(plugin) {
24676    var p = typeof plugin === 'string' ? getPlugin(plugin) : plugin;
24677
24678    return typeof p === 'function' && !Plugin.prototype.isPrototypeOf(p.prototype);
24679  };
24680
24681  /**
24682   * Register a Video.js plugin.
24683   *
24684   * @param   {string} name
24685   *          The name of the plugin to be registered. Must be a string and
24686   *          must not match an existing plugin or a method on the `Player`
24687   *          prototype.
24688   *
24689   * @param   {Function} plugin
24690   *          A sub-class of `Plugin` or a function for basic plugins.
24691   *
24692   * @returns {Function}
24693   *          For advanced plugins, a factory function for that plugin. For
24694   *          basic plugins, a wrapper function that initializes the plugin.
24695   */
24696
24697
24698  Plugin.registerPlugin = function registerPlugin(name, plugin) {
24699    if (typeof name !== 'string') {
24700      throw new Error('Illegal plugin name, "' + name + '", must be a string, was ' + (typeof name === 'undefined' ? 'undefined' : _typeof(name)) + '.');
24701    }
24702
24703    if (pluginExists(name)) {
24704      log$1.warn('A plugin named "' + name + '" already exists. You may want to avoid re-registering plugins!');
24705    } else if (Player.prototype.hasOwnProperty(name)) {
24706      throw new Error('Illegal plugin name, "' + name + '", cannot share a name with an existing player method!');
24707    }
24708
24709    if (typeof plugin !== 'function') {
24710      throw new Error('Illegal plugin for "' + name + '", must be a function, was ' + (typeof plugin === 'undefined' ? 'undefined' : _typeof(plugin)) + '.');
24711    }
24712
24713    pluginStorage[name] = plugin;
24714
24715    // Add a player prototype method for all sub-classed plugins (but not for
24716    // the base Plugin class).
24717    if (name !== BASE_PLUGIN_NAME) {
24718      if (Plugin.isBasic(plugin)) {
24719        Player.prototype[name] = createBasicPlugin(name, plugin);
24720      } else {
24721        Player.prototype[name] = createPluginFactory(name, plugin);
24722      }
24723    }
24724
24725    return plugin;
24726  };
24727
24728  /**
24729   * De-register a Video.js plugin.
24730   *
24731   * @param {string} name
24732   *        The name of the plugin to be deregistered.
24733   */
24734
24735
24736  Plugin.deregisterPlugin = function deregisterPlugin(name) {
24737    if (name === BASE_PLUGIN_NAME) {
24738      throw new Error('Cannot de-register base plugin.');
24739    }
24740    if (pluginExists(name)) {
24741      delete pluginStorage[name];
24742      delete Player.prototype[name];
24743    }
24744  };
24745
24746  /**
24747   * Gets an object containing multiple Video.js plugins.
24748   *
24749   * @param   {Array} [names]
24750   *          If provided, should be an array of plugin names. Defaults to _all_
24751   *          plugin names.
24752   *
24753   * @returns {Object|undefined}
24754   *          An object containing plugin(s) associated with their name(s) or
24755   *          `undefined` if no matching plugins exist).
24756   */
24757
24758
24759  Plugin.getPlugins = function getPlugins() {
24760    var names = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Object.keys(pluginStorage);
24761
24762    var result = void 0;
24763
24764    names.forEach(function (name) {
24765      var plugin = getPlugin(name);
24766
24767      if (plugin) {
24768        result = result || {};
24769        result[name] = plugin;
24770      }
24771    });
24772
24773    return result;
24774  };
24775
24776  /**
24777   * Gets a plugin's version, if available
24778   *
24779   * @param   {string} name
24780   *          The name of a plugin.
24781   *
24782   * @returns {string}
24783   *          The plugin's version or an empty string.
24784   */
24785
24786
24787  Plugin.getPluginVersion = function getPluginVersion(name) {
24788    var plugin = getPlugin(name);
24789
24790    return plugin && plugin.VERSION || '';
24791  };
24792
24793  return Plugin;
24794}();
24795
24796/**
24797 * Gets a plugin by name if it exists.
24798 *
24799 * @static
24800 * @method   getPlugin
24801 * @memberOf Plugin
24802 * @param    {string} name
24803 *           The name of a plugin.
24804 *
24805 * @returns  {Function|undefined}
24806 *           The plugin (or `undefined`).
24807 */
24808
24809
24810Plugin.getPlugin = getPlugin;
24811
24812/**
24813 * The name of the base plugin class as it is registered.
24814 *
24815 * @type {string}
24816 */
24817Plugin.BASE_PLUGIN_NAME = BASE_PLUGIN_NAME;
24818
24819Plugin.registerPlugin(BASE_PLUGIN_NAME, Plugin);
24820
24821/**
24822 * Documented in player.js
24823 *
24824 * @ignore
24825 */
24826Player.prototype.usingPlugin = function (name) {
24827  return !!this[PLUGIN_CACHE_KEY] && this[PLUGIN_CACHE_KEY][name] === true;
24828};
24829
24830/**
24831 * Documented in player.js
24832 *
24833 * @ignore
24834 */
24835Player.prototype.hasPlugin = function (name) {
24836  return !!pluginExists(name);
24837};
24838
24839/**
24840 * 
vendor: 3,442 bytes, lines 24840-24973
24840Signals that a plugin is about to be set up on a player.
24841 *
24842 * @event    Player#beforepluginsetup
24843 * @type     {Plugin~PluginEventHash}
24844 */
24845
24846/**
24847 * Signals that a plugin is about to be set up on a player - by name. The name
24848 * is the name of the plugin.
24849 *
24850 * @event    Player#beforepluginsetup:$name
24851 * @type     {Plugin~PluginEventHash}
24852 */
24853
24854/**
24855 * Signals that a plugin has just been set up on a player.
24856 *
24857 * @event    Player#pluginsetup
24858 * @type     {Plugin~PluginEventHash}
24859 */
24860
24861/**
24862 * Signals that a plugin has just been set up on a player - by name. The name
24863 * is the name of the plugin.
24864 *
24865 * @event    Player#pluginsetup:$name
24866 * @type     {Plugin~PluginEventHash}
24867 */
24868
24869/**
24870 * @typedef  {Object} Plugin~PluginEventHash
24871 *
24872 * @property {string} instance
24873 *           For basic plugins, the return value of the plugin function. For
24874 *           advanced plugins, the plugin instance on which the event is fired.
24875 *
24876 * @property {string} name
24877 *           The name of the plugin.
24878 *
24879 * @property {string} plugin
24880 *           For basic plugins, the plugin function. For advanced plugins, the
24881 *           plugin class/constructor.
24882 */
24883
24884/**
24885 * @file extend.js
24886 * @module extend
24887 */
24888
24889/**
24890 * A combination of node inherits and babel's inherits (after transpile).
24891 * Both work the same but node adds `super_` to the subClass
24892 * and Bable adds the superClass as __proto__. Both seem useful.
24893 *
24894 * @param {Object} subClass
24895 *        The class to inherit to
24896 *
24897 * @param {Object} superClass
24898 *        The class to inherit from
24899 *
24900 * @private
24901 */
24902var _inherits = function _inherits(subClass, superClass) {
24903  if (typeof superClass !== 'function' && superClass !== null) {
24904    throw new TypeError('Super expression must either be null or a function, not ' + (typeof superClass === 'undefined' ? 'undefined' : _typeof(superClass)));
24905  }
24906
24907  subClass.prototype = Object.create(superClass && superClass.prototype, {
24908    constructor: {
24909      value: subClass,
24910      enumerable: false,
24911      writable: true,
24912      configurable: true
24913    }
24914  });
24915
24916  if (superClass) {
24917    // node
24918    subClass.super_ = superClass;
24919  }
24920};
24921
24922/**
24923 * Function for subclassing using the same inheritance that
24924 * videojs uses internally
24925 *
24926 * @static
24927 * @const
24928 *
24929 * @param {Object} superClass
24930 *        The class to inherit from
24931 *
24932 * @param {Object} [subClassMethods={}]
24933 *        The class to inherit to
24934 *
24935 * @return {Object}
24936 *         The new object with subClassMethods that inherited superClass.
24937 */
24938var extendFn = function extendFn(superClass) {
24939  var subClassMethods = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
24940
24941  var subClass = function subClass() {
24942    superClass.apply(this, arguments);
24943  };
24944
24945  var methods = {};
24946
24947  if ((typeof subClassMethods === 'undefined' ? 'undefined' : _typeof(subClassMethods)) === 'object') {
24948    if (subClassMethods.constructor !== Object.prototype.constructor) {
24949      subClass = subClassMethods.constructor;
24950    }
24951    methods = subClassMethods;
24952  } else if (typeof subClassMethods === 'function') {
24953    subClass = subClassMethods;
24954  }
24955
24956  _inherits(subClass, superClass);
24957
24958  // Extend subObj's prototype with functions and other properties from props
24959  for (var name in methods) {
24960    if (methods.hasOwnProperty(name)) {
24961      subClass.prototype[name] = methods[name];
24962    }
24963  }
24964
24965  return subClass;
24966};
24967
24968/**
24969 * @file video.js
24970 * @module videojs
24971 */
24972// Include the built-in techs
24973// HTML5 Element Shim for IE8
24974if (typeof HTMLVideoElement === 'undefined' && isReal()) {
24975  document_1.createElement('video');
24976  document_1.createElement('audio');
24977  document_1.createElement('track');
24978  document_1.createElement('video-js');
24979}
24980
24981/**
24982 * Doubles as the main function for users to create a player instance and also
24983 * the main library object.
24984 * The `videojs` function can be used to initialize or retrieve a player.
24985  *
24986 * @param {string|Element} id
24987 *        Video element or video element ID
24988 *
24989 * @param {Object} [options]
24990 *        Optional options object for config/settings
24991 *
24992 * @param {Component~ReadyCallback} [ready]
24993 *        Optional ready callback
24994 *
24995 * @return {Player}
24996 *         A player instance
24997 */
24998function videojs(id, options, ready) {
24999  var tag = void 0;
25000
25001  // Allow for element or ID to be passed in
25002  // String ID
25003  if (typeof id === 'string') {
25004    var players = videojs.getPlayers();
25005
25006    // Adjust for jQuery ID syntax
25007    if (id.indexOf('#') === 0) {
25008      id = id.slice(1);
25009    }
25010
25011    // If a player instance has already been created for this ID return it.
25012    if (players[id]) {
25013
25014      // If options or ready function are passed, warn
25015      if (options) {
25016        log$1.warn('Player "' + id + '" is already initialised. Options will not be applied.');
25017      }
25018
25019      if (ready) {
25020        players[id].ready(ready);
25021      }
25022
25023      return players[id];
25024    }
25025
25026    // Otherwise get element for ID
25027    tag = $('#' + id);
25028
25029    // ID is a media element
25030  } else {
25031    tag = id;
25032  }
25033
25034  // Check for a useable element
25035  // re: nodeName, could be a box div also
25036  if (!tag || !tag.nodeName) {
25037    throw new TypeError('The element or ID supplied is not valid. (videojs)');
25038  }
25039
25040  // Element may have a player attr referring to an already created player instance.
25041  // If so return that otherwise set up a new player below
25042  if (tag.player || Player.players[tag.playerId]) {
25043    return tag.player || Player.players[tag.playerId];
25044  }
25045
25046  // Check if element is included in the DOM
25047  if (isEl(tag) && !document_1.body.contains(tag)) {
25048    log$1.warn('The element supplied is not included in the DOM');
25049  }
25050
25051  options = options || {};
25052
25053  videojs.hooks('beforesetup').forEach(function (hookFunction) {
25054    var opts = hookFunction(tag, mergeOptions(options));
25055
25056    if (!isObject(opts) || Array.isArray(opts)) {
25057      log$1.error('please return an object in beforesetup hooks');
25058      return;
25059    }
25060
25061    options = mergeOptions(options, opts);
25062  });
25063
25064  var PlayerComponent = Component.getComponent('Player');
25065  // If not, set up a new player
25066  var player = new PlayerComponent(tag, options, ready);
25067
25068  videojs.hooks('setup').forEach(function (hookFunction) {
25069    return hookFunction(player);
25070  });
25071
25072  return player;
25073}
25074
25075/**
25076 * An Object that contains lifecycle hooks as keys which point to an array
25077 * of functions that are run when a lifecycle is triggered
25078 */
25079videojs.hooks_ = {};
25080
25081/**
25082 * Get a list of hooks for a specific lifecycle
25083 * @function videojs.hooks
25084 *
25085 * @param {string} type
25086 *        the lifecyle to get hooks from
25087 *
25088 * @param {Function|Function[]} [fn]
25089 *        Optionally add a hook (or hooks) to the lifecycle that your are getting.
25090 *
25091 * @return {Array}
25092 *         an array of hooks, or an empty array if there are none.
25093 */
25094videojs.hooks = function (type, fn) {
25095  videojs.hooks_[type] = videojs.hooks_[type] || [];
25096  if (fn) {
25097    videojs.hooks_[type] = videojs.hooks_[type].concat(fn);
25098  }
25099  return videojs.hooks_[type];
25100};
25101
25102/**
25103 * Add a function hook to a specific videojs lifecycle.
25104 *
25105 * @param {string} type
25106 *        the lifecycle to hook the function to.
25107 *
25108 * @param {Function|Function[]}
25109 *        The function or array of functions to attach.
25110 */
25111videojs.hook = function (type, fn) {
25112  videojs.hooks(type, fn);
25113};
25114
25115/**
25116 * Add a function hook that will only run once to a specific videojs lifecycle.
25117 *
25118 * @param {string} type
25119 *        the lifecycle to hook the function to.
25120 *
25121 * @param {Function|Function[]}
25122 *        The function or array of functions to attach.
25123 */
25124videojs.hookOnce = function (type, fn) {
25125  videojs.hooks(type, [].concat(fn).map(function (original) {
25126    var wrapper = function wrapper() {
25127      videojs.removeHook(type, wrapper);
25128      return original.apply(undefined, arguments);
25129    };
25130
25131    return wrapper;
25132  }));
25133};
25134
25135/**
25136 * Remove a hook from a specific videojs lifecycle.
25137 *
25138 * @param {string} type
25139 *        the lifecycle that the function hooked to
25140 *
25141 * @param {Function} fn
25142 *        The hooked function to remove
25143 *
25144 * @return {boolean}
25145 *         The function that was removed or undef
25146 */
vendor: 5,198 bytes, lines 25147-25345
25147videojs.removeHook = function (type, fn) {
25148  var index = videojs.hooks(type).indexOf(fn);
25149
25150  if (index <= -1) {
25151    return false;
25152  }
25153
25154  videojs.hooks_[type] = videojs.hooks_[type].slice();
25155  videojs.hooks_[type].splice(index, 1);
25156
25157  return true;
25158};
25159
25160// Add default styles
25161if (window_1.VIDEOJS_NO_DYNAMIC_STYLE !== true && isReal()) {
25162  var style = $('.vjs-styles-defaults');
25163
25164  if (!style) {
25165    style = createStyleElement('vjs-styles-defaults');
25166    var head = $('head');
25167
25168    if (head) {
25169      head.insertBefore(style, head.firstChild);
25170    }
25171    setTextContent(style, '\n      .video-js {\n        width: 300px;\n        height: 150px;\n      }\n\n      .vjs-fluid {\n        padding-top: 56.25%\n      }\n    ');
25172  }
25173}
25174
25175// Run Auto-load players
25176// You have to wait at least once in case this script is loaded after your
25177// video in the DOM (weird behavior only with minified version)
25178autoSetupTimeout(1, videojs);
25179
25180/**
25181 * Current software version. Follows semver.
25182 *
25183 * @type {string}
25184 */
25185videojs.VERSION = version;
25186
25187/**
25188 * The global options object. These are the settings that take effect
25189 * if no overrides are specified when the player is created.
25190 *
25191 * @type {Object}
25192 */
25193videojs.options = Player.prototype.options_;
25194
25195/**
25196 * Get an object with the currently created players, keyed by player ID
25197 *
25198 * @return {Object}
25199 *         The created players
25200 */
25201videojs.getPlayers = function () {
25202  return Player.players;
25203};
25204
25205/**
25206 * Expose players object.
25207 *
25208 * @memberOf videojs
25209 * @property {Object} players
25210 */
25211videojs.players = Player.players;
25212
25213/**
25214 * Get a component class object by name
25215 *
25216 * @borrows Component.getComponent as videojs.getComponent
25217 */
25218videojs.getComponent = Component.getComponent;
25219
25220/**
25221 * Register a component so it can referred to by name. Used when adding to other
25222 * components, either through addChild `component.addChild('myComponent')` or through
25223 * default children options  `{ children: ['myComponent'] }`.
25224 *
25225 * > NOTE: You could also just initialize the component before adding.
25226 * `component.addChild(new MyComponent());`
25227 *
25228 * @param {string} name
25229 *        The class name of the component
25230 *
25231 * @param {Component} comp
25232 *        The component class
25233 *
25234 * @return {Component}
25235 *         The newly registered component
25236 */
25237videojs.registerComponent = function (name$$1, comp) {
25238  if (Tech.isTech(comp)) {
25239    log$1.warn('The ' + name$$1 + ' tech was registered as a component. It should instead be registered using videojs.registerTech(name, tech)');
25240  }
25241
25242  Component.registerComponent.call(Component, name$$1, comp);
25243};
25244
25245/**
25246 * Get a Tech class object by name
25247 *
25248 * @borrows Tech.getTech as videojs.getTech
25249 */
25250videojs.getTech = Tech.getTech;
25251
25252/**
25253 * Register a Tech so it can referred to by name.
25254 * This is used in the tech order for the player.
25255 *
25256 * @borrows Tech.registerTech as videojs.registerTech
25257 */
25258videojs.registerTech = Tech.registerTech;
25259
25260videojs.use = use;
25261
25262/**
25263 * A suite of browser and device tests from {@link browser}.
25264 *
25265 * @type {Object}
25266 * @private
25267 */
25268videojs.browser = browser;
25269
25270/**
25271 * Whether or not the browser supports touch events. Included for backward
25272 * compatibility with 4.x, but deprecated. Use `videojs.browser.TOUCH_ENABLED`
25273 * instead going forward.
25274 *
25275 * @deprecated since version 5.0
25276 * @type {boolean}
25277 */
25278videojs.TOUCH_ENABLED = TOUCH_ENABLED;
25279
25280/**
25281 * Subclass an existing class
25282 * Mimics ES6 subclassing with the `extend` keyword
25283 *
25284 * @borrows extend:extendFn as videojs.extend
25285 */
25286videojs.extend = extendFn;
25287
25288/**
25289 * Merge two options objects recursively
25290 * Performs a deep merge like lodash.merge but **only merges plain objects**
25291 * (not arrays, elements, anything else)
25292 * Other values will be copied directly from the second object.
25293 *
25294 * @borrows merge-options:mergeOptions as videojs.mergeOptions
25295 */
25296videojs.mergeOptions = mergeOptions;
25297
25298/**
25299 * Change the context (this) of a function
25300 *
25301 * > NOTE: as of v5.0 we require an ES5 shim, so you should use the native
25302 * `function() {}.bind(newContext);` instead of this.
25303 *
25304 * @borrows fn:bind as videojs.bind
25305 */
25306videojs.bind = bind;
25307
25308/**
25309 * Register a Video.js plugin.
25310 *
25311 * @borrows plugin:registerPlugin as videojs.registerPlugin
25312 * @method registerPlugin
25313 *
25314 * @param  {string} name
25315 *         The name of the plugin to be registered. Must be a string and
25316 *         must not match an existing plugin or a method on the `Player`
25317 *         prototype.
25318 *
25319 * @param  {Function} plugin
25320 *         A sub-class of `Plugin` or a function for basic plugins.
25321 *
25322 * @return {Function}
25323 *         For advanced plugins, a factory function for that plugin. For
25324 *         basic plugins, a wrapper function that initializes the plugin.
25325 */
25326videojs.registerPlugin = Plugin.registerPlugin;
25327
25328/**
25329 * Deprecated method to register a plugin with Video.js
25330 *
25331 * @deprecated
25332 *        videojs.plugin() is deprecated; use videojs.registerPlugin() instead
25333 *
25334 * @param {string} name
25335 *        The plugin name
25336 *
25337 * @param {Plugin|Function} plugin
25338 *         The plugin sub-class or function
25339 */
25340videojs.plugin = function (name$$1, plugin) {
25341  log$1.warn('videojs.plugin() is deprecated; use videojs.registerPlugin() instead');
25342  return Plugin.registerPlugin(name$$1, plugin);
25343};
25344
25345/**
25346 * Gets an object containing multiple Video.js plugins.
25347 *
25348 * @param  {Array} [names]
25349 *         If provided, should be an array of plugin names. Defaults to _all_
25350 *         plugin names.
25351 *
25352 * @return {Object|undefined}
25353 *         An object containing plugin(s) associated with their name(s) or
25354 *         `undefined` if no matching plugins exist).
25355 */
25356videojs.getPlugins = Plugin.getPlugins;
25357
25358/**
25359 * Gets a plugin by name if it exists.
25360 *
25361 * @param  {string} name
25362 *         The name of a plugin.
25363 *
25364 * @return {Function|undefined}
25365 *         The plugin (or `undefined`).
25366 */
25367videojs.getPlugin = Plugin.getPlugin;
25368
25369/**
25370 * Gets a plugin's version, if available
25371 *
25372 * @param  {string} name
25373 *         The name of a plugin.
25374 *
25375 * @return {string}
25376 *         The plugin's version or an empty string.
25377 */
25378videojs.getPluginVersion = Plugin.getPluginVersion;
25379
25380/**
25381 * Adding languages so that they're available to all players.
25382 * Example: `videojs.addLanguage('es', { 'Hello': 'Hola' });`
25383 *
25384 * @param {string} code
25385 *        The language code or dictionary property
25386 *
25387 * @param {Object} data
25388 *        The data values to be translated
25389 *
25390 * @return {Object}
25391 *         The resulting language dictionary object
25392 */
25393videojs.addLanguage = function (code, data) {
25394  var _mergeOptions;
25395
25396  code = ('' + code).toLowerCase();
25397
25398  videojs.options.languages = mergeOptions(videojs.options.languages, (_mergeOptions = {}, _mergeOptions[code] = data, _mergeOptions));
25399
25400  return videojs.options.languages[code];
25401};
25402
25403/**
25404 * Log messages
25405 *
25406 * @borrows log:log as videojs.log
25407 */
25408videojs.log = log$1;
25409
25410/**
25411 * Creates an emulated TimeRange object.
25412 *
25413 * @borrows time-ranges:createTimeRanges as videojs.createTimeRange
25414 */
25415/**
25416 * @borrows time-ranges:createTimeRanges as videojs.createTimeRanges
25417 */
25418videojs.createTimeRange = videojs.createTimeRanges = createTimeRanges;
25419
25420/**
25421 * Format seconds as a time string, H:MM:SS or M:SS
25422 * Supplying a guide (in seconds) will force a number of leading zeros
25423 * to cover the length of the guide
25424 *
25425 * @borrows format-time:formatTime as videojs.formatTime
25426 */
25427videojs.formatTime = formatTime;
25428
25429/**
25430 * Resolve and parse the elements of a URL
25431 *
25432 * @borrows url:parseUrl as videojs.parseUrl
25433 */
25434videojs.parseUrl = parseUrl;
25435
25436/**
25437 * Returns whether the url passed is a cross domain request or not.
25438 *
25439 * @borrows url:isCrossOrigin as videojs.isCrossOrigin
25440 */
25441videojs.isCrossOrigin = isCrossOrigin;
25442
25443/**
25444 * Event target class.
25445 *
25446 * @borrows EventTarget as videojs.EventTarget
25447 */
25448videojs.EventTarget = EventTarget;
25449
25450/**
25451 * Add an event listener to element
25452 * It stores the handler function in a separate cache object
25453 * and adds a generic handler to the element's event,
25454 * along with a unique id (guid) to the element.
25455 *
25456 * @borrows events:on as videojs.on
25457 */
25458videojs.on = on;
25459
25460/**
25461 * Trigger a listener only once for an event
25462 *
25463 * @borrows events:one as videojs.one
25464 */
25465videojs.one = one;
25466
25467/**
25468 * Removes event listeners from an element
25469 *
25470 * @borrows events:off as videojs.off
25471 */
25472videojs.off = off;
25473
25474/**
25475 * Trigger an event for an element
25476 *
25477 * @borrows events:trigger as videojs.trigger
25478 */
25479videojs.trigger = trigger;
25480
25481/**
25482 * A cross-browser XMLHttpRequest wrapper. Here's a simple example:
25483 *
25484 * @param {Object} options
25485 *        settings for the request.
25486 *
25487 * @return {XMLHttpRequest|XDomainRequest}
25488 *         The request object.
25489 *
25490 * @see https://github.com/Raynos/xhr
25491 */
25492videojs.xhr = xhr;
25493
25494/**
25495 * TextTrack class
25496 *
25497 * @borrows TextTrack as videojs.TextTrack
25498 */
25499videojs.TextTrack = TextTrack;
25500
25501/**
25502 * export the AudioTrack class so that source handlers can create
25503 * AudioTracks and then add them to the players AudioTrackList
25504 *
25505 * @borrows AudioTrack as videojs.AudioTrack
25506 */
25507videojs.AudioTrack = AudioTrack;
25508
25509/**
25510 * export the VideoTrack class so that source handlers can create
25511 * VideoTracks and then add them to the players VideoTrackList
25512 *
25513 * @borrows VideoTrack as videojs.VideoTrack
25514 */
25515videojs.VideoTrack = VideoTrack;
25516
25517/**
25518 * Determines, via duck typing, whether or not a value is a DOM element.
25519 *
25520 * @borrows dom:isEl as videojs.isEl
25521 * @deprecated Use videojs.dom.isEl() instead
25522 */
25523
25524/**
25525 * Determines, via duck typing, whether or not a value is a text node.
25526 *
25527 * @borrows dom:isTextNode as videojs.isTextNode
25528 * @deprecated Use videojs.dom.isTextNode() instead
25529 */
25530
25531/**
25532 * Creates an element and applies properties.
25533 *
25534 * @borrows dom:createEl as videojs.createEl
25535 * @deprecated Use videojs.dom.createEl() instead
25536 */
25537
25538/**
25539 * Check if an element has a CSS class
25540 *
25541 * @borrows dom:hasElClass as videojs.hasClass
25542 * @deprecated Use videojs.dom.hasClass() instead
25543 */
25544
25545/**
25546 * Add a CSS class name to an element
25547 *
25548 * @borrows dom:addElClass as videojs.addClass
25549 * @deprecated Use videojs.dom.addClass() instead
25550 */
25551
25552/**
25553 * Remove a CSS class name from an element
25554 *
25555 * @borrows dom:removeElClass as videojs.removeClass
25556 * @deprecated Use videojs.dom.removeClass() instead
25557 */
25558
25559/**
25560 * Adds or removes a CSS class name on an element depending on an optional
25561 * condition or the presence/absence of the class name.
25562 *
25563 * @borrows dom:toggleElClass as videojs.toggleClass
25564 * @deprecated Use videojs.dom.toggleClass() instead
25565 */
25566
25567/**
25568 * Apply attributes to an HTML element.
25569 *
25570 * @borrows dom:setElAttributes as videojs.setAttribute
25571 * @deprecated Use videojs.dom.setAttributes() instead
25572 */
25573
25574/**
25575 * Get an element's attribute values, as defined on the HTML tag
25576 * Attributes are not the same as properties. They're defined on the tag
25577 * or with setAttribute (which shouldn't be used with HTML)
25578 * This will return true or false for boolean attributes.
25579 *
25580 * @borrows dom:getElAttributes as videojs.getAttributes
25581 * @deprecated Use videojs.dom.getAttributes() instead
25582 */
25583
25584/**
25585 * Empties the contents of an element.
25586 *
25587 * @borrows dom:emptyEl as videojs.emptyEl
25588 * @deprecated Use videojs.dom.emptyEl() instead
25589 */
25590
25591/**
25592 * Normalizes and appends content to an element.
25593 *
25594 * The content for an element can be passed in multiple types and
25595 * combinations, whose behavior is as follows:
25596 *
25597 * - String
25598 *   Normalized into a text node.
25599 *
25600 * - Element, TextNode
25601 *   Passed through.
25602 *
25603 * - Array
25604 *   A one-dimensional array of strings, elements, nodes, or functions (which
25605 *   return single strings, elements, or nodes).
25606 *
25607 * - Function
25608 *   If the sole argument, is expected to produce a string, element,
25609 *   node, or array.
25610 *
25611 * @borrows dom:appendContents as videojs.appendContet
25612 * @deprecated Use videojs.dom.appendContent() instead
25613 */
25614
25615/**
25616 * Normalizes and inserts content into an element; this is identical to
25617 * `appendContent()`, except it empties the element first.
25618 *
25619 * The content for an element can be passed in multiple types and
25620 * combinations, whose behavior is as follows:
25621 *
25622 * - String
25623 *   Normalized into a text node.
25624 *
25625 * - Element, TextNode
25626 *   Passed through.
25627 *
25628 * - Array
25629 *   A one-dimensional array of strings, elements, nodes, or functions (which
25630 *   return single strings, elements, or nodes).
25631 *
25632 * - Function
25633 *   If the sole argument, is expected to produce a string, element,
25634 *   node, or array.
25635 *
25636 * @borrows dom:insertContent as videojs.insertContent
25637 * @deprecated Use videojs.dom.insertContent() instead
25638 */
25639['isEl', 'isTextNode', 'createEl', 'hasClass', 'addClass', 'removeClass', 'toggleClass', 'setAttributes', 'getAttributes', 'emptyEl', 'appendContent', 'insertContent'].forEach(function (k) {
25640  videojs[k] = function () {
25641    log$1.warn('videojs.' + k + '() is deprecated; use videojs.dom.' + k + '() instead');
25642    return Dom[k].apply(null, arguments);
25643  };
25644});
25645
25646/**
25647 * A safe getComputedStyle with an IE8 fallback.
25648 *
25649 * This is because in Firefox, if the player is loaded in an iframe with `display:none`,
25650 * then `getComputedStyle` returns `null`, so, we do a null-check to make sure
25651 * that the player doesn't break in these cases.
25652 * See https://bugzilla.mozilla.org/show_bug.cgi?id=548397 for more details.
25653 *
25654 * @borrows computed-style:computedStyle as videojs.computedStyle
25655 */
25656videojs.computedStyle = computedStyle;
25657
25658/**
25659 * Export the Dom utilities for use in external plugins
25660 * and Tech's
25661 */
25662videojs.dom = Dom;
25663
25664/**
25665 * Export the Url utilities for use in external plugins
25666 * and Tech's
25667 */
25668videojs.url = Url;
25669
25670return videojs;
25671
25672})));
25673
25674!function(){!function(a){var b=a&&a.videojs;b&&(b.CDN_VERSION="6.6.3")}(window),function(a,b,c,d,e,f,g){b&&b.HELP_IMPROVE_VIDEOJS!==!1&&(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
25674dth+"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)}();

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.