Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / node_modules / video.js / es5 / player.js
1 'use strict';
2
3 exports.__esModule = true;
4
5 var _component = require('./component.js');
6
7 var _component2 = _interopRequireDefault(_component);
8
9 var _document = require('global/document');
10
11 var _document2 = _interopRequireDefault(_document);
12
13 var _window = require('global/window');
14
15 var _window2 = _interopRequireDefault(_window);
16
17 var _events = require('./utils/events.js');
18
19 var Events = _interopRequireWildcard(_events);
20
21 var _dom = require('./utils/dom.js');
22
23 var Dom = _interopRequireWildcard(_dom);
24
25 var _fn = require('./utils/fn.js');
26
27 var Fn = _interopRequireWildcard(_fn);
28
29 var _guid = require('./utils/guid.js');
30
31 var Guid = _interopRequireWildcard(_guid);
32
33 var _browser = require('./utils/browser.js');
34
35 var browser = _interopRequireWildcard(_browser);
36
37 var _log = require('./utils/log.js');
38
39 var _log2 = _interopRequireDefault(_log);
40
41 var _toTitleCase = require('./utils/to-title-case.js');
42
43 var _toTitleCase2 = _interopRequireDefault(_toTitleCase);
44
45 var _timeRanges = require('./utils/time-ranges.js');
46
47 var _buffer = require('./utils/buffer.js');
48
49 var _stylesheet = require('./utils/stylesheet.js');
50
51 var stylesheet = _interopRequireWildcard(_stylesheet);
52
53 var _fullscreenApi = require('./fullscreen-api.js');
54
55 var _fullscreenApi2 = _interopRequireDefault(_fullscreenApi);
56
57 var _mediaError = require('./media-error.js');
58
59 var _mediaError2 = _interopRequireDefault(_mediaError);
60
61 var _tuple = require('safe-json-parse/tuple');
62
63 var _tuple2 = _interopRequireDefault(_tuple);
64
65 var _obj = require('./utils/obj');
66
67 var _mergeOptions = require('./utils/merge-options.js');
68
69 var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
70
71 var _textTrackListConverter = require('./tracks/text-track-list-converter.js');
72
73 var _textTrackListConverter2 = _interopRequireDefault(_textTrackListConverter);
74
75 var _modalDialog = require('./modal-dialog');
76
77 var _modalDialog2 = _interopRequireDefault(_modalDialog);
78
79 var _tech = require('./tech/tech.js');
80
81 var _tech2 = _interopRequireDefault(_tech);
82
83 var _audioTrackList = require('./tracks/audio-track-list.js');
84
85 var _audioTrackList2 = _interopRequireDefault(_audioTrackList);
86
87 var _videoTrackList = require('./tracks/video-track-list.js');
88
89 var _videoTrackList2 = _interopRequireDefault(_videoTrackList);
90
91 require('./tech/loader.js');
92
93 require('./tech/flash.js');
94
95 require('./poster-image.js');
96
97 require('./tracks/text-track-display.js');
98
99 require('./loading-spinner.js');
100
101 require('./big-play-button.js');
102
103 require('./close-button.js');
104
105 require('./control-bar/control-bar.js');
106
107 require('./error-display.js');
108
109 require('./tracks/text-track-settings.js');
110
111 require('./tech/html5.js');
112
113 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
114
115 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
116
117 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
118
119 function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
120
121 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /**
122                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 * @file player.js
123                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 */
124 // Subclasses Component
125
126
127 // The following imports are used only to ensure that the corresponding modules
128 // are always included in the video.js package. Importing the modules will
129 // execute them and they will register themselves with video.js.
130
131
132 // Import Html5 tech, at least for disposing the original video tag.
133
134
135 // The following tech events are simply re-triggered
136 // on the player when they happen
137 var TECH_EVENTS_RETRIGGER = [
138 /**
139  * Fired while the user agent is downloading media data.
140  *
141  * @event Player#progress
142  * @type {EventTarget~Event}
143  */
144 /**
145  * Retrigger the `progress` event that was triggered by the {@link Tech}.
146  *
147  * @private
148  * @method Player#handleTechProgress_
149  * @fires Player#progress
150  * @listens Tech#progress
151  */
152 'progress',
153
154 /**
155  * Fires when the loading of an audio/video is aborted.
156  *
157  * @event Player#abort
158  * @type {EventTarget~Event}
159  */
160 /**
161  * Retrigger the `abort` event that was triggered by the {@link Tech}.
162  *
163  * @private
164  * @method Player#handleTechAbort_
165  * @fires Player#abort
166  * @listens Tech#abort
167  */
168 'abort',
169
170 /**
171  * Fires when the browser is intentionally not getting media data.
172  *
173  * @event Player#suspend
174  * @type {EventTarget~Event}
175  */
176 /**
177  * Retrigger the `suspend` event that was triggered by the {@link Tech}.
178  *
179  * @private
180  * @method Player#handleTechSuspend_
181  * @fires Player#suspend
182  * @listens Tech#suspend
183  */
184 'suspend',
185
186 /**
187  * Fires when the current playlist is empty.
188  *
189  * @event Player#emptied
190  * @type {EventTarget~Event}
191  */
192 /**
193  * Retrigger the `emptied` event that was triggered by the {@link Tech}.
194  *
195  * @private
196  * @method Player#handleTechEmptied_
197  * @fires Player#emptied
198  * @listens Tech#emptied
199  */
200 'emptied',
201 /**
202  * Fires when the browser is trying to get media data, but data is not available.
203  *
204  * @event Player#stalled
205  * @type {EventTarget~Event}
206  */
207 /**
208  * Retrigger the `stalled` event that was triggered by the {@link Tech}.
209  *
210  * @private
211  * @method Player#handleTechStalled_
212  * @fires Player#stalled
213  * @listens Tech#stalled
214  */
215 'stalled',
216
217 /**
218  * Fires when the browser has loaded meta data for the audio/video.
219  *
220  * @event Player#loadedmetadata
221  * @type {EventTarget~Event}
222  */
223 /**
224  * Retrigger the `stalled` event that was triggered by the {@link Tech}.
225  *
226  * @private
227  * @method Player#handleTechLoadedmetadata_
228  * @fires Player#loadedmetadata
229  * @listens Tech#loadedmetadata
230  */
231 'loadedmetadata',
232
233 /**
234  * Fires when the browser has loaded the current frame of the audio/video.
235  *
236  * @event player#loadeddata
237  * @type {event}
238  */
239 /**
240  * Retrigger the `loadeddata` event that was triggered by the {@link Tech}.
241  *
242  * @private
243  * @method Player#handleTechLoaddeddata_
244  * @fires Player#loadeddata
245  * @listens Tech#loadeddata
246  */
247 'loadeddata',
248
249 /**
250  * Fires when the current playback position has changed.
251  *
252  * @event player#timeupdate
253  * @type {event}
254  */
255 /**
256  * Retrigger the `timeupdate` event that was triggered by the {@link Tech}.
257  *
258  * @private
259  * @method Player#handleTechTimeUpdate_
260  * @fires Player#timeupdate
261  * @listens Tech#timeupdate
262  */
263 'timeupdate',
264
265 /**
266  * Fires when the playing speed of the audio/video is changed
267  *
268  * @event player#ratechange
269  * @type {event}
270  */
271 /**
272  * Retrigger the `ratechange` event that was triggered by the {@link Tech}.
273  *
274  * @private
275  * @method Player#handleTechRatechange_
276  * @fires Player#ratechange
277  * @listens Tech#ratechange
278  */
279 'ratechange',
280
281 /**
282  * Fires when the volume has been changed
283  *
284  * @event player#volumechange
285  * @type {event}
286  */
287 /**
288  * Retrigger the `volumechange` event that was triggered by the {@link Tech}.
289  *
290  * @private
291  * @method Player#handleTechVolumechange_
292  * @fires Player#volumechange
293  * @listens Tech#volumechange
294  */
295 'volumechange',
296
297 /**
298  * Fires when the text track has been changed
299  *
300  * @event player#texttrackchange
301  * @type {event}
302  */
303 /**
304  * Retrigger the `texttrackchange` event that was triggered by the {@link Tech}.
305  *
306  * @private
307  * @method Player#handleTechTexttrackchange_
308  * @fires Player#texttrackchange
309  * @listens Tech#texttrackchange
310  */
311 'texttrackchange'];
312
313 /**
314  * An instance of the `Player` class is created when any of the Video.js setup methods
315  * are used to initialize a video.
316  *
317  * After an instance has been created it can be accessed globally in two ways:
318  * 1. By calling `videojs('example_video_1');`
319  * 2. By using it directly via  `videojs.players.example_video_1;`
320  *
321  * @extends Component
322  */
323
324 var Player = function (_Component) {
325   _inherits(Player, _Component);
326
327   /**
328    * Create an instance of this class.
329    *
330    * @param {Element} tag
331    *        The original video DOM element used for configuring options.
332    *
333    * @param {Object} [options]
334    *        Object of option names and values.
335    *
336    * @param {Component~ReadyCallback} [ready]
337    *        Ready callback function.
338    */
339   function Player(tag, options, ready) {
340     _classCallCheck(this, Player);
341
342     // Make sure tag ID exists
343     tag.id = tag.id || 'vjs_video_' + Guid.newGUID();
344
345     // Set Options
346     // The options argument overrides options set in the video tag
347     // which overrides globally set options.
348     // This latter part coincides with the load order
349     // (tag must exist before Player)
350     options = (0, _obj.assign)(Player.getTagSettings(tag), options);
351
352     // Delay the initialization of children because we need to set up
353     // player properties first, and can't use `this` before `super()`
354     options.initChildren = false;
355
356     // Same with creating the element
357     options.createEl = false;
358
359     // we don't want the player to report touch activity on itself
360     // see enableTouchActivity in Component
361     options.reportTouchActivity = false;
362
363     // If language is not set, get the closest lang attribute
364     if (!options.language) {
365       if (typeof tag.closest === 'function') {
366         var closest = tag.closest('[lang]');
367
368         if (closest) {
369           options.language = closest.getAttribute('lang');
370         }
371       } else {
372         var element = tag;
373
374         while (element && element.nodeType === 1) {
375           if (Dom.getElAttributes(element).hasOwnProperty('lang')) {
376             options.language = element.getAttribute('lang');
377             break;
378           }
379           element = element.parentNode;
380         }
381       }
382     }
383
384     // Run base component initializing with new options
385
386     // if the global option object was accidentally blown away by
387     // someone, bail early with an informative error
388     var _this = _possibleConstructorReturn(this, _Component.call(this, null, options, ready));
389
390     if (!_this.options_ || !_this.options_.techOrder || !_this.options_.techOrder.length) {
391       throw new Error('No techOrder specified. Did you overwrite ' + 'videojs.options instead of just changing the ' + 'properties you want to override?');
392     }
393
394     // Store the original tag used to set options
395     _this.tag = tag;
396
397     // Store the tag attributes used to restore html5 element
398     _this.tagAttributes = tag && Dom.getElAttributes(tag);
399
400     // Update current language
401     _this.language(_this.options_.language);
402
403     // Update Supported Languages
404     if (options.languages) {
405       // Normalise player option languages to lowercase
406       var languagesToLower = {};
407
408       Object.getOwnPropertyNames(options.languages).forEach(function (name) {
409         languagesToLower[name.toLowerCase()] = options.languages[name];
410       });
411       _this.languages_ = languagesToLower;
412     } else {
413       _this.languages_ = Player.prototype.options_.languages;
414     }
415
416     // Cache for video property values.
417     _this.cache_ = {};
418
419     // Set poster
420     _this.poster_ = options.poster || '';
421
422     // Set controls
423     _this.controls_ = !!options.controls;
424
425     // Original tag settings stored in options
426     // now remove immediately so native controls don't flash.
427     // May be turned back on by HTML5 tech if nativeControlsForTouch is true
428     tag.controls = false;
429
430     /*
431      * Store the internal state of scrubbing
432      *
433      * @private
434      * @return {Boolean} True if the user is scrubbing
435      */
436     _this.scrubbing_ = false;
437
438     _this.el_ = _this.createEl();
439
440     // We also want to pass the original player options to each component and plugin
441     // as well so they don't need to reach back into the player for options later.
442     // We also need to do another copy of this.options_ so we don't end up with
443     // an infinite loop.
444     var playerOptionsCopy = (0, _mergeOptions2['default'])(_this.options_);
445
446     // Load plugins
447     if (options.plugins) {
448       var plugins = options.plugins;
449
450       Object.getOwnPropertyNames(plugins).forEach(function (name) {
451         if (typeof this[name] === 'function') {
452           this[name](plugins[name]);
453         } else {
454           _log2['default'].error('Unable to find plugin:', name);
455         }
456       }, _this);
457     }
458
459     _this.options_.playerOptions = playerOptionsCopy;
460
461     _this.initChildren();
462
463     // Set isAudio based on whether or not an audio tag was used
464     _this.isAudio(tag.nodeName.toLowerCase() === 'audio');
465
466     // Update controls className. Can't do this when the controls are initially
467     // set because the element doesn't exist yet.
468     if (_this.controls()) {
469       _this.addClass('vjs-controls-enabled');
470     } else {
471       _this.addClass('vjs-controls-disabled');
472     }
473
474     // Set ARIA label and region role depending on player type
475     _this.el_.setAttribute('role', 'region');
476     if (_this.isAudio()) {
477       _this.el_.setAttribute('aria-label', 'audio player');
478     } else {
479       _this.el_.setAttribute('aria-label', 'video player');
480     }
481
482     if (_this.isAudio()) {
483       _this.addClass('vjs-audio');
484     }
485
486     if (_this.flexNotSupported_()) {
487       _this.addClass('vjs-no-flex');
488     }
489
490     // TODO: Make this smarter. Toggle user state between touching/mousing
491     // using events, since devices can have both touch and mouse events.
492     // if (browser.TOUCH_ENABLED) {
493     //   this.addClass('vjs-touch-enabled');
494     // }
495
496     // iOS Safari has broken hover handling
497     if (!browser.IS_IOS) {
498       _this.addClass('vjs-workinghover');
499     }
500
501     // Make player easily findable by ID
502     Player.players[_this.id_] = _this;
503
504     // When the player is first initialized, trigger activity so components
505     // like the control bar show themselves if needed
506     _this.userActive(true);
507     _this.reportUserActivity();
508     _this.listenForUserActivity_();
509
510     _this.on('fullscreenchange', _this.handleFullscreenChange_);
511     _this.on('stageclick', _this.handleStageClick_);
512     return _this;
513   }
514
515   /**
516    * Destroys the video player and does any necessary cleanup.
517    *
518    * This is especially helpful if you are dynamically adding and removing videos
519    * to/from the DOM.
520    *
521    * @fires Player#dispose
522    */
523
524
525   Player.prototype.dispose = function dispose() {
526     /**
527      * Called when the player is being disposed of.
528      *
529      * @event Player#dispose
530      * @type {EventTarget~Event}
531      */
532     this.trigger('dispose');
533     // prevent dispose from being called twice
534     this.off('dispose');
535
536     if (this.styleEl_ && this.styleEl_.parentNode) {
537       this.styleEl_.parentNode.removeChild(this.styleEl_);
538     }
539
540     // Kill reference to this player
541     Player.players[this.id_] = null;
542
543     if (this.tag && this.tag.player) {
544       this.tag.player = null;
545     }
546
547     if (this.el_ && this.el_.player) {
548       this.el_.player = null;
549     }
550
551     if (this.tech_) {
552       this.tech_.dispose();
553     }
554
555     _Component.prototype.dispose.call(this);
556   };
557
558   /**
559    * Create the `Player`'s DOM element.
560    *
561    * @return {Element}
562    *         The DOM element that gets created.
563    */
564
565
566   Player.prototype.createEl = function createEl() {
567     var tag = this.tag;
568     var el = void 0;
569     var playerElIngest = this.playerElIngest_ = tag.parentNode && tag.parentNode.hasAttribute && tag.parentNode.hasAttribute('data-vjs-player');
570
571     if (playerElIngest) {
572       el = this.el_ = tag.parentNode;
573     } else {
574       el = this.el_ = _Component.prototype.createEl.call(this, 'div');
575     }
576
577     // set tabindex to -1 so we could focus on the player element
578     tag.setAttribute('tabindex', '-1');
579
580     // Remove width/height attrs from tag so CSS can make it 100% width/height
581     tag.removeAttribute('width');
582     tag.removeAttribute('height');
583
584     // Copy over all the attributes from the tag, including ID and class
585     // ID will now reference player box, not the video tag
586     var attrs = Dom.getElAttributes(tag);
587
588     Object.getOwnPropertyNames(attrs).forEach(function (attr) {
589       // workaround so we don't totally break IE7
590       // http://stackoverflow.com/questions/3653444/css-styles-not-applied-on-dynamic-elements-in-internet-explorer-7
591       if (attr === 'class') {
592         el.className += ' ' + attrs[attr];
593       } else {
594         el.setAttribute(attr, attrs[attr]);
595       }
596     });
597
598     // Update tag id/class for use as HTML5 playback tech
599     // Might think we should do this after embedding in container so .vjs-tech class
600     // doesn't flash 100% width/height, but class only applies with .video-js parent
601     tag.playerId = tag.id;
602     tag.id += '_html5_api';
603     tag.className = 'vjs-tech';
604
605     // Make player findable on elements
606     tag.player = el.player = this;
607     // Default state of video is paused
608     this.addClass('vjs-paused');
609
610     // Add a style element in the player that we'll use to set the width/height
611     // of the player in a way that's still overrideable by CSS, just like the
612     // video element
613     if (_window2['default'].VIDEOJS_NO_DYNAMIC_STYLE !== true) {
614       this.styleEl_ = stylesheet.createStyleElement('vjs-styles-dimensions');
615       var defaultsStyleEl = Dom.$('.vjs-styles-defaults');
616       var head = Dom.$('head');
617
618       head.insertBefore(this.styleEl_, defaultsStyleEl ? defaultsStyleEl.nextSibling : head.firstChild);
619     }
620
621     // Pass in the width/height/aspectRatio options which will update the style el
622     this.width(this.options_.width);
623     this.height(this.options_.height);
624     this.fluid(this.options_.fluid);
625     this.aspectRatio(this.options_.aspectRatio);
626
627     // Hide any links within the video/audio tag, because IE doesn't hide them completely.
628     var links = tag.getElementsByTagName('a');
629
630     for (var i = 0; i < links.length; i++) {
631       var linkEl = links.item(i);
632
633       Dom.addElClass(linkEl, 'vjs-hidden');
634       linkEl.setAttribute('hidden', 'hidden');
635     }
636
637     // insertElFirst seems to cause the networkState to flicker from 3 to 2, so
638     // keep track of the original for later so we can know if the source originally failed
639     tag.initNetworkState_ = tag.networkState;
640
641     // Wrap video tag in div (el/box) container
642     if (tag.parentNode && !playerElIngest) {
643       tag.parentNode.insertBefore(el, tag);
644     }
645
646     // insert the tag as the first child of the player element
647     // then manually add it to the children array so that this.addChild
648     // will work properly for other components
649     //
650     // Breaks iPhone, fixed in HTML5 setup.
651     Dom.insertElFirst(tag, el);
652     this.children_.unshift(tag);
653
654     this.el_ = el;
655
656     return el;
657   };
658
659   /**
660    * A getter/setter for the `Player`'s width.
661    *
662    * @param {number} [value]
663    *        The value to set the `Player's width to.
664    *
665    * @return {number}
666    *         The current width of the `Player`.
667    */
668
669
670   Player.prototype.width = function width(value) {
671     return this.dimension('width', value);
672   };
673
674   /**
675    * A getter/setter for the `Player`'s height.
676    *
677    * @param {number} [value]
678    *        The value to set the `Player's heigth to.
679    *
680    * @return {number}
681    *         The current heigth of the `Player`.
682    */
683
684
685   Player.prototype.height = function height(value) {
686     return this.dimension('height', value);
687   };
688
689   /**
690    * A getter/setter for the `Player`'s width & height.
691    *
692    * @param {string} dimension
693    *        This string can be:
694    *        - 'width'
695    *        - 'height'
696    *
697    * @param {number} [value]
698    *        Value for dimension specified in the first argument.
699    *
700    * @return {Player|number}
701    *         - Returns itself when setting; method can be chained.
702    *         - The dimension arguments value when getting (width/height).
703    */
704
705
706   Player.prototype.dimension = function dimension(_dimension, value) {
707     var privDimension = _dimension + '_';
708
709     if (value === undefined) {
710       return this[privDimension] || 0;
711     }
712
713     if (value === '') {
714       // If an empty string is given, reset the dimension to be automatic
715       this[privDimension] = undefined;
716     } else {
717       var parsedVal = parseFloat(value);
718
719       if (isNaN(parsedVal)) {
720         _log2['default'].error('Improper value "' + value + '" supplied for for ' + _dimension);
721         return this;
722       }
723
724       this[privDimension] = parsedVal;
725     }
726
727     this.updateStyleEl_();
728     return this;
729   };
730
731   /**
732    * A getter/setter/toggler for the vjs-fluid `className` on the `Player`.
733    *
734    * @param {boolean} [bool]
735    *        - A value of true adds the class.
736    *        - A value of false removes the class.
737    *        - No value will toggle the fluid class.
738    *
739    * @return {boolean|undefined}
740    *         - The value of fluid when getting.
741    *         - `undefined` when setting.
742    */
743
744
745   Player.prototype.fluid = function fluid(bool) {
746     if (bool === undefined) {
747       return !!this.fluid_;
748     }
749
750     this.fluid_ = !!bool;
751
752     if (bool) {
753       this.addClass('vjs-fluid');
754     } else {
755       this.removeClass('vjs-fluid');
756     }
757
758     this.updateStyleEl_();
759   };
760
761   /**
762    * Get/Set the aspect ratio
763    *
764    * @param {string} [ratio]
765    *        Aspect ratio for player
766    *
767    * @return {string|undefined}
768    *         returns the current aspect ratio when getting
769    */
770
771   /**
772    * A getter/setter for the `Player`'s aspect ratio.
773    *
774    * @param {string} [ratio]
775    *        The value to set the `Player's aspect ratio to.
776    *
777    * @return {string|undefined}
778    *         - The current aspect ratio of the `Player` when getting.
779    *         - undefined when setting
780    */
781
782
783   Player.prototype.aspectRatio = function aspectRatio(ratio) {
784     if (ratio === undefined) {
785       return this.aspectRatio_;
786     }
787
788     // Check for width:height format
789     if (!/^\d+\:\d+$/.test(ratio)) {
790       throw new Error('Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.');
791     }
792     this.aspectRatio_ = ratio;
793
794     // We're assuming if you set an aspect ratio you want fluid mode,
795     // because in fixed mode you could calculate width and height yourself.
796     this.fluid(true);
797
798     this.updateStyleEl_();
799   };
800
801   /**
802    * Update styles of the `Player` element (height, width and aspect ratio).
803    *
804    * @private
805    * @listens Tech#loadedmetadata
806    */
807
808
809   Player.prototype.updateStyleEl_ = function updateStyleEl_() {
810     if (_window2['default'].VIDEOJS_NO_DYNAMIC_STYLE === true) {
811       var _width = typeof this.width_ === 'number' ? this.width_ : this.options_.width;
812       var _height = typeof this.height_ === 'number' ? this.height_ : this.options_.height;
813       var techEl = this.tech_ && this.tech_.el();
814
815       if (techEl) {
816         if (_width >= 0) {
817           techEl.width = _width;
818         }
819         if (_height >= 0) {
820           techEl.height = _height;
821         }
822       }
823
824       return;
825     }
826
827     var width = void 0;
828     var height = void 0;
829     var aspectRatio = void 0;
830     var idClass = void 0;
831
832     // The aspect ratio is either used directly or to calculate width and height.
833     if (this.aspectRatio_ !== undefined && this.aspectRatio_ !== 'auto') {
834       // Use any aspectRatio that's been specifically set
835       aspectRatio = this.aspectRatio_;
836     } else if (this.videoWidth() > 0) {
837       // Otherwise try to get the aspect ratio from the video metadata
838       aspectRatio = this.videoWidth() + ':' + this.videoHeight();
839     } else {
840       // Or use a default. The video element's is 2:1, but 16:9 is more common.
841       aspectRatio = '16:9';
842     }
843
844     // Get the ratio as a decimal we can use to calculate dimensions
845     var ratioParts = aspectRatio.split(':');
846     var ratioMultiplier = ratioParts[1] / ratioParts[0];
847
848     if (this.width_ !== undefined) {
849       // Use any width that's been specifically set
850       width = this.width_;
851     } else if (this.height_ !== undefined) {
852       // Or calulate the width from the aspect ratio if a height has been set
853       width = this.height_ / ratioMultiplier;
854     } else {
855       // Or use the video's metadata, or use the video el's default of 300
856       width = this.videoWidth() || 300;
857     }
858
859     if (this.height_ !== undefined) {
860       // Use any height that's been specifically set
861       height = this.height_;
862     } else {
863       // Otherwise calculate the height from the ratio and the width
864       height = width * ratioMultiplier;
865     }
866
867     // Ensure the CSS class is valid by starting with an alpha character
868     if (/^[^a-zA-Z]/.test(this.id())) {
869       idClass = 'dimensions-' + this.id();
870     } else {
871       idClass = this.id() + '-dimensions';
872     }
873
874     // Ensure the right class is still on the player for the style element
875     this.addClass(idClass);
876
877     stylesheet.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    ');
878   };
879
880   /**
881    * Load/Create an instance of playback {@link Tech} including element
882    * and API methods. Then append the `Tech` element in `Player` as a child.
883    *
884    * @param {string} techName
885    *        name of the playback technology
886    *
887    * @param {string} source
888    *        video source
889    *
890    * @private
891    */
892
893
894   Player.prototype.loadTech_ = function loadTech_(techName, source) {
895     var _this2 = this;
896
897     // Pause and remove current playback technology
898     if (this.tech_) {
899       this.unloadTech_();
900     }
901
902     // get rid of the HTML5 video tag as soon as we are using another tech
903     if (techName !== 'Html5' && this.tag) {
904       _tech2['default'].getTech('Html5').disposeMediaElement(this.tag);
905       this.tag.player = null;
906       this.tag = null;
907     }
908
909     this.techName_ = techName;
910
911     // Turn off API access because we're loading a new tech that might load asynchronously
912     this.isReady_ = false;
913
914     // Grab tech-specific options from player options and add source and parent element to use.
915     var techOptions = (0, _obj.assign)({
916       source: source,
917       'nativeControlsForTouch': this.options_.nativeControlsForTouch,
918       'playerId': this.id(),
919       'techId': this.id() + '_' + techName + '_api',
920       'videoTracks': this.videoTracks_,
921       'textTracks': this.textTracks_,
922       'audioTracks': this.audioTracks_,
923       'autoplay': this.options_.autoplay,
924       'preload': this.options_.preload,
925       'loop': this.options_.loop,
926       'muted': this.options_.muted,
927       'poster': this.poster(),
928       'language': this.language(),
929       'playerElIngest': this.playerElIngest_ || false,
930       'vtt.js': this.options_['vtt.js']
931     }, this.options_[techName.toLowerCase()]);
932
933     if (this.tag) {
934       techOptions.tag = this.tag;
935     }
936
937     if (source) {
938       this.currentType_ = source.type;
939
940       if (source.src === this.cache_.src && this.cache_.currentTime > 0) {
941         techOptions.startTime = this.cache_.currentTime;
942       }
943
944       this.cache_.sources = null;
945       this.cache_.source = source;
946       this.cache_.src = source.src;
947     }
948
949     // Initialize tech instance
950     var TechComponent = _tech2['default'].getTech(techName);
951
952     // Support old behavior of techs being registered as components.
953     // Remove once that deprecated behavior is removed.
954     if (!TechComponent) {
955       TechComponent = _component2['default'].getComponent(techName);
956     }
957     this.tech_ = new TechComponent(techOptions);
958
959     // player.triggerReady is always async, so don't need this to be async
960     this.tech_.ready(Fn.bind(this, this.handleTechReady_), true);
961
962     _textTrackListConverter2['default'].jsonToTextTracks(this.textTracksJson_ || [], this.tech_);
963
964     // Listen to all HTML5-defined events and trigger them on the player
965     TECH_EVENTS_RETRIGGER.forEach(function (event) {
966       _this2.on(_this2.tech_, event, _this2['handleTech' + (0, _toTitleCase2['default'])(event) + '_']);
967     });
968     this.on(this.tech_, 'loadstart', this.handleTechLoadStart_);
969     this.on(this.tech_, 'waiting', this.handleTechWaiting_);
970     this.on(this.tech_, 'canplay', this.handleTechCanPlay_);
971     this.on(this.tech_, 'canplaythrough', this.handleTechCanPlayThrough_);
972     this.on(this.tech_, 'playing', this.handleTechPlaying_);
973     this.on(this.tech_, 'ended', this.handleTechEnded_);
974     this.on(this.tech_, 'seeking', this.handleTechSeeking_);
975     this.on(this.tech_, 'seeked', this.handleTechSeeked_);
976     this.on(this.tech_, 'play', this.handleTechPlay_);
977     this.on(this.tech_, 'firstplay', this.handleTechFirstPlay_);
978     this.on(this.tech_, 'pause', this.handleTechPause_);
979     this.on(this.tech_, 'durationchange', this.handleTechDurationChange_);
980     this.on(this.tech_, 'fullscreenchange', this.handleTechFullscreenChange_);
981     this.on(this.tech_, 'error', this.handleTechError_);
982     this.on(this.tech_, 'loadedmetadata', this.updateStyleEl_);
983     this.on(this.tech_, 'posterchange', this.handleTechPosterChange_);
984     this.on(this.tech_, 'textdata', this.handleTechTextData_);
985
986     this.usingNativeControls(this.techGet_('controls'));
987
988     if (this.controls() && !this.usingNativeControls()) {
989       this.addTechControlsListeners_();
990     }
991
992     // Add the tech element in the DOM if it was not already there
993     // Make sure to not insert the original video element if using Html5
994     if (this.tech_.el().parentNode !== this.el() && (techName !== 'Html5' || !this.tag)) {
995       Dom.insertElFirst(this.tech_.el(), this.el());
996     }
997
998     // Get rid of the original video tag reference after the first tech is loaded
999     if (this.tag) {
1000       this.tag.player = null;
1001       this.tag = null;
1002     }
1003   };
1004
1005   /**
1006    * Unload and dispose of the current playback {@link Tech}.
1007    *
1008    * @private
1009    */
1010
1011
1012   Player.prototype.unloadTech_ = function unloadTech_() {
1013     // Save the current text tracks so that we can reuse the same text tracks with the next tech
1014     this.videoTracks_ = this.videoTracks();
1015     this.textTracks_ = this.textTracks();
1016     this.audioTracks_ = this.audioTracks();
1017     this.textTracksJson_ = _textTrackListConverter2['default'].textTracksToJson(this.tech_);
1018
1019     this.isReady_ = false;
1020
1021     this.tech_.dispose();
1022
1023     this.tech_ = false;
1024   };
1025
1026   /**
1027    * Return a reference to the current {@link Tech}, but only if given an object with the
1028    * `IWillNotUseThisInPlugins` property having a true value. This is try and prevent misuse
1029    * of techs by plugins.
1030    *
1031    * @param {Object} safety
1032    *        An object that must contain `{IWillNotUseThisInPlugins: true}`
1033    *
1034    * @param {boolean} safety.IWillNotUseThisInPlugins
1035    *        Must be set to true or else this function will throw an error.
1036    *
1037    * @return {Tech}
1038    *         The Tech
1039    */
1040
1041
1042   Player.prototype.tech = function tech(safety) {
1043     if (safety && safety.IWillNotUseThisInPlugins) {
1044       return this.tech_;
1045     }
1046     var errorText = '\n      Please make sure that you are not using this inside of a plugin.\n      To disable this alert and error, please pass in an object with\n      `IWillNotUseThisInPlugins` to the `tech` method. See\n      https://github.com/videojs/video.js/issues/2617 for more info.\n    ';
1047
1048     _window2['default'].alert(errorText);
1049     throw new Error(errorText);
1050   };
1051
1052   /**
1053    * Set up click and touch listeners for the playback element
1054    *
1055    * - On desktops: a click on the video itself will toggle playback
1056    * - On mobile devices: a click on the video toggles controls
1057    *   which is done by toggling the user state between active and
1058    *   inactive
1059    * - A tap can signal that a user has become active or has become inactive
1060    *   e.g. a quick tap on an iPhone movie should reveal the controls. Another
1061    *   quick tap should hide them again (signaling the user is in an inactive
1062    *   viewing state)
1063    * - In addition to this, we still want the user to be considered inactive after
1064    *   a few seconds of inactivity.
1065    *
1066    * > Note: the only part of iOS interaction we can't mimic with this setup
1067    * is a touch and hold on the video element counting as activity in order to
1068    * keep the controls showing, but that shouldn't be an issue. A touch and hold
1069    * on any controls will still keep the user active
1070    *
1071    * @private
1072    */
1073
1074
1075   Player.prototype.addTechControlsListeners_ = function addTechControlsListeners_() {
1076     // Make sure to remove all the previous listeners in case we are called multiple times.
1077     this.removeTechControlsListeners_();
1078
1079     // Some browsers (Chrome & IE) don't trigger a click on a flash swf, but do
1080     // trigger mousedown/up.
1081     // http://stackoverflow.com/questions/1444562/javascript-onclick-event-over-flash-object
1082     // Any touch events are set to block the mousedown event from happening
1083     this.on(this.tech_, 'mousedown', this.handleTechClick_);
1084
1085     // If the controls were hidden we don't want that to change without a tap event
1086     // so we'll check if the controls were already showing before reporting user
1087     // activity
1088     this.on(this.tech_, 'touchstart', this.handleTechTouchStart_);
1089     this.on(this.tech_, 'touchmove', this.handleTechTouchMove_);
1090     this.on(this.tech_, 'touchend', this.handleTechTouchEnd_);
1091
1092     // The tap listener needs to come after the touchend listener because the tap
1093     // listener cancels out any reportedUserActivity when setting userActive(false)
1094     this.on(this.tech_, 'tap', this.handleTechTap_);
1095   };
1096
1097   /**
1098    * Remove the listeners used for click and tap controls. This is needed for
1099    * toggling to controls disabled, where a tap/touch should do nothing.
1100    *
1101    * @private
1102    */
1103
1104
1105   Player.prototype.removeTechControlsListeners_ = function removeTechControlsListeners_() {
1106     // We don't want to just use `this.off()` because there might be other needed
1107     // listeners added by techs that extend this.
1108     this.off(this.tech_, 'tap', this.handleTechTap_);
1109     this.off(this.tech_, 'touchstart', this.handleTechTouchStart_);
1110     this.off(this.tech_, 'touchmove', this.handleTechTouchMove_);
1111     this.off(this.tech_, 'touchend', this.handleTechTouchEnd_);
1112     this.off(this.tech_, 'mousedown', this.handleTechClick_);
1113   };
1114
1115   /**
1116    * Player waits for the tech to be ready
1117    *
1118    * @private
1119    */
1120
1121
1122   Player.prototype.handleTechReady_ = function handleTechReady_() {
1123     this.triggerReady();
1124
1125     // Keep the same volume as before
1126     if (this.cache_.volume) {
1127       this.techCall_('setVolume', this.cache_.volume);
1128     }
1129
1130     // Look if the tech found a higher resolution poster while loading
1131     this.handleTechPosterChange_();
1132
1133     // Update the duration if available
1134     this.handleTechDurationChange_();
1135
1136     // Chrome and Safari both have issues with autoplay.
1137     // In Safari (5.1.1), when we move the video element into the container div, autoplay doesn't work.
1138     // In Chrome (15), if you have autoplay + a poster + no controls, the video gets hidden (but audio plays)
1139     // This fixes both issues. Need to wait for API, so it updates displays correctly
1140     if ((this.src() || this.currentSrc()) && this.tag && this.options_.autoplay && this.paused()) {
1141       try {
1142         // Chrome Fix. Fixed in Chrome v16.
1143         delete this.tag.poster;
1144       } catch (e) {
1145         (0, _log2['default'])('deleting tag.poster throws in some browsers', e);
1146       }
1147       this.play();
1148     }
1149   };
1150
1151   /**
1152    * Retrigger the `loadstart` event that was triggered by the {@link Tech}. This
1153    * function will also trigger {@link Player#firstplay} if it is the first loadstart
1154    * for a video.
1155    *
1156    * @fires Player#loadstart
1157    * @fires Player#firstplay
1158    * @listens Tech#loadstart
1159    * @private
1160    */
1161
1162
1163   Player.prototype.handleTechLoadStart_ = function handleTechLoadStart_() {
1164     // TODO: Update to use `emptied` event instead. See #1277.
1165
1166     this.removeClass('vjs-ended');
1167     this.removeClass('vjs-seeking');
1168
1169     // reset the error state
1170     this.error(null);
1171
1172     // If it's already playing we want to trigger a firstplay event now.
1173     // The firstplay event relies on both the play and loadstart events
1174     // which can happen in any order for a new source
1175     if (!this.paused()) {
1176       /**
1177        * Fired when the user agent begins looking for media data
1178        *
1179        * @event Player#loadstart
1180        * @type {EventTarget~Event}
1181        */
1182       this.trigger('loadstart');
1183       this.trigger('firstplay');
1184     } else {
1185       // reset the hasStarted state
1186       this.hasStarted(false);
1187       this.trigger('loadstart');
1188     }
1189   };
1190
1191   /**
1192    * Add/remove the vjs-has-started class
1193    *
1194    * @fires Player#firstplay
1195    *
1196    * @param {boolean} hasStarted
1197    *        - true: adds the class
1198    *        - false: remove the class
1199    *
1200    * @return {boolean}
1201    *         the boolean value of hasStarted
1202    */
1203
1204
1205   Player.prototype.hasStarted = function hasStarted(_hasStarted) {
1206     if (_hasStarted !== undefined) {
1207       // only update if this is a new value
1208       if (this.hasStarted_ !== _hasStarted) {
1209         this.hasStarted_ = _hasStarted;
1210         if (_hasStarted) {
1211           this.addClass('vjs-has-started');
1212           // trigger the firstplay event if this newly has played
1213           this.trigger('firstplay');
1214         } else {
1215           this.removeClass('vjs-has-started');
1216         }
1217       }
1218       return this;
1219     }
1220     return !!this.hasStarted_;
1221   };
1222
1223   /**
1224    * Fired whenever the media begins or resumes playback
1225    *
1226    * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play}
1227    * @fires Player#play
1228    * @listens Tech#play
1229    * @private
1230    */
1231
1232
1233   Player.prototype.handleTechPlay_ = function handleTechPlay_() {
1234     this.removeClass('vjs-ended');
1235     this.removeClass('vjs-paused');
1236     this.addClass('vjs-playing');
1237
1238     // hide the poster when the user hits play
1239     this.hasStarted(true);
1240     /**
1241      * Triggered whenever an {@link Tech#play} event happens. Indicates that
1242      * playback has started or resumed.
1243      *
1244      * @event Player#play
1245      * @type {EventTarget~Event}
1246      */
1247     this.trigger('play');
1248   };
1249
1250   /**
1251    * Retrigger the `waiting` event that was triggered by the {@link Tech}.
1252    *
1253    * @fires Player#waiting
1254    * @listens Tech#waiting
1255    * @private
1256    */
1257
1258
1259   Player.prototype.handleTechWaiting_ = function handleTechWaiting_() {
1260     var _this3 = this;
1261
1262     this.addClass('vjs-waiting');
1263     /**
1264      * A readyState change on the DOM element has caused playback to stop.
1265      *
1266      * @event Player#waiting
1267      * @type {EventTarget~Event}
1268      */
1269     this.trigger('waiting');
1270     this.one('timeupdate', function () {
1271       return _this3.removeClass('vjs-waiting');
1272     });
1273   };
1274
1275   /**
1276    * Retrigger the `canplay` event that was triggered by the {@link Tech}.
1277    * > Note: This is not consistent between browsers. See #1351
1278    *
1279    * @fires Player#canplay
1280    * @listens Tech#canplay
1281    * @private
1282    */
1283
1284
1285   Player.prototype.handleTechCanPlay_ = function handleTechCanPlay_() {
1286     this.removeClass('vjs-waiting');
1287     /**
1288      * The media has a readyState of HAVE_FUTURE_DATA or greater.
1289      *
1290      * @event Player#canplay
1291      * @type {EventTarget~Event}
1292      */
1293     this.trigger('canplay');
1294   };
1295
1296   /**
1297    * Retrigger the `canplaythrough` event that was triggered by the {@link Tech}.
1298    *
1299    * @fires Player#canplaythrough
1300    * @listens Tech#canplaythrough
1301    * @private
1302    */
1303
1304
1305   Player.prototype.handleTechCanPlayThrough_ = function handleTechCanPlayThrough_() {
1306     this.removeClass('vjs-waiting');
1307     /**
1308      * The media has a readyState of HAVE_ENOUGH_DATA or greater. This means that the
1309      * entire media file can be played without buffering.
1310      *
1311      * @event Player#canplaythrough
1312      * @type {EventTarget~Event}
1313      */
1314     this.trigger('canplaythrough');
1315   };
1316
1317   /**
1318    * Retrigger the `playing` event that was triggered by the {@link Tech}.
1319    *
1320    * @fires Player#playing
1321    * @listens Tech#playing
1322    * @private
1323    */
1324
1325
1326   Player.prototype.handleTechPlaying_ = function handleTechPlaying_() {
1327     this.removeClass('vjs-waiting');
1328     /**
1329      * The media is no longer blocked from playback, and has started playing.
1330      *
1331      * @event Player#playing
1332      * @type {EventTarget~Event}
1333      */
1334     this.trigger('playing');
1335   };
1336
1337   /**
1338    * Retrigger the `seeking` event that was triggered by the {@link Tech}.
1339    *
1340    * @fires Player#seeking
1341    * @listens Tech#seeking
1342    * @private
1343    */
1344
1345
1346   Player.prototype.handleTechSeeking_ = function handleTechSeeking_() {
1347     this.addClass('vjs-seeking');
1348     /**
1349      * Fired whenever the player is jumping to a new time
1350      *
1351      * @event Player#seeking
1352      * @type {EventTarget~Event}
1353      */
1354     this.trigger('seeking');
1355   };
1356
1357   /**
1358    * Retrigger the `seeked` event that was triggered by the {@link Tech}.
1359    *
1360    * @fires Player#seeked
1361    * @listens Tech#seeked
1362    * @private
1363    */
1364
1365
1366   Player.prototype.handleTechSeeked_ = function handleTechSeeked_() {
1367     this.removeClass('vjs-seeking');
1368     /**
1369      * Fired when the player has finished jumping to a new time
1370      *
1371      * @event Player#seeked
1372      * @type {EventTarget~Event}
1373      */
1374     this.trigger('seeked');
1375   };
1376
1377   /**
1378    * Retrigger the `firstplay` event that was triggered by the {@link Tech}.
1379    *
1380    * @fires Player#firstplay
1381    * @listens Tech#firstplay
1382    * @deprecated As of 6.0 passing the `starttime` option to the player will be deprecated
1383    * @private
1384    */
1385
1386
1387   Player.prototype.handleTechFirstPlay_ = function handleTechFirstPlay_() {
1388     // If the first starttime attribute is specified
1389     // then we will start at the given offset in seconds
1390     if (this.options_.starttime) {
1391       _log2['default'].warn('Passing the `starttime` option to the player will be deprecated in 6.0');
1392       this.currentTime(this.options_.starttime);
1393     }
1394
1395     this.addClass('vjs-has-started');
1396     /**
1397      * Fired the first time a video is played. Not part of the HLS spec, and this is
1398      * probably not the best implementation yet, so use sparingly. If you don't have a
1399      * reason to prevent playback, use `myPlayer.one('play');` instead.
1400      *
1401      * @event Player#firstplay
1402      * @type {EventTarget~Event}
1403      */
1404     this.trigger('firstplay');
1405   };
1406
1407   /**
1408    * Retrigger the `pause` event that was triggered by the {@link Tech}.
1409    *
1410    * @fires Player#pause
1411    * @listens Tech#pause
1412    * @private
1413    */
1414
1415
1416   Player.prototype.handleTechPause_ = function handleTechPause_() {
1417     this.removeClass('vjs-playing');
1418     this.addClass('vjs-paused');
1419     /**
1420      * Fired whenever the media has been paused
1421      *
1422      * @event Player#pause
1423      * @type {EventTarget~Event}
1424      */
1425     this.trigger('pause');
1426   };
1427
1428   /**
1429    * Retrigger the `ended` event that was triggered by the {@link Tech}.
1430    *
1431    * @fires Player#ended
1432    * @listens Tech#ended
1433    * @private
1434    */
1435
1436
1437   Player.prototype.handleTechEnded_ = function handleTechEnded_() {
1438     this.addClass('vjs-ended');
1439     if (this.options_.loop) {
1440       this.currentTime(0);
1441       this.play();
1442     } else if (!this.paused()) {
1443       this.pause();
1444     }
1445
1446     /**
1447      * Fired when the end of the media resource is reached (currentTime == duration)
1448      *
1449      * @event Player#ended
1450      * @type {EventTarget~Event}
1451      */
1452     this.trigger('ended');
1453   };
1454
1455   /**
1456    * Fired when the duration of the media resource is first known or changed
1457    *
1458    * @listens Tech#durationchange
1459    * @private
1460    */
1461
1462
1463   Player.prototype.handleTechDurationChange_ = function handleTechDurationChange_() {
1464     this.duration(this.techGet_('duration'));
1465   };
1466
1467   /**
1468    * Handle a click on the media element to play/pause
1469    *
1470    * @param {EventTarget~Event} event
1471    *        the event that caused this function to trigger
1472    *
1473    * @listens Tech#mousedown
1474    * @private
1475    */
1476
1477
1478   Player.prototype.handleTechClick_ = function handleTechClick_(event) {
1479     // We're using mousedown to detect clicks thanks to Flash, but mousedown
1480     // will also be triggered with right-clicks, so we need to prevent that
1481     if (event.button !== 0) {
1482       return;
1483     }
1484
1485     // When controls are disabled a click should not toggle playback because
1486     // the click is considered a control
1487     if (this.controls()) {
1488       if (this.paused()) {
1489         this.play();
1490       } else {
1491         this.pause();
1492       }
1493     }
1494   };
1495
1496   /**
1497    * Handle a tap on the media element. It will toggle the user
1498    * activity state, which hides and shows the controls.
1499    *
1500    * @listens Tech#tap
1501    * @private
1502    */
1503
1504
1505   Player.prototype.handleTechTap_ = function handleTechTap_() {
1506     this.userActive(!this.userActive());
1507   };
1508
1509   /**
1510    * Handle touch to start
1511    *
1512    * @listens Tech#touchstart
1513    * @private
1514    */
1515
1516
1517   Player.prototype.handleTechTouchStart_ = function handleTechTouchStart_() {
1518     this.userWasActive = this.userActive();
1519   };
1520
1521   /**
1522    * Handle touch to move
1523    *
1524    * @listens Tech#touchmove
1525    * @private
1526    */
1527
1528
1529   Player.prototype.handleTechTouchMove_ = function handleTechTouchMove_() {
1530     if (this.userWasActive) {
1531       this.reportUserActivity();
1532     }
1533   };
1534
1535   /**
1536    * Handle touch to end
1537    *
1538    * @param {EventTarget~Event} event
1539    *        the touchend event that triggered
1540    *        this function
1541    *
1542    * @listens Tech#touchend
1543    * @private
1544    */
1545
1546
1547   Player.prototype.handleTechTouchEnd_ = function handleTechTouchEnd_(event) {
1548     // Stop the mouse events from also happening
1549     event.preventDefault();
1550   };
1551
1552   /**
1553    * Fired when the player switches in or out of fullscreen mode
1554    *
1555    * @private
1556    * @listens Player#fullscreenchange
1557    */
1558
1559
1560   Player.prototype.handleFullscreenChange_ = function handleFullscreenChange_() {
1561     if (this.isFullscreen()) {
1562       this.addClass('vjs-fullscreen');
1563     } else {
1564       this.removeClass('vjs-fullscreen');
1565     }
1566   };
1567
1568   /**
1569    * native click events on the SWF aren't triggered on IE11, Win8.1RT
1570    * use stageclick events triggered from inside the SWF instead
1571    *
1572    * @private
1573    * @listens stageclick
1574    */
1575
1576
1577   Player.prototype.handleStageClick_ = function handleStageClick_() {
1578     this.reportUserActivity();
1579   };
1580
1581   /**
1582    * Handle Tech Fullscreen Change
1583    *
1584    * @param {EventTarget~Event} event
1585    *        the fullscreenchange event that triggered this function
1586    *
1587    * @param {Object} data
1588    *        the data that was sent with the event
1589    *
1590    * @private
1591    * @listens Tech#fullscreenchange
1592    * @fires Player#fullscreenchange
1593    */
1594
1595
1596   Player.prototype.handleTechFullscreenChange_ = function handleTechFullscreenChange_(event, data) {
1597     if (data) {
1598       this.isFullscreen(data.isFullscreen);
1599     }
1600     /**
1601      * Fired when going in and out of fullscreen.
1602      *
1603      * @event Player#fullscreenchange
1604      * @type {EventTarget~Event}
1605      */
1606     this.trigger('fullscreenchange');
1607   };
1608
1609   /**
1610    * Fires when an error occurred during the loading of an audio/video.
1611    *
1612    * @private
1613    * @listens Tech#error
1614    */
1615
1616
1617   Player.prototype.handleTechError_ = function handleTechError_() {
1618     var error = this.tech_.error();
1619
1620     this.error(error);
1621   };
1622
1623   /**
1624    * Retrigger the `textdata` event that was triggered by the {@link Tech}.
1625    *
1626    * @fires Player#textdata
1627    * @listens Tech#textdata
1628    * @private
1629    */
1630
1631
1632   Player.prototype.handleTechTextData_ = function handleTechTextData_() {
1633     var data = null;
1634
1635     if (arguments.length > 1) {
1636       data = arguments[1];
1637     }
1638
1639     /**
1640      * Fires when we get a textdata event from tech
1641      *
1642      * @event Player#textdata
1643      * @type {EventTarget~Event}
1644      */
1645     this.trigger('textdata', data);
1646   };
1647
1648   /**
1649    * Get object for cached values.
1650    *
1651    * @return {Object}
1652    *         get the current object cache
1653    */
1654
1655
1656   Player.prototype.getCache = function getCache() {
1657     return this.cache_;
1658   };
1659
1660   /**
1661    * Pass values to the playback tech
1662    *
1663    * @param {string} [method]
1664    *        the method to call
1665    *
1666    * @param {Object} arg
1667    *        the argument to pass
1668    *
1669    * @private
1670    */
1671
1672
1673   Player.prototype.techCall_ = function techCall_(method, arg) {
1674     // If it's not ready yet, call method when it is
1675     if (this.tech_ && !this.tech_.isReady_) {
1676       this.tech_.ready(function () {
1677         this[method](arg);
1678       }, true);
1679
1680       // Otherwise call method now
1681     } else {
1682       try {
1683         if (this.tech_) {
1684           this.tech_[method](arg);
1685         }
1686       } catch (e) {
1687         (0, _log2['default'])(e);
1688         throw e;
1689       }
1690     }
1691   };
1692
1693   /**
1694    * Get calls can't wait for the tech, and sometimes don't need to.
1695    *
1696    * @param {string} method
1697    *        Tech method
1698    *
1699    * @return {Function|undefined}
1700    *         the method or undefined
1701    *
1702    * @private
1703    */
1704
1705
1706   Player.prototype.techGet_ = function techGet_(method) {
1707     if (this.tech_ && this.tech_.isReady_) {
1708
1709       // Flash likes to die and reload when you hide or reposition it.
1710       // In these cases the object methods go away and we get errors.
1711       // When that happens we'll catch the errors and inform tech that it's not ready any more.
1712       try {
1713         return this.tech_[method]();
1714       } catch (e) {
1715         // When building additional tech libs, an expected method may not be defined yet
1716         if (this.tech_[method] === undefined) {
1717           (0, _log2['default'])('Video.js: ' + method + ' method not defined for ' + this.techName_ + ' playback technology.', e);
1718
1719           // When a method isn't available on the object it throws a TypeError
1720         } else if (e.name === 'TypeError') {
1721           (0, _log2['default'])('Video.js: ' + method + ' unavailable on ' + this.techName_ + ' playback technology element.', e);
1722           this.tech_.isReady_ = false;
1723         } else {
1724           (0, _log2['default'])(e);
1725         }
1726         throw e;
1727       }
1728     }
1729
1730     return;
1731   };
1732
1733   /**
1734    * start media playback
1735    *
1736    * @return {Player}
1737    *         A reference to the player object this function was called on
1738    */
1739
1740
1741   Player.prototype.play = function play() {
1742     // Only calls the tech's play if we already have a src loaded
1743     if (this.src() || this.currentSrc()) {
1744       this.techCall_('play');
1745     } else {
1746       this.tech_.one('loadstart', function () {
1747         this.play();
1748       });
1749     }
1750
1751     return this;
1752   };
1753
1754   /**
1755    * Pause the video playback
1756    *
1757    * @return {Player}
1758    *         A reference to the player object this function was called on
1759    */
1760
1761
1762   Player.prototype.pause = function pause() {
1763     this.techCall_('pause');
1764     return this;
1765   };
1766
1767   /**
1768    * Check if the player is paused or has yet to play
1769    *
1770    * @return {boolean}
1771    *         - false: if the media is currently playing
1772    *         - true: if media is not currently playing
1773    */
1774
1775
1776   Player.prototype.paused = function paused() {
1777     // The initial state of paused should be true (in Safari it's actually false)
1778     return this.techGet_('paused') === false ? false : true;
1779   };
1780
1781   /**
1782    * Returns whether or not the user is "scrubbing". Scrubbing is
1783    * when the user has clicked the progress bar handle and is
1784    * dragging it along the progress bar.
1785    *
1786    * @param {boolean} [isScrubbing]
1787    *        wether the user is or is not scrubbing
1788    *
1789    * @return {boolean|Player}
1790    *         A instance of the player that called this function when setting,
1791    *         and the value of scrubbing when getting
1792    */
1793
1794
1795   Player.prototype.scrubbing = function scrubbing(isScrubbing) {
1796     if (isScrubbing !== undefined) {
1797       this.scrubbing_ = !!isScrubbing;
1798
1799       if (isScrubbing) {
1800         this.addClass('vjs-scrubbing');
1801       } else {
1802         this.removeClass('vjs-scrubbing');
1803       }
1804
1805       return this;
1806     }
1807
1808     return this.scrubbing_;
1809   };
1810
1811   /**
1812    * Get or set the current time (in seconds)
1813    *
1814    * @param {number|string} [seconds]
1815    *        The time to seek to in seconds
1816    *
1817    * @return {Player|number}
1818    *         - the current time in seconds when getting
1819    *         - a reference to the current player object when setting
1820    */
1821
1822
1823   Player.prototype.currentTime = function currentTime(seconds) {
1824     if (seconds !== undefined) {
1825
1826       this.techCall_('setCurrentTime', seconds);
1827
1828       return this;
1829     }
1830
1831     // cache last currentTime and return. default to 0 seconds
1832     //
1833     // Caching the currentTime is meant to prevent a massive amount of reads on the tech's
1834     // currentTime when scrubbing, but may not provide much performance benefit afterall.
1835     // Should be tested. Also something has to read the actual current time or the cache will
1836     // never get updated.
1837     this.cache_.currentTime = this.techGet_('currentTime') || 0;
1838     return this.cache_.currentTime;
1839   };
1840
1841   /**
1842    * Normally gets the length in time of the video in seconds;
1843    * in all but the rarest use cases an argument will NOT be passed to the method
1844    *
1845    * > **NOTE**: The video must have started loading before the duration can be
1846    * known, and in the case of Flash, may not be known until the video starts
1847    * playing.
1848    *
1849    * @fires Player#durationchange
1850    *
1851    * @param {number} [seconds]
1852    *        The duration of the video to set in seconds
1853    *
1854    * @return {number|Player}
1855    *         - The duration of the video in seconds when getting
1856    *         - A reference to the player that called this function
1857    *           when setting
1858    */
1859
1860
1861   Player.prototype.duration = function duration(seconds) {
1862     if (seconds === undefined) {
1863       return this.cache_.duration || 0;
1864     }
1865
1866     seconds = parseFloat(seconds) || 0;
1867
1868     // Standardize on Inifity for signaling video is live
1869     if (seconds < 0) {
1870       seconds = Infinity;
1871     }
1872
1873     if (seconds !== this.cache_.duration) {
1874       // Cache the last set value for optimized scrubbing (esp. Flash)
1875       this.cache_.duration = seconds;
1876
1877       if (seconds === Infinity) {
1878         this.addClass('vjs-live');
1879       } else {
1880         this.removeClass('vjs-live');
1881       }
1882       /**
1883        * @event Player#durationchange
1884        * @type {EventTarget~Event}
1885        */
1886       this.trigger('durationchange');
1887     }
1888
1889     return this;
1890   };
1891
1892   /**
1893    * Calculates how much time is left in the video. Not part
1894    * of the native video API.
1895    *
1896    * @return {number}
1897    *         The time remaining in seconds
1898    */
1899
1900
1901   Player.prototype.remainingTime = function remainingTime() {
1902     return this.duration() - this.currentTime();
1903   };
1904
1905   //
1906   // Kind of like an array of portions of the video that have been downloaded.
1907
1908   /**
1909    * Get a TimeRange object with an array of the times of the video
1910    * that have been downloaded. If you just want the percent of the
1911    * video that's been downloaded, use bufferedPercent.
1912    *
1913    * @see [Buffered Spec]{@link http://dev.w3.org/html5/spec/video.html#dom-media-buffered}
1914    *
1915    * @return {TimeRange}
1916    *         A mock TimeRange object (following HTML spec)
1917    */
1918
1919
1920   Player.prototype.buffered = function buffered() {
1921     var buffered = this.techGet_('buffered');
1922
1923     if (!buffered || !buffered.length) {
1924       buffered = (0, _timeRanges.createTimeRange)(0, 0);
1925     }
1926
1927     return buffered;
1928   };
1929
1930   /**
1931    * Get the percent (as a decimal) of the video that's been downloaded.
1932    * This method is not a part of the native HTML video API.
1933    *
1934    * @return {number}
1935    *         A decimal between 0 and 1 representing the percent
1936    *         that is bufferred 0 being 0% and 1 being 100%
1937    */
1938
1939
1940   Player.prototype.bufferedPercent = function bufferedPercent() {
1941     return (0, _buffer.bufferedPercent)(this.buffered(), this.duration());
1942   };
1943
1944   /**
1945    * Get the ending time of the last buffered time range
1946    * This is used in the progress bar to encapsulate all time ranges.
1947    *
1948    * @return {number}
1949    *         The end of the last buffered time range
1950    */
1951
1952
1953   Player.prototype.bufferedEnd = function bufferedEnd() {
1954     var buffered = this.buffered();
1955     var duration = this.duration();
1956     var end = buffered.end(buffered.length - 1);
1957
1958     if (end > duration) {
1959       end = duration;
1960     }
1961
1962     return end;
1963   };
1964
1965   /**
1966    * Get or set the current volume of the media
1967    *
1968    * @param  {number} [percentAsDecimal]
1969    *         The new volume as a decimal percent:
1970    *         - 0 is muted/0%/off
1971    *         - 1.0 is 100%/full
1972    *         - 0.5 is half volume or 50%
1973    *
1974    * @return {Player|number}
1975    *         a reference to the calling player when setting and the
1976    *         current volume as a percent when getting
1977    */
1978
1979
1980   Player.prototype.volume = function volume(percentAsDecimal) {
1981     var vol = void 0;
1982
1983     if (percentAsDecimal !== undefined) {
1984       // Force value to between 0 and 1
1985       vol = Math.max(0, Math.min(1, parseFloat(percentAsDecimal)));
1986       this.cache_.volume = vol;
1987       this.techCall_('setVolume', vol);
1988
1989       return this;
1990     }
1991
1992     // Default to 1 when returning current volume.
1993     vol = parseFloat(this.techGet_('volume'));
1994     return isNaN(vol) ? 1 : vol;
1995   };
1996
1997   /**
1998    * Get the current muted state, or turn mute on or off
1999    *
2000    * @param {boolean} [muted]
2001    *        - true to mute
2002    *        - false to unmute
2003    *
2004    * @return {boolean|Player}
2005    *         - true if mute is on and getting
2006    *         - false if mute is off and getting
2007    *         - A reference to the current player when setting
2008    */
2009
2010
2011   Player.prototype.muted = function muted(_muted) {
2012     if (_muted !== undefined) {
2013       this.techCall_('setMuted', _muted);
2014       return this;
2015     }
2016     return this.techGet_('muted') || false;
2017   };
2018
2019   /**
2020    * Check if current tech can support native fullscreen
2021    * (e.g. with built in controls like iOS, so not our flash swf)
2022    *
2023    * @return {boolean}
2024    *         if native fullscreen is supported
2025    */
2026
2027
2028   Player.prototype.supportsFullScreen = function supportsFullScreen() {
2029     return this.techGet_('supportsFullScreen') || false;
2030   };
2031
2032   /**
2033    * Check if the player is in fullscreen mode or tell the player that it
2034    * is or is not in fullscreen mode.
2035    *
2036    * > NOTE: As of the latest HTML5 spec, isFullscreen is no longer an official
2037    * property and instead document.fullscreenElement is used. But isFullscreen is
2038    * still a valuable property for internal player workings.
2039    *
2040    * @param  {boolean} [isFS]
2041    *         Set the players current fullscreen state
2042    *
2043    * @return {boolean|Player}
2044    *         - true if fullscreen is on and getting
2045    *         - false if fullscreen is off and getting
2046    *         - A reference to the current player when setting
2047    */
2048
2049
2050   Player.prototype.isFullscreen = function isFullscreen(isFS) {
2051     if (isFS !== undefined) {
2052       this.isFullscreen_ = !!isFS;
2053       return this;
2054     }
2055     return !!this.isFullscreen_;
2056   };
2057
2058   /**
2059    * Increase the size of the video to full screen
2060    * In some browsers, full screen is not supported natively, so it enters
2061    * "full window mode", where the video fills the browser window.
2062    * In browsers and devices that support native full screen, sometimes the
2063    * browser's default controls will be shown, and not the Video.js custom skin.
2064    * This includes most mobile devices (iOS, Android) and older versions of
2065    * Safari.
2066    *
2067    * @fires Player#fullscreenchange
2068    * @return {Player}
2069    *         A reference to the current player
2070    */
2071
2072
2073   Player.prototype.requestFullscreen = function requestFullscreen() {
2074     var fsApi = _fullscreenApi2['default'];
2075
2076     this.isFullscreen(true);
2077
2078     if (fsApi.requestFullscreen) {
2079       // the browser supports going fullscreen at the element level so we can
2080       // take the controls fullscreen as well as the video
2081
2082       // Trigger fullscreenchange event after change
2083       // We have to specifically add this each time, and remove
2084       // when canceling fullscreen. Otherwise if there's multiple
2085       // players on a page, they would all be reacting to the same fullscreen
2086       // events
2087       Events.on(_document2['default'], fsApi.fullscreenchange, Fn.bind(this, function documentFullscreenChange(e) {
2088         this.isFullscreen(_document2['default'][fsApi.fullscreenElement]);
2089
2090         // If cancelling fullscreen, remove event listener.
2091         if (this.isFullscreen() === false) {
2092           Events.off(_document2['default'], fsApi.fullscreenchange, documentFullscreenChange);
2093         }
2094         /**
2095          * @event Player#fullscreenchange
2096          * @type {EventTarget~Event}
2097          */
2098         this.trigger('fullscreenchange');
2099       }));
2100
2101       this.el_[fsApi.requestFullscreen]();
2102     } else if (this.tech_.supportsFullScreen()) {
2103       // we can't take the video.js controls fullscreen but we can go fullscreen
2104       // with native controls
2105       this.techCall_('enterFullScreen');
2106     } else {
2107       // fullscreen isn't supported so we'll just stretch the video element to
2108       // fill the viewport
2109       this.enterFullWindow();
2110       /**
2111        * @event Player#fullscreenchange
2112        * @type {EventTarget~Event}
2113        */
2114       this.trigger('fullscreenchange');
2115     }
2116
2117     return this;
2118   };
2119
2120   /**
2121    * Return the video to its normal size after having been in full screen mode
2122    *
2123    * @fires Player#fullscreenchange
2124    *
2125    * @return {Player}
2126    *         A reference to the current player
2127    */
2128
2129
2130   Player.prototype.exitFullscreen = function exitFullscreen() {
2131     var fsApi = _fullscreenApi2['default'];
2132
2133     this.isFullscreen(false);
2134
2135     // Check for browser element fullscreen support
2136     if (fsApi.requestFullscreen) {
2137       _document2['default'][fsApi.exitFullscreen]();
2138     } else if (this.tech_.supportsFullScreen()) {
2139       this.techCall_('exitFullScreen');
2140     } else {
2141       this.exitFullWindow();
2142       /**
2143        * @event Player#fullscreenchange
2144        * @type {EventTarget~Event}
2145        */
2146       this.trigger('fullscreenchange');
2147     }
2148
2149     return this;
2150   };
2151
2152   /**
2153    * When fullscreen isn't supported we can stretch the
2154    * video container to as wide as the browser will let us.
2155    *
2156    * @fires Player#enterFullWindow
2157    */
2158
2159
2160   Player.prototype.enterFullWindow = function enterFullWindow() {
2161     this.isFullWindow = true;
2162
2163     // Storing original doc overflow value to return to when fullscreen is off
2164     this.docOrigOverflow = _document2['default'].documentElement.style.overflow;
2165
2166     // Add listener for esc key to exit fullscreen
2167     Events.on(_document2['default'], 'keydown', Fn.bind(this, this.fullWindowOnEscKey));
2168
2169     // Hide any scroll bars
2170     _document2['default'].documentElement.style.overflow = 'hidden';
2171
2172     // Apply fullscreen styles
2173     Dom.addElClass(_document2['default'].body, 'vjs-full-window');
2174
2175     /**
2176      * @event Player#enterFullWindow
2177      * @type {EventTarget~Event}
2178      */
2179     this.trigger('enterFullWindow');
2180   };
2181
2182   /**
2183    * Check for call to either exit full window or
2184    * full screen on ESC key
2185    *
2186    * @param {string} event
2187    *        Event to check for key press
2188    */
2189
2190
2191   Player.prototype.fullWindowOnEscKey = function fullWindowOnEscKey(event) {
2192     if (event.keyCode === 27) {
2193       if (this.isFullscreen() === true) {
2194         this.exitFullscreen();
2195       } else {
2196         this.exitFullWindow();
2197       }
2198     }
2199   };
2200
2201   /**
2202    * Exit full window
2203    *
2204    * @fires Player#exitFullWindow
2205    */
2206
2207
2208   Player.prototype.exitFullWindow = function exitFullWindow() {
2209     this.isFullWindow = false;
2210     Events.off(_document2['default'], 'keydown', this.fullWindowOnEscKey);
2211
2212     // Unhide scroll bars.
2213     _document2['default'].documentElement.style.overflow = this.docOrigOverflow;
2214
2215     // Remove fullscreen styles
2216     Dom.removeElClass(_document2['default'].body, 'vjs-full-window');
2217
2218     // Resize the box, controller, and poster to original sizes
2219     // this.positionAll();
2220     /**
2221      * @event Player#exitFullWindow
2222      * @type {EventTarget~Event}
2223      */
2224     this.trigger('exitFullWindow');
2225   };
2226
2227   /**
2228    * Check whether the player can play a given mimetype
2229    *
2230    * @see https://www.w3.org/TR/2011/WD-html5-20110113/video.html#dom-navigator-canplaytype
2231    *
2232    * @param {string} type
2233    *        The mimetype to check
2234    *
2235    * @return {string}
2236    *         'probably', 'maybe', or '' (empty string)
2237    */
2238
2239
2240   Player.prototype.canPlayType = function canPlayType(type) {
2241     var can = void 0;
2242
2243     // Loop through each playback technology in the options order
2244     for (var i = 0, j = this.options_.techOrder; i < j.length; i++) {
2245       var techName = (0, _toTitleCase2['default'])(j[i]);
2246       var tech = _tech2['default'].getTech(techName);
2247
2248       // Support old behavior of techs being registered as components.
2249       // Remove once that deprecated behavior is removed.
2250       if (!tech) {
2251         tech = _component2['default'].getComponent(techName);
2252       }
2253
2254       // Check if the current tech is defined before continuing
2255       if (!tech) {
2256         _log2['default'].error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
2257         continue;
2258       }
2259
2260       // Check if the browser supports this technology
2261       if (tech.isSupported()) {
2262         can = tech.canPlayType(type);
2263
2264         if (can) {
2265           return can;
2266         }
2267       }
2268     }
2269
2270     return '';
2271   };
2272
2273   /**
2274    * Select source based on tech-order or source-order
2275    * Uses source-order selection if `options.sourceOrder` is truthy. Otherwise,
2276    * defaults to tech-order selection
2277    *
2278    * @param {Array} sources
2279    *        The sources for a media asset
2280    *
2281    * @return {Object|boolean}
2282    *         Object of source and tech order or false
2283    */
2284
2285
2286   Player.prototype.selectSource = function selectSource(sources) {
2287     var _this4 = this;
2288
2289     // Get only the techs specified in `techOrder` that exist and are supported by the
2290     // current platform
2291     var techs = this.options_.techOrder.map(_toTitleCase2['default']).map(function (techName) {
2292       // `Component.getComponent(...)` is for support of old behavior of techs
2293       // being registered as components.
2294       // Remove once that deprecated behavior is removed.
2295       return [techName, _tech2['default'].getTech(techName) || _component2['default'].getComponent(techName)];
2296     }).filter(function (_ref) {
2297       var techName = _ref[0],
2298           tech = _ref[1];
2299
2300       // Check if the current tech is defined before continuing
2301       if (tech) {
2302         // Check if the browser supports this technology
2303         return tech.isSupported();
2304       }
2305
2306       _log2['default'].error('The "' + techName + '" tech is undefined. Skipped browser support check for that tech.');
2307       return false;
2308     });
2309
2310     // Iterate over each `innerArray` element once per `outerArray` element and execute
2311     // `tester` with both. If `tester` returns a non-falsy value, exit early and return
2312     // that value.
2313     var findFirstPassingTechSourcePair = function findFirstPassingTechSourcePair(outerArray, innerArray, tester) {
2314       var found = void 0;
2315
2316       outerArray.some(function (outerChoice) {
2317         return innerArray.some(function (innerChoice) {
2318           found = tester(outerChoice, innerChoice);
2319
2320           if (found) {
2321             return true;
2322           }
2323         });
2324       });
2325
2326       return found;
2327     };
2328
2329     var foundSourceAndTech = void 0;
2330     var flip = function flip(fn) {
2331       return function (a, b) {
2332         return fn(b, a);
2333       };
2334     };
2335     var finder = function finder(_ref2, source) {
2336       var techName = _ref2[0],
2337           tech = _ref2[1];
2338
2339       if (tech.canPlaySource(source, _this4.options_[techName.toLowerCase()])) {
2340         return { source: source, tech: techName };
2341       }
2342     };
2343
2344     // Depending on the truthiness of `options.sourceOrder`, we swap the order of techs and sources
2345     // to select from them based on their priority.
2346     if (this.options_.sourceOrder) {
2347       // Source-first ordering
2348       foundSourceAndTech = findFirstPassingTechSourcePair(sources, techs, flip(finder));
2349     } else {
2350       // Tech-first ordering
2351       foundSourceAndTech = findFirstPassingTechSourcePair(techs, sources, finder);
2352     }
2353
2354     return foundSourceAndTech || false;
2355   };
2356
2357   /**
2358    * The source function updates the video source
2359    * There are three types of variables you can pass as the argument.
2360    * **URL string**: A URL to the the video file. Use this method if you are sure
2361    * the current playback technology (HTML5/Flash) can support the source you
2362    * provide. Currently only MP4 files can be used in both HTML5 and Flash.
2363    *
2364    * @param {Tech~SourceObject|Tech~SourceObject[]} [source]
2365    *        One SourceObject or an array of SourceObjects
2366    *
2367    * @return {string|Player}
2368    *         - The current video source when getting
2369    *         - The player when setting
2370    */
2371
2372
2373   Player.prototype.src = function src(source) {
2374     if (source === undefined) {
2375       return this.techGet_('src');
2376     }
2377
2378     var currentTech = _tech2['default'].getTech(this.techName_);
2379
2380     // Support old behavior of techs being registered as components.
2381     // Remove once that deprecated behavior is removed.
2382     if (!currentTech) {
2383       currentTech = _component2['default'].getComponent(this.techName_);
2384     }
2385
2386     // case: Array of source objects to choose from and pick the best to play
2387     if (Array.isArray(source)) {
2388       this.sourceList_(source);
2389
2390       // case: URL String (http://myvideo...)
2391     } else if (typeof source === 'string') {
2392       // create a source object from the string
2393       this.src({ src: source });
2394
2395       // case: Source object { src: '', type: '' ... }
2396     } else if (source instanceof Object) {
2397       // check if the source has a type and the loaded tech cannot play the source
2398       // if there's no type we'll just try the current tech
2399       if (source.type && !currentTech.canPlaySource(source, this.options_[this.techName_.toLowerCase()])) {
2400         // create a source list with the current source and send through
2401         // the tech loop to check for a compatible technology
2402         this.sourceList_([source]);
2403       } else {
2404         this.cache_.sources = null;
2405         this.cache_.source = source;
2406         this.cache_.src = source.src;
2407
2408         this.currentType_ = source.type || '';
2409
2410         // wait until the tech is ready to set the source
2411         this.ready(function () {
2412
2413           // The setSource tech method was added with source handlers
2414           // so older techs won't support it
2415           // We need to check the direct prototype for the case where subclasses
2416           // of the tech do not support source handlers
2417           if (currentTech.prototype.hasOwnProperty('setSource')) {
2418             this.techCall_('setSource', source);
2419           } else {
2420             this.techCall_('src', source.src);
2421           }
2422
2423           if (this.options_.preload === 'auto') {
2424             this.load();
2425           }
2426
2427           if (this.options_.autoplay) {
2428             this.play();
2429           }
2430
2431           // Set the source synchronously if possible (#2326)
2432         }, true);
2433       }
2434     }
2435
2436     return this;
2437   };
2438
2439   /**
2440    * Handle an array of source objects
2441    *
2442    * @param  {Tech~SourceObject[]} sources
2443    *         Array of source objects
2444    *
2445    * @private
2446    */
2447
2448
2449   Player.prototype.sourceList_ = function sourceList_(sources) {
2450     var sourceTech = this.selectSource(sources);
2451
2452     if (sourceTech) {
2453       if (sourceTech.tech === this.techName_) {
2454         // if this technology is already loaded, set the source
2455         this.src(sourceTech.source);
2456       } else {
2457         // load this technology with the chosen source
2458         this.loadTech_(sourceTech.tech, sourceTech.source);
2459       }
2460
2461       this.cache_.sources = sources;
2462     } else {
2463       // We need to wrap this in a timeout to give folks a chance to add error event handlers
2464       this.setTimeout(function () {
2465         this.error({ code: 4, message: this.localize(this.options_.notSupportedMessage) });
2466       }, 0);
2467
2468       // we could not find an appropriate tech, but let's still notify the delegate that this is it
2469       // this needs a better comment about why this is needed
2470       this.triggerReady();
2471     }
2472   };
2473
2474   /**
2475    * Begin loading the src data.
2476    *
2477    * @return {Player}
2478    *         A reference to the player
2479    */
2480
2481
2482   Player.prototype.load = function load() {
2483     this.techCall_('load');
2484     return this;
2485   };
2486
2487   /**
2488    * Reset the player. Loads the first tech in the techOrder,
2489    * and calls `reset` on the tech`.
2490    *
2491    * @return {Player}
2492    *         A reference to the player
2493    */
2494
2495
2496   Player.prototype.reset = function reset() {
2497     this.loadTech_((0, _toTitleCase2['default'])(this.options_.techOrder[0]), null);
2498     this.techCall_('reset');
2499     return this;
2500   };
2501
2502   /**
2503    * Returns all of the current source objects.
2504    *
2505    * @return {Tech~SourceObject[]}
2506    *         The current source objects
2507    */
2508
2509
2510   Player.prototype.currentSources = function currentSources() {
2511     var source = this.currentSource();
2512     var sources = [];
2513
2514     // assume `{}` or `{ src }`
2515     if (Object.keys(source).length !== 0) {
2516       sources.push(source);
2517     }
2518
2519     return this.cache_.sources || sources;
2520   };
2521
2522   /**
2523    * Returns the current source object.
2524    *
2525    * @return {Tech~SourceObject}
2526    *         The current source object
2527    */
2528
2529
2530   Player.prototype.currentSource = function currentSource() {
2531     var source = {};
2532     var src = this.currentSrc();
2533
2534     if (src) {
2535       source.src = src;
2536     }
2537
2538     return this.cache_.source || source;
2539   };
2540
2541   /**
2542    * Returns the fully qualified URL of the current source value e.g. http://mysite.com/video.mp4
2543    * Can be used in conjuction with `currentType` to assist in rebuilding the current source object.
2544    *
2545    * @return {string}
2546    *         The current source
2547    */
2548
2549
2550   Player.prototype.currentSrc = function currentSrc() {
2551     return this.techGet_('currentSrc') || this.cache_.src || '';
2552   };
2553
2554   /**
2555    * Get the current source type e.g. video/mp4
2556    * This can allow you rebuild the current source object so that you could load the same
2557    * source and tech later
2558    *
2559    * @return {string}
2560    *         The source MIME type
2561    */
2562
2563
2564   Player.prototype.currentType = function currentType() {
2565     return this.currentType_ || '';
2566   };
2567
2568   /**
2569    * Get or set the preload attribute
2570    *
2571    * @param {boolean} [value]
2572    *        - true means that we should preload
2573    *        - false maens that we should not preload
2574    *
2575    * @return {string|Player}
2576    *         - the preload attribute value when getting
2577    *         - the player when setting
2578    */
2579
2580
2581   Player.prototype.preload = function preload(value) {
2582     if (value !== undefined) {
2583       this.techCall_('setPreload', value);
2584       this.options_.preload = value;
2585       return this;
2586     }
2587     return this.techGet_('preload');
2588   };
2589
2590   /**
2591    * Get or set the autoplay attribute.
2592    *
2593    * @param {boolean} [value]
2594    *        - true means that we should autoplay
2595    *        - false maens that we should not autoplay
2596    *
2597    * @return {string|Player}
2598    *         - the current value of autoplay
2599    *         - the player when setting
2600    */
2601
2602
2603   Player.prototype.autoplay = function autoplay(value) {
2604     if (value !== undefined) {
2605       this.techCall_('setAutoplay', value);
2606       this.options_.autoplay = value;
2607       return this;
2608     }
2609     return this.techGet_('autoplay', value);
2610   };
2611
2612   /**
2613    * Get or set the loop attribute on the video element.
2614    *
2615    * @param {boolean} [value]
2616    *        - true means that we should loop the video
2617    *        - false means that we should not loop the video
2618    *
2619    * @return {string|Player}
2620    *         - the current value of loop when getting
2621    *         - the player when setting
2622    */
2623
2624
2625   Player.prototype.loop = function loop(value) {
2626     if (value !== undefined) {
2627       this.techCall_('setLoop', value);
2628       this.options_.loop = value;
2629       return this;
2630     }
2631     return this.techGet_('loop');
2632   };
2633
2634   /**
2635    * Get or set the poster image source url
2636    *
2637    * @fires Player#posterchange
2638    *
2639    * @param {string} [src]
2640    *        Poster image source URL
2641    *
2642    * @return {string|Player}
2643    *         - the current value of poster when getting
2644    *         - the player when setting
2645    */
2646
2647
2648   Player.prototype.poster = function poster(src) {
2649     if (src === undefined) {
2650       return this.poster_;
2651     }
2652
2653     // The correct way to remove a poster is to set as an empty string
2654     // other falsey values will throw errors
2655     if (!src) {
2656       src = '';
2657     }
2658
2659     // update the internal poster variable
2660     this.poster_ = src;
2661
2662     // update the tech's poster
2663     this.techCall_('setPoster', src);
2664
2665     // alert components that the poster has been set
2666     /**
2667      * This event fires when the poster image is changed on the player.
2668      *
2669      * @event Player#posterchange
2670      * @type {EventTarget~Event}
2671      */
2672     this.trigger('posterchange');
2673
2674     return this;
2675   };
2676
2677   /**
2678    * Some techs (e.g. YouTube) can provide a poster source in an
2679    * asynchronous way. We want the poster component to use this
2680    * poster source so that it covers up the tech's controls.
2681    * (YouTube's play button). However we only want to use this
2682    * soruce if the player user hasn't set a poster through
2683    * the normal APIs.
2684    *
2685    * @fires Player#posterchange
2686    * @listens Tech#posterchange
2687    * @private
2688    */
2689
2690
2691   Player.prototype.handleTechPosterChange_ = function handleTechPosterChange_() {
2692     if (!this.poster_ && this.tech_ && this.tech_.poster) {
2693       this.poster_ = this.tech_.poster() || '';
2694
2695       // Let components know the poster has changed
2696       this.trigger('posterchange');
2697     }
2698   };
2699
2700   /**
2701    * Get or set whether or not the controls are showing.
2702    *
2703    * @fires Player#controlsenabled
2704    *
2705    * @param {boolean} [bool]
2706    *        - true to turn controls on
2707    *        - false to turn controls off
2708    *
2709    * @return {boolean|Player}
2710    *         - the current value of controls when getting
2711    *         - the player when setting
2712    */
2713
2714
2715   Player.prototype.controls = function controls(bool) {
2716     if (bool !== undefined) {
2717       bool = !!bool;
2718
2719       // Don't trigger a change event unless it actually changed
2720       if (this.controls_ !== bool) {
2721         this.controls_ = bool;
2722
2723         if (this.usingNativeControls()) {
2724           this.techCall_('setControls', bool);
2725         }
2726
2727         if (bool) {
2728           this.removeClass('vjs-controls-disabled');
2729           this.addClass('vjs-controls-enabled');
2730           /**
2731            * @event Player#controlsenabled
2732            * @type {EventTarget~Event}
2733            */
2734           this.trigger('controlsenabled');
2735
2736           if (!this.usingNativeControls()) {
2737             this.addTechControlsListeners_();
2738           }
2739         } else {
2740           this.removeClass('vjs-controls-enabled');
2741           this.addClass('vjs-controls-disabled');
2742           /**
2743            * @event Player#controlsdisabled
2744            * @type {EventTarget~Event}
2745            */
2746           this.trigger('controlsdisabled');
2747
2748           if (!this.usingNativeControls()) {
2749             this.removeTechControlsListeners_();
2750           }
2751         }
2752       }
2753       return this;
2754     }
2755     return !!this.controls_;
2756   };
2757
2758   /**
2759    * Toggle native controls on/off. Native controls are the controls built into
2760    * devices (e.g. default iPhone controls), Flash, or other techs
2761    * (e.g. Vimeo Controls)
2762    * **This should only be set by the current tech, because only the tech knows
2763    * if it can support native controls**
2764    *
2765    * @fires Player#usingnativecontrols
2766    * @fires Player#usingcustomcontrols
2767    *
2768    * @param {boolean} [bool]
2769    *        - true to turn native controls on
2770    *        - false to turn native controls off
2771    *
2772    * @return {boolean|Player}
2773    *         - the current value of native controls when getting
2774    *         - the player when setting
2775    */
2776
2777
2778   Player.prototype.usingNativeControls = function usingNativeControls(bool) {
2779     if (bool !== undefined) {
2780       bool = !!bool;
2781
2782       // Don't trigger a change event unless it actually changed
2783       if (this.usingNativeControls_ !== bool) {
2784         this.usingNativeControls_ = bool;
2785         if (bool) {
2786           this.addClass('vjs-using-native-controls');
2787
2788           /**
2789            * player is using the native device controls
2790            *
2791            * @event Player#usingnativecontrols
2792            * @type {EventTarget~Event}
2793            */
2794           this.trigger('usingnativecontrols');
2795         } else {
2796           this.removeClass('vjs-using-native-controls');
2797
2798           /**
2799            * player is using the custom HTML controls
2800            *
2801            * @event Player#usingcustomcontrols
2802            * @type {EventTarget~Event}
2803            */
2804           this.trigger('usingcustomcontrols');
2805         }
2806       }
2807       return this;
2808     }
2809     return !!this.usingNativeControls_;
2810   };
2811
2812   /**
2813    * Set or get the current MediaError
2814    *
2815    * @fires Player#error
2816    *
2817    * @param  {MediaError|string|number} [err]
2818    *         A MediaError or a string/number to be turned
2819    *         into a MediaError
2820    *
2821    * @return {MediaError|null|Player}
2822    *         - The current MediaError when getting (or null)
2823    *         - The player when setting
2824    */
2825
2826
2827   Player.prototype.error = function error(err) {
2828     if (err === undefined) {
2829       return this.error_ || null;
2830     }
2831
2832     // restoring to default
2833     if (err === null) {
2834       this.error_ = err;
2835       this.removeClass('vjs-error');
2836       if (this.errorDisplay) {
2837         this.errorDisplay.close();
2838       }
2839       return this;
2840     }
2841
2842     this.error_ = new _mediaError2['default'](err);
2843
2844     // add the vjs-error classname to the player
2845     this.addClass('vjs-error');
2846
2847     // log the name of the error type and any message
2848     // ie8 just logs "[object object]" if you just log the error object
2849     _log2['default'].error('(CODE:' + this.error_.code + ' ' + _mediaError2['default'].errorTypes[this.error_.code] + ')', this.error_.message, this.error_);
2850
2851     /**
2852      * @event Player#error
2853      * @type {EventTarget~Event}
2854      */
2855     this.trigger('error');
2856
2857     return this;
2858   };
2859
2860   /**
2861    * Report user activity
2862    *
2863    * @param {Object} event
2864    *        Event object
2865    */
2866
2867
2868   Player.prototype.reportUserActivity = function reportUserActivity(event) {
2869     this.userActivity_ = true;
2870   };
2871
2872   /**
2873    * Get/set if user is active
2874    *
2875    * @fires Player#useractive
2876    * @fires Player#userinactive
2877    *
2878    * @param {boolean} [bool]
2879    *        - true if the user is active
2880    *        - false if the user is inactive
2881    * @return {boolean|Player}
2882    *         - the current value of userActive when getting
2883    *         - the player when setting
2884    */
2885
2886
2887   Player.prototype.userActive = function userActive(bool) {
2888     if (bool !== undefined) {
2889       bool = !!bool;
2890       if (bool !== this.userActive_) {
2891         this.userActive_ = bool;
2892         if (bool) {
2893           // If the user was inactive and is now active we want to reset the
2894           // inactivity timer
2895           this.userActivity_ = true;
2896           this.removeClass('vjs-user-inactive');
2897           this.addClass('vjs-user-active');
2898           /**
2899            * @event Player#useractive
2900            * @type {EventTarget~Event}
2901            */
2902           this.trigger('useractive');
2903         } else {
2904           // We're switching the state to inactive manually, so erase any other
2905           // activity
2906           this.userActivity_ = false;
2907
2908           // Chrome/Safari/IE have bugs where when you change the cursor it can
2909           // trigger a mousemove event. This causes an issue when you're hiding
2910           // the cursor when the user is inactive, and a mousemove signals user
2911           // activity. Making it impossible to go into inactive mode. Specifically
2912           // this happens in fullscreen when we really need to hide the cursor.
2913           //
2914           // When this gets resolved in ALL browsers it can be removed
2915           // https://code.google.com/p/chromium/issues/detail?id=103041
2916           if (this.tech_) {
2917             this.tech_.one('mousemove', function (e) {
2918               e.stopPropagation();
2919               e.preventDefault();
2920             });
2921           }
2922
2923           this.removeClass('vjs-user-active');
2924           this.addClass('vjs-user-inactive');
2925           /**
2926            * @event Player#userinactive
2927            * @type {EventTarget~Event}
2928            */
2929           this.trigger('userinactive');
2930         }
2931       }
2932       return this;
2933     }
2934     return this.userActive_;
2935   };
2936
2937   /**
2938    * Listen for user activity based on timeout value
2939    *
2940    * @private
2941    */
2942
2943
2944   Player.prototype.listenForUserActivity_ = function listenForUserActivity_() {
2945     var mouseInProgress = void 0;
2946     var lastMoveX = void 0;
2947     var lastMoveY = void 0;
2948     var handleActivity = Fn.bind(this, this.reportUserActivity);
2949
2950     var handleMouseMove = function handleMouseMove(e) {
2951       // #1068 - Prevent mousemove spamming
2952       // Chrome Bug: https://code.google.com/p/chromium/issues/detail?id=366970
2953       if (e.screenX !== lastMoveX || e.screenY !== lastMoveY) {
2954         lastMoveX = e.screenX;
2955         lastMoveY = e.screenY;
2956         handleActivity();
2957       }
2958     };
2959
2960     var handleMouseDown = function handleMouseDown() {
2961       handleActivity();
2962       // For as long as the they are touching the device or have their mouse down,
2963       // we consider them active even if they're not moving their finger or mouse.
2964       // So we want to continue to update that they are active
2965       this.clearInterval(mouseInProgress);
2966       // Setting userActivity=true now and setting the interval to the same time
2967       // as the activityCheck interval (250) should ensure we never miss the
2968       // next activityCheck
2969       mouseInProgress = this.setInterval(handleActivity, 250);
2970     };
2971
2972     var handleMouseUp = function handleMouseUp(event) {
2973       handleActivity();
2974       // Stop the interval that maintains activity if the mouse/touch is down
2975       this.clearInterval(mouseInProgress);
2976     };
2977
2978     // Any mouse movement will be considered user activity
2979     this.on('mousedown', handleMouseDown);
2980     this.on('mousemove', handleMouseMove);
2981     this.on('mouseup', handleMouseUp);
2982
2983     // Listen for keyboard navigation
2984     // Shouldn't need to use inProgress interval because of key repeat
2985     this.on('keydown', handleActivity);
2986     this.on('keyup', handleActivity);
2987
2988     // Run an interval every 250 milliseconds instead of stuffing everything into
2989     // the mousemove/touchmove function itself, to prevent performance degradation.
2990     // `this.reportUserActivity` simply sets this.userActivity_ to true, which
2991     // then gets picked up by this loop
2992     // http://ejohn.org/blog/learning-from-twitter/
2993     var inactivityTimeout = void 0;
2994
2995     this.setInterval(function () {
2996       // Check to see if mouse/touch activity has happened
2997       if (this.userActivity_) {
2998         // Reset the activity tracker
2999         this.userActivity_ = false;
3000
3001         // If the user state was inactive, set the state to active
3002         this.userActive(true);
3003
3004         // Clear any existing inactivity timeout to start the timer over
3005         this.clearTimeout(inactivityTimeout);
3006
3007         var timeout = this.options_.inactivityTimeout;
3008
3009         if (timeout > 0) {
3010           // In <timeout> milliseconds, if no more activity has occurred the
3011           // user will be considered inactive
3012           inactivityTimeout = this.setTimeout(function () {
3013             // Protect against the case where the inactivityTimeout can trigger just
3014             // before the next user activity is picked up by the activity check loop
3015             // causing a flicker
3016             if (!this.userActivity_) {
3017               this.userActive(false);
3018             }
3019           }, timeout);
3020         }
3021       }
3022     }, 250);
3023   };
3024
3025   /**
3026    * Gets or sets the current playback rate. A playback rate of
3027    * 1.0 represents normal speed and 0.5 would indicate half-speed
3028    * playback, for instance.
3029    *
3030    * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-playbackrate
3031    *
3032    * @param {number} [rate]
3033    *       New playback rate to set.
3034    *
3035    * @return {number|Player}
3036    *         - The current playback rate when getting or 1.0
3037    *         - the player when setting
3038    */
3039
3040
3041   Player.prototype.playbackRate = function playbackRate(rate) {
3042     if (rate !== undefined) {
3043       this.techCall_('setPlaybackRate', rate);
3044       return this;
3045     }
3046
3047     if (this.tech_ && this.tech_.featuresPlaybackRate) {
3048       return this.techGet_('playbackRate');
3049     }
3050     return 1.0;
3051   };
3052
3053   /**
3054    * Gets or sets the audio flag
3055    *
3056    * @param {boolean} bool
3057    *        - true signals that this is an audio player
3058    *        - false signals that this is not an audio player
3059    *
3060    * @return {Player|boolean}
3061    *         - the current value of isAudio when getting
3062    *         - the player if setting
3063    */
3064
3065
3066   Player.prototype.isAudio = function isAudio(bool) {
3067     if (bool !== undefined) {
3068       this.isAudio_ = !!bool;
3069       return this;
3070     }
3071
3072     return !!this.isAudio_;
3073   };
3074
3075   /**
3076    * Get the {@link VideoTrackList}
3077    *
3078    * @see https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
3079    *
3080    * @return {VideoTrackList}
3081    *         the current video track list
3082    */
3083
3084
3085   Player.prototype.videoTracks = function videoTracks() {
3086     // if we have not yet loadTech_, we create videoTracks_
3087     // these will be passed to the tech during loading
3088     if (!this.tech_) {
3089       this.videoTracks_ = this.videoTracks_ || new _videoTrackList2['default']();
3090       return this.videoTracks_;
3091     }
3092
3093     return this.tech_.videoTracks();
3094   };
3095
3096   /**
3097    * Get the {@link AudioTrackList}
3098    *
3099    * @see https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
3100    *
3101    * @return {AudioTrackList}
3102    *         the current audio track list
3103    */
3104
3105
3106   Player.prototype.audioTracks = function audioTracks() {
3107     // if we have not yet loadTech_, we create videoTracks_
3108     // these will be passed to the tech during loading
3109     if (!this.tech_) {
3110       this.audioTracks_ = this.audioTracks_ || new _audioTrackList2['default']();
3111       return this.audioTracks_;
3112     }
3113
3114     return this.tech_.audioTracks();
3115   };
3116
3117   /**
3118    * Get the {@link TextTrackList}
3119    *
3120    * Text tracks are tracks of timed text events.
3121    * - Captions: text displayed over the video
3122    *             for the hearing impaired
3123    * - Subtitles: text displayed over the video for
3124    *              those who don't understand language in the video
3125    * - Chapters: text displayed in a menu allowing the user to jump
3126    *             to particular points (chapters) in the video
3127    * - Descriptions: (not yet implemented) audio descriptions that are read back to
3128    *                 the user by a screen reading device
3129    *
3130    * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
3131    *
3132    * @return {TextTrackList|undefined}
3133    *         The current TextTrackList or undefined if
3134    *         or undefined if we don't have a tech
3135    */
3136
3137
3138   Player.prototype.textTracks = function textTracks() {
3139     // cannot use techGet_ directly because it checks to see whether the tech is ready.
3140     // Flash is unlikely to be ready in time but textTracks should still work.
3141     if (this.tech_) {
3142       return this.tech_.textTracks();
3143     }
3144   };
3145
3146   /**
3147    * Get the "remote" {@link TextTrackList}. Remote Text Tracks
3148    * are tracks that were added to the HTML video element and can
3149    * be removed, whereas normal texttracks cannot be removed.
3150    *
3151    *
3152    * @return {TextTrackList|undefined}
3153    *         The current remote text track list or undefined
3154    *         if we don't have a tech
3155    */
3156
3157
3158   Player.prototype.remoteTextTracks = function remoteTextTracks() {
3159     if (this.tech_) {
3160       return this.tech_.remoteTextTracks();
3161     }
3162   };
3163
3164   /**
3165    * Get the "remote" {@link HTMLTrackElementList}.
3166    * This gives the user all of the DOM elements that match up
3167    * with the remote {@link TextTrackList}.
3168    *
3169    * @return {HTMLTrackElementList}
3170    *         The current remote text track list elements
3171    *         or undefined if we don't have a tech
3172    */
3173
3174
3175   Player.prototype.remoteTextTrackEls = function remoteTextTrackEls() {
3176     if (this.tech_) {
3177       return this.tech_.remoteTextTrackEls();
3178     }
3179   };
3180
3181   /**
3182    * A helper method for adding a {@link TextTrack} to our
3183    * {@link TextTrackList}.
3184    *
3185    * In addition to the W3C settings we allow adding additional info through options.
3186    *
3187    * @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-addtexttrack
3188    *
3189    * @param {string} [kind]
3190    *        the kind of TextTrack you are adding
3191    *
3192    * @param {string} [label]
3193    *        the label to give the TextTrack label
3194    *
3195    * @param {string} [language]
3196    *        the language to set on the TextTrack
3197    *
3198    * @return {TextTrack|undefined}
3199    *         the TextTrack that was added or undefined
3200    *         if there is no tech
3201    */
3202
3203
3204   Player.prototype.addTextTrack = function addTextTrack(kind, label, language) {
3205     if (this.tech_) {
3206       return this.tech_.addTextTrack(kind, label, language);
3207     }
3208   };
3209
3210   /**
3211    * Create a remote {@link TextTrack} and an {@link HTMLTrackElement}. It will
3212    * automatically removed from the video element whenever the source changes, unless
3213    * manualCleanup is set to false.
3214    *
3215    * @param {Object} options
3216    *        Options to pass to {@link HTMLTrackElement} during creation. See
3217    *        {@link HTMLTrackElement} for object properties that you should use.
3218    *
3219    * @param {boolean} [manualCleanup=true] if set to false, the TextTrack will be
3220    *
3221    * @return {HTMLTrackElement}
3222    *         the HTMLTrackElement that was created and added
3223    *         to the HTMLTrackElementList and the remote
3224    *         TextTrackList
3225    *
3226    * @deprecated The default value of the "manualCleanup" parameter will default
3227    *             to "false" in upcoming versions of Video.js
3228    */
3229
3230
3231   Player.prototype.addRemoteTextTrack = function addRemoteTextTrack(options, manualCleanup) {
3232     if (this.tech_) {
3233       return this.tech_.addRemoteTextTrack(options, manualCleanup);
3234     }
3235   };
3236
3237   /**
3238    * Remove a remote {@link TextTrack} from the respective
3239    * {@link TextTrackList} and {@link HTMLTrackElementList}.
3240    *
3241    * @param {Object} track
3242    *        Remote {@link TextTrack} to remove
3243    *
3244    * @return {undefined}
3245    *         does not return anything
3246    */
3247
3248
3249   Player.prototype.removeRemoteTextTrack = function removeRemoteTextTrack() {
3250     var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
3251         _ref3$track = _ref3.track,
3252         track = _ref3$track === undefined ? arguments[0] : _ref3$track;
3253
3254     // destructure the input into an object with a track argument, defaulting to arguments[0]
3255     // default the whole argument to an empty object if nothing was passed in
3256
3257     if (this.tech_) {
3258       return this.tech_.removeRemoteTextTrack(track);
3259     }
3260   };
3261
3262   /**
3263    * Get video width
3264    *
3265    * @return {number}
3266    *         current video width
3267    */
3268
3269
3270   Player.prototype.videoWidth = function videoWidth() {
3271     return this.tech_ && this.tech_.videoWidth && this.tech_.videoWidth() || 0;
3272   };
3273
3274   /**
3275    * Get video height
3276    *
3277    * @return {number}
3278    *         current video height
3279    */
3280
3281
3282   Player.prototype.videoHeight = function videoHeight() {
3283     return this.tech_ && this.tech_.videoHeight && this.tech_.videoHeight() || 0;
3284   };
3285
3286   // Methods to add support for
3287   // initialTime: function() { return this.techCall_('initialTime'); },
3288   // startOffsetTime: function() { return this.techCall_('startOffsetTime'); },
3289   // played: function() { return this.techCall_('played'); },
3290   // defaultPlaybackRate: function() { return this.techCall_('defaultPlaybackRate'); },
3291   // defaultMuted: function() { return this.techCall_('defaultMuted'); }
3292
3293   /**
3294    * The player's language code
3295    * NOTE: The language should be set in the player options if you want the
3296    * the controls to be built with a specific language. Changing the lanugage
3297    * later will not update controls text.
3298    *
3299    * @param {string} [code]
3300    *        the language code to set the player to
3301    *
3302    * @return {string|Player}
3303    *         - The current language code when getting
3304    *         - A reference to the player when setting
3305    */
3306
3307
3308   Player.prototype.language = function language(code) {
3309     if (code === undefined) {
3310       return this.language_;
3311     }
3312
3313     this.language_ = String(code).toLowerCase();
3314     return this;
3315   };
3316
3317   /**
3318    * Get the player's language dictionary
3319    * Merge every time, because a newly added plugin might call videojs.addLanguage() at any time
3320    * Languages specified directly in the player options have precedence
3321    *
3322    * @return {Array}
3323    *         An array of of supported languages
3324    */
3325
3326
3327   Player.prototype.languages = function languages() {
3328     return (0, _mergeOptions2['default'])(Player.prototype.options_.languages, this.languages_);
3329   };
3330
3331   /**
3332    * returns a JavaScript object reperesenting the current track
3333    * information. **DOES not return it as JSON**
3334    *
3335    * @return {Object}
3336    *         Object representing the current of track info
3337    */
3338
3339
3340   Player.prototype.toJSON = function toJSON() {
3341     var options = (0, _mergeOptions2['default'])(this.options_);
3342     var tracks = options.tracks;
3343
3344     options.tracks = [];
3345
3346     for (var i = 0; i < tracks.length; i++) {
3347       var track = tracks[i];
3348
3349       // deep merge tracks and null out player so no circular references
3350       track = (0, _mergeOptions2['default'])(track);
3351       track.player = undefined;
3352       options.tracks[i] = track;
3353     }
3354
3355     return options;
3356   };
3357
3358   /**
3359    * Creates a simple modal dialog (an instance of the {@link ModalDialog}
3360    * component) that immediately overlays the player with arbitrary
3361    * content and removes itself when closed.
3362    *
3363    * @param {string|Function|Element|Array|null} content
3364    *        Same as {@link ModalDialog#content}'s param of the same name.
3365    *        The most straight-forward usage is to provide a string or DOM
3366    *        element.
3367    *
3368    * @param {Object} [options]
3369    *        Extra options which will be passed on to the {@link ModalDialog}.
3370    *
3371    * @return {ModalDialog}
3372    *         the {@link ModalDialog} that was created
3373    */
3374
3375
3376   Player.prototype.createModal = function createModal(content, options) {
3377     var _this5 = this;
3378
3379     options = options || {};
3380     options.content = content || '';
3381
3382     var modal = new _modalDialog2['default'](this, options);
3383
3384     this.addChild(modal);
3385     modal.on('dispose', function () {
3386       _this5.removeChild(modal);
3387     });
3388
3389     return modal.open();
3390   };
3391
3392   /**
3393    * Gets tag settings
3394    *
3395    * @param {Element} tag
3396    *        The player tag
3397    *
3398    * @return {Object}
3399    *         An object containing all of the settings
3400    *         for a player tag
3401    */
3402
3403
3404   Player.getTagSettings = function getTagSettings(tag) {
3405     var baseOptions = {
3406       sources: [],
3407       tracks: []
3408     };
3409
3410     var tagOptions = Dom.getElAttributes(tag);
3411     var dataSetup = tagOptions['data-setup'];
3412
3413     if (Dom.hasElClass(tag, 'vjs-fluid')) {
3414       tagOptions.fluid = true;
3415     }
3416
3417     // Check if data-setup attr exists.
3418     if (dataSetup !== null) {
3419       // Parse options JSON
3420       // If empty string, make it a parsable json object.
3421       var _safeParseTuple = (0, _tuple2['default'])(dataSetup || '{}'),
3422           err = _safeParseTuple[0],
3423           data = _safeParseTuple[1];
3424
3425       if (err) {
3426         _log2['default'].error(err);
3427       }
3428       (0, _obj.assign)(tagOptions, data);
3429     }
3430
3431     (0, _obj.assign)(baseOptions, tagOptions);
3432
3433     // Get tag children settings
3434     if (tag.hasChildNodes()) {
3435       var children = tag.childNodes;
3436
3437       for (var i = 0, j = children.length; i < j; i++) {
3438         var child = children[i];
3439         // Change case needed: http://ejohn.org/blog/nodename-case-sensitivity/
3440         var childName = child.nodeName.toLowerCase();
3441
3442         if (childName === 'source') {
3443           baseOptions.sources.push(Dom.getElAttributes(child));
3444         } else if (childName === 'track') {
3445           baseOptions.tracks.push(Dom.getElAttributes(child));
3446         }
3447       }
3448     }
3449
3450     return baseOptions;
3451   };
3452
3453   /**
3454    * Determine wether or not flexbox is supported
3455    *
3456    * @return {boolean}
3457    *         - true if flexbox is supported
3458    *         - false if flexbox is not supported
3459    */
3460
3461
3462   Player.prototype.flexNotSupported_ = function flexNotSupported_() {
3463     var elem = _document2['default'].createElement('i');
3464
3465     // Note: We don't actually use flexBasis (or flexOrder), but it's one of the more
3466     // common flex features that we can rely on when checking for flex support.
3467     return !('flexBasis' in elem.style || 'webkitFlexBasis' in elem.style || 'mozFlexBasis' in elem.style || 'msFlexBasis' in elem.style ||
3468     // IE10-specific (2012 flex spec)
3469     'msFlexOrder' in elem.style);
3470   };
3471
3472   return Player;
3473 }(_component2['default']);
3474
3475 /**
3476  * Global player list
3477  *
3478  * @type {Object}
3479  */
3480
3481
3482 Player.players = {};
3483
3484 var navigator = _window2['default'].navigator;
3485
3486 /*
3487  * Player instance options, surfaced using options
3488  * options = Player.prototype.options_
3489  * Make changes in options, not here.
3490  *
3491  * @type {Object}
3492  * @private
3493  */
3494 Player.prototype.options_ = {
3495   // Default order of fallback technology
3496   techOrder: ['html5', 'flash'],
3497   // techOrder: ['flash','html5'],
3498
3499   html5: {},
3500   flash: {},
3501
3502   // defaultVolume: 0.85,
3503   defaultVolume: 0.00,
3504
3505   // default inactivity timeout
3506   inactivityTimeout: 2000,
3507
3508   // default playback rates
3509   playbackRates: [],
3510   // Add playback rate selection by adding rates
3511   // 'playbackRates': [0.5, 1, 1.5, 2],
3512
3513   // Included control sets
3514   children: ['mediaLoader', 'posterImage', 'textTrackDisplay', 'loadingSpinner', 'bigPlayButton', 'controlBar', 'errorDisplay', 'textTrackSettings'],
3515
3516   language: navigator && (navigator.languages && navigator.languages[0] || navigator.userLanguage || navigator.language) || 'en',
3517
3518   // locales and their language translations
3519   languages: {},
3520
3521   // Default message to show when a video cannot be played.
3522   notSupportedMessage: 'No compatible source was found for this media.'
3523 };
3524
3525 [
3526 /**
3527  * Returns whether or not the player is in the "ended" state.
3528  *
3529  * @return {Boolean} True if the player is in the ended state, false if not.
3530  * @method Player#ended
3531  */
3532 'ended',
3533 /**
3534  * Returns whether or not the player is in the "seeking" state.
3535  *
3536  * @return {Boolean} True if the player is in the seeking state, false if not.
3537  * @method Player#seeking
3538  */
3539 'seeking',
3540 /**
3541  * Returns the TimeRanges of the media that are currently available
3542  * for seeking to.
3543  *
3544  * @return {TimeRanges} the seekable intervals of the media timeline
3545  * @method Player#seekable
3546  */
3547 'seekable',
3548 /**
3549  * Returns the current state of network activity for the element, from
3550  * the codes in the list below.
3551  * - NETWORK_EMPTY (numeric value 0)
3552  *   The element has not yet been initialised. All attributes are in
3553  *   their initial states.
3554  * - NETWORK_IDLE (numeric value 1)
3555  *   The element's resource selection algorithm is active and has
3556  *   selected a resource, but it is not actually using the network at
3557  *   this time.
3558  * - NETWORK_LOADING (numeric value 2)
3559  *   The user agent is actively trying to download data.
3560  * - NETWORK_NO_SOURCE (numeric value 3)
3561  *   The element's resource selection algorithm is active, but it has
3562  *   not yet found a resource to use.
3563  *
3564  * @see https://html.spec.whatwg.org/multipage/embedded-content.html#network-states
3565  * @return {number} the current network activity state
3566  * @method Player#networkState
3567  */
3568 'networkState',
3569 /**
3570  * Returns a value that expresses the current state of the element
3571  * with respect to rendering the current playback position, from the
3572  * codes in the list below.
3573  * - HAVE_NOTHING (numeric value 0)
3574  *   No information regarding the media resource is available.
3575  * - HAVE_METADATA (numeric value 1)
3576  *   Enough of the resource has been obtained that the duration of the
3577  *   resource is available.
3578  * - HAVE_CURRENT_DATA (numeric value 2)
3579  *   Data for the immediate current playback position is available.
3580  * - HAVE_FUTURE_DATA (numeric value 3)
3581  *   Data for the immediate current playback position is available, as
3582  *   well as enough data for the user agent to advance the current
3583  *   playback position in the direction of playback.
3584  * - HAVE_ENOUGH_DATA (numeric value 4)
3585  *   The user agent estimates that enough data is available for
3586  *   playback to proceed uninterrupted.
3587  *
3588  * @see https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-readystate
3589  * @return {number} the current playback rendering state
3590  * @method Player#readyState
3591  */
3592 'readyState'].forEach(function (fn) {
3593   Player.prototype[fn] = function () {
3594     return this.techGet_(fn);
3595   };
3596 });
3597
3598 TECH_EVENTS_RETRIGGER.forEach(function (event) {
3599   Player.prototype['handleTech' + (0, _toTitleCase2['default'])(event) + '_'] = function () {
3600     return this.trigger(event);
3601   };
3602 });
3603
3604 /**
3605  * Fired when the player has initial duration and dimension information
3606  *
3607  * @event Player#loadedmetadata
3608  * @type {EventTarget~Event}
3609  */
3610
3611 /**
3612  * Fired when the player has downloaded data at the current playback position
3613  *
3614  * @event Player#loadeddata
3615  * @type {EventTarget~Event}
3616  */
3617
3618 /**
3619  * Fired when the current playback position has changed *
3620  * During playback this is fired every 15-250 milliseconds, depending on the
3621  * playback technology in use.
3622  *
3623  * @event Player#timeupdate
3624  * @type {EventTarget~Event}
3625  */
3626
3627 /**
3628  * Fired when the volume changes
3629  *
3630  * @event Player#volumechange
3631  * @type {EventTarget~Event}
3632  */
3633
3634 _component2['default'].registerComponent('Player', Player);
3635 exports['default'] = Player;