1
0
mirror of https://github.com/videojs/video.js.git synced 2025-01-06 06:50:51 +02:00

@BrandonOCasey converted remaining text-track modules to ES6. closes #3130

This commit is contained in:
brandonocasey 2016-02-29 11:08:38 -05:00 committed by Gary Katsevman
parent d9a0a4503c
commit cb3d709237
5 changed files with 475 additions and 424 deletions

View File

@ -3,6 +3,7 @@ CHANGELOG
## HEAD (Unreleased) ## HEAD (Unreleased)
* @gkatsev updated videojs badges in the README ([view](https://github.com/videojs/video.js/pull/3134)) * @gkatsev updated videojs badges in the README ([view](https://github.com/videojs/video.js/pull/3134))
* @BrandonOCasey converted remaining text-track modules to ES6 ([view](https://github.com/videojs/video.js/pull/3130))
-------------------- --------------------

View File

@ -4,7 +4,8 @@
import * as browser from '../utils/browser.js'; import * as browser from '../utils/browser.js';
import document from 'global/document'; import document from 'global/document';
/* /**
* A List of text track cues as defined in:
* https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist * https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackcuelist
* *
* interface TextTrackCueList { * interface TextTrackCueList {
@ -12,9 +13,13 @@ import document from 'global/document';
* getter TextTrackCue (unsigned long index); * getter TextTrackCue (unsigned long index);
* TextTrackCue? getCueById(DOMString id); * TextTrackCue? getCueById(DOMString id);
* }; * };
*
* @param {Array} cues A list of cues to be initialized with
* @class TextTrackCueList
*/ */
function TextTrackCueList (cues) { class TextTrackCueList {
constructor(cues) {
let list = this; let list = this;
if (browser.IS_IE8) { if (browser.IS_IE8) {
@ -30,7 +35,7 @@ function TextTrackCueList (cues) {
TextTrackCueList.prototype.setCues_.call(list, cues); TextTrackCueList.prototype.setCues_.call(list, cues);
Object.defineProperty(list, 'length', { Object.defineProperty(list, 'length', {
get: function() { get() {
return this.length_; return this.length_;
} }
}); });
@ -38,9 +43,16 @@ function TextTrackCueList (cues) {
if (browser.IS_IE8) { if (browser.IS_IE8) {
return list; return list;
} }
} }
TextTrackCueList.prototype.setCues_ = function(cues) { /**
* A setter for cues in this list
*
* @param {Array} cues an array of cues
* @method setCues_
* @private
*/
setCues_(cues) {
let oldLength = this.length || 0; let oldLength = this.length || 0;
let i = 0; let i = 0;
let l = cues.length; let l = cues.length;
@ -48,11 +60,11 @@ TextTrackCueList.prototype.setCues_ = function(cues) {
this.cues_ = cues; this.cues_ = cues;
this.length_ = cues.length; this.length_ = cues.length;
let defineProp = function(i) { let defineProp = function(index) {
if (!(''+i in this)) { if (!('' + index in this)) {
Object.defineProperty(this, '' + i, { Object.defineProperty(this, '' + index, {
get: function() { get() {
return this.cues_[i]; return this.cues_[index];
} }
}); });
} }
@ -61,16 +73,25 @@ TextTrackCueList.prototype.setCues_ = function(cues) {
if (oldLength < l) { if (oldLength < l) {
i = oldLength; i = oldLength;
for(; i < l; i++) { for (; i < l; i++) {
defineProp.call(this, i); defineProp.call(this, i);
} }
} }
}; }
TextTrackCueList.prototype.getCueById = function(id) { /**
* Get a cue that is currently in the Cue list by id
*
* @param {String} id
* @method getCueById
* @return {Object} a single cue
*/
getCueById(id) {
let result = null; let result = null;
for (let i = 0, l = this.length; i < l; i++) { for (let i = 0, l = this.length; i < l; i++) {
let cue = this[i]; let cue = this[i];
if (cue.id === id) { if (cue.id === id) {
result = cue; result = cue;
break; break;
@ -78,6 +99,7 @@ TextTrackCueList.prototype.getCueById = function(id) {
} }
return result; return result;
}; }
}
export default TextTrackCueList; export default TextTrackCueList;

View File

@ -1,27 +1,39 @@
/** /**
* @file text-track-enums.js * @file text-track-enums.js
* */
/**
* https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode * https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackmode
* *
* enum TextTrackMode { "disabled", "hidden", "showing" }; * enum TextTrackMode { "disabled", "hidden", "showing" };
*/ */
var TextTrackMode = { const TextTrackMode = {
'disabled': 'disabled', disabled: 'disabled',
'hidden': 'hidden', hidden: 'hidden',
'showing': 'showing' showing: 'showing'
}; };
/* /**
* https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackkind * https://html.spec.whatwg.org/multipage/embedded-content.html#texttrackkind
* *
* enum TextTrackKind { "subtitles", "captions", "descriptions", "chapters", "metadata" }; * enum TextTrackKind {
* "subtitles",
* "captions",
* "descriptions",
* "chapters",
* "metadata"
* };
*/ */
var TextTrackKind = { const TextTrackKind = {
'subtitles': 'subtitles', subtitles: 'subtitles',
'captions': 'captions', captions: 'captions',
'descriptions': 'descriptions', descriptions: 'descriptions',
'chapters': 'chapters', chapters: 'chapters',
'metadata': 'metadata' metadata: 'metadata'
}; };
/* jshint ignore:start */
// we ignore jshint here because it does not see
// TextTrackMode or TextTrackKind as defined here somehow...
export { TextTrackMode, TextTrackKind }; export { TextTrackMode, TextTrackKind };
/* jshint ignore:end */

View File

@ -6,7 +6,8 @@ import * as Fn from '../utils/fn.js';
import * as browser from '../utils/browser.js'; import * as browser from '../utils/browser.js';
import document from 'global/document'; import document from 'global/document';
/* /**
* A text track list as defined in:
* https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist * https://html.spec.whatwg.org/multipage/embedded-content.html#texttracklist
* *
* interface TextTrackList : EventTarget { * interface TextTrackList : EventTarget {
@ -18,8 +19,15 @@ import document from 'global/document';
* attribute EventHandler onaddtrack; * attribute EventHandler onaddtrack;
* attribute EventHandler onremovetrack; * attribute EventHandler onremovetrack;
* }; * };
*
* @param {Track[]} tracks A list of tracks to initialize the list with
* @extends EventTarget
* @class TextTrackList
*/ */
function TextTrackList (tracks) {
class TextTrackList extends EventTarget {
constructor(tracks = []) {
super();
let list = this; let list = this;
if (browser.IS_IE8) { if (browser.IS_IE8) {
@ -32,11 +40,10 @@ function TextTrackList (tracks) {
} }
} }
tracks = tracks || [];
list.tracks_ = []; list.tracks_ = [];
Object.defineProperty(list, 'length', { Object.defineProperty(list, 'length', {
get: function() { get() {
return this.tracks_.length; return this.tracks_.length;
} }
}); });
@ -48,39 +55,21 @@ function TextTrackList (tracks) {
if (browser.IS_IE8) { if (browser.IS_IE8) {
return list; return list;
} }
} }
TextTrackList.prototype = Object.create(EventTarget.prototype); /**
TextTrackList.prototype.constructor = TextTrackList;
/*
* change - One or more tracks in the track list have been enabled or disabled.
* addtrack - A track has been added to the track list.
* removetrack - A track has been removed from the track list.
*/
TextTrackList.prototype.allowedEvents_ = {
'change': 'change',
'addtrack': 'addtrack',
'removetrack': 'removetrack'
};
// emulate attribute EventHandler support to allow for feature detection
for (let event in TextTrackList.prototype.allowedEvents_) {
TextTrackList.prototype['on' + event] = null;
}
/**
* Add TextTrack from TextTrackList * Add TextTrack from TextTrackList
* *
* @param {TextTrack} track * @param {TextTrack} track
* @method addTrack_ * @method addTrack_
* @private * @private
*/ */
TextTrackList.prototype.addTrack_ = function(track) { addTrack_(track) {
let index = this.tracks_.length; let index = this.tracks_.length;
if (!(''+index in this)) {
if (!('' + index in this)) {
Object.defineProperty(this, index, { Object.defineProperty(this, index, {
get: function() { get() {
return this.tracks_[index]; return this.tracks_[index];
} }
}); });
@ -92,12 +81,12 @@ TextTrackList.prototype.addTrack_ = function(track) {
this.tracks_.push(track); this.tracks_.push(track);
this.trigger({ this.trigger({
type: 'addtrack', track,
track: track type: 'addtrack'
}); });
}; }
/** /**
* Remove TextTrack from TextTrackList * Remove TextTrack from TextTrackList
* NOTE: Be mindful of what is passed in as it may be a HTMLTrackElement * NOTE: Be mindful of what is passed in as it may be a HTMLTrackElement
* *
@ -105,7 +94,7 @@ TextTrackList.prototype.addTrack_ = function(track) {
* @method removeTrack_ * @method removeTrack_
* @private * @private
*/ */
TextTrackList.prototype.removeTrack_ = function(rtrack) { removeTrack_(rtrack) {
let track; let track;
for (let i = 0, l = this.length; i < l; i++) { for (let i = 0, l = this.length; i < l; i++) {
@ -126,16 +115,25 @@ TextTrackList.prototype.removeTrack_ = function(rtrack) {
} }
this.trigger({ this.trigger({
type: 'removetrack', track,
track: track type: 'removetrack'
}); });
}; }
TextTrackList.prototype.getTrackById = function(id) { /**
* Get a TextTrack from TextTrackList by a tracks id
*
* @param {String} id - the id of the track to get
* @method getTrackById
* @return {TextTrack}
* @private
*/
getTrackById(id) {
let result = null; let result = null;
for (let i = 0, l = this.length; i < l; i++) { for (let i = 0, l = this.length; i < l; i++) {
let track = this[i]; let track = this[i];
if (track.id === id) { if (track.id === id) {
result = track; result = track;
break; break;
@ -143,6 +141,23 @@ TextTrackList.prototype.getTrackById = function(id) {
} }
return result; return result;
}
}
/**
* change - One or more tracks in the track list have been enabled or disabled.
* addtrack - A track has been added to the track list.
* removetrack - A track has been removed from the track list.
*/
TextTrackList.prototype.allowedEvents_ = {
change: 'change',
addtrack: 'addtrack',
removetrack: 'removetrack'
}; };
// emulate attribute EventHandler support to allow for feature detection
for (let event in TextTrackList.prototype.allowedEvents_) {
TextTrackList.prototype['on' + event] = null;
}
export default TextTrackList; export default TextTrackList;

View File

@ -13,229 +13,16 @@ import window from 'global/window';
import { isCrossOrigin } from '../utils/url.js'; import { isCrossOrigin } from '../utils/url.js';
import XHR from 'xhr'; import XHR from 'xhr';
/* /**
* https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack * takes a webvtt file contents and parses it into cues
* *
* interface TextTrack : EventTarget { * @param {String} srcContent webVTT file contents
* readonly attribute TextTrackKind kind; * @param {Track} track track to addcues to
* readonly attribute DOMString label;
* readonly attribute DOMString language;
*
* readonly attribute DOMString id;
* readonly attribute DOMString inBandMetadataTrackDispatchType;
*
* attribute TextTrackMode mode;
*
* readonly attribute TextTrackCueList? cues;
* readonly attribute TextTrackCueList? activeCues;
*
* void addCue(TextTrackCue cue);
* void removeCue(TextTrackCue cue);
*
* attribute EventHandler oncuechange;
* };
*/ */
function TextTrack (options={}) { const parseCues = function(srcContent, track) {
if (!options.tech) { let parser = new window.WebVTT.Parser(window,
throw new Error('A tech was not provided.'); window.vttjs,
} window.WebVTT.StringDecoder());
let tt = this;
if (browser.IS_IE8) {
tt = document.createElement('custom');
for (let prop in TextTrack.prototype) {
if (prop !== 'constructor') {
tt[prop] = TextTrack.prototype[prop];
}
}
}
tt.tech_ = options.tech;
let mode = TextTrackEnum.TextTrackMode[options['mode']] || 'disabled';
let kind = TextTrackEnum.TextTrackKind[options['kind']] || 'subtitles';
let label = options['label'] || '';
let language = options['language'] || options['srclang'] || '';
let id = options['id'] || 'vjs_text_track_' + Guid.newGUID();
if (kind === 'metadata' || kind === 'chapters') {
mode = 'hidden';
}
tt.cues_ = [];
tt.activeCues_ = [];
let cues = new TextTrackCueList(tt.cues_);
let activeCues = new TextTrackCueList(tt.activeCues_);
let changed = false;
let timeupdateHandler = Fn.bind(tt, function() {
this['activeCues'];
if (changed) {
this['trigger']('cuechange');
changed = false;
}
});
if (mode !== 'disabled') {
tt.tech_.on('timeupdate', timeupdateHandler);
}
Object.defineProperty(tt, 'kind', {
get: function() {
return kind;
},
set: Function.prototype
});
Object.defineProperty(tt, 'label', {
get: function() {
return label;
},
set: Function.prototype
});
Object.defineProperty(tt, 'language', {
get: function() {
return language;
},
set: Function.prototype
});
Object.defineProperty(tt, 'id', {
get: function() {
return id;
},
set: Function.prototype
});
Object.defineProperty(tt, 'mode', {
get: function() {
return mode;
},
set: function(newMode) {
if (!TextTrackEnum.TextTrackMode[newMode]) {
return;
}
mode = newMode;
if (mode === 'showing') {
this.tech_.on('timeupdate', timeupdateHandler);
}
this.trigger('modechange');
}
});
Object.defineProperty(tt, 'cues', {
get: function() {
if (!this.loaded_) {
return null;
}
return cues;
},
set: Function.prototype
});
Object.defineProperty(tt, 'activeCues', {
get: function() {
if (!this.loaded_) {
return null;
}
if (this['cues'].length === 0) {
return activeCues; // nothing to do
}
let ct = this.tech_.currentTime();
let active = [];
for (let i = 0, l = this['cues'].length; i < l; i++) {
let cue = this['cues'][i];
if (cue['startTime'] <= ct && cue['endTime'] >= ct) {
active.push(cue);
} else if (cue['startTime'] === cue['endTime'] && cue['startTime'] <= ct && cue['startTime'] + 0.5 >= ct) {
active.push(cue);
}
}
changed = false;
if (active.length !== this.activeCues_.length) {
changed = true;
} else {
for (let i = 0; i < active.length; i++) {
if (indexOf.call(this.activeCues_, active[i]) === -1) {
changed = true;
}
}
}
this.activeCues_ = active;
activeCues.setCues_(this.activeCues_);
return activeCues;
},
set: Function.prototype
});
if (options.src) {
tt.src = options.src;
loadTrack(options.src, tt);
} else {
tt.loaded_ = true;
}
if (browser.IS_IE8) {
return tt;
}
}
TextTrack.prototype = Object.create(EventTarget.prototype);
TextTrack.prototype.constructor = TextTrack;
/*
* cuechange - One or more cues in the track have become active or stopped being active.
*/
TextTrack.prototype.allowedEvents_ = {
'cuechange': 'cuechange'
};
TextTrack.prototype.addCue = function(cue) {
let tracks = this.tech_.textTracks();
if (tracks) {
for (let i = 0; i < tracks.length; i++) {
if (tracks[i] !== this) {
tracks[i].removeCue(cue);
}
}
}
this.cues_.push(cue);
this['cues'].setCues_(this.cues_);
};
TextTrack.prototype.removeCue = function(removeCue) {
let removed = false;
for (let i = 0, l = this.cues_.length; i < l; i++) {
let cue = this.cues_[i];
if (cue === removeCue) {
this.cues_.splice(i, 1);
removed = true;
}
}
if (removed) {
this.cues.setCues_(this.cues_);
}
};
/*
* Downloading stuff happens below this point
*/
var parseCues = function(srcContent, track) {
let parser = new window.WebVTT.Parser(window, window.vttjs, window.WebVTT.StringDecoder());
parser.oncue = function(cue) { parser.oncue = function(cue) {
track.addCue(cue); track.addCue(cue);
@ -256,17 +43,24 @@ var parseCues = function(srcContent, track) {
parser.flush(); parser.flush();
}; };
var loadTrack = function(src, track) {
/**
* load a track from a specifed url
*
* @param {String} src url to load track from
* @param {Track} track track to addcues to
*/
const loadTrack = function(src, track) {
let opts = { let opts = {
uri: src uri: src
}; };
let crossOrigin = isCrossOrigin(src); let crossOrigin = isCrossOrigin(src);
if (crossOrigin) { if (crossOrigin) {
opts.cors = crossOrigin; opts.cors = crossOrigin;
} }
XHR(opts, Fn.bind(this, function(err, response, responseBody){ XHR(opts, Fn.bind(this, function(err, response, responseBody) {
if (err) { if (err) {
return log.error(err, response); return log.error(err, response);
} }
@ -284,38 +78,245 @@ var loadTrack = function(src, track) {
})); }));
}; };
var indexOf = function(searchElement, fromIndex) { /**
if (this == null) { * A single text track as defined in:
throw new TypeError('"this" is null or not defined'); * https://html.spec.whatwg.org/multipage/embedded-content.html#texttrack
*
* interface TextTrack : EventTarget {
* readonly attribute TextTrackKind kind;
* readonly attribute DOMString label;
* readonly attribute DOMString language;
*
* readonly attribute DOMString id;
* readonly attribute DOMString inBandMetadataTrackDispatchType;
*
* attribute TextTrackMode mode;
*
* readonly attribute TextTrackCueList? cues;
* readonly attribute TextTrackCueList? activeCues;
*
* void addCue(TextTrackCue cue);
* void removeCue(TextTrackCue cue);
*
* attribute EventHandler oncuechange;
* };
*
* @param {Object=} options Object of option names and values
* @extends EventTarget
* @class TextTrack
*/
class TextTrack extends EventTarget {
constructor(options = {}) {
super();
if (!options.tech) {
throw new Error('A tech was not provided.');
} }
let O = Object(this); let tt = this;
let len = O.length >>> 0; if (browser.IS_IE8) {
tt = document.createElement('custom');
if (len === 0) { for (let prop in TextTrack.prototype) {
return -1; if (prop !== 'constructor') {
tt[prop] = TextTrack.prototype[prop];
}
}
} }
let n = +fromIndex || 0; tt.tech_ = options.tech;
if (Math.abs(n) === Infinity) { let mode = TextTrackEnum.TextTrackMode[options.mode] || 'disabled';
n = 0; let kind = TextTrackEnum.TextTrackKind[options.kind] || 'subtitles';
let label = options.label || '';
let language = options.language || options.srclang || '';
let id = options.id || 'vjs_text_track_' + Guid.newGUID();
if (kind === 'metadata' || kind === 'chapters') {
mode = 'hidden';
} }
if (n >= len) { tt.cues_ = [];
return -1; tt.activeCues_ = [];
let cues = new TextTrackCueList(tt.cues_);
let activeCues = new TextTrackCueList(tt.activeCues_);
let changed = false;
let timeupdateHandler = Fn.bind(tt, function() {
this.activeCues;
if (changed) {
this.trigger('cuechange');
changed = false;
}
});
if (mode !== 'disabled') {
tt.tech_.on('timeupdate', timeupdateHandler);
} }
let k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); Object.defineProperty(tt, 'kind', {
get() {
return kind;
},
set() {}
});
while (k < len) { Object.defineProperty(tt, 'label', {
if (k in O && O[k] === searchElement) { get() {
return k; return label;
},
set() {}
});
Object.defineProperty(tt, 'language', {
get() {
return language;
},
set() {}
});
Object.defineProperty(tt, 'id', {
get() {
return id;
},
set() {}
});
Object.defineProperty(tt, 'mode', {
get() {
return mode;
},
set(newMode) {
if (!TextTrackEnum.TextTrackMode[newMode]) {
return;
} }
k++; mode = newMode;
if (mode === 'showing') {
this.tech_.on('timeupdate', timeupdateHandler);
} }
return -1; this.trigger('modechange');
}
});
Object.defineProperty(tt, 'cues', {
get() {
if (!this.loaded_) {
return null;
}
return cues;
},
set() {}
});
Object.defineProperty(tt, 'activeCues', {
get() {
if (!this.loaded_) {
return null;
}
// nothing to do
if (this.cues.length === 0) {
return activeCues;
}
let ct = this.tech_.currentTime();
let active = [];
for (let i = 0, l = this.cues.length; i < l; i++) {
let cue = this.cues[i];
if (cue.startTime <= ct && cue.endTime >= ct) {
active.push(cue);
} else if (cue.startTime === cue.endTime &&
cue.startTime <= ct &&
cue.startTime + 0.5 >= ct) {
active.push(cue);
}
}
changed = false;
if (active.length !== this.activeCues_.length) {
changed = true;
} else {
for (let i = 0; i < active.length; i++) {
if (this.activeCues_.indexOf(active[i]) === -1) {
changed = true;
}
}
}
this.activeCues_ = active;
activeCues.setCues_(this.activeCues_);
return activeCues;
},
set() {}
});
if (options.src) {
tt.src = options.src;
loadTrack(options.src, tt);
} else {
tt.loaded_ = true;
}
if (browser.IS_IE8) {
return tt;
}
}
/**
* add a cue to the internal list of cues
*
* @param {Object} cue the cue to add to our internal list
* @method addCue
*/
addCue(cue) {
let tracks = this.tech_.textTracks();
if (tracks) {
for (let i = 0; i < tracks.length; i++) {
if (tracks[i] !== this) {
tracks[i].removeCue(cue);
}
}
}
this.cues_.push(cue);
this.cues.setCues_(this.cues_);
}
/**
* remvoe a cue from our internal list
*
* @param {Object} removeCue the cue to remove from our internal list
* @method removeCue
*/
removeCue(removeCue) {
let removed = false;
for (let i = 0, l = this.cues_.length; i < l; i++) {
let cue = this.cues_[i];
if (cue === removeCue) {
this.cues_.splice(i, 1);
removed = true;
}
}
if (removed) {
this.cues.setCues_(this.cues_);
}
}
}
/**
* cuechange - One or more cues in the track have become active or stopped being active.
*/
TextTrack.prototype.allowedEvents_ = {
cuechange: 'cuechange'
}; };
export default TextTrack; export default TextTrack;