1
0
mirror of https://github.com/videojs/video.js.git synced 2025-07-15 01:34:23 +02:00

refactor: unify all Track and TrackList APIs (#3783)

Unify all track-related usage internally.

BREAKING CHANGE: some externally accessibly functions for tracks are now private.
This commit is contained in:
Brandon Casey
2017-01-19 15:16:28 -05:00
committed by Gary Katsevman
parent 7bafcc2a55
commit 49bed07039
24 changed files with 417 additions and 595 deletions

View File

@ -22,7 +22,7 @@ class AudioTrackButton extends TrackButton {
* The key/value store of player options. * The key/value store of player options.
*/ */
constructor(player, options = {}) { constructor(player, options = {}) {
options.tracks = player.audioTracks && player.audioTracks(); options.tracks = player.audioTracks();
super(player, options); super(player, options);
@ -49,11 +49,7 @@ class AudioTrackButton extends TrackButton {
* An array of menu items * An array of menu items
*/ */
createItems(items = []) { createItems(items = []) {
const tracks = this.player_.audioTracks && this.player_.audioTracks(); const tracks = this.player_.audioTracks();
if (!tracks) {
return items;
}
for (let i = 0; i < tracks.length; i++) { for (let i = 0; i < tracks.length; i++) {
const track = tracks[i]; const track = tracks[i];

View File

@ -33,14 +33,12 @@ class AudioTrackMenuItem extends MenuItem {
this.track = track; this.track = track;
if (tracks) { const changeHandler = Fn.bind(this, this.handleTracksChange);
const changeHandler = Fn.bind(this, this.handleTracksChange);
tracks.addEventListener('change', changeHandler); tracks.addEventListener('change', changeHandler);
this.on('dispose', () => { this.on('dispose', () => {
tracks.removeEventListener('change', changeHandler); tracks.removeEventListener('change', changeHandler);
}); });
}
} }
/** /**
@ -59,10 +57,6 @@ class AudioTrackMenuItem extends MenuItem {
super.handleClick(event); super.handleClick(event);
if (!tracks) {
return;
}
for (let i = 0; i < tracks.length; i++) { for (let i = 0; i < tracks.length; i++) {
const track = tracks[i]; const track = tracks[i];

View File

@ -29,15 +29,12 @@ class DescriptionsButton extends TextTrackButton {
this.el_.setAttribute('aria-label', 'Descriptions Menu'); this.el_.setAttribute('aria-label', 'Descriptions Menu');
const tracks = player.textTracks(); const tracks = player.textTracks();
const changeHandler = Fn.bind(this, this.handleTracksChange);
if (tracks) { tracks.addEventListener('change', changeHandler);
const changeHandler = Fn.bind(this, this.handleTracksChange); this.on('dispose', function() {
tracks.removeEventListener('change', changeHandler);
tracks.addEventListener('change', changeHandler); });
this.on('dispose', function() {
tracks.removeEventListener('change', changeHandler);
});
}
} }
/** /**

View File

@ -43,10 +43,6 @@ class TextTrackButton extends TrackButton {
const tracks = this.player_.textTracks(); const tracks = this.player_.textTracks();
if (!tracks) {
return items;
}
for (let i = 0; i < tracks.length; i++) { for (let i = 0; i < tracks.length; i++) {
const track = tracks[i]; const track = tracks[i];

View File

@ -34,15 +34,12 @@ class TextTrackMenuItem extends MenuItem {
super(player, options); super(player, options);
this.track = track; this.track = track;
const changeHandler = Fn.bind(this, this.handleTracksChange);
if (tracks) { tracks.addEventListener('change', changeHandler);
const changeHandler = Fn.bind(this, this.handleTracksChange); this.on('dispose', function() {
tracks.removeEventListener('change', changeHandler);
tracks.addEventListener('change', changeHandler); });
this.on('dispose', function() {
tracks.removeEventListener('change', changeHandler);
});
}
// iOS7 doesn't dispatch change events to TextTrackLists when an // iOS7 doesn't dispatch change events to TextTrackLists when an
// associated track's mode changes. Without something like // associated track's mode changes. Without something like
@ -50,7 +47,7 @@ class TextTrackMenuItem extends MenuItem {
// possible to detect changes to the mode attribute and polyfill // possible to detect changes to the mode attribute and polyfill
// the change event. As a poor substitute, we manually dispatch // the change event. As a poor substitute, we manually dispatch
// change events whenever the controls modify the mode. // change events whenever the controls modify the mode.
if (tracks && tracks.onchange === undefined) { if (tracks.onchange === undefined) {
let event; let event;
this.on(['tap', 'click'], function() { this.on(['tap', 'click'], function() {

View File

@ -25,8 +25,7 @@ import mergeOptions from './utils/merge-options.js';
import textTrackConverter from './tracks/text-track-list-converter.js'; import textTrackConverter from './tracks/text-track-list-converter.js';
import ModalDialog from './modal-dialog'; import ModalDialog from './modal-dialog';
import Tech from './tech/tech.js'; import Tech from './tech/tech.js';
import AudioTrackList from './tracks/audio-track-list.js'; import {ALL as TRACK_TYPES} from './tracks/track-types';
import VideoTrackList from './tracks/video-track-list.js';
// The following imports are used only to ensure that the corresponding modules // The following imports are used only to ensure that the corresponding modules
// are always included in the video.js package. Importing the modules will // are always included in the video.js package. Importing the modules will
@ -809,14 +808,11 @@ class Player extends Component {
this.isReady_ = false; this.isReady_ = false;
// Grab tech-specific options from player options and add source and parent element to use. // Grab tech-specific options from player options and add source and parent element to use.
const techOptions = assign({ const techOptions = {
source, source,
'nativeControlsForTouch': this.options_.nativeControlsForTouch, 'nativeControlsForTouch': this.options_.nativeControlsForTouch,
'playerId': this.id(), 'playerId': this.id(),
'techId': `${this.id()}_${techName}_api`, 'techId': `${this.id()}_${techName}_api`,
'videoTracks': this.videoTracks_,
'textTracks': this.textTracks_,
'audioTracks': this.audioTracks_,
'autoplay': this.options_.autoplay, 'autoplay': this.options_.autoplay,
'preload': this.options_.preload, 'preload': this.options_.preload,
'loop': this.options_.loop, 'loop': this.options_.loop,
@ -825,7 +821,15 @@ class Player extends Component {
'language': this.language(), 'language': this.language(),
'playerElIngest': this.playerElIngest_ || false, 'playerElIngest': this.playerElIngest_ || false,
'vtt.js': this.options_['vtt.js'] 'vtt.js': this.options_['vtt.js']
}, this.options_[techName.toLowerCase()]); };
TRACK_TYPES.names.forEach((name) => {
const props = TRACK_TYPES[name];
techOptions[props.getterName] = this[props.privateName];
});
assign(techOptions, this.options_[techName.toLowerCase()]);
if (this.tag) { if (this.tag) {
techOptions.tag = this.tag; techOptions.tag = this.tag;
@ -906,9 +910,11 @@ class Player extends Component {
*/ */
unloadTech_() { unloadTech_() {
// Save the current text tracks so that we can reuse the same text tracks with the next tech // Save the current text tracks so that we can reuse the same text tracks with the next tech
this.videoTracks_ = this.videoTracks(); TRACK_TYPES.names.forEach((name) => {
this.textTracks_ = this.textTracks(); const props = TRACK_TYPES[name];
this.audioTracks_ = this.audioTracks();
this[props.privateName] = this[props.getterName]();
});
this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_); this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_);
this.isReady_ = false; this.isReady_ = false;
@ -2841,102 +2847,6 @@ class Player extends Component {
return !!this.isAudio_; return !!this.isAudio_;
} }
/**
* Get the {@link VideoTrackList}
*
* @see https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
*
* @return {VideoTrackList}
* the current video track list
*/
videoTracks() {
// if we have not yet loadTech_, we create videoTracks_
// these will be passed to the tech during loading
if (!this.tech_) {
this.videoTracks_ = this.videoTracks_ || new VideoTrackList();
return this.videoTracks_;
}
return this.tech_.videoTracks();
}
/**
* Get the {@link AudioTrackList}
*
* @see https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
*
* @return {AudioTrackList}
* the current audio track list
*/
audioTracks() {
// if we have not yet loadTech_, we create videoTracks_
// these will be passed to the tech during loading
if (!this.tech_) {
this.audioTracks_ = this.audioTracks_ || new AudioTrackList();
return this.audioTracks_;
}
return this.tech_.audioTracks();
}
/**
* Get the {@link TextTrackList}
*
* Text tracks are tracks of timed text events.
* - Captions: text displayed over the video
* for the hearing impaired
* - Subtitles: text displayed over the video for
* those who don't understand language in the video
* - Chapters: text displayed in a menu allowing the user to jump
* to particular points (chapters) in the video
* - Descriptions: (not yet implemented) audio descriptions that are read back to
* the user by a screen reading device
*
* @see http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
*
* @return {TextTrackList|undefined}
* The current TextTrackList or undefined if
* or undefined if we don't have a tech
*/
textTracks() {
// cannot use techGet_ directly because it checks to see whether the tech is ready.
// Flash is unlikely to be ready in time but textTracks should still work.
if (this.tech_) {
return this.tech_.textTracks();
}
}
/**
* Get the "remote" {@link TextTrackList}. Remote Text Tracks
* are tracks that were added to the HTML video element and can
* be removed, whereas normal texttracks cannot be removed.
*
*
* @return {TextTrackList|undefined}
* The current remote text track list or undefined
* if we don't have a tech
*/
remoteTextTracks() {
if (this.tech_) {
return this.tech_.remoteTextTracks();
}
}
/**
* Get the "remote" {@link HTMLTrackElementList}.
* This gives the user all of the DOM elements that match up
* with the remote {@link TextTrackList}.
*
* @return {HTMLTrackElementList}
* The current remote text track list elements
* or undefined if we don't have a tech
*/
remoteTextTrackEls() {
if (this.tech_) {
return this.tech_.remoteTextTrackEls();
}
}
/** /**
* A helper method for adding a {@link TextTrack} to our * A helper method for adding a {@link TextTrack} to our
* {@link TextTrackList}. * {@link TextTrackList}.
@ -3194,6 +3104,70 @@ class Player extends Component {
} }
} }
/**
* Get the {@link VideoTrackList}
* @link https://html.spec.whatwg.org/multipage/embedded-content.html#videotracklist
*
* @return {VideoTrackList}
* the current video track list
*
* @method Player.prototype.videoTracks
*/
/**
* Get the {@link AudioTrackList}
* @link https://html.spec.whatwg.org/multipage/embedded-content.html#audiotracklist
*
* @return {AudioTrackList}
* the current audio track list
*
* @method Player.prototype.audioTracks
*/
/**
* Get the {@link TextTrackList}
*
* @link http://www.w3.org/html/wg/drafts/html/master/embedded-content-0.html#dom-media-texttracks
*
* @return {TextTrackList}
* the current text track list
*
* @method Player.prototype.textTracks
*/
/**
* Get the remote {@link TextTrackList}
*
* @return {TextTrackList}
* The current remote text track list
*
* @method Player.prototype.textTracks
*/
/**
* Get the remote {@link HTMLTrackElementList} tracks.
*
* @return {HTMLTrackElementList}
* The current remote text track element list
*
* @method Player.prototype.remoteTextTrackEls
*/
TRACK_TYPES.names.forEach(function(name) {
const props = TRACK_TYPES[name];
Player.prototype[props.getterName] = function() {
if (this.tech_) {
return this.tech_[props.getterName]();
}
// if we have not yet loadTech_, we create {video,audio,text}Tracks_
// these will be passed to the tech during loading
this[props.privateName] = this[props.privateName] || new props.ListClass();
return this[props.privateName];
};
});
/** /**
* Global player list * Global player list
* *

View File

@ -4,7 +4,6 @@
import Tech from './tech.js'; import Tech from './tech.js';
import * as Dom from '../utils/dom.js'; import * as Dom from '../utils/dom.js';
import * as Url from '../utils/url.js'; import * as Url from '../utils/url.js';
import * as Fn from '../utils/fn.js';
import log from '../utils/log.js'; import log from '../utils/log.js';
import tsml from 'tsml'; import tsml from 'tsml';
import * as browser from '../utils/browser.js'; import * as browser from '../utils/browser.js';
@ -13,6 +12,7 @@ import window from 'global/window';
import {assign} from '../utils/obj'; import {assign} from '../utils/obj';
import mergeOptions from '../utils/merge-options.js'; import mergeOptions from '../utils/merge-options.js';
import toTitleCase from '../utils/to-title-case.js'; import toTitleCase from '../utils/to-title-case.js';
import {NORMAL as TRACK_TYPES} from '../tracks/track-types';
/** /**
* HTML5 Media Controller - Wrapper for HTML5 Media API * HTML5 Media Controller - Wrapper for HTML5 Media API
@ -67,7 +67,7 @@ class Html5 extends Tech {
} else { } else {
// store HTMLTrackElement and TextTrack to remote list // store HTMLTrackElement and TextTrack to remote list
this.remoteTextTrackEls().addTrackElement_(node); this.remoteTextTrackEls().addTrackElement_(node);
this.remoteTextTracks().addTrack_(node.track); this.remoteTextTracks().addTrack(node.track);
if (!crossoriginTracks && if (!crossoriginTracks &&
!this.el_.hasAttribute('crossorigin') && !this.el_.hasAttribute('crossorigin') &&
Url.isCrossOrigin(node.src)) { Url.isCrossOrigin(node.src)) {
@ -82,52 +82,10 @@ class Html5 extends Tech {
} }
} }
// TODO: add text tracks into this list this.proxyNativeTracks_();
const trackTypes = ['audio', 'video']; if (this.featuresNativeTextTracks && crossoriginTracks) {
log.warn(tsml`Text Tracks are being loaded from another origin but the crossorigin attribute isn't used.
// ProxyNative Video/Audio Track
trackTypes.forEach((type) => {
const elTracks = this.el()[`${type}Tracks`];
const techTracks = this[`${type}Tracks`]();
const capitalType = toTitleCase(type);
if (!this[`featuresNative${capitalType}Tracks`] ||
!elTracks ||
!elTracks.addEventListener) {
return;
}
this[`handle${capitalType}TrackChange_`] = (e) => {
techTracks.trigger({
type: 'change',
target: techTracks,
currentTarget: techTracks,
srcElement: techTracks
});
};
this[`handle${capitalType}TrackAdd_`] = (e) => techTracks.addTrack(e.track);
this[`handle${capitalType}TrackRemove_`] = (e) => techTracks.removeTrack(e.track);
elTracks.addEventListener('change', this[`handle${capitalType}TrackChange_`]);
elTracks.addEventListener('addtrack', this[`handle${capitalType}TrackAdd_`]);
elTracks.addEventListener('removetrack', this[`handle${capitalType}TrackRemove_`]);
this[`removeOld${capitalType}Tracks_`] = (e) => this.removeOldTracks_(techTracks, elTracks);
// Remove (native) tracks that are not used anymore
this.on('loadstart', this[`removeOld${capitalType}Tracks_`]);
});
if (this.featuresNativeTextTracks) {
if (crossoriginTracks) {
log.warn(tsml`Text Tracks are being loaded from another origin but the crossorigin attribute isn't used.
This may prevent text tracks from loading.`); This may prevent text tracks from loading.`);
}
this.handleTextTrackChange_ = Fn.bind(this, this.handleTextTrackChange);
this.handleTextTrackAdd_ = Fn.bind(this, this.handleTextTrackAdd);
this.handleTextTrackRemove_ = Fn.bind(this, this.handleTextTrackRemove);
this.proxyNativeTextTracks_();
} }
// Determine if native controls should be used // Determine if native controls should be used
@ -150,28 +108,81 @@ class Html5 extends Tech {
* Dispose of `HTML5` media element and remove all tracks. * Dispose of `HTML5` media element and remove all tracks.
*/ */
dispose() { dispose() {
// Un-ProxyNativeTracks
['audio', 'video', 'text'].forEach((type) => {
const capitalType = toTitleCase(type);
const tl = this.el_[`${type}Tracks`];
if (tl && tl.removeEventListener) {
tl.removeEventListener('change', this[`handle${capitalType}TrackChange_`]);
tl.removeEventListener('addtrack', this[`handle${capitalType}TrackAdd_`]);
tl.removeEventListener('removetrack', this[`handle${capitalType}TrackRemove_`]);
}
// Stop removing old text tracks
if (tl) {
this.off('loadstart', this[`removeOld${capitalType}Tracks_`]);
}
});
Html5.disposeMediaElement(this.el_); Html5.disposeMediaElement(this.el_);
// tech will handle clearing of the emulated track list // tech will handle clearing of the emulated track list
super.dispose(); super.dispose();
} }
/**
* Proxy all native track list events to our track lists if the browser we are playing
* in supports that type of track list.
*
* @private
*/
proxyNativeTracks_() {
TRACK_TYPES.names.forEach((name) => {
const props = TRACK_TYPES[name];
const elTracks = this.el()[props.getterName];
const techTracks = this[props.getterName]();
if (!this[`featuresNative${props.capitalName}Tracks`] ||
!elTracks ||
!elTracks.addEventListener) {
return;
}
const listeners = {
change(e) {
techTracks.trigger({
type: 'change',
target: techTracks,
currentTarget: techTracks,
srcElement: techTracks
});
},
addtrack(e) {
techTracks.addTrack(e.track);
},
removetrack(e) {
techTracks.removeTrack(e.track);
}
};
const removeOldTracks = function() {
const removeTracks = [];
for (let i = 0; i < techTracks.length; i++) {
let found = false;
for (let j = 0; j < elTracks.length; j++) {
if (elTracks[j] === techTracks[i]) {
found = true;
break;
}
}
if (!found) {
removeTracks.push(techTracks[i]);
}
}
while (removeTracks.length) {
techTracks.removeTrack(removeTracks.shift());
}
};
Object.keys(listeners).forEach((eventName) => {
const listener = listeners[eventName];
elTracks.addEventListener(eventName, listener);
this.on('dispose', (e) => elTracks.removeEventListener(eventName, listener));
});
// Remove (native) tracks that are not used anymore
this.on('loadstart', removeOldTracks);
this.on('dispose', (e) => this.off('loadstart', removeOldTracks));
});
}
/** /**
* Create the `Html5` Tech's DOM element. * Create the `Html5` Tech's DOM element.
* *
@ -331,130 +342,18 @@ class Html5 extends Tech {
} }
/** /**
* Add event listeners to native text track events. This adds the native text tracks * Called by {@link Player#play} to play using the `Html5` `Tech`.
* to our emulated {@link TextTrackList}.
*/ */
proxyNativeTextTracks_() { play() {
const tt = this.el().textTracks; const playPromise = this.el_.play();
if (tt) { // Catch/silence error when a pause interrupts a play request
// Add tracks - if player is initialised after DOM loaded, textTracks // on browsers which return a promise
// will not trigger addtrack if (playPromise !== undefined && typeof playPromise.then === 'function') {
for (let i = 0; i < tt.length; i++) { playPromise.then(null, (e) => {});
this.textTracks().addTrack_(tt[i]);
}
if (tt.addEventListener) {
tt.addEventListener('change', this.handleTextTrackChange_);
tt.addEventListener('addtrack', this.handleTextTrackAdd_);
tt.addEventListener('removetrack', this.handleTextTrackRemove_);
}
// Remove (native) texttracks that are not used anymore
this.on('loadstart', this.removeOldTextTracks_);
} }
} }
/**
* Handle any {@link TextTrackList} `change` event.
*
* @param {EventTarget~Event} e
* The `change` event that caused this to run.
*
* @listens TextTrackList#change
*/
handleTextTrackChange(e) {
const tt = this.textTracks();
this.textTracks().trigger({
type: 'change',
target: tt,
currentTarget: tt,
srcElement: tt
});
}
/**
* Handle any {@link TextTrackList} `addtrack` event.
*
* @param {EventTarget~Event} e
* The `addtrack` event that caused this to run.
*
* @listens TextTrackList#addtrack
*/
handleTextTrackAdd(e) {
this.textTracks().addTrack_(e.track);
}
/**
* Handle any {@link TextTrackList} `removetrack` event.
*
* @param {EventTarget~Event} e
* The `removetrack` event that caused this to run.
*
* @listens TextTrackList#removetrack
*/
handleTextTrackRemove(e) {
this.textTracks().removeTrack_(e.track);
}
/**
* This function removes any {@link AudioTrack}s, {@link VideoTrack}s, or
* {@link TextTrack}s that are not in the media elements TrackList.
*
* @param {TrackList} techTracks
* HTML5 Tech's TrackList to search through
*
* @param {TrackList} elTracks
* HTML5 media elements TrackList to search trough.
*
* @private
*/
removeOldTracks_(techTracks, elTracks) {
// This will loop over the techTracks and check if they are still used by the HTML5 media element
// If not, they will be removed from the emulated list
const removeTracks = [];
if (!elTracks) {
return;
}
for (let i = 0; i < techTracks.length; i++) {
const techTrack = techTracks[i];
let found = false;
for (let j = 0; j < elTracks.length; j++) {
if (elTracks[j] === techTrack) {
found = true;
break;
}
}
if (!found) {
removeTracks.push(techTrack);
}
}
for (let i = 0; i < removeTracks.length; i++) {
const track = removeTracks[i];
techTracks.removeTrack_(track);
}
}
/**
* Remove {@link TextTrack}s that dont exist in the native track list from our
* emulated {@link TextTrackList}.
*
* @listens Tech#loadstart
*/
removeOldTextTracks_(e) {
const techTracks = this.textTracks();
const elTracks = this.el().textTracks;
this.removeOldTracks_(techTracks, elTracks);
}
/** /**
* Set current time for the `HTML5` tech. * Set current time for the `HTML5` tech.
* *

View File

@ -3,13 +3,7 @@
*/ */
import Component from '../component'; import Component from '../component';
import HTMLTrackElement from '../tracks/html-track-element';
import HTMLTrackElementList from '../tracks/html-track-element-list';
import mergeOptions from '../utils/merge-options.js'; import mergeOptions from '../utils/merge-options.js';
import TextTrack from '../tracks/text-track';
import TextTrackList from '../tracks/text-track-list';
import VideoTrackList from '../tracks/video-track-list';
import AudioTrackList from '../tracks/audio-track-list';
import * as Fn from '../utils/fn.js'; import * as Fn from '../utils/fn.js';
import log from '../utils/log.js'; import log from '../utils/log.js';
import { createTimeRange } from '../utils/time-ranges.js'; import { createTimeRange } from '../utils/time-ranges.js';
@ -18,6 +12,7 @@ import MediaError from '../media-error.js';
import window from 'global/window'; import window from 'global/window';
import document from 'global/document'; import document from 'global/document';
import {isPlain} from '../utils/obj'; import {isPlain} from '../utils/obj';
import * as TRACK_TYPES from '../tracks/track-types';
/** /**
* An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string * An Object containing a structure like: `{src: 'url', type: 'mimetype'}` or string
@ -68,9 +63,9 @@ function createTrackHelper(self, kind, label, language, options = {}) {
} }
options.tech = self; options.tech = self;
const track = new TextTrack(options); const track = new TRACK_TYPES.ALL.text.TrackClass(options);
tracks.addTrack_(track); tracks.addTrack(track);
return track; return track;
} }
@ -108,9 +103,13 @@ class Tech extends Component {
this.hasStarted_ = false; this.hasStarted_ = false;
}); });
this.textTracks_ = options.textTracks; TRACK_TYPES.ALL.names.forEach((name) => {
this.videoTracks_ = options.videoTracks; const props = TRACK_TYPES.ALL[name];
this.audioTracks_ = options.audioTracks;
if (options && options[props.getterName]) {
this[props.privateName] = options[props.getterName];
}
});
// Manually track progress in cases where the browser/flash player doesn't report it. // Manually track progress in cases where the browser/flash player doesn't report it.
if (!this.featuresProgressEvents) { if (!this.featuresProgressEvents) {
@ -136,9 +135,8 @@ class Tech extends Component {
this.emulateTextTracks(); this.emulateTextTracks();
} }
this.autoRemoteTextTracks_ = new TextTrackList(); this.autoRemoteTextTracks_ = new TRACK_TYPES.ALL.text.ListClass();
this.initTextTrackListeners();
this.initTrackListeners(); this.initTrackListeners();
// Turn on component tap events only if not using native controls // Turn on component tap events only if not using native controls
@ -332,7 +330,7 @@ class Tech extends Component {
dispose() { dispose() {
// clear out all tracks because we can't reuse them between techs // clear out all tracks because we can't reuse them between techs
this.clearTracks(['audio', 'video', 'text']); this.clearTracks(TRACK_TYPES.NORMAL.names);
// Turn off any manual progress or timeupdate tracking // Turn off any manual progress or timeupdate tracking
if (this.manualProgress) { if (this.manualProgress) {
@ -369,7 +367,7 @@ class Tech extends Component {
if (type === 'text') { if (type === 'text') {
this.removeRemoteTextTrack(track); this.removeRemoteTextTrack(track);
} }
list.removeTrack_(track); list.removeTrack(track);
} }
}); });
} }
@ -450,49 +448,16 @@ class Tech extends Component {
} }
/** /**
* Turn on listeners for {@link TextTrackList} events. This adds * Turn on listeners for {@link VideoTrackList}, {@link {AudioTrackList}, and
* {@link EventTarget~EventListeners} for `texttrackchange`, `addtrack` and * {@link TextTrackList} events.
* `removetrack`.
* *
* @fires Tech#texttrackchange
*/
initTextTrackListeners() {
const textTrackListChanges = Fn.bind(this, function() {
/**
* Triggered when tracks are added or removed on the Tech {@link TextTrackList}
*
* @event Tech#texttrackchange
* @type {EventTarget~Event}
*/
this.trigger('texttrackchange');
});
const tracks = this.textTracks();
if (!tracks) {
return;
}
tracks.addEventListener('removetrack', textTrackListChanges);
tracks.addEventListener('addtrack', textTrackListChanges);
this.on('dispose', Fn.bind(this, function() {
tracks.removeEventListener('removetrack', textTrackListChanges);
tracks.removeEventListener('addtrack', textTrackListChanges);
}));
}
/**
* Turn on listeners for {@link VideoTrackList} and {@link {AudioTrackList} events.
* This adds {@link EventTarget~EventListeners} for `addtrack`, and `removetrack`. * This adds {@link EventTarget~EventListeners} for `addtrack`, and `removetrack`.
* *
* @fires Tech#audiotrackchange * @fires Tech#audiotrackchange
* @fires Tech#videotrackchange * @fires Tech#videotrackchange
* @fires Tech#texttrackchange
*/ */
initTrackListeners() { initTrackListeners() {
const trackTypes = ['video', 'audio'];
trackTypes.forEach((type) => {
/** /**
* Triggered when tracks are added or removed on the Tech {@link AudioTrackList} * Triggered when tracks are added or removed on the Tech {@link AudioTrackList}
* *
@ -506,11 +471,20 @@ class Tech extends Component {
* @event Tech#videotrackchange * @event Tech#videotrackchange
* @type {EventTarget~Event} * @type {EventTarget~Event}
*/ */
/**
* Triggered when tracks are added or removed on the Tech {@link TextTrackList}
*
* @event Tech#texttrackchange
* @type {EventTarget~Event}
*/
TRACK_TYPES.NORMAL.names.forEach((name) => {
const props = TRACK_TYPES.NORMAL[name];
const trackListChanges = () => { const trackListChanges = () => {
this.trigger(`${type}trackchange`); this.trigger(`${name}trackchange`);
}; };
const tracks = this[`${type}Tracks`](); const tracks = this[props.getterName]();
tracks.addEventListener('removetrack', trackListChanges); tracks.addEventListener('removetrack', trackListChanges);
tracks.addEventListener('addtrack', trackListChanges); tracks.addEventListener('addtrack', trackListChanges);
@ -585,16 +559,12 @@ class Tech extends Component {
emulateTextTracks() { emulateTextTracks() {
const tracks = this.textTracks(); const tracks = this.textTracks();
if (!tracks) {
return;
}
this.remoteTextTracks().on('addtrack', (e) => { this.remoteTextTracks().on('addtrack', (e) => {
this.textTracks().addTrack_(e.track); tracks.addTrack(e.track);
}); });
this.remoteTextTracks().on('removetrack', (e) => { this.remoteTextTracks().on('removetrack', (e) => {
this.textTracks().removeTrack_(e.track); tracks.removeTrack(e.track);
}); });
// Initially, Tech.el_ is a child of a dummy-div wait until the Component system // Initially, Tech.el_ is a child of a dummy-div wait until the Component system
@ -624,63 +594,6 @@ class Tech extends Component {
}); });
} }
/**
* Get the `Tech`s {@link VideoTrackList}.
*
* @return {VideoTrackList}
* The video track list that the Tech is currently using.
*/
videoTracks() {
this.videoTracks_ = this.videoTracks_ || new VideoTrackList();
return this.videoTracks_;
}
/**
* Get the `Tech`s {@link AudioTrackList}.
*
* @return {AudioTrackList}
* The audio track list that the Tech is currently using.
*/
audioTracks() {
this.audioTracks_ = this.audioTracks_ || new AudioTrackList();
return this.audioTracks_;
}
/**
* Get the `Tech`s {@link TextTrackList}.
*
* @return {TextTrackList}
* The text track list that the Tech is currently using.
*/
textTracks() {
this.textTracks_ = this.textTracks_ || new TextTrackList();
return this.textTracks_;
}
/**
* Get the `Tech`s remote {@link TextTrackList}, which is created from elements
* that were added to the DOM.
*
* @return {TextTrackList}
* The remote text track list that the Tech is currently using.
*/
remoteTextTracks() {
this.remoteTextTracks_ = this.remoteTextTracks_ || new TextTrackList();
return this.remoteTextTracks_;
}
/**
* Get The `Tech`s {HTMLTrackElementList}, which are the elements in the DOM that are
* being used as TextTracks.
*
* @return {HTMLTrackElementList}
* The current HTML track elements that exist for the tech.
*/
remoteTextTrackEls() {
this.remoteTextTrackEls_ = this.remoteTextTrackEls_ || new HTMLTrackElementList();
return this.remoteTextTrackEls_;
}
/** /**
* Create and returns a remote {@link TextTrack} object. * Create and returns a remote {@link TextTrack} object.
* *
@ -730,7 +643,7 @@ class Tech extends Component {
tech: this tech: this
}); });
return new HTMLTrackElement(track); return new TRACK_TYPES.REMOTE.remoteTextEl.TrackClass(track);
} }
/** /**
@ -764,11 +677,11 @@ class Tech extends Component {
// store HTMLTrackElement and TextTrack to remote list // store HTMLTrackElement and TextTrack to remote list
this.remoteTextTrackEls().addTrackElement_(htmlTrackElement); this.remoteTextTrackEls().addTrackElement_(htmlTrackElement);
this.remoteTextTracks().addTrack_(htmlTrackElement.track); this.remoteTextTracks().addTrack(htmlTrackElement.track);
if (manualCleanup !== true) { if (manualCleanup !== true) {
// create the TextTrackList if it doesn't exist // create the TextTrackList if it doesn't exist
this.autoRemoteTextTracks_.addTrack_(htmlTrackElement.track); this.autoRemoteTextTracks_.addTrack(htmlTrackElement.track);
} }
return htmlTrackElement; return htmlTrackElement;
@ -785,8 +698,8 @@ class Tech extends Component {
// remove HTMLTrackElement and TextTrack from remote list // remove HTMLTrackElement and TextTrack from remote list
this.remoteTextTrackEls().removeTrackElement_(trackElement); this.remoteTextTrackEls().removeTrackElement_(trackElement);
this.remoteTextTracks().removeTrack_(track); this.remoteTextTracks().removeTrack(track);
this.autoRemoteTextTracks_.removeTrack_(track); this.autoRemoteTextTracks_.removeTrack(track);
} }
/** /**
@ -878,28 +791,72 @@ class Tech extends Component {
} }
/** /**
* List of associated text tracks. * Get the {@link VideoTrackList}
*
* @returns {VideoTrackList}
* @method Tech.prototype.videoTracks
*/
/**
* Get the {@link AudioTrackList}
*
* @returns {AudioTrackList}
* @method Tech.prototype.audioTracks
*/
/**
* Get the {@link TextTrackList}
*
* @returns {TextTrackList}
* @method Tech.prototype.textTracks
*/
/**
* Get the remote element {@link TextTrackList}
*
* @returns {TextTrackList}
* @method Tech.prototype.remoteTextTracks
*/
/**
* Get the remote element {@link HTMLTrackElementList}
*
* @returns {HTMLTrackElementList}
* @method Tech.prototype.remoteTextTrackEls
*/
TRACK_TYPES.ALL.names.forEach(function(name) {
const props = TRACK_TYPES.ALL[name];
Tech.prototype[props.getterName] = function() {
this[props.privateName] = this[props.privateName] || new props.ListClass();
return this[props.privateName];
};
});
/**
* List of associated text tracks
* *
* @type {TextTrackList} * @type {TextTrackList}
* @private * @private
* @property Tech#textTracks_
*/ */
Tech.prototype.textTracks_; // eslint-disable-line
/** /**
* List of associated audio tracks. * List of associated audio tracks.
* *
* @type {AudioTrackList} * @type {AudioTrackList}
* @private * @private
* @property Tech#audioTracks_
*/ */
Tech.prototype.audioTracks_; // eslint-disable-line
/** /**
* List of associated video tracks. * List of associated video tracks.
* *
* @type {VideoTrackList} * @type {VideoTrackList}
* @private * @private
* @property Tech#videoTracks_
*/ */
Tech.prototype.videoTracks_; // eslint-disable-line
/** /**
* Boolean indicating wether the `Tech` supports volume control. * Boolean indicating wether the `Tech` supports volume control.

View File

@ -81,15 +81,14 @@ class AudioTrackList extends TrackList {
* @param {AudioTrack} track * @param {AudioTrack} track
* The AudioTrack to add to the list * The AudioTrack to add to the list
* *
* @fires Track#addtrack * @fires TrackList#addtrack
* @private
*/ */
addTrack_(track) { addTrack(track) {
if (track.enabled) { if (track.enabled) {
disableOthers(this, track); disableOthers(this, track);
} }
super.addTrack_(track); super.addTrack(track);
// native tracks don't have this // native tracks don't have this
if (!track.addEventListener) { if (!track.addEventListener) {
return; return;
@ -112,31 +111,6 @@ class AudioTrackList extends TrackList {
this.trigger('change'); this.trigger('change');
}); });
} }
/**
* Add an {@link AudioTrack} to the `AudioTrackList`.
*
* @param {AudioTrack} track
* The AudioTrack to add to the list
*
* @fires Track#addtrack
*/
addTrack(track) {
this.addTrack_(track);
}
/**
* Remove an {@link AudioTrack} from the `AudioTrackList`.
*
* @param {AudioTrack} track
* The AudioTrack to remove from the list
*
* @fires Track#removetrack
*/
removeTrack(track) {
super.removeTrack_(track);
}
} }
export default AudioTrackList; export default AudioTrackList;

View File

@ -114,28 +114,26 @@ class TextTrackDisplay extends Component {
let firstDesc; let firstDesc;
let firstCaptions; let firstCaptions;
if (trackList) { for (let i = 0; i < trackList.length; i++) {
for (let i = 0; i < trackList.length; i++) { const track = trackList[i];
const track = trackList[i];
if (track.default) { if (track.default) {
if (track.kind === 'descriptions' && !firstDesc) { if (track.kind === 'descriptions' && !firstDesc) {
firstDesc = track; firstDesc = track;
} else if (track.kind in modes && !firstCaptions) { } else if (track.kind in modes && !firstCaptions) {
firstCaptions = track; firstCaptions = track;
}
} }
} }
}
// We want to show the first default track but captions and subtitles // We want to show the first default track but captions and subtitles
// take precedence over descriptions. // take precedence over descriptions.
// So, display the first default captions or subtitles track // So, display the first default captions or subtitles track
// and otherwise the first default descriptions track. // and otherwise the first default descriptions track.
if (firstCaptions) { if (firstCaptions) {
firstCaptions.mode = 'showing'; firstCaptions.mode = 'showing';
} else if (firstDesc) { } else if (firstDesc) {
firstDesc.mode = 'showing'; firstDesc.mode = 'showing';
}
} }
})); }));
} }
@ -192,17 +190,12 @@ class TextTrackDisplay extends Component {
this.clearDisplay(); this.clearDisplay();
if (!tracks) {
return;
}
// Track display prioritization model: if multiple tracks are 'showing', // Track display prioritization model: if multiple tracks are 'showing',
// display the first 'subtitles' or 'captions' track which is 'showing', // display the first 'subtitles' or 'captions' track which is 'showing',
// otherwise display the first 'descriptions' track which is 'showing' // otherwise display the first 'descriptions' track which is 'showing'
let descriptionsTrack = null; let descriptionsTrack = null;
let captionsSubtitlesTrack = null; let captionsSubtitlesTrack = null;
let i = tracks.length; let i = tracks.length;
while (i--) { while (i--) {

View File

@ -50,10 +50,9 @@ class TextTrackList extends TrackList {
* The text track to add to the list. * The text track to add to the list.
* *
* @fires TrackList#addtrack * @fires TrackList#addtrack
* @private
*/ */
addTrack_(track) { addTrack(track) {
super.addTrack_(track); super.addTrack(track);
/** /**
* @listens TextTrack#modechange * @listens TextTrack#modechange

View File

@ -331,11 +331,9 @@ class TextTrack extends Track {
addCue(cue) { addCue(cue) {
const tracks = this.tech_.textTracks(); const tracks = this.tech_.textTracks();
if (tracks) { for (let i = 0; i < tracks.length; i++) {
for (let i = 0; i < tracks.length; i++) { if (tracks[i] !== this) {
if (tracks[i] !== this) { tracks[i].removeCue(cue);
tracks[i].removeCue(cue);
}
} }
} }

View File

@ -50,7 +50,7 @@ class TrackList extends EventTarget {
}); });
for (let i = 0; i < tracks.length; i++) { for (let i = 0; i < tracks.length; i++) {
list.addTrack_(tracks[i]); list.addTrack(tracks[i]);
} }
// must return the object, as for ie8 it will not be this // must return the object, as for ie8 it will not be this
@ -65,9 +65,8 @@ class TrackList extends EventTarget {
* The audio, video, or text track to add to the list. * The audio, video, or text track to add to the list.
* *
* @fires TrackList#addtrack * @fires TrackList#addtrack
* @private
*/ */
addTrack_(track) { addTrack(track) {
const index = this.tracks_.length; const index = this.tracks_.length;
if (!('' + index in this)) { if (!('' + index in this)) {
@ -99,13 +98,12 @@ class TrackList extends EventTarget {
/** /**
* Remove a {@link Track} from the `TrackList` * Remove a {@link Track} from the `TrackList`
* *
* @param {Track} track * @param {Track} rtrack
* The audio, video, or text track to remove from the list. * The audio, video, or text track to remove from the list.
* *
* @fires TrackList#removetrack * @fires TrackList#removetrack
* @private
*/ */
removeTrack_(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++) {

View File

@ -0,0 +1,68 @@
import AudioTrackList from './audio-track-list';
import VideoTrackList from './video-track-list';
import TextTrackList from './text-track-list';
import HTMLTrackElementList from './html-track-element-list';
import TextTrack from './text-track';
import AudioTrack from './audio-track';
import VideoTrack from './video-track';
import HTMLTrackElement from './html-track-element';
import mergeOptions from '../utils/merge-options';
/**
* This file contains all track properties that are used in
* player.js, tech.js, html5.js and possibly other techs in the future.
*/
const NORMAL = {
audio: {
ListClass: AudioTrackList,
TrackClass: AudioTrack,
capitalName: 'Audio'
},
video: {
ListClass: VideoTrackList,
TrackClass: VideoTrack,
capitalName: 'Video'
},
text: {
ListClass: TextTrackList,
TrackClass: TextTrack,
capitalName: 'Text'
}
};
Object.keys(NORMAL).forEach(function(type) {
NORMAL[type].getterName = `${type}Tracks`;
NORMAL[type].privateName = `${type}Tracks_`;
});
const REMOTE = {
remoteText: {
ListClass: TextTrackList,
TrackClass: TextTrack,
capitalName: 'RemoteText',
getterName: 'remoteTextTracks',
privateName: 'remoteTextTracks_'
},
remoteTextEl: {
ListClass: HTMLTrackElementList,
TrackClass: HTMLTrackElement,
capitalName: 'RemoteTextTrackEls',
getterName: 'remoteTextTrackEls',
privateName: 'remoteTextTrackEls_'
}
};
const ALL = mergeOptions(NORMAL, REMOTE);
REMOTE.names = Object.keys(REMOTE);
NORMAL.names = Object.keys(NORMAL);
ALL.names = [].concat(REMOTE.names).concat(NORMAL.names);
export {
NORMAL,
REMOTE,
ALL
};

View File

@ -97,14 +97,13 @@ class VideoTrackList extends TrackList {
* The VideoTrack to add to the list * The VideoTrack to add to the list
* *
* @fires TrackList#addtrack * @fires TrackList#addtrack
* @private
*/ */
addTrack_(track) { addTrack(track) {
if (track.selected) { if (track.selected) {
disableOthers(this, track); disableOthers(this, track);
} }
super.addTrack_(track); super.addTrack(track);
// native tracks don't have this // native tracks don't have this
if (!track.addEventListener) { if (!track.addEventListener) {
return; return;
@ -124,31 +123,6 @@ class VideoTrackList extends TrackList {
this.trigger('change'); this.trigger('change');
}); });
} }
/**
* Add a {@link VideoTrack} to the `VideoTrackList`.
*
* @param {VideoTrack} track
* The VideoTrack to add to the list
*
* @fires TrackList#addtrack
*/
addTrack(track) {
this.addTrack_(track);
}
/**
* Remove a {@link VideoTrack} to the `VideoTrackList`.
*
* @param {VideoTrack} track
* The VideoTrack to remove from the list.
*
* @fires TrackList#removetrack
*/
removeTrack(track) {
super.removeTrack_(track);
}
} }
export default VideoTrackList; export default VideoTrackList;

View File

@ -139,11 +139,11 @@ QUnit.test('dispose() should clear all tracks that are added after creation', fu
tech.addRemoteTextTrack({}, true); tech.addRemoteTextTrack({}, true);
tech.addRemoteTextTrack({}, true); tech.addRemoteTextTrack({}, true);
tech.audioTracks().addTrack_(new AudioTrack()); tech.audioTracks().addTrack(new AudioTrack());
tech.audioTracks().addTrack_(new AudioTrack()); tech.audioTracks().addTrack(new AudioTrack());
tech.videoTracks().addTrack_(new VideoTrack()); tech.videoTracks().addTrack(new VideoTrack());
tech.videoTracks().addTrack_(new VideoTrack()); tech.videoTracks().addTrack(new VideoTrack());
assert.equal(tech.audioTracks().length, 2, 'should have two audio tracks at the start'); assert.equal(tech.audioTracks().length, 2, 'should have two audio tracks at the start');
assert.equal(tech.videoTracks().length, 2, 'should have two video tracks at the start'); assert.equal(tech.videoTracks().length, 2, 'should have two video tracks at the start');
@ -354,11 +354,11 @@ QUnit.test('should add the source handler interface to a tech', function(assert)
tech.addRemoteTextTrack({}, true); tech.addRemoteTextTrack({}, true);
tech.addRemoteTextTrack({}, true); tech.addRemoteTextTrack({}, true);
tech.audioTracks().addTrack_(new AudioTrack()); tech.audioTracks().addTrack(new AudioTrack());
tech.audioTracks().addTrack_(new AudioTrack()); tech.audioTracks().addTrack(new AudioTrack());
tech.videoTracks().addTrack_(new VideoTrack()); tech.videoTracks().addTrack(new VideoTrack());
tech.videoTracks().addTrack_(new VideoTrack()); tech.videoTracks().addTrack(new VideoTrack());
assert.equal(tech.audioTracks().length, 2, 'should have two audio tracks at the start'); assert.equal(tech.audioTracks().length, 2, 'should have two audio tracks at the start');
assert.equal(tech.videoTracks().length, 2, 'should have two video tracks at the start'); assert.equal(tech.videoTracks().length, 2, 'should have two video tracks at the start');
@ -586,4 +586,3 @@ QUnit.test('setSource after previous setSource should dispose source handler onc
assert.equal(disposeCount, 2, 'did dispose for third setSource'); assert.equal(disposeCount, 2, 'did dispose for third setSource');
}); });

View File

@ -40,7 +40,7 @@ QUnit.test('only one track is ever enabled', function(assert) {
assert.equal(track.enabled, true, 'track is enabled'); assert.equal(track.enabled, true, 'track is enabled');
assert.equal(track2.enabled, false, 'track2 is disabled'); assert.equal(track2.enabled, false, 'track2 is disabled');
list.addTrack_(track3); list.addTrack(track3);
assert.equal(track.enabled, false, 'track is disabled'); assert.equal(track.enabled, false, 'track is disabled');
assert.equal(track2.enabled, false, 'track2 is disabled'); assert.equal(track2.enabled, false, 'track2 is disabled');
assert.equal(track3.enabled, true, 'track3 is enabled'); assert.equal(track3.enabled, true, 'track3 is enabled');
@ -50,7 +50,7 @@ QUnit.test('only one track is ever enabled', function(assert) {
assert.equal(track2.enabled, false, 'track2 is disabled'); assert.equal(track2.enabled, false, 'track2 is disabled');
assert.equal(track3.enabled, false, 'track3 is disabled'); assert.equal(track3.enabled, false, 'track3 is disabled');
list.addTrack_(track4); list.addTrack(track4);
assert.equal(track.enabled, true, 'track is enabled'); assert.equal(track.enabled, true, 'track is enabled');
assert.equal(track2.enabled, false, 'track2 is disabled'); assert.equal(track2.enabled, false, 'track2 is disabled');
assert.equal(track3.enabled, false, 'track3 is disabled'); assert.equal(track3.enabled, false, 'track3 is disabled');
@ -92,7 +92,7 @@ QUnit.test('trigger a change event per enabled change', function(assert) {
track.enabled = true; track.enabled = true;
assert.equal(change, 1, 'one change triggered'); assert.equal(change, 1, 'one change triggered');
list.addTrack_(track3); list.addTrack(track3);
assert.equal(change, 2, 'another change triggered by adding an enabled track'); assert.equal(change, 2, 'another change triggered by adding an enabled track');
track.enabled = true; track.enabled = true;
@ -101,7 +101,7 @@ QUnit.test('trigger a change event per enabled change', function(assert) {
track.enabled = false; track.enabled = false;
assert.equal(change, 4, 'another change trigger by changing enabled'); assert.equal(change, 4, 'another change trigger by changing enabled');
list.addTrack_(track4); list.addTrack(track4);
assert.equal(change, 4, 'no change triggered by adding a disabled track'); assert.equal(change, 4, 'no change triggered by adding a disabled track');
}); });

View File

@ -1,8 +1,11 @@
/* eslint-env qunit */ /* eslint-env qunit */
import HTMLTrackElement from '../../../src/js/tracks/html-track-element.js'; import HTMLTrackElement from '../../../src/js/tracks/html-track-element.js';
import TextTrackList from '../../../src/js/tracks/text-track-list.js';
const defaultTech = { const defaultTech = {
textTracks() {}, textTracks() {
return new TextTrackList();
},
on() {}, on() {},
off() {}, off() {},
currentTime() {} currentTime() {}

View File

@ -56,8 +56,8 @@ if (Html5.supportsNativeTextTracks()) {
const tt = new TextTrackList(); const tt = new TextTrackList();
tt.addTrack_(nativeTrack.track); tt.addTrack(nativeTrack.track);
tt.addTrack_(emulatedTrack); tt.addTrack(emulatedTrack);
const tech = { const tech = {
$$() { $$() {
@ -107,8 +107,8 @@ if (Html5.supportsNativeTextTracks()) {
const tt = new TextTrackList(); const tt = new TextTrackList();
tt.addTrack_(nativeTrack.track); tt.addTrack(nativeTrack.track);
tt.addTrack_(emulatedTrack); tt.addTrack(emulatedTrack);
let addRemotes = 0; let addRemotes = 0;
const tech = { const tech = {
@ -177,8 +177,8 @@ QUnit.test('textTracksToJson produces good json output for emulated only', funct
const tt = new TextTrackList(); const tt = new TextTrackList();
tt.addTrack_(anotherTrack); tt.addTrack(anotherTrack);
tt.addTrack_(emulatedTrack); tt.addTrack(emulatedTrack);
const tech = { const tech = {
$$() { $$() {
@ -231,8 +231,8 @@ QUnit.test('jsonToTextTracks calls addRemoteTextTrack on the tech with emulated
const tt = new TextTrackList(); const tt = new TextTrackList();
tt.addTrack_(anotherTrack); tt.addTrack(anotherTrack);
tt.addTrack_(emulatedTrack); tt.addTrack(emulatedTrack);
let addRemotes = 0; let addRemotes = 0;
const tech = { const tech = {

View File

@ -7,10 +7,13 @@ import TextTrack from '../../../src/js/tracks/text-track.js';
import TestHelpers from '../test-helpers.js'; import TestHelpers from '../test-helpers.js';
import proxyquireify from 'proxyquireify'; import proxyquireify from 'proxyquireify';
import sinon from 'sinon'; import sinon from 'sinon';
import TextTrackList from '../../../src/js/tracks/text-track-list.js';
const proxyquire = proxyquireify(require); const proxyquire = proxyquireify(require);
const defaultTech = { const defaultTech = {
textTracks() {}, textTracks() {
return new TextTrackList();
},
on() {}, on() {},
off() {}, off() {},
currentTime() {} currentTime() {}

View File

@ -370,7 +370,7 @@ QUnit.test('removes cuechange event when text track is hidden for emulated track
startTime: 2, startTime: 2,
endTime: 5 endTime: 5
}); });
player.tech_.textTracks().addTrack_(tt); player.tech_.textTracks().addTrack(tt);
let numTextTrackChanges = 0; let numTextTrackChanges = 0;

View File

@ -33,20 +33,20 @@ QUnit.test('can get tracks by int and string id', function(assert) {
QUnit.test('length is updated when new tracks are added or removed', function(assert) { QUnit.test('length is updated when new tracks are added or removed', function(assert) {
const trackList = new TrackList(this.tracks); const trackList = new TrackList(this.tracks);
trackList.addTrack_(newTrack('100')); trackList.addTrack(newTrack('100'));
assert.equal(trackList.length, assert.equal(trackList.length,
this.tracks.length + 1, this.tracks.length + 1,
'the length is ' + (this.tracks.length + 1)); 'the length is ' + (this.tracks.length + 1));
trackList.addTrack_(newTrack('101')); trackList.addTrack(newTrack('101'));
assert.equal(trackList.length, assert.equal(trackList.length,
this.tracks.length + 2, this.tracks.length + 2,
'the length is ' + (this.tracks.length + 2)); 'the length is ' + (this.tracks.length + 2));
trackList.removeTrack_(trackList.getTrackById('101')); trackList.removeTrack(trackList.getTrackById('101'));
assert.equal(trackList.length, assert.equal(trackList.length,
this.tracks.length + 1, this.tracks.length + 1,
'the length is ' + (this.tracks.length + 1)); 'the length is ' + (this.tracks.length + 1));
trackList.removeTrack_(trackList.getTrackById('100')); trackList.removeTrack(trackList.getTrackById('100'));
assert.equal(trackList.length, assert.equal(trackList.length,
this.tracks.length, this.tracks.length,
'the length is ' + this.tracks.length); 'the length is ' + this.tracks.length);
@ -68,23 +68,23 @@ QUnit.test('can access items by index', function(assert) {
QUnit.test('can access new items by index', function(assert) { QUnit.test('can access new items by index', function(assert) {
const trackList = new TrackList(this.tracks); const trackList = new TrackList(this.tracks);
trackList.addTrack_(newTrack('100')); trackList.addTrack(newTrack('100'));
assert.equal(trackList[3].id, '100', 'id of item at index 3 is 100'); assert.equal(trackList[3].id, '100', 'id of item at index 3 is 100');
trackList.addTrack_(newTrack('101')); trackList.addTrack(newTrack('101'));
assert.equal(trackList[4].id, '101', 'id of item at index 4 is 101'); assert.equal(trackList[4].id, '101', 'id of item at index 4 is 101');
}); });
QUnit.test('cannot access removed items by index', function(assert) { QUnit.test('cannot access removed items by index', function(assert) {
const trackList = new TrackList(this.tracks); const trackList = new TrackList(this.tracks);
trackList.addTrack_(newTrack('100')); trackList.addTrack(newTrack('100'));
trackList.addTrack_(newTrack('101')); trackList.addTrack(newTrack('101'));
assert.equal(trackList[3].id, '100', 'id of item at index 3 is 100'); assert.equal(trackList[3].id, '100', 'id of item at index 3 is 100');
assert.equal(trackList[4].id, '101', 'id of item at index 4 is 101'); assert.equal(trackList[4].id, '101', 'id of item at index 4 is 101');
trackList.removeTrack_(trackList.getTrackById('101')); trackList.removeTrack(trackList.getTrackById('101'));
trackList.removeTrack_(trackList.getTrackById('100')); trackList.removeTrack(trackList.getTrackById('100'));
assert.ok(!trackList[3], 'nothing at index 3'); assert.ok(!trackList[3], 'nothing at index 3');
assert.ok(!trackList[4], 'nothing at index 4'); assert.ok(!trackList[4], 'nothing at index 4');
@ -93,13 +93,13 @@ QUnit.test('cannot access removed items by index', function(assert) {
QUnit.test('new item available at old index', function(assert) { QUnit.test('new item available at old index', function(assert) {
const trackList = new TrackList(this.tracks); const trackList = new TrackList(this.tracks);
trackList.addTrack_(newTrack('100')); trackList.addTrack(newTrack('100'));
assert.equal(trackList[3].id, '100', 'id of item at index 3 is 100'); assert.equal(trackList[3].id, '100', 'id of item at index 3 is 100');
trackList.removeTrack_(trackList.getTrackById('100')); trackList.removeTrack(trackList.getTrackById('100'));
assert.ok(!trackList[3], 'nothing at index 3'); assert.ok(!trackList[3], 'nothing at index 3');
trackList.addTrack_(newTrack('101')); trackList.addTrack(newTrack('101'));
assert.equal(trackList[3].id, '101', 'id of new item at index 3 is now 101'); assert.equal(trackList[3].id, '101', 'id of new item at index 3 is now 101');
}); });
@ -116,14 +116,14 @@ QUnit.test('a "addtrack" event is triggered when new tracks are added', function
trackList.on('addtrack', addHandler); trackList.on('addtrack', addHandler);
trackList.addTrack_(newTrack('100')); trackList.addTrack(newTrack('100'));
trackList.addTrack_(newTrack('101')); trackList.addTrack(newTrack('101'));
trackList.off('addtrack', addHandler); trackList.off('addtrack', addHandler);
trackList.onaddtrack = addHandler; trackList.onaddtrack = addHandler;
trackList.addTrack_(newTrack('102')); trackList.addTrack(newTrack('102'));
trackList.addTrack_(newTrack('103')); trackList.addTrack(newTrack('103'));
assert.equal(adds, 4, 'we got ' + adds + ' "addtrack" events'); assert.equal(adds, 4, 'we got ' + adds + ' "addtrack" events');
assert.equal(tracks, 4, 'we got a track with every event'); assert.equal(tracks, 4, 'we got a track with every event');
@ -141,12 +141,12 @@ QUnit.test('a "removetrack" event is triggered when tracks are removed', functio
}; };
trackList.on('removetrack', rmHandler); trackList.on('removetrack', rmHandler);
trackList.removeTrack_(trackList.getTrackById('1')); trackList.removeTrack(trackList.getTrackById('1'));
trackList.removeTrack_(trackList.getTrackById('2')); trackList.removeTrack(trackList.getTrackById('2'));
trackList.off('removetrack', rmHandler); trackList.off('removetrack', rmHandler);
trackList.onremovetrack = rmHandler; trackList.onremovetrack = rmHandler;
trackList.removeTrack_(trackList.getTrackById('3')); trackList.removeTrack(trackList.getTrackById('3'));
assert.equal(rms, 3, 'we got ' + rms + ' "removetrack" events'); assert.equal(rms, 3, 'we got ' + rms + ' "removetrack" events');
assert.equal(tracks, 3, 'we got a track with every event'); assert.equal(tracks, 3, 'we got a track with every event');

View File

@ -2,9 +2,12 @@
import TechFaker from '../tech/tech-faker'; import TechFaker from '../tech/tech-faker';
import TrackBaseline from './track-baseline'; import TrackBaseline from './track-baseline';
import Track from '../../../src/js/tracks/track.js'; import Track from '../../../src/js/tracks/track.js';
import TextTrackList from '../../../src/js/tracks/text-track-list.js';
const defaultTech = { const defaultTech = {
textTracks() {}, textTracks() {
return new TextTrackList();
},
on() {}, on() {},
off() {}, off() {},
currentTime() {} currentTime() {}

View File

@ -40,7 +40,7 @@ QUnit.test('only one track is ever selected', function(assert) {
assert.equal(track.selected, true, 'track is selected'); assert.equal(track.selected, true, 'track is selected');
assert.equal(track2.selected, false, 'track2 is unselected'); assert.equal(track2.selected, false, 'track2 is unselected');
list.addTrack_(track3); list.addTrack(track3);
assert.equal(track.selected, false, 'track is unselected'); assert.equal(track.selected, false, 'track is unselected');
assert.equal(track2.selected, false, 'track2 is unselected'); assert.equal(track2.selected, false, 'track2 is unselected');
assert.equal(track3.selected, true, 'track3 is selected'); assert.equal(track3.selected, true, 'track3 is selected');
@ -50,7 +50,7 @@ QUnit.test('only one track is ever selected', function(assert) {
assert.equal(track2.selected, false, 'track2 is unselected'); assert.equal(track2.selected, false, 'track2 is unselected');
assert.equal(track3.selected, false, 'track3 is unselected'); assert.equal(track3.selected, false, 'track3 is unselected');
list.addTrack_(track4); list.addTrack(track4);
assert.equal(track.selected, true, 'track is selected'); assert.equal(track.selected, true, 'track is selected');
assert.equal(track2.selected, false, 'track2 is unselected'); assert.equal(track2.selected, false, 'track2 is unselected');
assert.equal(track3.selected, false, 'track3 is unselected'); assert.equal(track3.selected, false, 'track3 is unselected');
@ -89,7 +89,7 @@ QUnit.test('trigger a change event per selected change', function(assert) {
track.selected = true; track.selected = true;
assert.equal(change, 1, 'one change triggered'); assert.equal(change, 1, 'one change triggered');
list.addTrack_(track3); list.addTrack(track3);
assert.equal(change, 2, 'another change triggered by adding an selected track'); assert.equal(change, 2, 'another change triggered by adding an selected track');
track.selected = true; track.selected = true;
@ -98,6 +98,6 @@ QUnit.test('trigger a change event per selected change', function(assert) {
track.selected = false; track.selected = false;
assert.equal(change, 4, 'another change trigger by changing selected'); assert.equal(change, 4, 'another change trigger by changing selected');
list.addTrack_(track4); list.addTrack(track4);
assert.equal(change, 4, 'no change triggered by adding a unselected track'); assert.equal(change, 4, 'no change triggered by adding a unselected track');
}); });