From 49bed0703922073cf8a50421f7ae8a72428d173f Mon Sep 17 00:00:00 2001 From: Brandon Casey Date: Thu, 19 Jan 2017 15:16:28 -0500 Subject: [PATCH] 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. --- .../audio-track-button.js | 8 +- .../audio-track-menu-item.js | 16 +- .../descriptions-button.js | 13 +- .../text-track-controls/text-track-button.js | 4 - .../text-track-menu-item.js | 15 +- src/js/player.js | 186 ++++++------ src/js/tech/html5.js | 265 ++++++------------ src/js/tech/tech.js | 207 ++++++-------- src/js/tracks/audio-track-list.js | 32 +-- src/js/tracks/text-track-display.js | 39 ++- src/js/tracks/text-track-list.js | 5 +- src/js/tracks/text-track.js | 8 +- src/js/tracks/track-list.js | 10 +- src/js/tracks/track-types.js | 68 +++++ src/js/tracks/video-track-list.js | 30 +- test/unit/tech/tech.test.js | 17 +- test/unit/tracks/audio-track-list.test.js | 8 +- test/unit/tracks/html-track-element.test.js | 5 +- .../tracks/text-track-list-converter.test.js | 16 +- test/unit/tracks/text-track.test.js | 5 +- test/unit/tracks/text-tracks.test.js | 2 +- test/unit/tracks/track-list.test.js | 40 +-- test/unit/tracks/track.test.js | 5 +- test/unit/tracks/video-track-list.test.js | 8 +- 24 files changed, 417 insertions(+), 595 deletions(-) create mode 100644 src/js/tracks/track-types.js diff --git a/src/js/control-bar/audio-track-controls/audio-track-button.js b/src/js/control-bar/audio-track-controls/audio-track-button.js index 2a5874602..9d8bb303f 100644 --- a/src/js/control-bar/audio-track-controls/audio-track-button.js +++ b/src/js/control-bar/audio-track-controls/audio-track-button.js @@ -22,7 +22,7 @@ class AudioTrackButton extends TrackButton { * The key/value store of player options. */ constructor(player, options = {}) { - options.tracks = player.audioTracks && player.audioTracks(); + options.tracks = player.audioTracks(); super(player, options); @@ -49,11 +49,7 @@ class AudioTrackButton extends TrackButton { * An array of menu items */ createItems(items = []) { - const tracks = this.player_.audioTracks && this.player_.audioTracks(); - - if (!tracks) { - return items; - } + const tracks = this.player_.audioTracks(); for (let i = 0; i < tracks.length; i++) { const track = tracks[i]; diff --git a/src/js/control-bar/audio-track-controls/audio-track-menu-item.js b/src/js/control-bar/audio-track-controls/audio-track-menu-item.js index e885ebbbd..50035056a 100644 --- a/src/js/control-bar/audio-track-controls/audio-track-menu-item.js +++ b/src/js/control-bar/audio-track-controls/audio-track-menu-item.js @@ -33,14 +33,12 @@ class AudioTrackMenuItem extends MenuItem { this.track = track; - if (tracks) { - const changeHandler = Fn.bind(this, this.handleTracksChange); + const changeHandler = Fn.bind(this, this.handleTracksChange); - tracks.addEventListener('change', changeHandler); - this.on('dispose', () => { - tracks.removeEventListener('change', changeHandler); - }); - } + tracks.addEventListener('change', changeHandler); + this.on('dispose', () => { + tracks.removeEventListener('change', changeHandler); + }); } /** @@ -59,10 +57,6 @@ class AudioTrackMenuItem extends MenuItem { super.handleClick(event); - if (!tracks) { - return; - } - for (let i = 0; i < tracks.length; i++) { const track = tracks[i]; diff --git a/src/js/control-bar/text-track-controls/descriptions-button.js b/src/js/control-bar/text-track-controls/descriptions-button.js index 1f74af933..08a7fecc3 100644 --- a/src/js/control-bar/text-track-controls/descriptions-button.js +++ b/src/js/control-bar/text-track-controls/descriptions-button.js @@ -29,15 +29,12 @@ class DescriptionsButton extends TextTrackButton { this.el_.setAttribute('aria-label', 'Descriptions Menu'); const tracks = player.textTracks(); + const changeHandler = Fn.bind(this, this.handleTracksChange); - if (tracks) { - const changeHandler = Fn.bind(this, this.handleTracksChange); - - tracks.addEventListener('change', changeHandler); - this.on('dispose', function() { - tracks.removeEventListener('change', changeHandler); - }); - } + tracks.addEventListener('change', changeHandler); + this.on('dispose', function() { + tracks.removeEventListener('change', changeHandler); + }); } /** diff --git a/src/js/control-bar/text-track-controls/text-track-button.js b/src/js/control-bar/text-track-controls/text-track-button.js index bc2d36c77..e7656ed16 100644 --- a/src/js/control-bar/text-track-controls/text-track-button.js +++ b/src/js/control-bar/text-track-controls/text-track-button.js @@ -43,10 +43,6 @@ class TextTrackButton extends TrackButton { const tracks = this.player_.textTracks(); - if (!tracks) { - return items; - } - for (let i = 0; i < tracks.length; i++) { const track = tracks[i]; diff --git a/src/js/control-bar/text-track-controls/text-track-menu-item.js b/src/js/control-bar/text-track-controls/text-track-menu-item.js index 82e96a83b..b19aa2e74 100644 --- a/src/js/control-bar/text-track-controls/text-track-menu-item.js +++ b/src/js/control-bar/text-track-controls/text-track-menu-item.js @@ -34,15 +34,12 @@ class TextTrackMenuItem extends MenuItem { super(player, options); this.track = track; + const changeHandler = Fn.bind(this, this.handleTracksChange); - if (tracks) { - const changeHandler = Fn.bind(this, this.handleTracksChange); - - tracks.addEventListener('change', changeHandler); - 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 // 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 // the change event. As a poor substitute, we manually dispatch // change events whenever the controls modify the mode. - if (tracks && tracks.onchange === undefined) { + if (tracks.onchange === undefined) { let event; this.on(['tap', 'click'], function() { diff --git a/src/js/player.js b/src/js/player.js index f8324df87..c46868b55 100644 --- a/src/js/player.js +++ b/src/js/player.js @@ -25,8 +25,7 @@ import mergeOptions from './utils/merge-options.js'; import textTrackConverter from './tracks/text-track-list-converter.js'; import ModalDialog from './modal-dialog'; import Tech from './tech/tech.js'; -import AudioTrackList from './tracks/audio-track-list.js'; -import VideoTrackList from './tracks/video-track-list.js'; +import {ALL as TRACK_TYPES} from './tracks/track-types'; // The following imports are used only to ensure that the corresponding modules // are always included in the video.js package. Importing the modules will @@ -809,14 +808,11 @@ class Player extends Component { this.isReady_ = false; // Grab tech-specific options from player options and add source and parent element to use. - const techOptions = assign({ + const techOptions = { source, 'nativeControlsForTouch': this.options_.nativeControlsForTouch, 'playerId': this.id(), 'techId': `${this.id()}_${techName}_api`, - 'videoTracks': this.videoTracks_, - 'textTracks': this.textTracks_, - 'audioTracks': this.audioTracks_, 'autoplay': this.options_.autoplay, 'preload': this.options_.preload, 'loop': this.options_.loop, @@ -825,7 +821,15 @@ class Player extends Component { 'language': this.language(), 'playerElIngest': this.playerElIngest_ || false, '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) { techOptions.tag = this.tag; @@ -906,9 +910,11 @@ class Player extends Component { */ unloadTech_() { // Save the current text tracks so that we can reuse the same text tracks with the next tech - this.videoTracks_ = this.videoTracks(); - this.textTracks_ = this.textTracks(); - this.audioTracks_ = this.audioTracks(); + TRACK_TYPES.names.forEach((name) => { + const props = TRACK_TYPES[name]; + + this[props.privateName] = this[props.getterName](); + }); this.textTracksJson_ = textTrackConverter.textTracksToJson(this.tech_); this.isReady_ = false; @@ -2841,102 +2847,6 @@ class Player extends Component { 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 * {@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 * diff --git a/src/js/tech/html5.js b/src/js/tech/html5.js index 7f1e79623..f106d057a 100644 --- a/src/js/tech/html5.js +++ b/src/js/tech/html5.js @@ -4,7 +4,6 @@ import Tech from './tech.js'; import * as Dom from '../utils/dom.js'; import * as Url from '../utils/url.js'; -import * as Fn from '../utils/fn.js'; import log from '../utils/log.js'; import tsml from 'tsml'; import * as browser from '../utils/browser.js'; @@ -13,6 +12,7 @@ import window from 'global/window'; import {assign} from '../utils/obj'; import mergeOptions from '../utils/merge-options.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 @@ -67,7 +67,7 @@ class Html5 extends Tech { } else { // store HTMLTrackElement and TextTrack to remote list this.remoteTextTrackEls().addTrackElement_(node); - this.remoteTextTracks().addTrack_(node.track); + this.remoteTextTracks().addTrack(node.track); if (!crossoriginTracks && !this.el_.hasAttribute('crossorigin') && Url.isCrossOrigin(node.src)) { @@ -82,52 +82,10 @@ class Html5 extends Tech { } } - // TODO: add text tracks into this list - const trackTypes = ['audio', 'video']; - - // 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.proxyNativeTracks_(); + if (this.featuresNativeTextTracks && 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.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 @@ -150,28 +108,81 @@ class Html5 extends Tech { * Dispose of `HTML5` media element and remove all tracks. */ 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_); // tech will handle clearing of the emulated track list 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. * @@ -331,130 +342,18 @@ class Html5 extends Tech { } /** - * Add event listeners to native text track events. This adds the native text tracks - * to our emulated {@link TextTrackList}. + * Called by {@link Player#play} to play using the `Html5` `Tech`. */ - proxyNativeTextTracks_() { - const tt = this.el().textTracks; + play() { + const playPromise = this.el_.play(); - if (tt) { - // Add tracks - if player is initialised after DOM loaded, textTracks - // will not trigger addtrack - for (let i = 0; i < tt.length; i++) { - 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_); + // Catch/silence error when a pause interrupts a play request + // on browsers which return a promise + if (playPromise !== undefined && typeof playPromise.then === 'function') { + playPromise.then(null, (e) => {}); } } - /** - * 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. * diff --git a/src/js/tech/tech.js b/src/js/tech/tech.js index 8695a522a..618249f8e 100644 --- a/src/js/tech/tech.js +++ b/src/js/tech/tech.js @@ -3,13 +3,7 @@ */ 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 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 log from '../utils/log.js'; import { createTimeRange } from '../utils/time-ranges.js'; @@ -18,6 +12,7 @@ import MediaError from '../media-error.js'; import window from 'global/window'; import document from 'global/document'; 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 @@ -68,9 +63,9 @@ function createTrackHelper(self, kind, label, language, options = {}) { } 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; } @@ -108,9 +103,13 @@ class Tech extends Component { this.hasStarted_ = false; }); - this.textTracks_ = options.textTracks; - this.videoTracks_ = options.videoTracks; - this.audioTracks_ = options.audioTracks; + TRACK_TYPES.ALL.names.forEach((name) => { + const props = TRACK_TYPES.ALL[name]; + + 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. if (!this.featuresProgressEvents) { @@ -136,9 +135,8 @@ class Tech extends Component { this.emulateTextTracks(); } - this.autoRemoteTextTracks_ = new TextTrackList(); + this.autoRemoteTextTracks_ = new TRACK_TYPES.ALL.text.ListClass(); - this.initTextTrackListeners(); this.initTrackListeners(); // Turn on component tap events only if not using native controls @@ -332,7 +330,7 @@ class Tech extends Component { dispose() { // 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 if (this.manualProgress) { @@ -369,7 +367,7 @@ class Tech extends Component { if (type === 'text') { 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 - * {@link EventTarget~EventListeners} for `texttrackchange`, `addtrack` and - * `removetrack`. + * Turn on listeners for {@link VideoTrackList}, {@link {AudioTrackList}, and + * {@link TextTrackList} events. * - * @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`. * * @fires Tech#audiotrackchange * @fires Tech#videotrackchange + * @fires Tech#texttrackchange */ initTrackListeners() { - const trackTypes = ['video', 'audio']; - - trackTypes.forEach((type) => { /** * Triggered when tracks are added or removed on the Tech {@link AudioTrackList} * @@ -506,11 +471,20 @@ class Tech extends Component { * @event Tech#videotrackchange * @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 = () => { - this.trigger(`${type}trackchange`); + this.trigger(`${name}trackchange`); }; - const tracks = this[`${type}Tracks`](); + const tracks = this[props.getterName](); tracks.addEventListener('removetrack', trackListChanges); tracks.addEventListener('addtrack', trackListChanges); @@ -585,16 +559,12 @@ class Tech extends Component { emulateTextTracks() { const tracks = this.textTracks(); - if (!tracks) { - return; - } - this.remoteTextTracks().on('addtrack', (e) => { - this.textTracks().addTrack_(e.track); + tracks.addTrack(e.track); }); 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 @@ -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. * @@ -730,7 +643,7 @@ class Tech extends Component { 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 this.remoteTextTrackEls().addTrackElement_(htmlTrackElement); - this.remoteTextTracks().addTrack_(htmlTrackElement.track); + this.remoteTextTracks().addTrack(htmlTrackElement.track); if (manualCleanup !== true) { // create the TextTrackList if it doesn't exist - this.autoRemoteTextTracks_.addTrack_(htmlTrackElement.track); + this.autoRemoteTextTracks_.addTrack(htmlTrackElement.track); } return htmlTrackElement; @@ -785,8 +698,8 @@ class Tech extends Component { // remove HTMLTrackElement and TextTrack from remote list this.remoteTextTrackEls().removeTrackElement_(trackElement); - this.remoteTextTracks().removeTrack_(track); - this.autoRemoteTextTracks_.removeTrack_(track); + this.remoteTextTracks().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} * @private + * @property Tech#textTracks_ */ -Tech.prototype.textTracks_; // eslint-disable-line /** * List of associated audio tracks. * * @type {AudioTrackList} * @private + * @property Tech#audioTracks_ */ -Tech.prototype.audioTracks_; // eslint-disable-line /** * List of associated video tracks. * * @type {VideoTrackList} * @private + * @property Tech#videoTracks_ */ -Tech.prototype.videoTracks_; // eslint-disable-line /** * Boolean indicating wether the `Tech` supports volume control. diff --git a/src/js/tracks/audio-track-list.js b/src/js/tracks/audio-track-list.js index 60b8c5bcc..671e79043 100644 --- a/src/js/tracks/audio-track-list.js +++ b/src/js/tracks/audio-track-list.js @@ -81,15 +81,14 @@ class AudioTrackList extends TrackList { * @param {AudioTrack} track * The AudioTrack to add to the list * - * @fires Track#addtrack - * @private + * @fires TrackList#addtrack */ - addTrack_(track) { + addTrack(track) { if (track.enabled) { disableOthers(this, track); } - super.addTrack_(track); + super.addTrack(track); // native tracks don't have this if (!track.addEventListener) { return; @@ -112,31 +111,6 @@ class AudioTrackList extends TrackList { 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; diff --git a/src/js/tracks/text-track-display.js b/src/js/tracks/text-track-display.js index 40343ec93..d69514638 100644 --- a/src/js/tracks/text-track-display.js +++ b/src/js/tracks/text-track-display.js @@ -114,28 +114,26 @@ class TextTrackDisplay extends Component { let firstDesc; let firstCaptions; - if (trackList) { - for (let i = 0; i < trackList.length; i++) { - const track = trackList[i]; + for (let i = 0; i < trackList.length; i++) { + const track = trackList[i]; - if (track.default) { - if (track.kind === 'descriptions' && !firstDesc) { - firstDesc = track; - } else if (track.kind in modes && !firstCaptions) { - firstCaptions = track; - } + if (track.default) { + if (track.kind === 'descriptions' && !firstDesc) { + firstDesc = track; + } else if (track.kind in modes && !firstCaptions) { + firstCaptions = track; } } + } - // We want to show the first default track but captions and subtitles - // take precedence over descriptions. - // So, display the first default captions or subtitles track - // and otherwise the first default descriptions track. - if (firstCaptions) { - firstCaptions.mode = 'showing'; - } else if (firstDesc) { - firstDesc.mode = 'showing'; - } + // We want to show the first default track but captions and subtitles + // take precedence over descriptions. + // So, display the first default captions or subtitles track + // and otherwise the first default descriptions track. + if (firstCaptions) { + firstCaptions.mode = 'showing'; + } else if (firstDesc) { + firstDesc.mode = 'showing'; } })); } @@ -192,17 +190,12 @@ class TextTrackDisplay extends Component { this.clearDisplay(); - if (!tracks) { - return; - } - // Track display prioritization model: if multiple tracks are 'showing', // display the first 'subtitles' or 'captions' track which is 'showing', // otherwise display the first 'descriptions' track which is 'showing' let descriptionsTrack = null; let captionsSubtitlesTrack = null; - let i = tracks.length; while (i--) { diff --git a/src/js/tracks/text-track-list.js b/src/js/tracks/text-track-list.js index 60f94e426..ae2579ffe 100644 --- a/src/js/tracks/text-track-list.js +++ b/src/js/tracks/text-track-list.js @@ -50,10 +50,9 @@ class TextTrackList extends TrackList { * The text track to add to the list. * * @fires TrackList#addtrack - * @private */ - addTrack_(track) { - super.addTrack_(track); + addTrack(track) { + super.addTrack(track); /** * @listens TextTrack#modechange diff --git a/src/js/tracks/text-track.js b/src/js/tracks/text-track.js index 4b2bd5ce3..90f041560 100644 --- a/src/js/tracks/text-track.js +++ b/src/js/tracks/text-track.js @@ -331,11 +331,9 @@ class TextTrack extends Track { addCue(cue) { const tracks = this.tech_.textTracks(); - if (tracks) { - for (let i = 0; i < tracks.length; i++) { - if (tracks[i] !== this) { - tracks[i].removeCue(cue); - } + for (let i = 0; i < tracks.length; i++) { + if (tracks[i] !== this) { + tracks[i].removeCue(cue); } } diff --git a/src/js/tracks/track-list.js b/src/js/tracks/track-list.js index 19438e60d..44f7fa55e 100644 --- a/src/js/tracks/track-list.js +++ b/src/js/tracks/track-list.js @@ -50,7 +50,7 @@ class TrackList extends EventTarget { }); 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 @@ -65,9 +65,8 @@ class TrackList extends EventTarget { * The audio, video, or text track to add to the list. * * @fires TrackList#addtrack - * @private */ - addTrack_(track) { + addTrack(track) { const index = this.tracks_.length; if (!('' + index in this)) { @@ -99,13 +98,12 @@ class TrackList extends EventTarget { /** * Remove a {@link Track} from the `TrackList` * - * @param {Track} track + * @param {Track} rtrack * The audio, video, or text track to remove from the list. * * @fires TrackList#removetrack - * @private */ - removeTrack_(rtrack) { + removeTrack(rtrack) { let track; for (let i = 0, l = this.length; i < l; i++) { diff --git a/src/js/tracks/track-types.js b/src/js/tracks/track-types.js new file mode 100644 index 000000000..3f3f3511a --- /dev/null +++ b/src/js/tracks/track-types.js @@ -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 +}; diff --git a/src/js/tracks/video-track-list.js b/src/js/tracks/video-track-list.js index 1d9aad20c..15b9b7995 100644 --- a/src/js/tracks/video-track-list.js +++ b/src/js/tracks/video-track-list.js @@ -97,14 +97,13 @@ class VideoTrackList extends TrackList { * The VideoTrack to add to the list * * @fires TrackList#addtrack - * @private */ - addTrack_(track) { + addTrack(track) { if (track.selected) { disableOthers(this, track); } - super.addTrack_(track); + super.addTrack(track); // native tracks don't have this if (!track.addEventListener) { return; @@ -124,31 +123,6 @@ class VideoTrackList extends TrackList { 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; diff --git a/test/unit/tech/tech.test.js b/test/unit/tech/tech.test.js index 30044bbd5..158163db2 100644 --- a/test/unit/tech/tech.test.js +++ b/test/unit/tech/tech.test.js @@ -139,11 +139,11 @@ QUnit.test('dispose() should clear all tracks that are added after creation', fu 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.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.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.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'); }); - diff --git a/test/unit/tracks/audio-track-list.test.js b/test/unit/tracks/audio-track-list.test.js index 860ec5195..35fab2b5c 100644 --- a/test/unit/tracks/audio-track-list.test.js +++ b/test/unit/tracks/audio-track-list.test.js @@ -40,7 +40,7 @@ QUnit.test('only one track is ever enabled', function(assert) { assert.equal(track.enabled, true, 'track is enabled'); assert.equal(track2.enabled, false, 'track2 is disabled'); - list.addTrack_(track3); + list.addTrack(track3); assert.equal(track.enabled, false, 'track is disabled'); assert.equal(track2.enabled, false, 'track2 is disabled'); 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(track3.enabled, false, 'track3 is disabled'); - list.addTrack_(track4); + list.addTrack(track4); assert.equal(track.enabled, true, 'track is enabled'); assert.equal(track2.enabled, false, 'track2 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; 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'); track.enabled = true; @@ -101,7 +101,7 @@ QUnit.test('trigger a change event per enabled change', function(assert) { track.enabled = false; 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'); }); diff --git a/test/unit/tracks/html-track-element.test.js b/test/unit/tracks/html-track-element.test.js index c816c5cd8..c6a549041 100644 --- a/test/unit/tracks/html-track-element.test.js +++ b/test/unit/tracks/html-track-element.test.js @@ -1,8 +1,11 @@ /* eslint-env qunit */ import HTMLTrackElement from '../../../src/js/tracks/html-track-element.js'; +import TextTrackList from '../../../src/js/tracks/text-track-list.js'; const defaultTech = { - textTracks() {}, + textTracks() { + return new TextTrackList(); + }, on() {}, off() {}, currentTime() {} diff --git a/test/unit/tracks/text-track-list-converter.test.js b/test/unit/tracks/text-track-list-converter.test.js index 2efae33b5..6224e911e 100644 --- a/test/unit/tracks/text-track-list-converter.test.js +++ b/test/unit/tracks/text-track-list-converter.test.js @@ -56,8 +56,8 @@ if (Html5.supportsNativeTextTracks()) { const tt = new TextTrackList(); - tt.addTrack_(nativeTrack.track); - tt.addTrack_(emulatedTrack); + tt.addTrack(nativeTrack.track); + tt.addTrack(emulatedTrack); const tech = { $$() { @@ -107,8 +107,8 @@ if (Html5.supportsNativeTextTracks()) { const tt = new TextTrackList(); - tt.addTrack_(nativeTrack.track); - tt.addTrack_(emulatedTrack); + tt.addTrack(nativeTrack.track); + tt.addTrack(emulatedTrack); let addRemotes = 0; const tech = { @@ -177,8 +177,8 @@ QUnit.test('textTracksToJson produces good json output for emulated only', funct const tt = new TextTrackList(); - tt.addTrack_(anotherTrack); - tt.addTrack_(emulatedTrack); + tt.addTrack(anotherTrack); + tt.addTrack(emulatedTrack); const tech = { $$() { @@ -231,8 +231,8 @@ QUnit.test('jsonToTextTracks calls addRemoteTextTrack on the tech with emulated const tt = new TextTrackList(); - tt.addTrack_(anotherTrack); - tt.addTrack_(emulatedTrack); + tt.addTrack(anotherTrack); + tt.addTrack(emulatedTrack); let addRemotes = 0; const tech = { diff --git a/test/unit/tracks/text-track.test.js b/test/unit/tracks/text-track.test.js index c17c1e9b6..c98907839 100644 --- a/test/unit/tracks/text-track.test.js +++ b/test/unit/tracks/text-track.test.js @@ -7,10 +7,13 @@ import TextTrack from '../../../src/js/tracks/text-track.js'; import TestHelpers from '../test-helpers.js'; import proxyquireify from 'proxyquireify'; import sinon from 'sinon'; +import TextTrackList from '../../../src/js/tracks/text-track-list.js'; const proxyquire = proxyquireify(require); const defaultTech = { - textTracks() {}, + textTracks() { + return new TextTrackList(); + }, on() {}, off() {}, currentTime() {} diff --git a/test/unit/tracks/text-tracks.test.js b/test/unit/tracks/text-tracks.test.js index fb7e5cb89..aab476c02 100644 --- a/test/unit/tracks/text-tracks.test.js +++ b/test/unit/tracks/text-tracks.test.js @@ -370,7 +370,7 @@ QUnit.test('removes cuechange event when text track is hidden for emulated track startTime: 2, endTime: 5 }); - player.tech_.textTracks().addTrack_(tt); + player.tech_.textTracks().addTrack(tt); let numTextTrackChanges = 0; diff --git a/test/unit/tracks/track-list.test.js b/test/unit/tracks/track-list.test.js index 2140e3924..56aaeae71 100644 --- a/test/unit/tracks/track-list.test.js +++ b/test/unit/tracks/track-list.test.js @@ -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) { const trackList = new TrackList(this.tracks); - trackList.addTrack_(newTrack('100')); + trackList.addTrack(newTrack('100')); assert.equal(trackList.length, this.tracks.length + 1, 'the length is ' + (this.tracks.length + 1)); - trackList.addTrack_(newTrack('101')); + trackList.addTrack(newTrack('101')); assert.equal(trackList.length, 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, 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, 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) { 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'); - trackList.addTrack_(newTrack('101')); + trackList.addTrack(newTrack('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) { const trackList = new TrackList(this.tracks); - trackList.addTrack_(newTrack('100')); - trackList.addTrack_(newTrack('101')); + trackList.addTrack(newTrack('100')); + trackList.addTrack(newTrack('101')); 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'); - trackList.removeTrack_(trackList.getTrackById('101')); - trackList.removeTrack_(trackList.getTrackById('100')); + trackList.removeTrack(trackList.getTrackById('101')); + trackList.removeTrack(trackList.getTrackById('100')); assert.ok(!trackList[3], 'nothing at index 3'); 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) { 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'); - trackList.removeTrack_(trackList.getTrackById('100')); + trackList.removeTrack(trackList.getTrackById('100')); 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'); }); @@ -116,14 +116,14 @@ QUnit.test('a "addtrack" event is triggered when new tracks are added', function trackList.on('addtrack', addHandler); - trackList.addTrack_(newTrack('100')); - trackList.addTrack_(newTrack('101')); + trackList.addTrack(newTrack('100')); + trackList.addTrack(newTrack('101')); trackList.off('addtrack', addHandler); trackList.onaddtrack = addHandler; - trackList.addTrack_(newTrack('102')); - trackList.addTrack_(newTrack('103')); + trackList.addTrack(newTrack('102')); + trackList.addTrack(newTrack('103')); assert.equal(adds, 4, 'we got ' + adds + ' "addtrack" events'); 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.removeTrack_(trackList.getTrackById('1')); - trackList.removeTrack_(trackList.getTrackById('2')); + trackList.removeTrack(trackList.getTrackById('1')); + trackList.removeTrack(trackList.getTrackById('2')); trackList.off('removetrack', 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(tracks, 3, 'we got a track with every event'); diff --git a/test/unit/tracks/track.test.js b/test/unit/tracks/track.test.js index dd5bf0c38..f1d445755 100644 --- a/test/unit/tracks/track.test.js +++ b/test/unit/tracks/track.test.js @@ -2,9 +2,12 @@ import TechFaker from '../tech/tech-faker'; import TrackBaseline from './track-baseline'; import Track from '../../../src/js/tracks/track.js'; +import TextTrackList from '../../../src/js/tracks/text-track-list.js'; const defaultTech = { - textTracks() {}, + textTracks() { + return new TextTrackList(); + }, on() {}, off() {}, currentTime() {} diff --git a/test/unit/tracks/video-track-list.test.js b/test/unit/tracks/video-track-list.test.js index 01901a6d7..ff37551a6 100644 --- a/test/unit/tracks/video-track-list.test.js +++ b/test/unit/tracks/video-track-list.test.js @@ -40,7 +40,7 @@ QUnit.test('only one track is ever selected', function(assert) { assert.equal(track.selected, true, 'track is selected'); assert.equal(track2.selected, false, 'track2 is unselected'); - list.addTrack_(track3); + list.addTrack(track3); assert.equal(track.selected, false, 'track is unselected'); assert.equal(track2.selected, false, 'track2 is unselected'); 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(track3.selected, false, 'track3 is unselected'); - list.addTrack_(track4); + list.addTrack(track4); assert.equal(track.selected, true, 'track is selected'); assert.equal(track2.selected, false, 'track2 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; 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'); track.selected = true; @@ -98,6 +98,6 @@ QUnit.test('trigger a change event per selected change', function(assert) { track.selected = false; 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'); });