Added event handling and state change listeners.

This commit is contained in:
Harrison Deng 2022-04-15 17:46:17 -05:00
parent 0ad9dd9008
commit b94622dddf

View File

@ -13,6 +13,14 @@ export default class SongPlayer {
this._playlist = playlist; this._playlist = playlist;
this._current = 0; this._current = 0;
this._volume = 1; this._volume = 1;
this._playing = true;
this._playlistChangeListeners = [];
this._currentSongChangeListeners = [];
this._volumeChangeListeners = [];
this._playingChangeListener = [];
this._allAudioEventListeners = {};
} }
/** /**
@ -21,8 +29,12 @@ export default class SongPlayer {
*/ */
setPlaylist(playlist) { setPlaylist(playlist) {
this._playlist.unloadAllAudio(); this._playlist.unloadAllAudio();
const old = this._playlist;
this._playlist = playlist; this._playlist = playlist;
this._current = 0; this._current = 0;
this._playlistChangeListeners.forEach(playlistChangeListener => {
playlistChangeListener(old, this.getPlaylist());
});
} }
/** /**
@ -38,10 +50,7 @@ export default class SongPlayer {
* @returns {boolean} true if and only if successful in going to the next song. * @returns {boolean} true if and only if successful in going to the next song.
*/ */
next() { next() {
if (this._current >= this._playlist.total() - 1) return false; return this.changeCurrentSongIndex(this.getCurrentSongIndex() + 1);
this.getCurrentSong().unloadAudio();
this._current += 1;
return true;
} }
/** /**
@ -50,10 +59,7 @@ export default class SongPlayer {
* @returns {boolean} true if and only if successful in going to the previous song. * @returns {boolean} true if and only if successful in going to the previous song.
*/ */
previous() { previous() {
if (this._current <= 0) return false; return this.changeCurrentSongIndex(this.getCurrentSongIndex() - 1);
this.getCurrentSong().unloadAudio();
this._current -= 1;
return true;
} }
/** /**
@ -61,24 +67,53 @@ export default class SongPlayer {
* @param {number} index the index of the song to jump to. * @param {number} index the index of the song to jump to.
* @returns {boolean} true if and only if successful jumping to the given index. * @returns {boolean} true if and only if successful jumping to the given index.
*/ */
changeCurrent(index) { changeCurrentSongIndex(index) {
if (index >= this._playlist.total()) return false; if (index >= this._playlist.total()) return false;
if (index <= 0) return false; if (index <= 0) return false;
Object.keys(this._allAudioEventListeners).forEach(key => {
const listeners = this._allAudioEventListeners[key];
listeners.forEach(listener => {
this.getCurrentSong().getAudio().removeEventListener(key, listener);
});
});
this.getCurrentSong().unloadAudio(); this.getCurrentSong().unloadAudio();
const old = this.getCurrentSong();
this._current = index; this._current = index;
this._currentSongChangeListeners.forEach(currentChangeListener => {
currentChangeListener(old, this.getCurrentSong());
});
Object.keys(this._allAudioEventListeners).forEach(key => {
const listeners = this._allAudioEventListeners[key];
listeners.forEach(listener => {
this.getCurrentSong().getAudio().addEventListener(key, listener);
});
});
if (this._playing) {
this.playCurrent();
}
return true; return true;
} }
/**
*
* @returns {number} the current song's index in the playlist.
*/
getCurrentSongIndex() {
return this._current;
}
playCurrent() { playCurrent() {
this.getCurrentSong().getAudio((audio) => { this.getCurrentSong().getAudio((audio) => {
// TODO: May need to perform synchronization check to see if this is still the song to be played. // TODO: May need to perform synchronization check to see if this is still the song to be played.
audio.volume = this._volume; audio.volume = this._volume;
audio.play(); audio.play();
}); });
this._playing = true;
} }
pauseCurrent() { pauseCurrent() {
this.getCurrentSong().getAudio().pause(); this.getCurrentSong().getAudio().pause();
this._playing = true;
} }
/** /**
@ -118,8 +153,16 @@ export default class SongPlayer {
return this.getCurrentSong().getAudio().duration; return this.getCurrentSong().getAudio().duration;
} }
generatePlayElement() { generatePlayElement(size = 64) {
// TODO: Generates a play button in html. const playButton = document.createElement("button");
playButton.classList.add("player-ctrl");
playButton.classList.add("play");
playButton.style.width = size + "px";
playButton.style.height = size + "px";
playButton.style.borderLeftWidth = size + "px";
playButton.style.borderTopWidth = Math.floor(size / 2) + "px";
playButton.style.borderBottomWidth = Math.ceil(size / 2) + "px";
// TODO: Finish this play button with event listeners.
} }
generateNextElement() { generateNextElement() {
@ -141,4 +184,142 @@ export default class SongPlayer {
getCurrentSong() { getCurrentSong() {
return this._playlist.songAtIndex(this._current); return this._playlist.songAtIndex(this._current);
} }
/**
* @callback changeListener
* @param {any} old the previous value.
* @param {any} current the the current (new) value.
*/
/**
*
* @param {changeListener} listener the listener to receive the updates.
* @returns {boolean} true if and only if successfully added the listener.
*/
addCurrentSongChangeListener(listener) {
if (this._currentSongChangeListeners.includes(listener)) return false;
this._currentChangeListener.push(listener);
return true;
}
/**
*
* @param {changeListener} listener the song change listener to remove.
* @returns {boolean} true if and only if the song change listener given was successfully removed.
*/
removeCurrentSongChangeListener(listener) {
const removeIndex = this._currentSongChangeListeners.indexOf(listener);
if (removeIndex < 0) return false;
this._currentSongChangeListeners.splice(removeIndex, 1);
return true;
}
/**
*
* @param {changeListener} listener the playlist change listener to add to the callback list.
* @returns {boolean} true if and only if the given listener was successfully registered.
*/
addPlaylistChangeListener(listener) {
if (this._playlistChangeListeners.includes(listener)) return false;
this._playlistChangeListeners.push(listener);
return true;
}
/**
*
* @param {changeListener} listener the playlist change listener to remove.
* @returns {boolean} true if and only if a listener was successfully removed from the callback list.
*/
removePlaylistChangeListener(listener) {
const removeIndex = this._playlistChangeListeners.indexOf(listener);
if (removeIndex < 0) return false;
this.playlistChangeListener.splice(removeIndex, 1);
return true;
}
/**
*
* @param {changeListener} listener the listener that is called when the player's volume is changed.
* @returns {boolean} true if and only if the listener was successfully added.
*/
addVolumeChangeListener(listener) {
if (this._volumeChangeListeners.includes(listener)) return false;
this._volumeChangeListeners.push(listener);
return true;
}
/**
*
* @param {changeListener} listener the volume change listener to remove.
* @returns {boolean} true if and only if a listener was successfully removed from the callback list.
*/
removeVolumeChangeListener(listener) {
const removeIndex = this._volumeChangeListeners.indexOf(listener);
if (removeIndex < 0) return false;
this._volumeChangeListeners.splice(removeIndex, 1);
return true;
}
/**
*
* @param {changeListener} listener the listener that is called when the player's volume is changed.
* @returns {boolean} true if and only if the listener was successfully added.
*/
addPlayChangeListener(listener) {
if (this._playingChangeListener.includes(listener)) return false;
this._playingChangeListener.push(listener);
return true;
}
/**
*
* @param {changeListener} listener the play change listener to remove.
* @returns {boolean} true if and only if a listener was successfully removed from the callback list.
*/
removePlayChangeListener(listener) {
const removeIndex = this._playingChangeListener.indexOf(listener);
if (removeIndex < 0) return false;
this._playingChangeListener.splice(removeIndex, 1);
return true;
}
/**
*
* @param {string} type the type of the listener on the {@link HTMLAudioElement}.
* @param {EventListener|EventListenerObject} eventListener the event listener.
* @returns {boolean} true if and only if successfully registered event listener.
*/
addEventListenerToCurrentAudio(type, eventListener) {
let typeListeners = this._allAudioEventListeners[type];
if (!typeListeners) {
typeListeners = [];
this._allAudioEventListeners[type] = typeListeners;
}
if (typeListeners.includes(eventListener)) return false;
typeListeners.push(eventListener);
this.getCurrentSong().getAudio().addEventListener(type, eventListener);
return true;
}
/**
*
* @param {string} type the type of the listener on the {@link HTMLAudioElement}.
* @param {EventListener|EventListenerObject} eventListener the event listener.
* @returns {boolean} true if and only if the event listener was successfully added.
*/
removeEventListenerFromCurrentAudio(type, eventListener) {
let typeListeners = this._allAudioEventListeners[type];
if (!typeListeners) return false;
const removeIndex = typeListeners.indexOf(eventListener);
if (removeIndex < 0) return false;
typeListeners.splice(removeIndex, 1);
this.getCurrentSong().getAudio().removeEventListener(type, eventListener);
return true;
}
} }