Version 1
[yaffs-website] / node_modules / video.js / es5 / tracks / text-track.js
1 'use strict';
2
3 exports.__esModule = true;
4
5 var _textTrackCueList = require('./text-track-cue-list');
6
7 var _textTrackCueList2 = _interopRequireDefault(_textTrackCueList);
8
9 var _fn = require('../utils/fn.js');
10
11 var Fn = _interopRequireWildcard(_fn);
12
13 var _trackEnums = require('./track-enums');
14
15 var _log = require('../utils/log.js');
16
17 var _log2 = _interopRequireDefault(_log);
18
19 var _window = require('global/window');
20
21 var _window2 = _interopRequireDefault(_window);
22
23 var _track = require('./track.js');
24
25 var _track2 = _interopRequireDefault(_track);
26
27 var _url = require('../utils/url.js');
28
29 var _xhr = require('xhr');
30
31 var _xhr2 = _interopRequireDefault(_xhr);
32
33 var _mergeOptions = require('../utils/merge-options');
34
35 var _mergeOptions2 = _interopRequireDefault(_mergeOptions);
36
37 var _browser = require('../utils/browser.js');
38
39 var browser = _interopRequireWildcard(_browser);
40
41 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; } }
42
43 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
44
45 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
46
47 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; }
48
49 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; } /**
50                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 * @file text-track.js
51                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 */
52
53
54 /**
55  * Takes a webvtt file contents and parses it into cues
56  *
57  * @param {string} srcContent
58  *        webVTT file contents
59  *
60  * @param {TextTrack} track
61  *        TextTrack to add cues to. Cues come from the srcContent.
62  *
63  * @private
64  */
65 var parseCues = function parseCues(srcContent, track) {
66   var parser = new _window2['default'].WebVTT.Parser(_window2['default'], _window2['default'].vttjs, _window2['default'].WebVTT.StringDecoder());
67   var errors = [];
68
69   parser.oncue = function (cue) {
70     track.addCue(cue);
71   };
72
73   parser.onparsingerror = function (error) {
74     errors.push(error);
75   };
76
77   parser.onflush = function () {
78     track.trigger({
79       type: 'loadeddata',
80       target: track
81     });
82   };
83
84   parser.parse(srcContent);
85   if (errors.length > 0) {
86     if (_window2['default'].console && _window2['default'].console.groupCollapsed) {
87       _window2['default'].console.groupCollapsed('Text Track parsing errors for ' + track.src);
88     }
89     errors.forEach(function (error) {
90       return _log2['default'].error(error);
91     });
92     if (_window2['default'].console && _window2['default'].console.groupEnd) {
93       _window2['default'].console.groupEnd();
94     }
95   }
96
97   parser.flush();
98 };
99
100 /**
101  * Load a `TextTrack` from a specifed url.
102  *
103  * @param {string} src
104  *        Url to load track from.
105  *
106  * @param {TextTrack} track
107  *        Track to add cues to. Comes from the content at the end of `url`.
108  *
109  * @private
110  */
111 var loadTrack = function loadTrack(src, track) {
112   var opts = {
113     uri: src
114   };
115   var crossOrigin = (0, _url.isCrossOrigin)(src);
116
117   if (crossOrigin) {
118     opts.cors = crossOrigin;
119   }
120
121   (0, _xhr2['default'])(opts, Fn.bind(this, function (err, response, responseBody) {
122     if (err) {
123       return _log2['default'].error(err, response);
124     }
125
126     track.loaded_ = true;
127
128     // Make sure that vttjs has loaded, otherwise, wait till it finished loading
129     // NOTE: this is only used for the alt/video.novtt.js build
130     if (typeof _window2['default'].WebVTT !== 'function') {
131       if (track.tech_) {
132         var loadHandler = function loadHandler() {
133           return parseCues(responseBody, track);
134         };
135
136         track.tech_.on('vttjsloaded', loadHandler);
137         track.tech_.on('vttjserror', function () {
138           _log2['default'].error('vttjs failed to load, stopping trying to process ' + track.src);
139           track.tech_.off('vttjsloaded', loadHandler);
140         });
141       }
142     } else {
143       parseCues(responseBody, track);
144     }
145   }));
146 };
147
148 /**
149  * A representation of a single `TextTrack`.
150  *
151  * @see [Spec]{@link https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack}
152  * @extends Track
153  */
154
155 var TextTrack = function (_Track) {
156   _inherits(TextTrack, _Track);
157
158   /**
159    * Create an instance of this class.
160    *
161    * @param {Object} options={}
162    *        Object of option names and values
163    *
164    * @param {Tech} options.tech
165    *        A reference to the tech that owns this TextTrack.
166    *
167    * @param {TextTrack~Kind} [options.kind='subtitles']
168    *        A valid text track kind.
169    *
170    * @param {TextTrack~Mode} [options.mode='disabled']
171    *        A valid text track mode.
172    *
173    * @param {string} [options.id='vjs_track_' + Guid.newGUID()]
174    *        A unique id for this TextTrack.
175    *
176    * @param {string} [options.label='']
177    *        The menu label for this track.
178    *
179    * @param {string} [options.language='']
180    *        A valid two character language code.
181    *
182    * @param {string} [options.srclang='']
183    *        A valid two character language code. An alternative, but deprioritized
184    *        vesion of `options.language`
185    *
186    * @param {string} [options.src]
187    *        A url to TextTrack cues.
188    *
189    * @param {boolean} [options.default]
190    *        If this track should default to on or off.
191    */
192   function TextTrack() {
193     var _this, _ret;
194
195     var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
196
197     _classCallCheck(this, TextTrack);
198
199     if (!options.tech) {
200       throw new Error('A tech was not provided.');
201     }
202
203     var settings = (0, _mergeOptions2['default'])(options, {
204       kind: _trackEnums.TextTrackKind[options.kind] || 'subtitles',
205       language: options.language || options.srclang || ''
206     });
207     var mode = _trackEnums.TextTrackMode[settings.mode] || 'disabled';
208     var default_ = settings['default'];
209
210     if (settings.kind === 'metadata' || settings.kind === 'chapters') {
211       mode = 'hidden';
212     }
213     // on IE8 this will be a document element
214     // for every other browser this will be a normal object
215     var tt = (_this = _possibleConstructorReturn(this, _Track.call(this, settings)), _this);
216
217     tt.tech_ = settings.tech;
218
219     if (browser.IS_IE8) {
220       for (var prop in TextTrack.prototype) {
221         if (prop !== 'constructor') {
222           tt[prop] = TextTrack.prototype[prop];
223         }
224       }
225     }
226
227     tt.cues_ = [];
228     tt.activeCues_ = [];
229
230     var cues = new _textTrackCueList2['default'](tt.cues_);
231     var activeCues = new _textTrackCueList2['default'](tt.activeCues_);
232     var changed = false;
233     var timeupdateHandler = Fn.bind(tt, function () {
234
235       // Accessing this.activeCues for the side-effects of updating itself
236       // due to it's nature as a getter function. Do not remove or cues will
237       // stop updating!
238       /* eslint-disable no-unused-expressions */
239       this.activeCues;
240       /* eslint-enable no-unused-expressions */
241       if (changed) {
242         this.trigger('cuechange');
243         changed = false;
244       }
245     });
246
247     if (mode !== 'disabled') {
248       tt.tech_.ready(function () {
249         tt.tech_.on('timeupdate', timeupdateHandler);
250       }, true);
251     }
252
253     /**
254      * @member {boolean} default
255      *         If this track was set to be on or off by default. Cannot be changed after
256      *         creation.
257      *
258      * @readonly
259      */
260     Object.defineProperty(tt, 'default', {
261       get: function get() {
262         return default_;
263       },
264       set: function set() {}
265     });
266
267     /**
268      * @member {string} mode
269      *         Set the mode of this TextTrack to a valid {@link TextTrack~Mode}. Will
270      *         not be set if setting to an invalid mode.
271      *
272      * @fires TextTrack#modechange
273      */
274     Object.defineProperty(tt, 'mode', {
275       get: function get() {
276         return mode;
277       },
278       set: function set(newMode) {
279         var _this2 = this;
280
281         if (!_trackEnums.TextTrackMode[newMode]) {
282           return;
283         }
284         mode = newMode;
285         if (mode === 'showing') {
286           this.tech_.ready(function () {
287             _this2.tech_.on('timeupdate', timeupdateHandler);
288           }, true);
289         }
290         /**
291          * An event that fires when mode changes on this track. This allows
292          * the TextTrackList that holds this track to act accordingly.
293          *
294          * > Note: This is not part of the spec!
295          *
296          * @event TextTrack#modechange
297          * @type {EventTarget~Event}
298          */
299         this.trigger('modechange');
300       }
301     });
302
303     /**
304      * @member {TextTrackCueList} cues
305      *         The text track cue list for this TextTrack.
306      */
307     Object.defineProperty(tt, 'cues', {
308       get: function get() {
309         if (!this.loaded_) {
310           return null;
311         }
312
313         return cues;
314       },
315       set: function set() {}
316     });
317
318     /**
319      * @member {TextTrackCueList} activeCues
320      *         The list text track cues that are currently active for this TextTrack.
321      */
322     Object.defineProperty(tt, 'activeCues', {
323       get: function get() {
324         if (!this.loaded_) {
325           return null;
326         }
327
328         // nothing to do
329         if (this.cues.length === 0) {
330           return activeCues;
331         }
332
333         var ct = this.tech_.currentTime();
334         var active = [];
335
336         for (var i = 0, l = this.cues.length; i < l; i++) {
337           var cue = this.cues[i];
338
339           if (cue.startTime <= ct && cue.endTime >= ct) {
340             active.push(cue);
341           } else if (cue.startTime === cue.endTime && cue.startTime <= ct && cue.startTime + 0.5 >= ct) {
342             active.push(cue);
343           }
344         }
345
346         changed = false;
347
348         if (active.length !== this.activeCues_.length) {
349           changed = true;
350         } else {
351           for (var _i = 0; _i < active.length; _i++) {
352             if (this.activeCues_.indexOf(active[_i]) === -1) {
353               changed = true;
354             }
355           }
356         }
357
358         this.activeCues_ = active;
359         activeCues.setCues_(this.activeCues_);
360
361         return activeCues;
362       },
363       set: function set() {}
364     });
365
366     if (settings.src) {
367       tt.src = settings.src;
368       loadTrack(settings.src, tt);
369     } else {
370       tt.loaded_ = true;
371     }
372
373     return _ret = tt, _possibleConstructorReturn(_this, _ret);
374   }
375
376   /**
377    * Add a cue to the internal list of cues.
378    *
379    * @param {TextTrack~Cue} cue
380    *        The cue to add to our internal list
381    */
382
383
384   TextTrack.prototype.addCue = function addCue(originalCue) {
385     var cue = originalCue;
386
387     if (_window2['default'].vttjs && !(originalCue instanceof _window2['default'].vttjs.VTTCue)) {
388       cue = new _window2['default'].vttjs.VTTCue(originalCue.startTime, originalCue.endTime, originalCue.text);
389
390       for (var prop in originalCue) {
391         if (!(prop in cue)) {
392           cue[prop] = originalCue[prop];
393         }
394       }
395
396       // make sure that `id` is copied over
397       cue.id = originalCue.id;
398       cue.originalCue_ = originalCue;
399     }
400
401     var tracks = this.tech_.textTracks();
402
403     if (tracks) {
404       for (var i = 0; i < tracks.length; i++) {
405         if (tracks[i] !== this) {
406           tracks[i].removeCue(cue);
407         }
408       }
409     }
410
411     this.cues_.push(cue);
412     this.cues.setCues_(this.cues_);
413   };
414
415   /**
416    * Remove a cue from our internal list
417    *
418    * @param {TextTrack~Cue} removeCue
419    *        The cue to remove from our internal list
420    */
421
422
423   TextTrack.prototype.removeCue = function removeCue(_removeCue) {
424     var i = this.cues_.length;
425
426     while (i--) {
427       var cue = this.cues_[i];
428
429       if (cue === _removeCue || cue.originalCue_ && cue.originalCue_ === _removeCue) {
430         this.cues_.splice(i, 1);
431         this.cues.setCues_(this.cues_);
432         break;
433       }
434     }
435   };
436
437   return TextTrack;
438 }(_track2['default']);
439
440 /**
441  * cuechange - One or more cues in the track have become active or stopped being active.
442  */
443
444
445 TextTrack.prototype.allowedEvents_ = {
446   cuechange: 'cuechange'
447 };
448
449 exports['default'] = TextTrack;