Version 1
[yaffs-website] / node_modules / video.js / es5 / component.js
1 'use strict';
2
3 exports.__esModule = true;
4
5 var _window = require('global/window');
6
7 var _window2 = _interopRequireDefault(_window);
8
9 var _dom = require('./utils/dom.js');
10
11 var Dom = _interopRequireWildcard(_dom);
12
13 var _fn = require('./utils/fn.js');
14
15 var Fn = _interopRequireWildcard(_fn);
16
17 var _guid = require('./utils/guid.js');
18
19 var Guid = _interopRequireWildcard(_guid);
20
21 var _events = require('./utils/events.js');
22
23 var Events = _interopRequireWildcard(_events);
24
25 var _log = require('./utils/log.js');
26
27 var _log2 = _interopRequireDefault(_log);
28
29 var _toTitleCase = require('./utils/to-title-case.js');
30
31 var _toTitleCase2 = _interopRequireDefault(_toTitleCase);
32
33 var _mergeOptions = require('./utils/merge-options.js');
34
35 var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
36
37 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; } }
38
39 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
40
41 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /**
42                                                                                                                                                            * Player Component - Base class for all UI objects
43                                                                                                                                                            *
44                                                                                                                                                            * @file component.js
45                                                                                                                                                            */
46
47
48 /**
49  * Base class for all UI Components.
50  * Components are UI objects which represent both a javascript object and an element
51  * in the DOM. They can be children of other components, and can have
52  * children themselves.
53  *
54  * Components can also use methods from {@link EventTarget}
55  */
56 var Component = function () {
57
58   /**
59    * A callback that is called when a component is ready. Does not have any
60    * paramters and any callback value will be ignored.
61    *
62    * @callback Component~ReadyCallback
63    * @this Component
64    */
65
66   /**
67    * Creates an instance of this class.
68    *
69    * @param {Player} player
70    *        The `Player` that this class should be attached to.
71    *
72    * @param {Object} [options]
73    *        The key/value store of player options.
74    #
75    * @param {Object[]} [options.children]
76    *        An array of children objects to intialize this component with. Children objects have
77    *        a name property that will be used if more than one component of the same type needs to be
78    *        added.
79    *
80    * @param {Component~ReadyCallback} [ready]
81    *        Function that gets called when the `Component` is ready.
82    */
83   function Component(player, options, ready) {
84     _classCallCheck(this, Component);
85
86     // The component might be the player itself and we can't pass `this` to super
87     if (!player && this.play) {
88       this.player_ = player = this; // eslint-disable-line
89     } else {
90       this.player_ = player;
91     }
92
93     // Make a copy of prototype.options_ to protect against overriding defaults
94     this.options_ = (0, _mergeOptions2['default'])({}, this.options_);
95
96     // Updated options with supplied options
97     options = this.options_ = (0, _mergeOptions2['default'])(this.options_, options);
98
99     // Get ID from options or options element if one is supplied
100     this.id_ = options.id || options.el && options.el.id;
101
102     // If there was no ID from the options, generate one
103     if (!this.id_) {
104       // Don't require the player ID function in the case of mock players
105       var id = player && player.id && player.id() || 'no_player';
106
107       this.id_ = id + '_component_' + Guid.newGUID();
108     }
109
110     this.name_ = options.name || null;
111
112     // Create element if one wasn't provided in options
113     if (options.el) {
114       this.el_ = options.el;
115     } else if (options.createEl !== false) {
116       this.el_ = this.createEl();
117     }
118
119     this.children_ = [];
120     this.childIndex_ = {};
121     this.childNameIndex_ = {};
122
123     // Add any child components in options
124     if (options.initChildren !== false) {
125       this.initChildren();
126     }
127
128     this.ready(ready);
129     // Don't want to trigger ready here or it will before init is actually
130     // finished for all children that run this constructor
131
132     if (options.reportTouchActivity !== false) {
133       this.enableTouchActivity();
134     }
135   }
136
137   /**
138    * Dispose of the `Component` and all child components.
139    *
140    * @fires Component#dispose
141    */
142
143
144   Component.prototype.dispose = function dispose() {
145
146     /**
147      * Triggered when a `Component` is disposed.
148      *
149      * @event Component#dispose
150      * @type {EventTarget~Event}
151      *
152      * @property {boolean} [bubbles=false]
153      *           set to false so that the close event does not
154      *           bubble up
155      */
156     this.trigger({ type: 'dispose', bubbles: false });
157
158     // Dispose all children.
159     if (this.children_) {
160       for (var i = this.children_.length - 1; i >= 0; i--) {
161         if (this.children_[i].dispose) {
162           this.children_[i].dispose();
163         }
164       }
165     }
166
167     // Delete child references
168     this.children_ = null;
169     this.childIndex_ = null;
170     this.childNameIndex_ = null;
171
172     // Remove all event listeners.
173     this.off();
174
175     // Remove element from DOM
176     if (this.el_.parentNode) {
177       this.el_.parentNode.removeChild(this.el_);
178     }
179
180     Dom.removeElData(this.el_);
181     this.el_ = null;
182   };
183
184   /**
185    * Return the {@link Player} that the `Component` has attached to.
186    *
187    * @return {Player}
188    *         The player that this `Component` has attached to.
189    */
190
191
192   Component.prototype.player = function player() {
193     return this.player_;
194   };
195
196   /**
197    * Deep merge of options objects with new options.
198    * > Note: When both `obj` and `options` contain properties whose values are objects.
199    *         The two properties get merged using {@link module:mergeOptions}
200    *
201    * @param {Object} obj
202    *        The object that contains new options.
203    *
204    * @return {Object}
205    *         A new object of `this.options_` and `obj` merged together.
206    *
207    * @deprecated since version 5
208    */
209
210
211   Component.prototype.options = function options(obj) {
212     _log2['default'].warn('this.options() has been deprecated and will be moved to the constructor in 6.0');
213
214     if (!obj) {
215       return this.options_;
216     }
217
218     this.options_ = (0, _mergeOptions2['default'])(this.options_, obj);
219     return this.options_;
220   };
221
222   /**
223    * Get the `Component`s DOM element
224    *
225    * @return {Element}
226    *         The DOM element for this `Component`.
227    */
228
229
230   Component.prototype.el = function el() {
231     return this.el_;
232   };
233
234   /**
235    * Create the `Component`s DOM element.
236    *
237    * @param {string} [tagName]
238    *        Element's DOM node type. e.g. 'div'
239    *
240    * @param {Object} [properties]
241    *        An object of properties that should be set.
242    *
243    * @param {Object} [attributes]
244    *        An object of attributes that should be set.
245    *
246    * @return {Element}
247    *         The element that gets created.
248    */
249
250
251   Component.prototype.createEl = function createEl(tagName, properties, attributes) {
252     return Dom.createEl(tagName, properties, attributes);
253   };
254
255   /**
256    * Localize a string given the string in english.
257    *
258    * @param {string} string
259    *        The string to localize.
260    *
261    * @return {string}
262    *         The localized string or if no localization exists the english string.
263    */
264
265
266   Component.prototype.localize = function localize(string) {
267     var code = this.player_.language && this.player_.language();
268     var languages = this.player_.languages && this.player_.languages();
269
270     if (!code || !languages) {
271       return string;
272     }
273
274     var language = languages[code];
275
276     if (language && language[string]) {
277       return language[string];
278     }
279
280     var primaryCode = code.split('-')[0];
281     var primaryLang = languages[primaryCode];
282
283     if (primaryLang && primaryLang[string]) {
284       return primaryLang[string];
285     }
286
287     return string;
288   };
289
290   /**
291    * Return the `Component`s DOM element. This is where children get inserted.
292    * This will usually be the the same as the element returned in {@link Component#el}.
293    *
294    * @return {Element}
295    *         The content element for this `Component`.
296    */
297
298
299   Component.prototype.contentEl = function contentEl() {
300     return this.contentEl_ || this.el_;
301   };
302
303   /**
304    * Get this `Component`s ID
305    *
306    * @return {string}
307    *         The id of this `Component`
308    */
309
310
311   Component.prototype.id = function id() {
312     return this.id_;
313   };
314
315   /**
316    * Get the `Component`s name. The name gets used to reference the `Component`
317    * and is set during registration.
318    *
319    * @return {string}
320    *         The name of this `Component`.
321    */
322
323
324   Component.prototype.name = function name() {
325     return this.name_;
326   };
327
328   /**
329    * Get an array of all child components
330    *
331    * @return {Array}
332    *         The children
333    */
334
335
336   Component.prototype.children = function children() {
337     return this.children_;
338   };
339
340   /**
341    * Returns the child `Component` with the given `id`.
342    *
343    * @param {string} id
344    *        The id of the child `Component` to get.
345    *
346    * @return {Component|undefined}
347    *         The child `Component` with the given `id` or undefined.
348    */
349
350
351   Component.prototype.getChildById = function getChildById(id) {
352     return this.childIndex_[id];
353   };
354
355   /**
356    * Returns the child `Component` with the given `name`.
357    *
358    * @param {string} name
359    *        The name of the child `Component` to get.
360    *
361    * @return {Component|undefined}
362    *         The child `Component` with the given `name` or undefined.
363    */
364
365
366   Component.prototype.getChild = function getChild(name) {
367     if (!name) {
368       return;
369     }
370
371     name = (0, _toTitleCase2['default'])(name);
372
373     return this.childNameIndex_[name];
374   };
375
376   /**
377    * Add a child `Component` inside the current `Component`.
378    *
379    *
380    * @param {string|Component} child
381    *        The name or instance of a child to add.
382    *
383    * @param {Object} [options={}]
384    *        The key/value store of options that will get passed to children of
385    *        the child.
386    *
387    * @param {number} [index=this.children_.length]
388    *        The index to attempt to add a child into.
389    *
390    * @return {Component}
391    *         The `Component` that gets added as a child. When using a string the
392    *         `Component` will get created by this process.
393    */
394
395
396   Component.prototype.addChild = function addChild(child) {
397     var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
398     var index = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.children_.length;
399
400     var component = void 0;
401     var componentName = void 0;
402
403     // If child is a string, create component with options
404     if (typeof child === 'string') {
405       componentName = (0, _toTitleCase2['default'])(child);
406
407       // Options can also be specified as a boolean,
408       // so convert to an empty object if false.
409       if (!options) {
410         options = {};
411       }
412
413       // Same as above, but true is deprecated so show a warning.
414       if (options === true) {
415         _log2['default'].warn('Initializing a child component with `true` is deprecated.' + 'Children should be defined in an array when possible, ' + 'but if necessary use an object instead of `true`.');
416         options = {};
417       }
418
419       var componentClassName = options.componentClass || componentName;
420
421       // Set name through options
422       options.name = componentName;
423
424       // Create a new object & element for this controls set
425       // If there's no .player_, this is a player
426       var ComponentClass = Component.getComponent(componentClassName);
427
428       if (!ComponentClass) {
429         throw new Error('Component ' + componentClassName + ' does not exist');
430       }
431
432       // data stored directly on the videojs object may be
433       // misidentified as a component to retain
434       // backwards-compatibility with 4.x. check to make sure the
435       // component class can be instantiated.
436       if (typeof ComponentClass !== 'function') {
437         return null;
438       }
439
440       component = new ComponentClass(this.player_ || this, options);
441
442       // child is a component instance
443     } else {
444       component = child;
445     }
446
447     this.children_.splice(index, 0, component);
448
449     if (typeof component.id === 'function') {
450       this.childIndex_[component.id()] = component;
451     }
452
453     // If a name wasn't used to create the component, check if we can use the
454     // name function of the component
455     componentName = componentName || component.name && (0, _toTitleCase2['default'])(component.name());
456
457     if (componentName) {
458       this.childNameIndex_[componentName] = component;
459     }
460
461     // Add the UI object's element to the container div (box)
462     // Having an element is not required
463     if (typeof component.el === 'function' && component.el()) {
464       var childNodes = this.contentEl().children;
465       var refNode = childNodes[index] || null;
466
467       this.contentEl().insertBefore(component.el(), refNode);
468     }
469
470     // Return so it can stored on parent object if desired.
471     return component;
472   };
473
474   /**
475    * Remove a child `Component` from this `Component`s list of children. Also removes
476    * the child `Component`s element from this `Component`s element.
477    *
478    * @param {Component} component
479    *        The child `Component` to remove.
480    */
481
482
483   Component.prototype.removeChild = function removeChild(component) {
484     if (typeof component === 'string') {
485       component = this.getChild(component);
486     }
487
488     if (!component || !this.children_) {
489       return;
490     }
491
492     var childFound = false;
493
494     for (var i = this.children_.length - 1; i >= 0; i--) {
495       if (this.children_[i] === component) {
496         childFound = true;
497         this.children_.splice(i, 1);
498         break;
499       }
500     }
501
502     if (!childFound) {
503       return;
504     }
505
506     this.childIndex_[component.id()] = null;
507     this.childNameIndex_[component.name()] = null;
508
509     var compEl = component.el();
510
511     if (compEl && compEl.parentNode === this.contentEl()) {
512       this.contentEl().removeChild(component.el());
513     }
514   };
515
516   /**
517    * Add and initialize default child `Component`s based upon options.
518    */
519
520
521   Component.prototype.initChildren = function initChildren() {
522     var _this = this;
523
524     var children = this.options_.children;
525
526     if (children) {
527       // `this` is `parent`
528       var parentOptions = this.options_;
529
530       var handleAdd = function handleAdd(child) {
531         var name = child.name;
532         var opts = child.opts;
533
534         // Allow options for children to be set at the parent options
535         // e.g. videojs(id, { controlBar: false });
536         // instead of videojs(id, { children: { controlBar: false });
537         if (parentOptions[name] !== undefined) {
538           opts = parentOptions[name];
539         }
540
541         // Allow for disabling default components
542         // e.g. options['children']['posterImage'] = false
543         if (opts === false) {
544           return;
545         }
546
547         // Allow options to be passed as a simple boolean if no configuration
548         // is necessary.
549         if (opts === true) {
550           opts = {};
551         }
552
553         // We also want to pass the original player options
554         // to each component as well so they don't need to
555         // reach back into the player for options later.
556         opts.playerOptions = _this.options_.playerOptions;
557
558         // Create and add the child component.
559         // Add a direct reference to the child by name on the parent instance.
560         // If two of the same component are used, different names should be supplied
561         // for each
562         var newChild = _this.addChild(name, opts);
563
564         if (newChild) {
565           _this[name] = newChild;
566         }
567       };
568
569       // Allow for an array of children details to passed in the options
570       var workingChildren = void 0;
571       var Tech = Component.getComponent('Tech');
572
573       if (Array.isArray(children)) {
574         workingChildren = children;
575       } else {
576         workingChildren = Object.keys(children);
577       }
578
579       workingChildren
580       // children that are in this.options_ but also in workingChildren  would
581       // give us extra children we do not want. So, we want to filter them out.
582       .concat(Object.keys(this.options_).filter(function (child) {
583         return !workingChildren.some(function (wchild) {
584           if (typeof wchild === 'string') {
585             return child === wchild;
586           }
587           return child === wchild.name;
588         });
589       })).map(function (child) {
590         var name = void 0;
591         var opts = void 0;
592
593         if (typeof child === 'string') {
594           name = child;
595           opts = children[name] || _this.options_[name] || {};
596         } else {
597           name = child.name;
598           opts = child;
599         }
600
601         return { name: name, opts: opts };
602       }).filter(function (child) {
603         // we have to make sure that child.name isn't in the techOrder since
604         // techs are registerd as Components but can't aren't compatible
605         // See https://github.com/videojs/video.js/issues/2772
606         var c = Component.getComponent(child.opts.componentClass || (0, _toTitleCase2['default'])(child.name));
607
608         return c && !Tech.isTech(c);
609       }).forEach(handleAdd);
610     }
611   };
612
613   /**
614    * Builds the default DOM class name. Should be overriden by sub-components.
615    *
616    * @return {string}
617    *         The DOM class name for this object.
618    *
619    * @abstract
620    */
621
622
623   Component.prototype.buildCSSClass = function buildCSSClass() {
624     // Child classes can include a function that does:
625     // return 'CLASS NAME' + this._super();
626     return '';
627   };
628
629   /**
630    * Add an `event listener` to this `Component`s element.
631    *
632    * The benefit of using this over the following:
633    * - `VjsEvents.on(otherElement, 'eventName', myFunc)`
634    * - `otherComponent.on('eventName', myFunc)`
635    *
636    * 1. Is that the listeners will get cleaned up when either component gets disposed.
637    * 1. It will also bind `myComponent` as the context of `myFunc`.
638    * > NOTE: If you remove the element from the DOM that has used `on` you need to
639    *         clean up references using: `myComponent.trigger(el, 'dispose')`
640    *         This will also allow the browser to garbage collect it. In special
641    *         cases such as with `window` and `document`, which are both permanent,
642    *         this is not necessary.
643    *
644    * @param {string|Component|string[]} [first]
645    *        The event name, and array of event names, or another `Component`.
646    *
647    * @param {EventTarget~EventListener|string|string[]} [second]
648    *        The listener function, an event name, or an Array of events names.
649    *
650    * @param {EventTarget~EventListener} [third]
651    *        The event handler if `first` is a `Component` and `second` is an event name
652    *        or an Array of event names.
653    *
654    * @return {Component}
655    *         Returns itself; method can be chained.
656    *
657    * @listens Component#dispose
658    */
659
660
661   Component.prototype.on = function on(first, second, third) {
662     var _this2 = this;
663
664     if (typeof first === 'string' || Array.isArray(first)) {
665       Events.on(this.el_, first, Fn.bind(this, second));
666
667       // Targeting another component or element
668     } else {
669       var target = first;
670       var type = second;
671       var fn = Fn.bind(this, third);
672
673       // When this component is disposed, remove the listener from the other component
674       var removeOnDispose = function removeOnDispose() {
675         return _this2.off(target, type, fn);
676       };
677
678       // Use the same function ID so we can remove it later it using the ID
679       // of the original listener
680       removeOnDispose.guid = fn.guid;
681       this.on('dispose', removeOnDispose);
682
683       // If the other component is disposed first we need to clean the reference
684       // to the other component in this component's removeOnDispose listener
685       // Otherwise we create a memory leak.
686       var cleanRemover = function cleanRemover() {
687         return _this2.off('dispose', removeOnDispose);
688       };
689
690       // Add the same function ID so we can easily remove it later
691       cleanRemover.guid = fn.guid;
692
693       // Check if this is a DOM node
694       if (first.nodeName) {
695         // Add the listener to the other element
696         Events.on(target, type, fn);
697         Events.on(target, 'dispose', cleanRemover);
698
699         // Should be a component
700         // Not using `instanceof Component` because it makes mock players difficult
701       } else if (typeof first.on === 'function') {
702         // Add the listener to the other component
703         target.on(type, fn);
704         target.on('dispose', cleanRemover);
705       }
706     }
707
708     return this;
709   };
710
711   /**
712    * Remove an event listener from this `Component`s element. If the second argument is
713    * exluded all listeners for the type passed in as the first argument will be removed.
714    *
715    * @param {string|Component|string[]} [first]
716    *        The event name, and array of event names, or another `Component`.
717    *
718    * @param {EventTarget~EventListener|string|string[]} [second]
719    *        The listener function, an event name, or an Array of events names.
720    *
721    * @param {EventTarget~EventListener} [third]
722    *        The event handler if `first` is a `Component` and `second` is an event name
723    *        or an Array of event names.
724    *
725    * @return {Component}
726    *         Returns itself; method can be chained.
727    */
728
729
730   Component.prototype.off = function off(first, second, third) {
731     if (!first || typeof first === 'string' || Array.isArray(first)) {
732       Events.off(this.el_, first, second);
733     } else {
734       var target = first;
735       var type = second;
736       // Ensure there's at least a guid, even if the function hasn't been used
737       var fn = Fn.bind(this, third);
738
739       // Remove the dispose listener on this component,
740       // which was given the same guid as the event listener
741       this.off('dispose', fn);
742
743       if (first.nodeName) {
744         // Remove the listener
745         Events.off(target, type, fn);
746         // Remove the listener for cleaning the dispose listener
747         Events.off(target, 'dispose', fn);
748       } else {
749         target.off(type, fn);
750         target.off('dispose', fn);
751       }
752     }
753
754     return this;
755   };
756
757   /**
758    * Add an event listener that gets triggered only once and then gets removed.
759    *
760    * @param {string|Component|string[]} [first]
761    *        The event name, and array of event names, or another `Component`.
762    *
763    * @param {EventTarget~EventListener|string|string[]} [second]
764    *        The listener function, an event name, or an Array of events names.
765    *
766    * @param {EventTarget~EventListener} [third]
767    *        The event handler if `first` is a `Component` and `second` is an event name
768    *        or an Array of event names.
769    *
770    * @return {Component}
771    *         Returns itself; method can be chained.
772    */
773
774
775   Component.prototype.one = function one(first, second, third) {
776     var _this3 = this,
777         _arguments = arguments;
778
779     if (typeof first === 'string' || Array.isArray(first)) {
780       Events.one(this.el_, first, Fn.bind(this, second));
781     } else {
782       var target = first;
783       var type = second;
784       var fn = Fn.bind(this, third);
785
786       var newFunc = function newFunc() {
787         _this3.off(target, type, newFunc);
788         fn.apply(null, _arguments);
789       };
790
791       // Keep the same function ID so we can remove it later
792       newFunc.guid = fn.guid;
793
794       this.on(target, type, newFunc);
795     }
796
797     return this;
798   };
799
800   /**
801    * Trigger an event on an element.
802    *
803    * @param {EventTarget~Event|Object|string} event
804    *        The event name, and Event, or an event-like object with a type attribute
805    *        set to the event name.
806    *
807    * @param {Object} [hash]
808    *        Data hash to pass along with the event
809    *
810    * @return {Component}
811    *         Returns itself; method can be chained.
812    */
813
814
815   Component.prototype.trigger = function trigger(event, hash) {
816     Events.trigger(this.el_, event, hash);
817     return this;
818   };
819
820   /**
821    * Bind a listener to the component's ready state. If the ready event has already
822    * happened it will trigger the function immediately.
823    *
824    * @param  {Component~ReadyCallback} fn
825    *         A function to call when ready is triggered.
826    *
827    * @param  {boolean} [sync=false]
828    *         Execute the listener synchronously if `Component` is ready.
829    *
830    * @return {Component}
831    *         Returns itself; method can be chained.
832    */
833
834
835   Component.prototype.ready = function ready(fn) {
836     var sync = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
837
838     if (fn) {
839       if (this.isReady_) {
840         if (sync) {
841           fn.call(this);
842         } else {
843           // Call the function asynchronously by default for consistency
844           this.setTimeout(fn, 1);
845         }
846       } else {
847         this.readyQueue_ = this.readyQueue_ || [];
848         this.readyQueue_.push(fn);
849       }
850     }
851     return this;
852   };
853
854   /**
855    * Trigger all the ready listeners for this `Component`.
856    *
857    * @fires Component#ready
858    */
859
860
861   Component.prototype.triggerReady = function triggerReady() {
862     this.isReady_ = true;
863
864     // Ensure ready is triggerd asynchronously
865     this.setTimeout(function () {
866       var readyQueue = this.readyQueue_;
867
868       // Reset Ready Queue
869       this.readyQueue_ = [];
870
871       if (readyQueue && readyQueue.length > 0) {
872         readyQueue.forEach(function (fn) {
873           fn.call(this);
874         }, this);
875       }
876
877       // Allow for using event listeners also
878       /**
879        * Triggered when a `Component` is ready.
880        *
881        * @event Component#ready
882        * @type {EventTarget~Event}
883        */
884       this.trigger('ready');
885     }, 1);
886   };
887
888   /**
889    * Find a single DOM element matching a `selector`. This can be within the `Component`s
890    * `contentEl()` or another custom context.
891    *
892    * @param {string} selector
893    *        A valid CSS selector, which will be passed to `querySelector`.
894    *
895    * @param {Element|string} [context=this.contentEl()]
896    *        A DOM element within which to query. Can also be a selector string in
897    *        which case the first matching element will get used as context. If
898    *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
899    *        nothing it falls back to `document`.
900    *
901    * @return {Element|null}
902    *         the dom element that was found, or null
903    *
904    * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
905    */
906
907
908   Component.prototype.$ = function $(selector, context) {
909     return Dom.$(selector, context || this.contentEl());
910   };
911
912   /**
913    * Finds all DOM element matching a `selector`. This can be within the `Component`s
914    * `contentEl()` or another custom context.
915    *
916    * @param {string} selector
917    *        A valid CSS selector, which will be passed to `querySelectorAll`.
918    *
919    * @param {Element|string} [context=this.contentEl()]
920    *        A DOM element within which to query. Can also be a selector string in
921    *        which case the first matching element will get used as context. If
922    *        missing `this.contentEl()` gets used. If  `this.contentEl()` returns
923    *        nothing it falls back to `document`.
924    *
925    * @return {NodeList}
926    *         a list of dom elements that were found
927    *
928    * @see [Information on CSS Selectors](https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Getting_Started/Selectors)
929    */
930
931
932   Component.prototype.$$ = function $$(selector, context) {
933     return Dom.$$(selector, context || this.contentEl());
934   };
935
936   /**
937    * Check if a component's element has a CSS class name.
938    *
939    * @param {string} classToCheck
940    *        CSS class name to check.
941    *
942    * @return {boolean}
943    *         - True if the `Component` has the class.
944    *         - False if the `Component` does not have the class`
945    */
946
947
948   Component.prototype.hasClass = function hasClass(classToCheck) {
949     return Dom.hasElClass(this.el_, classToCheck);
950   };
951
952   /**
953    * Add a CSS class name to the `Component`s element.
954    *
955    * @param {string} classToAdd
956    *        CSS class name to add
957    *
958    * @return {Component}
959    *         Returns itself; method can be chained.
960    */
961
962
963   Component.prototype.addClass = function addClass(classToAdd) {
964     Dom.addElClass(this.el_, classToAdd);
965     return this;
966   };
967
968   /**
969    * Remove a CSS class name from the `Component`s element.
970    *
971    * @param {string} classToRemove
972    *        CSS class name to remove
973    *
974    * @return {Component}
975    *         Returns itself; method can be chained.
976    */
977
978
979   Component.prototype.removeClass = function removeClass(classToRemove) {
980     Dom.removeElClass(this.el_, classToRemove);
981     return this;
982   };
983
984   /**
985    * Add or remove a CSS class name from the component's element.
986    * - `classToToggle` gets added when {@link Component#hasClass} would return false.
987    * - `classToToggle` gets removed when {@link Component#hasClass} would return true.
988    *
989    * @param  {string} classToToggle
990    *         The class to add or remove based on (@link Component#hasClass}
991    *
992    * @param  {boolean|Dom~predicate} [predicate]
993    *         An {@link Dom~predicate} function or a boolean
994    *
995    * @return {Component}
996    *         Returns itself; method can be chained.
997    */
998
999
1000   Component.prototype.toggleClass = function toggleClass(classToToggle, predicate) {
1001     Dom.toggleElClass(this.el_, classToToggle, predicate);
1002     return this;
1003   };
1004
1005   /**
1006    * Show the `Component`s element if it is hidden by removing the
1007    * 'vjs-hidden' class name from it.
1008    *
1009    * @return {Component}
1010    *         Returns itself; method can be chained.
1011    */
1012
1013
1014   Component.prototype.show = function show() {
1015     this.removeClass('vjs-hidden');
1016     return this;
1017   };
1018
1019   /**
1020    * Hide the `Component`s element if it is currently showing by adding the
1021    * 'vjs-hidden` class name to it.
1022    *
1023    * @return {Component}
1024    *         Returns itself; method can be chained.
1025    */
1026
1027
1028   Component.prototype.hide = function hide() {
1029     this.addClass('vjs-hidden');
1030     return this;
1031   };
1032
1033   /**
1034    * Lock a `Component`s element in its visible state by adding the 'vjs-lock-showing'
1035    * class name to it. Used during fadeIn/fadeOut.
1036    *
1037    * @return {Component}
1038    *         Returns itself; method can be chained.
1039    *
1040    * @private
1041    */
1042
1043
1044   Component.prototype.lockShowing = function lockShowing() {
1045     this.addClass('vjs-lock-showing');
1046     return this;
1047   };
1048
1049   /**
1050    * Unlock a `Component`s element from its visible state by removing the 'vjs-lock-showing'
1051    * class name from it. Used during fadeIn/fadeOut.
1052    *
1053    * @return {Component}
1054    *         Returns itself; method can be chained.
1055    *
1056    * @private
1057    */
1058
1059
1060   Component.prototype.unlockShowing = function unlockShowing() {
1061     this.removeClass('vjs-lock-showing');
1062     return this;
1063   };
1064
1065   /**
1066    * Get the value of an attribute on the `Component`s element.
1067    *
1068    * @param {string} attribute
1069    *        Name of the attribute to get the value from.
1070    *
1071    * @return {string|null}
1072    *         - The value of the attribute that was asked for.
1073    *         - Can be an empty string on some browsers if the attribute does not exist
1074    *           or has no value
1075    *         - Most browsers will return null if the attibute does not exist or has
1076    *           no value.
1077    *
1078    * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute}
1079    */
1080
1081
1082   Component.prototype.getAttribute = function getAttribute(attribute) {
1083     return Dom.getAttribute(this.el_, attribute);
1084   };
1085
1086   /**
1087    * Set the value of an attribute on the `Component`'s element
1088    *
1089    * @param {string} attribute
1090    *        Name of the attribute to set.
1091    *
1092    * @param {string} value
1093    *        Value to set the attribute to.
1094    *
1095    * @return {Component}
1096    *         Returns itself; method can be chained.
1097    *
1098    * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute}
1099    */
1100
1101
1102   Component.prototype.setAttribute = function setAttribute(attribute, value) {
1103     Dom.setAttribute(this.el_, attribute, value);
1104     return this;
1105   };
1106
1107   /**
1108    * Remove an attribute from the `Component`s element.
1109    *
1110    * @param {string} attribute
1111    *        Name of the attribute to remove.
1112    *
1113    * @return {Component}
1114    *         Returns itself; method can be chained.
1115    *
1116    * @see [DOM API]{@link https://developer.mozilla.org/en-US/docs/Web/API/Element/removeAttribute}
1117    */
1118
1119
1120   Component.prototype.removeAttribute = function removeAttribute(attribute) {
1121     Dom.removeAttribute(this.el_, attribute);
1122     return this;
1123   };
1124
1125   /**
1126    * Get or set the width of the component based upon the CSS styles.
1127    * See {@link Component#dimension} for more detailed information.
1128    *
1129    * @param {number|string} [num]
1130    *        The width that you want to set postfixed with '%', 'px' or nothing.
1131    *
1132    * @param {boolean} [skipListeners]
1133    *        Skip the resize event trigger
1134    *
1135    * @return {Component|number|string}
1136    *         - The width when getting, zero if there is no width. Can be a string
1137    *           postpixed with '%' or 'px'.
1138    *         - Returns itself when setting; method can be chained.
1139    */
1140
1141
1142   Component.prototype.width = function width(num, skipListeners) {
1143     return this.dimension('width', num, skipListeners);
1144   };
1145
1146   /**
1147    * Get or set the height of the component based upon the CSS styles.
1148    * See {@link Component#dimension} for more detailed information.
1149    *
1150    * @param {number|string} [num]
1151    *        The height that you want to set postfixed with '%', 'px' or nothing.
1152    *
1153    * @param {boolean} [skipListeners]
1154    *        Skip the resize event trigger
1155    *
1156    * @return {Component|number|string}
1157    *         - The width when getting, zero if there is no width. Can be a string
1158    *           postpixed with '%' or 'px'.
1159    *         - Returns itself when setting; method can be chained.
1160    */
1161
1162
1163   Component.prototype.height = function height(num, skipListeners) {
1164     return this.dimension('height', num, skipListeners);
1165   };
1166
1167   /**
1168    * Set both the width and height of the `Component` element at the same time.
1169    *
1170    * @param  {number|string} width
1171    *         Width to set the `Component`s element to.
1172    *
1173    * @param  {number|string} height
1174    *         Height to set the `Component`s element to.
1175    *
1176    * @return {Component}
1177    *         Returns itself; method can be chained.
1178    */
1179
1180
1181   Component.prototype.dimensions = function dimensions(width, height) {
1182     // Skip resize listeners on width for optimization
1183     return this.width(width, true).height(height);
1184   };
1185
1186   /**
1187    * Get or set width or height of the `Component` element. This is the shared code
1188    * for the {@link Component#width} and {@link Component#height}.
1189    *
1190    * Things to know:
1191    * - If the width or height in an number this will return the number postfixed with 'px'.
1192    * - If the width/height is a percent this will return the percent postfixed with '%'
1193    * - Hidden elements have a width of 0 with `window.getComputedStyle`. This function
1194    *   defaults to the `Component`s `style.width` and falls back to `window.getComputedStyle`.
1195    *   See [this]{@link http://www.foliotek.com/devblog/getting-the-width-of-a-hidden-element-with-jquery-using-width/}
1196    *   for more information
1197    * - If you want the computed style of the component, use {@link Component#currentWidth}
1198    *   and {@link {Component#currentHeight}
1199    *
1200    * @fires Component#resize
1201    *
1202    * @param {string} widthOrHeight
1203    8        'width' or 'height'
1204    *
1205    * @param  {number|string} [num]
1206    8         New dimension
1207    *
1208    * @param  {boolean} [skipListeners]
1209    *         Skip resize event trigger
1210    *
1211    * @return {Component}
1212    *         - the dimension when getting or 0 if unset
1213    *         - Returns itself when setting; method can be chained.
1214    */
1215
1216
1217   Component.prototype.dimension = function dimension(widthOrHeight, num, skipListeners) {
1218     if (num !== undefined) {
1219       // Set to zero if null or literally NaN (NaN !== NaN)
1220       if (num === null || num !== num) {
1221         num = 0;
1222       }
1223
1224       // Check if using css width/height (% or px) and adjust
1225       if (('' + num).indexOf('%') !== -1 || ('' + num).indexOf('px') !== -1) {
1226         this.el_.style[widthOrHeight] = num;
1227       } else if (num === 'auto') {
1228         this.el_.style[widthOrHeight] = '';
1229       } else {
1230         this.el_.style[widthOrHeight] = num + 'px';
1231       }
1232
1233       // skipListeners allows us to avoid triggering the resize event when setting both width and height
1234       if (!skipListeners) {
1235         /**
1236          * Triggered when a component is resized.
1237          *
1238          * @event Component#resize
1239          * @type {EventTarget~Event}
1240          */
1241         this.trigger('resize');
1242       }
1243
1244       // Return component
1245       return this;
1246     }
1247
1248     // Not setting a value, so getting it
1249     // Make sure element exists
1250     if (!this.el_) {
1251       return 0;
1252     }
1253
1254     // Get dimension value from style
1255     var val = this.el_.style[widthOrHeight];
1256     var pxIndex = val.indexOf('px');
1257
1258     if (pxIndex !== -1) {
1259       // Return the pixel value with no 'px'
1260       return parseInt(val.slice(0, pxIndex), 10);
1261     }
1262
1263     // No px so using % or no style was set, so falling back to offsetWidth/height
1264     // If component has display:none, offset will return 0
1265     // TODO: handle display:none and no dimension style using px
1266     return parseInt(this.el_['offset' + (0, _toTitleCase2['default'])(widthOrHeight)], 10);
1267   };
1268
1269   /**
1270    * Get the width or the height of the `Component` elements computed style. Uses
1271    * `window.getComputedStyle`.
1272    *
1273    * @param {string} widthOrHeight
1274    *        A string containing 'width' or 'height'. Whichever one you want to get.
1275    *
1276    * @return {number}
1277    *         The dimension that gets asked for or 0 if nothing was set
1278    *         for that dimension.
1279    */
1280
1281
1282   Component.prototype.currentDimension = function currentDimension(widthOrHeight) {
1283     var computedWidthOrHeight = 0;
1284
1285     if (widthOrHeight !== 'width' && widthOrHeight !== 'height') {
1286       throw new Error('currentDimension only accepts width or height value');
1287     }
1288
1289     if (typeof _window2['default'].getComputedStyle === 'function') {
1290       var computedStyle = _window2['default'].getComputedStyle(this.el_);
1291
1292       computedWidthOrHeight = computedStyle.getPropertyValue(widthOrHeight) || computedStyle[widthOrHeight];
1293     }
1294
1295     // remove 'px' from variable and parse as integer
1296     computedWidthOrHeight = parseFloat(computedWidthOrHeight);
1297
1298     // if the computed value is still 0, it's possible that the browser is lying
1299     // and we want to check the offset values.
1300     // This code also runs on IE8 and wherever getComputedStyle doesn't exist.
1301     if (computedWidthOrHeight === 0) {
1302       var rule = 'offset' + (0, _toTitleCase2['default'])(widthOrHeight);
1303
1304       computedWidthOrHeight = this.el_[rule];
1305     }
1306
1307     return computedWidthOrHeight;
1308   };
1309
1310   /**
1311    * An object that contains width and height values of the `Component`s
1312    * computed style. Uses `window.getComputedStyle`.
1313    *
1314    * @typedef {Object} Component~DimensionObject
1315    *
1316    * @property {number} width
1317    *           The width of the `Component`s computed style.
1318    *
1319    * @property {number} height
1320    *           The height of the `Component`s computed style.
1321    */
1322
1323   /**
1324    * Get an object that contains width and height values of the `Component`s
1325    * computed style.
1326    *
1327    * @return {Component~DimensionObject}
1328    *         The dimensions of the components element
1329    */
1330
1331
1332   Component.prototype.currentDimensions = function currentDimensions() {
1333     return {
1334       width: this.currentDimension('width'),
1335       height: this.currentDimension('height')
1336     };
1337   };
1338
1339   /**
1340    * Get the width of the `Component`s computed style. Uses `window.getComputedStyle`.
1341    *
1342    * @return {number} width
1343    *           The width of the `Component`s computed style.
1344    */
1345
1346
1347   Component.prototype.currentWidth = function currentWidth() {
1348     return this.currentDimension('width');
1349   };
1350
1351   /**
1352    * Get the height of the `Component`s computed style. Uses `window.getComputedStyle`.
1353    *
1354    * @return {number} height
1355    *           The height of the `Component`s computed style.
1356    */
1357
1358
1359   Component.prototype.currentHeight = function currentHeight() {
1360     return this.currentDimension('height');
1361   };
1362
1363   /**
1364    * Set the focus to this component
1365    */
1366
1367
1368   Component.prototype.focus = function focus() {
1369     this.el_.focus();
1370   };
1371
1372   /**
1373    * Remove the focus from this component
1374    */
1375
1376
1377   Component.prototype.blur = function blur() {
1378     this.el_.blur();
1379   };
1380
1381   /**
1382    * Emit a 'tap' events when touch event support gets detected. This gets used to
1383    * support toggling the controls through a tap on the video. They get enabled
1384    * because every sub-component would have extra overhead otherwise.
1385    *
1386    * @private
1387    * @fires Component#tap
1388    * @listens Component#touchstart
1389    * @listens Component#touchmove
1390    * @listens Component#touchleave
1391    * @listens Component#touchcancel
1392    * @listens Component#touchend
1393     */
1394
1395
1396   Component.prototype.emitTapEvents = function emitTapEvents() {
1397     // Track the start time so we can determine how long the touch lasted
1398     var touchStart = 0;
1399     var firstTouch = null;
1400
1401     // Maximum movement allowed during a touch event to still be considered a tap
1402     // Other popular libs use anywhere from 2 (hammer.js) to 15,
1403     // so 10 seems like a nice, round number.
1404     var tapMovementThreshold = 10;
1405
1406     // The maximum length a touch can be while still being considered a tap
1407     var touchTimeThreshold = 200;
1408
1409     var couldBeTap = void 0;
1410
1411     this.on('touchstart', function (event) {
1412       // If more than one finger, don't consider treating this as a click
1413       if (event.touches.length === 1) {
1414         // Copy pageX/pageY from the object
1415         firstTouch = {
1416           pageX: event.touches[0].pageX,
1417           pageY: event.touches[0].pageY
1418         };
1419         // Record start time so we can detect a tap vs. "touch and hold"
1420         touchStart = new Date().getTime();
1421         // Reset couldBeTap tracking
1422         couldBeTap = true;
1423       }
1424     });
1425
1426     this.on('touchmove', function (event) {
1427       // If more than one finger, don't consider treating this as a click
1428       if (event.touches.length > 1) {
1429         couldBeTap = false;
1430       } else if (firstTouch) {
1431         // Some devices will throw touchmoves for all but the slightest of taps.
1432         // So, if we moved only a small distance, this could still be a tap
1433         var xdiff = event.touches[0].pageX - firstTouch.pageX;
1434         var ydiff = event.touches[0].pageY - firstTouch.pageY;
1435         var touchDistance = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
1436
1437         if (touchDistance > tapMovementThreshold) {
1438           couldBeTap = false;
1439         }
1440       }
1441     });
1442
1443     var noTap = function noTap() {
1444       couldBeTap = false;
1445     };
1446
1447     // TODO: Listen to the original target. http://youtu.be/DujfpXOKUp8?t=13m8s
1448     this.on('touchleave', noTap);
1449     this.on('touchcancel', noTap);
1450
1451     // When the touch ends, measure how long it took and trigger the appropriate
1452     // event
1453     this.on('touchend', function (event) {
1454       firstTouch = null;
1455       // Proceed only if the touchmove/leave/cancel event didn't happen
1456       if (couldBeTap === true) {
1457         // Measure how long the touch lasted
1458         var touchTime = new Date().getTime() - touchStart;
1459
1460         // Make sure the touch was less than the threshold to be considered a tap
1461         if (touchTime < touchTimeThreshold) {
1462           // Don't let browser turn this into a click
1463           event.preventDefault();
1464           /**
1465            * Triggered when a `Component` is tapped.
1466            *
1467            * @event Component#tap
1468            * @type {EventTarget~Event}
1469            */
1470           this.trigger('tap');
1471           // It may be good to copy the touchend event object and change the
1472           // type to tap, if the other event properties aren't exact after
1473           // Events.fixEvent runs (e.g. event.target)
1474         }
1475       }
1476     });
1477   };
1478
1479   /**
1480    * This function reports user activity whenever touch events happen. This can get
1481    * turned off by any sub-components that wants touch events to act another way.
1482    *
1483    * Report user touch activity when touch events occur. User activity gets used to
1484    * determine when controls should show/hide. It is simple when it comes to mouse
1485    * events, because any mouse event should show the controls. So we capture mouse
1486    * events that bubble up to the player and report activity when that happens.
1487    * With touch events it isn't as easy as `touchstart` and `touchend` toggle player
1488    * controls. So touch events can't help us at the player level either.
1489    *
1490    * User activity gets checked asynchronously. So what could happen is a tap event
1491    * on the video turns the controls off. Then the `touchend` event bubbles up to
1492    * the player. Which, if it reported user activity, would turn the controls right
1493    * back on. We also don't want to completely block touch events from bubbling up.
1494    * Furthermore a `touchmove` event and anything other than a tap, should not turn
1495    * controls back on.
1496    *
1497    * @listens Component#touchstart
1498    * @listens Component#touchmove
1499    * @listens Component#touchend
1500    * @listens Component#touchcancel
1501    */
1502
1503
1504   Component.prototype.enableTouchActivity = function enableTouchActivity() {
1505     // Don't continue if the root player doesn't support reporting user activity
1506     if (!this.player() || !this.player().reportUserActivity) {
1507       return;
1508     }
1509
1510     // listener for reporting that the user is active
1511     var report = Fn.bind(this.player(), this.player().reportUserActivity);
1512
1513     var touchHolding = void 0;
1514
1515     this.on('touchstart', function () {
1516       report();
1517       // For as long as the they are touching the device or have their mouse down,
1518       // we consider them active even if they're not moving their finger or mouse.
1519       // So we want to continue to update that they are active
1520       this.clearInterval(touchHolding);
1521       // report at the same interval as activityCheck
1522       touchHolding = this.setInterval(report, 250);
1523     });
1524
1525     var touchEnd = function touchEnd(event) {
1526       report();
1527       // stop the interval that maintains activity if the touch is holding
1528       this.clearInterval(touchHolding);
1529     };
1530
1531     this.on('touchmove', report);
1532     this.on('touchend', touchEnd);
1533     this.on('touchcancel', touchEnd);
1534   };
1535
1536   /**
1537    * A callback that has no parameters and is bound into `Component`s context.
1538    *
1539    * @callback Component~GenericCallback
1540    * @this Component
1541    */
1542
1543   /**
1544    * Creates a function that runs after an `x` millisecond timeout. This function is a
1545    * wrapper around `window.setTimeout`. There are a few reasons to use this one
1546    * instead though:
1547    * 1. It gets cleared via  {@link Component#clearTimeout} when
1548    *    {@link Component#dispose} gets called.
1549    * 2. The function callback will gets turned into a {@link Component~GenericCallback}
1550    *
1551    * > Note: You can use `window.clearTimeout` on the id returned by this function. This
1552    *         will cause its dispose listener not to get cleaned up! Please use
1553    *         {@link Component#clearTimeout} or {@link Component#dispose}.
1554    *
1555    * @param {Component~GenericCallback} fn
1556    *        The function that will be run after `timeout`.
1557    *
1558    * @param {number} timeout
1559    *        Timeout in milliseconds to delay before executing the specified function.
1560    *
1561    * @return {number}
1562    *         Returns a timeout ID that gets used to identify the timeout. It can also
1563    *         get used in {@link Component#clearTimeout} to clear the timeout that
1564    *         was set.
1565    *
1566    * @listens Component#dispose
1567    * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout}
1568    */
1569
1570
1571   Component.prototype.setTimeout = function setTimeout(fn, timeout) {
1572     fn = Fn.bind(this, fn);
1573
1574     var timeoutId = _window2['default'].setTimeout(fn, timeout);
1575     var disposeFn = function disposeFn() {
1576       this.clearTimeout(timeoutId);
1577     };
1578
1579     disposeFn.guid = 'vjs-timeout-' + timeoutId;
1580
1581     this.on('dispose', disposeFn);
1582
1583     return timeoutId;
1584   };
1585
1586   /**
1587    * Clears a timeout that gets created via `window.setTimeout` or
1588    * {@link Component#setTimeout}. If you set a timeout via {@link Component#setTimeout}
1589    * use this function instead of `window.clearTimout`. If you don't your dispose
1590    * listener will not get cleaned up until {@link Component#dispose}!
1591    *
1592    * @param {number} timeoutId
1593    *        The id of the timeout to clear. The return value of
1594    *        {@link Component#setTimeout} or `window.setTimeout`.
1595    *
1596    * @return {number}
1597    *         Returns the timeout id that was cleared.
1598    *
1599    * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearTimeout}
1600    */
1601
1602
1603   Component.prototype.clearTimeout = function clearTimeout(timeoutId) {
1604     _window2['default'].clearTimeout(timeoutId);
1605
1606     var disposeFn = function disposeFn() {};
1607
1608     disposeFn.guid = 'vjs-timeout-' + timeoutId;
1609
1610     this.off('dispose', disposeFn);
1611
1612     return timeoutId;
1613   };
1614
1615   /**
1616    * Creates a function that gets run every `x` milliseconds. This function is a wrapper
1617    * around `window.setInterval`. There are a few reasons to use this one instead though.
1618    * 1. It gets cleared via  {@link Component#clearInterval} when
1619    *    {@link Component#dispose} gets called.
1620    * 2. The function callback will be a {@link Component~GenericCallback}
1621    *
1622    * @param {Component~GenericCallback} fn
1623    *        The function to run every `x` seconds.
1624    *
1625    * @param {number} interval
1626    *        Execute the specified function every `x` milliseconds.
1627    *
1628    * @return {number}
1629    *         Returns an id that can be used to identify the interval. It can also be be used in
1630    *         {@link Component#clearInterval} to clear the interval.
1631    *
1632    * @listens Component#dispose
1633    * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setInterval}
1634    */
1635
1636
1637   Component.prototype.setInterval = function setInterval(fn, interval) {
1638     fn = Fn.bind(this, fn);
1639
1640     var intervalId = _window2['default'].setInterval(fn, interval);
1641
1642     var disposeFn = function disposeFn() {
1643       this.clearInterval(intervalId);
1644     };
1645
1646     disposeFn.guid = 'vjs-interval-' + intervalId;
1647
1648     this.on('dispose', disposeFn);
1649
1650     return intervalId;
1651   };
1652
1653   /**
1654    * Clears an interval that gets created via `window.setInterval` or
1655    * {@link Component#setInterval}. If you set an inteval via {@link Component#setInterval}
1656    * use this function instead of `window.clearInterval`. If you don't your dispose
1657    * listener will not get cleaned up until {@link Component#dispose}!
1658    *
1659    * @param {number} intervalId
1660    *        The id of the interval to clear. The return value of
1661    *        {@link Component#setInterval} or `window.setInterval`.
1662    *
1663    * @return {number}
1664    *         Returns the interval id that was cleared.
1665    *
1666    * @see [Similar to]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/clearInterval}
1667    */
1668
1669
1670   Component.prototype.clearInterval = function clearInterval(intervalId) {
1671     _window2['default'].clearInterval(intervalId);
1672
1673     var disposeFn = function disposeFn() {};
1674
1675     disposeFn.guid = 'vjs-interval-' + intervalId;
1676
1677     this.off('dispose', disposeFn);
1678
1679     return intervalId;
1680   };
1681
1682   /**
1683    * Register a `Component` with `videojs` given the name and the component.
1684    *
1685    * > NOTE: {@link Tech}s should not be registered as a `Component`. {@link Tech}s
1686    *         should be registered using {@link Tech.registerTech} or
1687    *         {@link videojs:videojs.registerTech}.
1688    *
1689    * > NOTE: This function can also be seen on videojs as
1690    *         {@link videojs:videojs.registerComponent}.
1691    *
1692    * @param {string} name
1693    *        The name of the `Component` to register.
1694    *
1695    * @param {Component} comp
1696    *        The `Component` class to register.
1697    *
1698    * @return {Component}
1699    *         The `Component` that was registered.
1700    */
1701
1702
1703   Component.registerComponent = function registerComponent(name, comp) {
1704     if (!name) {
1705       return;
1706     }
1707
1708     name = (0, _toTitleCase2['default'])(name);
1709
1710     if (!Component.components_) {
1711       Component.components_ = {};
1712     }
1713
1714     if (name === 'Player' && Component.components_[name]) {
1715       var Player = Component.components_[name];
1716
1717       // If we have players that were disposed, then their name will still be
1718       // in Players.players. So, we must loop through and verify that the value
1719       // for each item is not null. This allows registration of the Player component
1720       // after all players have been disposed or before any were created.
1721       if (Player.players && Object.keys(Player.players).length > 0 && Object.keys(Player.players).map(function (playerName) {
1722         return Player.players[playerName];
1723       }).every(Boolean)) {
1724         throw new Error('Can not register Player component after player has been created');
1725       }
1726     }
1727
1728     Component.components_[name] = comp;
1729
1730     return comp;
1731   };
1732
1733   /**
1734    * Get a `Component` based on the name it was registered with.
1735    *
1736    * @param {string} name
1737    *        The Name of the component to get.
1738    *
1739    * @return {Component}
1740    *         The `Component` that got registered under the given name.
1741    *
1742    * @deprecated In `videojs` 6 this will not return `Component`s that were not
1743    *             registered using {@link Component.registerComponent}. Currently we
1744    *             check the global `videojs` object for a `Component` name and
1745    *             return that if it exists.
1746    */
1747
1748
1749   Component.getComponent = function getComponent(name) {
1750     if (!name) {
1751       return;
1752     }
1753
1754     name = (0, _toTitleCase2['default'])(name);
1755
1756     if (Component.components_ && Component.components_[name]) {
1757       return Component.components_[name];
1758     }
1759
1760     if (_window2['default'] && _window2['default'].videojs && _window2['default'].videojs[name]) {
1761       _log2['default'].warn('The ' + name + ' component was added to the videojs object when it should be registered using videojs.registerComponent(name, component)');
1762
1763       return _window2['default'].videojs[name];
1764     }
1765   };
1766
1767   /**
1768    * Sets up the constructor using the supplied init method or uses the init of the
1769    * parent object.
1770    *
1771    * @param {Object} [props={}]
1772    *        An object of properties.
1773    *
1774    * @return {Object}
1775    *         the extended object.
1776    *
1777    * @deprecated since version 5
1778    */
1779
1780
1781   Component.extend = function extend(props) {
1782     props = props || {};
1783
1784     _log2['default'].warn('Component.extend({}) has been deprecated, ' + ' use videojs.extend(Component, {}) instead');
1785
1786     // Set up the constructor using the supplied init method
1787     // or using the init of the parent object
1788     // Make sure to check the unobfuscated version for external libs
1789     var init = props.init || props.init || this.prototype.init || this.prototype.init || function () {};
1790     // In Resig's simple class inheritance (previously used) the constructor
1791     //  is a function that calls `this.init.apply(arguments)`
1792     // However that would prevent us from using `ParentObject.call(this);`
1793     //  in a Child constructor because the `this` in `this.init`
1794     //  would still refer to the Child and cause an infinite loop.
1795     // We would instead have to do
1796     //    `ParentObject.prototype.init.apply(this, arguments);`
1797     //  Bleh. We're not creating a _super() function, so it's good to keep
1798     //  the parent constructor reference simple.
1799     var subObj = function subObj() {
1800       init.apply(this, arguments);
1801     };
1802
1803     // Inherit from this object's prototype
1804     subObj.prototype = Object.create(this.prototype);
1805     // Reset the constructor property for subObj otherwise
1806     // instances of subObj would have the constructor of the parent Object
1807     subObj.prototype.constructor = subObj;
1808
1809     // Make the class extendable
1810     subObj.extend = Component.extend;
1811
1812     // Extend subObj's prototype with functions and other properties from props
1813     for (var name in props) {
1814       if (props.hasOwnProperty(name)) {
1815         subObj.prototype[name] = props[name];
1816       }
1817     }
1818
1819     return subObj;
1820   };
1821
1822   return Component;
1823 }();
1824
1825 Component.registerComponent('Component', Component);
1826 exports['default'] = Component;