a7984ead69f6ee48ffd0569afbd7eea29f59dd77
[yaffs-website] / node_modules / video.js / es5 / tech / tech.js
1 'use strict';
2
3 exports.__esModule = true;
4
5 var _component = require('../component');
6
7 var _component2 = _interopRequireDefault(_component);
8
9 var _htmlTrackElement = require('../tracks/html-track-element');
10
11 var _htmlTrackElement2 = _interopRequireDefault(_htmlTrackElement);
12
13 var _htmlTrackElementList = require('../tracks/html-track-element-list');
14
15 var _htmlTrackElementList2 = _interopRequireDefault(_htmlTrackElementList);
16
17 var _mergeOptions = require('../utils/merge-options.js');
18
19 var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
20
21 var _textTrack = require('../tracks/text-track');
22
23 var _textTrack2 = _interopRequireDefault(_textTrack);
24
25 var _textTrackList = require('../tracks/text-track-list');
26
27 var _textTrackList2 = _interopRequireDefault(_textTrackList);
28
29 var _videoTrackList = require('../tracks/video-track-list');
30
31 var _videoTrackList2 = _interopRequireDefault(_videoTrackList);
32
33 var _audioTrackList = require('../tracks/audio-track-list');
34
35 var _audioTrackList2 = _interopRequireDefault(_audioTrackList);
36
37 var _fn = require('../utils/fn.js');
38
39 var Fn = _interopRequireWildcard(_fn);
40
41 var _log = require('../utils/log.js');
42
43 var _log2 = _interopRequireDefault(_log);
44
45 var _timeRanges = require('../utils/time-ranges.js');
46
47 var _buffer = require('../utils/buffer.js');
48
49 var _mediaError = require('../media-error.js');
50
51 var _mediaError2 = _interopRequireDefault(_mediaError);
52
53 var _window = require('global/window');
54
55 var _window2 = _interopRequireDefault(_window);
56
57 var _document = require('global/document');
58
59 var _document2 = _interopRequireDefault(_document);
60
61 var _obj = require('../utils/obj');
62
63 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; } }
64
65 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
66
67 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
68
69 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; }
70
71 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; } /**
72                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 * @file tech.js
73                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 */
74
75 /**
76  * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string
77  * that just contains the src url alone.
78  * * `var SourceObject = {src: 'http://ex.com/video.mp4', type: 'video/mp4'};`
79    * `var SourceString = 'http://example.com/some-video.mp4';`
80  *
81  * @typedef {Object|string} Tech~SourceObject
82  *
83  * @property {string} src
84  *           The url to the source
85  *
86  * @property {string} type
87  *           The mime type of the source
88  */
89
90 /**
91  * A function used by {@link Tech} to create a new {@link TextTrack}.
92  *
93  * @param {Tech} self
94  *        An instance of the Tech class.
95  *
96  * @param {string} kind
97  *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
98  *
99  * @param {string} [label]
100  *        Label to identify the text track
101  *
102  * @param {string} [language]
103  *        Two letter language abbreviation
104  *
105  * @param {Object} [options={}]
106  *        An object with additional text track options
107  *
108  * @return {TextTrack}
109  *          The text track that was created.
110  */
111 function createTrackHelper(self, kind, label, language) {
112   var options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
113
114   var tracks = self.textTracks();
115
116   options.kind = kind;
117
118   if (label) {
119     options.label = label;
120   }
121   if (language) {
122     options.language = language;
123   }
124   options.tech = self;
125
126   var track = new _textTrack2['default'](options);
127
128   tracks.addTrack_(track);
129
130   return track;
131 }
132
133 /**
134  * This is the base class for media playback technology controllers, such as
135  * {@link Flash} and {@link HTML5}
136  *
137  * @extends Component
138  */
139
140 var Tech = function (_Component) {
141   _inherits(Tech, _Component);
142
143   /**
144    * Create an instance of this Tech.
145    *
146    * @param {Object} [options]
147    *        The key/value store of player options.
148    *
149    * @param {Component~ReadyCallback} ready
150    *        Callback function to call when the `HTML5` Tech is ready.
151    */
152   function Tech() {
153     var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
154     var ready = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
155
156     _classCallCheck(this, Tech);
157
158     // we don't want the tech to report user activity automatically.
159     // This is done manually in addControlsListeners
160     options.reportTouchActivity = false;
161
162     // keep track of whether the current source has played at all to
163     // implement a very limited played()
164     var _this = _possibleConstructorReturn(this, _Component.call(this, null, options, ready));
165
166     _this.hasStarted_ = false;
167     _this.on('playing', function () {
168       this.hasStarted_ = true;
169     });
170     _this.on('loadstart', function () {
171       this.hasStarted_ = false;
172     });
173
174     _this.textTracks_ = options.textTracks;
175     _this.videoTracks_ = options.videoTracks;
176     _this.audioTracks_ = options.audioTracks;
177
178     // Manually track progress in cases where the browser/flash player doesn't report it.
179     if (!_this.featuresProgressEvents) {
180       _this.manualProgressOn();
181     }
182
183     // Manually track timeupdates in cases where the browser/flash player doesn't report it.
184     if (!_this.featuresTimeupdateEvents) {
185       _this.manualTimeUpdatesOn();
186     }
187
188     ['Text', 'Audio', 'Video'].forEach(function (track) {
189       if (options['native' + track + 'Tracks'] === false) {
190         _this['featuresNative' + track + 'Tracks'] = false;
191       }
192     });
193
194     if (options.nativeCaptions === false) {
195       _this.featuresNativeTextTracks = false;
196     }
197
198     if (!_this.featuresNativeTextTracks) {
199       _this.emulateTextTracks();
200     }
201
202     _this.autoRemoteTextTracks_ = new _textTrackList2['default']();
203
204     _this.initTextTrackListeners();
205     _this.initTrackListeners();
206
207     // Turn on component tap events only if not using native controls
208     if (!options.nativeControlsForTouch) {
209       _this.emitTapEvents();
210     }
211
212     if (_this.constructor) {
213       _this.name_ = _this.constructor.name || 'Unknown Tech';
214     }
215     return _this;
216   }
217
218   /* Fallbacks for unsupported event types
219   ================================================================================ */
220
221   /**
222    * Polyfill the `progress` event for browsers that don't support it natively.
223    *
224    * @see {@link Tech#trackProgress}
225    */
226
227
228   Tech.prototype.manualProgressOn = function manualProgressOn() {
229     this.on('durationchange', this.onDurationChange);
230
231     this.manualProgress = true;
232
233     // Trigger progress watching when a source begins loading
234     this.one('ready', this.trackProgress);
235   };
236
237   /**
238    * Turn off the polyfill for `progress` events that was created in
239    * {@link Tech#manualProgressOn}
240    */
241
242
243   Tech.prototype.manualProgressOff = function manualProgressOff() {
244     this.manualProgress = false;
245     this.stopTrackingProgress();
246
247     this.off('durationchange', this.onDurationChange);
248   };
249
250   /**
251    * This is used to trigger a `progress` event when the buffered percent changes. It
252    * sets an interval function that will be called every 500 milliseconds to check if the
253    * buffer end percent has changed.
254    *
255    * > This function is called by {@link Tech#manualProgressOn}
256    *
257    * @param {EventTarget~Event} event
258    *        The `ready` event that caused this to run.
259    *
260    * @listens Tech#ready
261    * @fires Tech#progress
262    */
263
264
265   Tech.prototype.trackProgress = function trackProgress(event) {
266     this.stopTrackingProgress();
267     this.progressInterval = this.setInterval(Fn.bind(this, function () {
268       // Don't trigger unless buffered amount is greater than last time
269
270       var numBufferedPercent = this.bufferedPercent();
271
272       if (this.bufferedPercent_ !== numBufferedPercent) {
273         /**
274          * See {@link Player#progress}
275          *
276          * @event Tech#progress
277          * @type {EventTarget~Event}
278          */
279         this.trigger('progress');
280       }
281
282       this.bufferedPercent_ = numBufferedPercent;
283
284       if (numBufferedPercent === 1) {
285         this.stopTrackingProgress();
286       }
287     }), 500);
288   };
289
290   /**
291    * Update our internal duration on a `durationchange` event by calling
292    * {@link Tech#duration}.
293    *
294    * @param {EventTarget~Event} event
295    *        The `durationchange` event that caused this to run.
296    *
297    * @listens Tech#durationchange
298    */
299
300
301   Tech.prototype.onDurationChange = function onDurationChange(event) {
302     this.duration_ = this.duration();
303   };
304
305   /**
306    * Get and create a `TimeRange` object for buffering.
307    *
308    * @return {TimeRange}
309    *         The time range object that was created.
310    */
311
312
313   Tech.prototype.buffered = function buffered() {
314     return (0, _timeRanges.createTimeRange)(0, 0);
315   };
316
317   /**
318    * Get the percentage of the current video that is currently buffered.
319    *
320    * @return {number}
321    *         A number from 0 to 1 that represents the decimal percentage of the
322    *         video that is buffered.
323    *
324    */
325
326
327   Tech.prototype.bufferedPercent = function bufferedPercent() {
328     return (0, _buffer.bufferedPercent)(this.buffered(), this.duration_);
329   };
330
331   /**
332    * Turn off the polyfill for `progress` events that was created in
333    * {@link Tech#manualProgressOn}
334    * Stop manually tracking progress events by clearing the interval that was set in
335    * {@link Tech#trackProgress}.
336    */
337
338
339   Tech.prototype.stopTrackingProgress = function stopTrackingProgress() {
340     this.clearInterval(this.progressInterval);
341   };
342
343   /**
344    * Polyfill the `timeupdate` event for browsers that don't support it.
345    *
346    * @see {@link Tech#trackCurrentTime}
347    */
348
349
350   Tech.prototype.manualTimeUpdatesOn = function manualTimeUpdatesOn() {
351     this.manualTimeUpdates = true;
352
353     this.on('play', this.trackCurrentTime);
354     this.on('pause', this.stopTrackingCurrentTime);
355   };
356
357   /**
358    * Turn off the polyfill for `timeupdate` events that was created in
359    * {@link Tech#manualTimeUpdatesOn}
360    */
361
362
363   Tech.prototype.manualTimeUpdatesOff = function manualTimeUpdatesOff() {
364     this.manualTimeUpdates = false;
365     this.stopTrackingCurrentTime();
366     this.off('play', this.trackCurrentTime);
367     this.off('pause', this.stopTrackingCurrentTime);
368   };
369
370   /**
371    * Sets up an interval function to track current time and trigger `timeupdate` every
372    * 250 milliseconds.
373    *
374    * @listens Tech#play
375    * @triggers Tech#timeupdate
376    */
377
378
379   Tech.prototype.trackCurrentTime = function trackCurrentTime() {
380     if (this.currentTimeInterval) {
381       this.stopTrackingCurrentTime();
382     }
383     this.currentTimeInterval = this.setInterval(function () {
384       /**
385        * Triggered at an interval of 250ms to indicated that time is passing in the video.
386        *
387        * @event Tech#timeupdate
388        * @type {EventTarget~Event}
389        */
390       this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
391
392       // 42 = 24 fps // 250 is what Webkit uses // FF uses 15
393     }, 250);
394   };
395
396   /**
397    * Stop the interval function created in {@link Tech#trackCurrentTime} so that the
398    * `timeupdate` event is no longer triggered.
399    *
400    * @listens {Tech#pause}
401    */
402
403
404   Tech.prototype.stopTrackingCurrentTime = function stopTrackingCurrentTime() {
405     this.clearInterval(this.currentTimeInterval);
406
407     // #1002 - if the video ends right before the next timeupdate would happen,
408     // the progress bar won't make it all the way to the end
409     this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
410   };
411
412   /**
413    * Turn off all event polyfills, clear the `Tech`s {@link AudioTrackList},
414    * {@link VideoTrackList}, and {@link TextTrackList}, and dispose of this Tech.
415    *
416    * @fires Component#dispose
417    */
418
419
420   Tech.prototype.dispose = function dispose() {
421
422     // clear out all tracks because we can't reuse them between techs
423     this.clearTracks(['audio', 'video', 'text']);
424
425     // Turn off any manual progress or timeupdate tracking
426     if (this.manualProgress) {
427       this.manualProgressOff();
428     }
429
430     if (this.manualTimeUpdates) {
431       this.manualTimeUpdatesOff();
432     }
433
434     _Component.prototype.dispose.call(this);
435   };
436
437   /**
438    * Clear out a single `TrackList` or an array of `TrackLists` given their names.
439    *
440    * > Note: Techs without source handlers should call this between sources for `video`
441    *         & `audio` tracks. You don't want to use them between tracks!
442    *
443    * @param {string[]|string} types
444    *        TrackList names to clear, valid names are `video`, `audio`, and
445    *        `text`.
446    */
447
448
449   Tech.prototype.clearTracks = function clearTracks(types) {
450     var _this2 = this;
451
452     types = [].concat(types);
453     // clear out all tracks because we can't reuse them between techs
454     types.forEach(function (type) {
455       var list = _this2[type + 'Tracks']() || [];
456       var i = list.length;
457
458       while (i--) {
459         var track = list[i];
460
461         if (type === 'text') {
462           _this2.removeRemoteTextTrack(track);
463         }
464         list.removeTrack_(track);
465       }
466     });
467   };
468
469   /**
470    * Remove any TextTracks added via addRemoteTextTrack that are
471    * flagged for automatic garbage collection
472    */
473
474
475   Tech.prototype.cleanupAutoTextTracks = function cleanupAutoTextTracks() {
476     var list = this.autoRemoteTextTracks_ || [];
477     var i = list.length;
478
479     while (i--) {
480       var track = list[i];
481
482       this.removeRemoteTextTrack(track);
483     }
484   };
485
486   /**
487    * Reset the tech, which will removes all sources and reset the internal readyState.
488    *
489    * @abstract
490    */
491
492
493   Tech.prototype.reset = function reset() {};
494
495   /**
496    * Get or set an error on the Tech.
497    *
498    * @param {MediaError} [err]
499    *        Error to set on the Tech
500    *
501    * @return {MediaError|null}
502    *         The current error object on the tech, or null if there isn't one.
503    */
504
505
506   Tech.prototype.error = function error(err) {
507     if (err !== undefined) {
508       this.error_ = new _mediaError2['default'](err);
509       this.trigger('error');
510     }
511     return this.error_;
512   };
513
514   /**
515    * Returns the `TimeRange`s that have been played through for the current source.
516    *
517    * > NOTE: This implementation is incomplete. It does not track the played `TimeRange`.
518    *         It only checks wether the source has played at all or not.
519    *
520    * @return {TimeRange}
521    *         - A single time range if this video has played
522    *         - An empty set of ranges if not.
523    */
524
525
526   Tech.prototype.played = function played() {
527     if (this.hasStarted_) {
528       return (0, _timeRanges.createTimeRange)(0, 0);
529     }
530     return (0, _timeRanges.createTimeRange)();
531   };
532
533   /**
534    * Causes a manual time update to occur if {@link Tech#manualTimeUpdatesOn} was
535    * previously called.
536    *
537    * @fires Tech#timeupdate
538    */
539
540
541   Tech.prototype.setCurrentTime = function setCurrentTime() {
542     // improve the accuracy of manual timeupdates
543     if (this.manualTimeUpdates) {
544       /**
545        * A manual `timeupdate` event.
546        *
547        * @event Tech#timeupdate
548        * @type {EventTarget~Event}
549        */
550       this.trigger({ type: 'timeupdate', target: this, manuallyTriggered: true });
551     }
552   };
553
554   /**
555    * Turn on listeners for {@link TextTrackList} events. This adds
556    * {@link EventTarget~EventListeners} for `texttrackchange`, `addtrack` and
557    * `removetrack`.
558    *
559    * @fires Tech#texttrackchange
560    */
561
562
563   Tech.prototype.initTextTrackListeners = function initTextTrackListeners() {
564     var textTrackListChanges = Fn.bind(this, function () {
565       /**
566        * Triggered when tracks are added or removed on the Tech {@link TextTrackList}
567        *
568        * @event Tech#texttrackchange
569        * @type {EventTarget~Event}
570        */
571       this.trigger('texttrackchange');
572     });
573
574     var tracks = this.textTracks();
575
576     if (!tracks) {
577       return;
578     }
579
580     tracks.addEventListener('removetrack', textTrackListChanges);
581     tracks.addEventListener('addtrack', textTrackListChanges);
582
583     this.on('dispose', Fn.bind(this, function () {
584       tracks.removeEventListener('removetrack', textTrackListChanges);
585       tracks.removeEventListener('addtrack', textTrackListChanges);
586     }));
587   };
588
589   /**
590    * Turn on listeners for {@link VideoTrackList} and {@link {AudioTrackList} events.
591    * This adds {@link EventTarget~EventListeners} for `addtrack`, and  `removetrack`.
592    *
593    * @fires Tech#audiotrackchange
594    * @fires Tech#videotrackchange
595    */
596
597
598   Tech.prototype.initTrackListeners = function initTrackListeners() {
599     var _this3 = this;
600
601     var trackTypes = ['video', 'audio'];
602
603     trackTypes.forEach(function (type) {
604       /**
605        * Triggered when tracks are added or removed on the Tech {@link AudioTrackList}
606        *
607        * @event Tech#audiotrackchange
608        * @type {EventTarget~Event}
609        */
610
611       /**
612        * Triggered when tracks are added or removed on the Tech {@link VideoTrackList}
613        *
614        * @event Tech#videotrackchange
615        * @type {EventTarget~Event}
616        */
617       var trackListChanges = function trackListChanges() {
618         _this3.trigger(type + 'trackchange');
619       };
620
621       var tracks = _this3[type + 'Tracks']();
622
623       tracks.addEventListener('removetrack', trackListChanges);
624       tracks.addEventListener('addtrack', trackListChanges);
625
626       _this3.on('dispose', function () {
627         tracks.removeEventListener('removetrack', trackListChanges);
628         tracks.removeEventListener('addtrack', trackListChanges);
629       });
630     });
631   };
632
633   /**
634    * Emulate TextTracks using vtt.js if necessary
635    *
636    * @fires Tech#vttjsloaded
637    * @fires Tech#vttjserror
638    */
639
640
641   Tech.prototype.addWebVttScript_ = function addWebVttScript_() {
642     var _this4 = this;
643
644     if (_window2['default'].WebVTT) {
645       return;
646     }
647
648     // Initially, Tech.el_ is a child of a dummy-div wait until the Component system
649     // signals that the Tech is ready at which point Tech.el_ is part of the DOM
650     // before inserting the WebVTT script
651     if (_document2['default'].body.contains(this.el())) {
652       var vtt = require('videojs-vtt.js');
653
654       // load via require if available and vtt.js script location was not passed in
655       // as an option. novtt builds will turn the above require call into an empty object
656       // which will cause this if check to always fail.
657       if (!this.options_['vtt.js'] && (0, _obj.isPlain)(vtt) && Object.keys(vtt).length > 0) {
658         this.trigger('vttjsloaded');
659         return;
660       }
661
662       // load vtt.js via the script location option or the cdn of no location was
663       // passed in
664       var script = _document2['default'].createElement('script');
665
666       script.src = this.options_['vtt.js'] || 'https://vjs.zencdn.net/vttjs/0.12.3/vtt.min.js';
667       script.onload = function () {
668         /**
669          * Fired when vtt.js is loaded.
670          *
671          * @event Tech#vttjsloaded
672          * @type {EventTarget~Event}
673          */
674         _this4.trigger('vttjsloaded');
675       };
676       script.onerror = function () {
677         /**
678          * Fired when vtt.js was not loaded due to an error
679          *
680          * @event Tech#vttjsloaded
681          * @type {EventTarget~Event}
682          */
683         _this4.trigger('vttjserror');
684       };
685       this.on('dispose', function () {
686         script.onload = null;
687         script.onerror = null;
688       });
689       // but have not loaded yet and we set it to true before the inject so that
690       // we don't overwrite the injected window.WebVTT if it loads right away
691       _window2['default'].WebVTT = true;
692       this.el().parentNode.appendChild(script);
693     } else {
694       this.ready(this.addWebVttScript_);
695     }
696   };
697
698   /**
699    * Emulate texttracks
700    *
701    * @method emulateTextTracks
702    */
703
704
705   Tech.prototype.emulateTextTracks = function emulateTextTracks() {
706     var _this5 = this;
707
708     var tracks = this.textTracks();
709
710     if (!tracks) {
711       return;
712     }
713
714     var remoteTracks = this.remoteTextTracks();
715     var handleAddTrack = function handleAddTrack(e) {
716       return tracks.addTrack_(e.track);
717     };
718     var handleRemoveTrack = function handleRemoveTrack(e) {
719       return tracks.removeTrack_(e.track);
720     };
721
722     remoteTracks.on('addtrack', handleAddTrack);
723     remoteTracks.on('removetrack', handleRemoveTrack);
724
725     this.addWebVttScript_();
726
727     var updateDisplay = function updateDisplay() {
728       return _this5.trigger('texttrackchange');
729     };
730
731     var textTracksChanges = function textTracksChanges() {
732       updateDisplay();
733
734       for (var i = 0; i < tracks.length; i++) {
735         var track = tracks[i];
736
737         track.removeEventListener('cuechange', updateDisplay);
738         if (track.mode === 'showing') {
739           track.addEventListener('cuechange', updateDisplay);
740         }
741       }
742     };
743
744     textTracksChanges();
745     tracks.addEventListener('change', textTracksChanges);
746     tracks.addEventListener('addtrack', textTracksChanges);
747     tracks.addEventListener('removetrack', textTracksChanges);
748
749     this.on('dispose', function () {
750       remoteTracks.off('addtrack', handleAddTrack);
751       remoteTracks.off('removetrack', handleRemoveTrack);
752       tracks.removeEventListener('change', textTracksChanges);
753       tracks.removeEventListener('addtrack', textTracksChanges);
754       tracks.removeEventListener('removetrack', textTracksChanges);
755
756       for (var i = 0; i < tracks.length; i++) {
757         var track = tracks[i];
758
759         track.removeEventListener('cuechange', updateDisplay);
760       }
761     });
762   };
763
764   /**
765    * Get the `Tech`s {@link VideoTrackList}.
766    *
767    * @return {VideoTrackList}
768    *          The video track list that the Tech is currently using.
769    */
770
771
772   Tech.prototype.videoTracks = function videoTracks() {
773     this.videoTracks_ = this.videoTracks_ || new _videoTrackList2['default']();
774     return this.videoTracks_;
775   };
776
777   /**
778    * Get the `Tech`s {@link AudioTrackList}.
779    *
780    * @return {AudioTrackList}
781    *          The audio track list that the Tech is currently using.
782    */
783
784
785   Tech.prototype.audioTracks = function audioTracks() {
786     this.audioTracks_ = this.audioTracks_ || new _audioTrackList2['default']();
787     return this.audioTracks_;
788   };
789
790   /**
791    * Get the `Tech`s {@link TextTrackList}.
792    *
793    * @return {TextTrackList}
794    *          The text track list that the Tech is currently using.
795    */
796
797
798   Tech.prototype.textTracks = function textTracks() {
799     this.textTracks_ = this.textTracks_ || new _textTrackList2['default']();
800     return this.textTracks_;
801   };
802
803   /**
804    * Get the `Tech`s remote {@link TextTrackList}, which is created from elements
805    * that were added to the DOM.
806    *
807    * @return {TextTrackList}
808    *          The remote text track list that the Tech is currently using.
809    */
810
811
812   Tech.prototype.remoteTextTracks = function remoteTextTracks() {
813     this.remoteTextTracks_ = this.remoteTextTracks_ || new _textTrackList2['default']();
814     return this.remoteTextTracks_;
815   };
816
817   /**
818    * Get The `Tech`s  {HTMLTrackElementList}, which are the elements in the DOM that are
819    * being used as TextTracks.
820    *
821    * @return {HTMLTrackElementList}
822    *          The current HTML track elements that exist for the tech.
823    */
824
825
826   Tech.prototype.remoteTextTrackEls = function remoteTextTrackEls() {
827     this.remoteTextTrackEls_ = this.remoteTextTrackEls_ || new _htmlTrackElementList2['default']();
828     return this.remoteTextTrackEls_;
829   };
830
831   /**
832    * Create and returns a remote {@link TextTrack} object.
833    *
834    * @param {string} kind
835    *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata)
836    *
837    * @param {string} [label]
838    *        Label to identify the text track
839    *
840    * @param {string} [language]
841    *        Two letter language abbreviation
842    *
843    * @return {TextTrack}
844    *         The TextTrack that gets created.
845    */
846
847
848   Tech.prototype.addTextTrack = function addTextTrack(kind, label, language) {
849     if (!kind) {
850       throw new Error('TextTrack kind is required but was not provided');
851     }
852
853     return createTrackHelper(this, kind, label, language);
854   };
855
856   /**
857    * Create an emulated TextTrack for use by addRemoteTextTrack
858    *
859    * This is intended to be overridden by classes that inherit from
860    * Tech in order to create native or custom TextTracks.
861    *
862    * @param {Object} options
863    *        The object should contain the options to initialize the TextTrack with.
864    *
865    * @param {string} [options.kind]
866    *        `TextTrack` kind (subtitles, captions, descriptions, chapters, or metadata).
867    *
868    * @param {string} [options.label].
869    *        Label to identify the text track
870    *
871    * @param {string} [options.language]
872    *        Two letter language abbreviation.
873    *
874    * @return {HTMLTrackElement}
875    *         The track element that gets created.
876    */
877
878
879   Tech.prototype.createRemoteTextTrack = function createRemoteTextTrack(options) {
880     var track = (0, _mergeOptions2['default'])(options, {
881       tech: this
882     });
883
884     return new _htmlTrackElement2['default'](track);
885   };
886
887   /**
888    * Creates a remote text track object and returns an html track element.
889    *
890    * > Note: This can be an emulated {@link HTMLTrackElement} or a native one.
891    *
892    * @param {Object} options
893    *        See {@link Tech#createRemoteTextTrack} for more detailed properties.
894    *
895    * @param {boolean} [manualCleanup=true]
896    *        - When false: the TextTrack will be automatically removed from the video
897    *          element whenever the source changes
898    *        - When True: The TextTrack will have to be cleaned up manually
899    *
900    * @return {HTMLTrackElement}
901    *         An Html Track Element.
902    *
903    * @deprecated The default functionality for this function will be equivalent
904    *             to "manualCleanup=false" in the future. The manualCleanup parameter will
905    *             also be removed.
906    */
907
908
909   Tech.prototype.addRemoteTextTrack = function addRemoteTextTrack() {
910     var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
911     var manualCleanup = arguments[1];
912
913     var htmlTrackElement = this.createRemoteTextTrack(options);
914
915     if (manualCleanup !== true && manualCleanup !== false) {
916       // deprecation warning
917       _log2['default'].warn('Calling addRemoteTextTrack without explicitly setting the "manualCleanup" parameter to `true` is deprecated and default to `false` in future version of video.js');
918       manualCleanup = true;
919     }
920
921     // store HTMLTrackElement and TextTrack to remote list
922     this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
923     this.remoteTextTracks().addTrack_(htmlTrackElement.track);
924
925     if (manualCleanup !== true) {
926       // create the TextTrackList if it doesn't exist
927       this.autoRemoteTextTracks_.addTrack_(htmlTrackElement.track);
928     }
929
930     return htmlTrackElement;
931   };
932
933   /**
934    * Remove a remote text track from the remote `TextTrackList`.
935    *
936    * @param {TextTrack} track
937    *        `TextTrack` to remove from the `TextTrackList`
938    */
939
940
941   Tech.prototype.removeRemoteTextTrack = function removeRemoteTextTrack(track) {
942     var trackElement = this.remoteTextTrackEls().getTrackElementByTrack_(track);
943
944     // remove HTMLTrackElement and TextTrack from remote list
945     this.remoteTextTrackEls().removeTrackElement_(trackElement);
946     this.remoteTextTracks().removeTrack_(track);
947     this.autoRemoteTextTracks_.removeTrack_(track);
948   };
949
950   /**
951    * A method to set a poster from a `Tech`.
952    *
953    * @abstract
954    */
955
956
957   Tech.prototype.setPoster = function setPoster() {};
958
959   /*
960    * Check if the tech can support the given mime-type.
961    *
962    * The base tech does not support any type, but source handlers might
963    * overwrite this.
964    *
965    * @param  {string} type
966    *         The mimetype to check for support
967    *
968    * @return {string}
969    *         'probably', 'maybe', or empty string
970    *
971    * @see [Spec]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/canPlayType}
972    *
973    * @abstract
974    */
975
976
977   Tech.prototype.canPlayType = function canPlayType() {
978     return '';
979   };
980
981   /*
982    * Return whether the argument is a Tech or not.
983    * Can be passed either a Class like `Html5` or a instance like `player.tech_`
984    *
985    * @param {Object} component
986    *        The item to check
987    *
988    * @return {boolean}
989    *         Whether it is a tech or not
990    *         - True if it is a tech
991    *         - False if it is not
992    */
993
994
995   Tech.isTech = function isTech(component) {
996     return component.prototype instanceof Tech || component instanceof Tech || component === Tech;
997   };
998
999   /**
1000    * Registers a `Tech` into a shared list for videojs.
1001    *
1002    * @param {string} name
1003    *        Name of the `Tech` to register.
1004    *
1005    * @param {Object} tech
1006    *        The `Tech` class to register.
1007    */
1008
1009
1010   Tech.registerTech = function registerTech(name, tech) {
1011     if (!Tech.techs_) {
1012       Tech.techs_ = {};
1013     }
1014
1015     if (!Tech.isTech(tech)) {
1016       throw new Error('Tech ' + name + ' must be a Tech');
1017     }
1018
1019     Tech.techs_[name] = tech;
1020     return tech;
1021   };
1022
1023   /**
1024    * Get a `Tech` from the shared list by name.
1025    *
1026    * @param {string} name
1027    *        Name of the component to get
1028    *
1029    * @return {Tech|undefined}
1030    *         The `Tech` or undefined if there was no tech with the name requsted.
1031    */
1032
1033
1034   Tech.getTech = function getTech(name) {
1035     if (Tech.techs_ && Tech.techs_[name]) {
1036       return Tech.techs_[name];
1037     }
1038
1039     if (_window2['default'] && _window2['default'].videojs && _window2['default'].videojs[name]) {
1040       _log2['default'].warn('The ' + name + ' tech was added to the videojs object when it should be registered using videojs.registerTech(name, tech)');
1041       return _window2['default'].videojs[name];
1042     }
1043   };
1044
1045   return Tech;
1046 }(_component2['default']);
1047
1048 /**
1049  * List of associated text tracks.
1050  *
1051  * @type {TextTrackList}
1052  * @private
1053  */
1054
1055
1056 Tech.prototype.textTracks_; // eslint-disable-line
1057
1058 /**
1059  * List of associated audio tracks.
1060  *
1061  * @type {AudioTrackList}
1062  * @private
1063  */
1064 Tech.prototype.audioTracks_; // eslint-disable-line
1065
1066 /**
1067  * List of associated video tracks.
1068  *
1069  * @type {VideoTrackList}
1070  * @private
1071  */
1072 Tech.prototype.videoTracks_; // eslint-disable-line
1073
1074 /**
1075  * Boolean indicating wether the `Tech` supports volume control.
1076  *
1077  * @type {boolean}
1078  * @default
1079  */
1080 Tech.prototype.featuresVolumeControl = true;
1081
1082 /**
1083  * Boolean indicating wether the `Tech` support fullscreen resize control.
1084  * Resizing plugins using request fullscreen reloads the plugin
1085  *
1086  * @type {boolean}
1087  * @default
1088  */
1089 Tech.prototype.featuresFullscreenResize = false;
1090
1091 /**
1092  * Boolean indicating wether the `Tech` supports changing the speed at which the video
1093  * plays. Examples:
1094  *   - Set player to play 2x (twice) as fast
1095  *   - Set player to play 0.5x (half) as fast
1096  *
1097  * @type {boolean}
1098  * @default
1099  */
1100 Tech.prototype.featuresPlaybackRate = false;
1101
1102 /**
1103  * Boolean indicating wether the `Tech` supports the `progress` event. This is currently
1104  * not triggered by video-js-swf. This will be used to determine if
1105  * {@link Tech#manualProgressOn} should be called.
1106  *
1107  * @type {boolean}
1108  * @default
1109  */
1110 Tech.prototype.featuresProgressEvents = false;
1111
1112 /**
1113  * Boolean indicating wether the `Tech` supports the `timeupdate` event. This is currently
1114  * not triggered by video-js-swf. This will be used to determine if
1115  * {@link Tech#manualTimeUpdates} should be called.
1116  *
1117  * @type {boolean}
1118  * @default
1119  */
1120 Tech.prototype.featuresTimeupdateEvents = false;
1121
1122 /**
1123  * Boolean indicating wether the `Tech` supports the native `TextTrack`s.
1124  * This will help us integrate with native `TextTrack`s if the browser supports them.
1125  *
1126  * @type {boolean}
1127  * @default
1128  */
1129 Tech.prototype.featuresNativeTextTracks = false;
1130
1131 /**
1132  * A functional mixin for techs that want to use the Source Handler pattern.
1133  * Source handlers are scripts for handling specific formats.
1134  * The source handler pattern is used for adaptive formats (HLS, DASH) that
1135  * manually load video data and feed it into a Source Buffer (Media Source Extensions)
1136  * Example: `Tech.withSourceHandlers.call(MyTech);`
1137  *
1138  * @param {Tech} _Tech
1139  *        The tech to add source handler functions to.
1140  *
1141  * @mixes Tech~SourceHandlerAdditions
1142  */
1143 Tech.withSourceHandlers = function (_Tech) {
1144
1145   /**
1146    * Register a source handler
1147    *
1148    * @param {Function} handler
1149    *        The source handler class
1150    *
1151    * @param {number} [index]
1152    *        Register it at the following index
1153    */
1154   _Tech.registerSourceHandler = function (handler, index) {
1155     var handlers = _Tech.sourceHandlers;
1156
1157     if (!handlers) {
1158       handlers = _Tech.sourceHandlers = [];
1159     }
1160
1161     if (index === undefined) {
1162       // add to the end of the list
1163       index = handlers.length;
1164     }
1165
1166     handlers.splice(index, 0, handler);
1167   };
1168
1169   /**
1170    * Check if the tech can support the given type. Also checks the
1171    * Techs sourceHandlers.
1172    *
1173    * @param {string} type
1174    *         The mimetype to check.
1175    *
1176    * @return {string}
1177    *         'probably', 'maybe', or '' (empty string)
1178    */
1179   _Tech.canPlayType = function (type) {
1180     var handlers = _Tech.sourceHandlers || [];
1181     var can = void 0;
1182
1183     for (var i = 0; i < handlers.length; i++) {
1184       can = handlers[i].canPlayType(type);
1185
1186       if (can) {
1187         return can;
1188       }
1189     }
1190
1191     return '';
1192   };
1193
1194   /**
1195    * Returns the first source handler that supports the source.
1196    *
1197    * TODO: Answer question: should 'probably' be prioritized over 'maybe'
1198    *
1199    * @param {Tech~SourceObject} source
1200    *        The source object
1201    *
1202    * @param {Object} options
1203    *        The options passed to the tech
1204    *
1205    * @return {SourceHandler|null}
1206    *          The first source handler that supports the source or null if
1207    *          no SourceHandler supports the source
1208    */
1209   _Tech.selectSourceHandler = function (source, options) {
1210     var handlers = _Tech.sourceHandlers || [];
1211     var can = void 0;
1212
1213     for (var i = 0; i < handlers.length; i++) {
1214       can = handlers[i].canHandleSource(source, options);
1215
1216       if (can) {
1217         return handlers[i];
1218       }
1219     }
1220
1221     return null;
1222   };
1223
1224   /**
1225    * Check if the tech can support the given source.
1226    *
1227    * @param {Tech~SourceObject} srcObj
1228    *        The source object
1229    *
1230    * @param {Object} options
1231    *        The options passed to the tech
1232    *
1233    * @return {string}
1234    *         'probably', 'maybe', or '' (empty string)
1235    */
1236   _Tech.canPlaySource = function (srcObj, options) {
1237     var sh = _Tech.selectSourceHandler(srcObj, options);
1238
1239     if (sh) {
1240       return sh.canHandleSource(srcObj, options);
1241     }
1242
1243     return '';
1244   };
1245
1246   /**
1247    * When using a source handler, prefer its implementation of
1248    * any function normally provided by the tech.
1249    */
1250   var deferrable = ['seekable', 'duration'];
1251
1252   /**
1253    * A wrapper around {@link Tech#seekable} that will call a `SourceHandler`s seekable
1254    * function if it exists, with a fallback to the Techs seekable function.
1255    *
1256    * @method _Tech.seekable
1257    */
1258
1259   /**
1260    * A wrapper around {@link Tech#duration} that will call a `SourceHandler`s duration
1261    * function if it exists, otherwise it will fallback to the techs duration function.
1262    *
1263    * @method _Tech.duration
1264    */
1265
1266   deferrable.forEach(function (fnName) {
1267     var originalFn = this[fnName];
1268
1269     if (typeof originalFn !== 'function') {
1270       return;
1271     }
1272
1273     this[fnName] = function () {
1274       if (this.sourceHandler_ && this.sourceHandler_[fnName]) {
1275         return this.sourceHandler_[fnName].apply(this.sourceHandler_, arguments);
1276       }
1277       return originalFn.apply(this, arguments);
1278     };
1279   }, _Tech.prototype);
1280
1281   /**
1282    * Create a function for setting the source using a source object
1283    * and source handlers.
1284    * Should never be called unless a source handler was found.
1285    *
1286    * @param {Tech~SourceObject} source
1287    *        A source object with src and type keys
1288    *
1289    * @return {Tech}
1290    *         Returns itself; this method is chainable
1291    */
1292   _Tech.prototype.setSource = function (source) {
1293     var sh = _Tech.selectSourceHandler(source, this.options_);
1294
1295     if (!sh) {
1296       // Fall back to a native source hander when unsupported sources are
1297       // deliberately set
1298       if (_Tech.nativeSourceHandler) {
1299         sh = _Tech.nativeSourceHandler;
1300       } else {
1301         _log2['default'].error('No source hander found for the current source.');
1302       }
1303     }
1304
1305     // Dispose any existing source handler
1306     this.disposeSourceHandler();
1307     this.off('dispose', this.disposeSourceHandler);
1308
1309     if (sh !== _Tech.nativeSourceHandler) {
1310       this.currentSource_ = source;
1311
1312       // Catch if someone replaced the src without calling setSource.
1313       // If they do, set currentSource_ to null and dispose our source handler.
1314       this.off(this.el_, 'loadstart', _Tech.prototype.firstLoadStartListener_);
1315       this.off(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_);
1316       this.one(this.el_, 'loadstart', _Tech.prototype.firstLoadStartListener_);
1317     }
1318
1319     this.sourceHandler_ = sh.handleSource(source, this, this.options_);
1320     this.on('dispose', this.disposeSourceHandler);
1321
1322     return this;
1323   };
1324
1325   /**
1326    * Called once for the first loadstart of a video.
1327    *
1328    * @listens Tech#loadstart
1329    */
1330   _Tech.prototype.firstLoadStartListener_ = function () {
1331     this.one(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_);
1332   };
1333
1334   // On successive loadstarts when setSource has not been called again
1335   /**
1336    * Called after the first loadstart for a video occurs.
1337    *
1338    * @listens Tech#loadstart
1339    */
1340   _Tech.prototype.successiveLoadStartListener_ = function () {
1341     this.disposeSourceHandler();
1342     this.one(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_);
1343   };
1344
1345   /**
1346    * Clean up any existing SourceHandlers and listeners when the Tech is disposed.
1347    *
1348    * @listens Tech#dispose
1349    */
1350   _Tech.prototype.disposeSourceHandler = function () {
1351     // if we have a source and get another one
1352     // then we are loading something new
1353     // than clear all of our current tracks
1354     if (this.currentSource_) {
1355       this.clearTracks(['audio', 'video']);
1356       this.currentSource_ = null;
1357     }
1358
1359     // always clean up auto-text tracks
1360     this.cleanupAutoTextTracks();
1361
1362     if (this.sourceHandler_) {
1363       this.off(this.el_, 'loadstart', _Tech.prototype.firstLoadStartListener_);
1364       this.off(this.el_, 'loadstart', _Tech.prototype.successiveLoadStartListener_);
1365
1366       if (this.sourceHandler_.dispose) {
1367         this.sourceHandler_.dispose();
1368       }
1369
1370       this.sourceHandler_ = null;
1371     }
1372   };
1373 };
1374
1375 _component2['default'].registerComponent('Tech', Tech);
1376 // Old name for Tech
1377 // @deprecated
1378 _component2['default'].registerComponent('MediaTechController', Tech);
1379 Tech.registerTech('Tech', Tech);
1380 exports['default'] = Tech;