993 lines
37 KiB
HTML
993 lines
37 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>JSDoc: Source: panels/queuePanel/playlistManager.js</title>
|
|
|
|
<script src="scripts/prettify/prettify.js"> </script>
|
|
<script src="scripts/prettify/lang-css.js"> </script>
|
|
<!--[if lt IE 9]>
|
|
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
|
<![endif]-->
|
|
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
|
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
|
</head>
|
|
|
|
<body>
|
|
|
|
<div id="main">
|
|
|
|
<h1 class="page-title">Source: panels/queuePanel/playlistManager.js</h1>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<section>
|
|
<article>
|
|
<pre class="prettyprint source linenums"><code>/*Canopy - The next generation of stoner streaming software
|
|
Copyright (C) 2024-2025 Rainbownapkin and the TTN Community
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as
|
|
published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
|
/**
|
|
* Class representing Playlist Manager UX within the Queue Panel
|
|
*/
|
|
class playlistManager{
|
|
|
|
/**
|
|
* Instantiates a new playlist manager
|
|
* @param {channel} client - Parent Client Management Object
|
|
* @param {Document} panelDocument - Panel Document
|
|
* @param {queuePanel} queuePanel - Parent Queue Panel Object
|
|
*/
|
|
constructor(client, panelDocument, queuePanel){
|
|
/**
|
|
* Parent Client Management Object
|
|
*/
|
|
this.client = client;
|
|
|
|
/**
|
|
* Panel Document
|
|
*/
|
|
this.panelDocument = panelDocument;
|
|
|
|
/**
|
|
* Parent Queue Panel Object
|
|
*/
|
|
this.queuePanel = queuePanel;
|
|
|
|
/**
|
|
* Map of which playlists are open and which are not, for better refresh handling
|
|
*/
|
|
this.openMap = {
|
|
Channel: new Map(),
|
|
User: new Map()
|
|
};
|
|
|
|
//Define Listeners
|
|
this.defineListeners();
|
|
}
|
|
|
|
/**
|
|
* Handles Network-Related Event Listeners
|
|
*/
|
|
defineListeners(){
|
|
this.client.socket.on("chanPlaylists", this.renderChannelPlaylists.bind(this));
|
|
this.client.socket.on("userPlaylists", this.renderUserPlaylists.bind(this));
|
|
}
|
|
|
|
/**
|
|
* Handles Up-stream Document/Panel Changes from the parent Queue Panel object
|
|
*/
|
|
docSwitch(){
|
|
//Grab menus
|
|
this.channelPlaylistDiv = this.panelDocument.querySelector("#queue-channel-playlist-div");
|
|
this.userPlaylistDiv = this.panelDocument.querySelector("#queue-user-playlist-div");
|
|
|
|
//Grab controls
|
|
this.createPlaylistSpan = this.panelDocument.querySelector('#queue-add-playlist-span');
|
|
this.channelPlaylistLabel = this.panelDocument.querySelector('#queue-channel-playlist-span');
|
|
this.channelPlaylistCaret = this.panelDocument.querySelector('#queue-channel-playlist-toggle');
|
|
this.userPlaylistLabel = this.panelDocument.querySelector('#queue-user-playlist-span');
|
|
this.userPlaylistCaret = this.panelDocument.querySelector('#queue-user-playlist-toggle');
|
|
|
|
//Force playlist re-render to fix controls
|
|
this.client.socket.emit('getChannelPlaylists');
|
|
this.client.socket.emit('getUserPlaylists');
|
|
|
|
//Setup Input
|
|
this.setupInput();
|
|
}
|
|
|
|
/**
|
|
* Handles Input-Related Event Listeners
|
|
*/
|
|
setupInput(){
|
|
this.createPlaylistSpan.addEventListener('click', (event)=>{new newPlaylistPopup(event, this.client, this.queuePanel.ownerDoc)})
|
|
this.channelPlaylistLabel.addEventListener('click', this.toggleChannelPlaylists.bind(this));
|
|
this.userPlaylistLabel.addEventListener('click', this.toggleUserPlaylists.bind(this));
|
|
}
|
|
|
|
/* queue control button functions */
|
|
|
|
/**
|
|
* Toggle Channel Playlists
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
toggleChannelPlaylists(event){
|
|
//If the div is hidden
|
|
if(this.channelPlaylistDiv.style.display == 'none'){
|
|
//Light up the button
|
|
this.channelPlaylistLabel.classList.add('positive');
|
|
//Flip the caret
|
|
this.channelPlaylistCaret.classList.replace('bi-caret-right-fill', 'bi-caret-down-fill');
|
|
//Show the div
|
|
this.channelPlaylistDiv.style.display = '';
|
|
}else{
|
|
//Unlight the button
|
|
this.channelPlaylistLabel.classList.remove('positive');
|
|
//Flip the caret
|
|
this.channelPlaylistCaret.classList.replace('bi-caret-down-fill', 'bi-caret-right-fill');
|
|
//Hide the div
|
|
this.channelPlaylistDiv.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Toggle User Playlists
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
toggleUserPlaylists(event){
|
|
//If the div is hidden
|
|
if(this.userPlaylistDiv.style.display == 'none'){
|
|
//Light up the button
|
|
this.userPlaylistLabel.classList.add('positive');
|
|
//Flip the caret
|
|
this.userPlaylistCaret.classList.replace('bi-caret-right-fill', 'bi-caret-down-fill');
|
|
//Show the div
|
|
this.userPlaylistDiv.style.display = '';
|
|
}else{
|
|
//Unlight the button
|
|
this.userPlaylistLabel.classList.remove('positive');
|
|
//Flip the caret
|
|
this.userPlaylistCaret.classList.replace('bi-caret-down-fill', 'bi-caret-right-fill');
|
|
//Hide the div
|
|
this.userPlaylistDiv.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks which playlists where open before a refresh and re-opens them
|
|
* @param {String} location - Whether or not we're dealing with user or channel playlists
|
|
*/
|
|
checkOpenPlaylists(location){
|
|
//If open map is a string, indicating we just renamed a playlist with it's media open
|
|
if(typeof this.openMap[location] == 'string'){
|
|
//Create new map to hold status with the new name of the renamed playlist already added
|
|
this.openMap[location] = new Map([[this.openMap[location], true]]);
|
|
}else{
|
|
//Create new map to hold status
|
|
this.openMap[location] = new Map();
|
|
}
|
|
|
|
let mediaContainerDivs = [];
|
|
|
|
if(location == 'Channel'){
|
|
mediaContainerDivs = this.channelPlaylistDiv.querySelectorAll('.queue-playlist-media-container-div')
|
|
}else{
|
|
mediaContainerDivs = this.userPlaylistDiv.querySelectorAll('.queue-playlist-media-container-div')
|
|
}
|
|
|
|
//For each container Div rendered
|
|
for(let containerDiv of mediaContainerDivs){
|
|
//Set whether or not it's visible in the map
|
|
this.openMap[location].set(containerDiv.dataset['playlist'], (containerDiv.style.display != 'none'));
|
|
}
|
|
}
|
|
|
|
//Main playlist rendering functions
|
|
|
|
/**
|
|
* Renders Channel Playlist list
|
|
* @param {Object} data - Data from server
|
|
*/
|
|
renderChannelPlaylists(data){
|
|
//Check for open playlists
|
|
this.checkOpenPlaylists('Channel');
|
|
|
|
//Clear channel playlist div
|
|
this.channelPlaylistDiv.innerHTML = '';
|
|
|
|
//Append rendered playlists
|
|
this.channelPlaylistDiv.append(...this.renderPlaylists(data, 'Channel'));
|
|
}
|
|
|
|
/**
|
|
* Renders User Playlist list
|
|
* @param {Object} data - Data from server
|
|
*/
|
|
renderUserPlaylists(data){
|
|
//Check for open playlists
|
|
this.checkOpenPlaylists('User');
|
|
|
|
//Clear channel playlist div
|
|
this.userPlaylistDiv.innerHTML = '';
|
|
|
|
//Append rendered playlists
|
|
this.userPlaylistDiv.append(...this.renderPlaylists(data, 'User'));
|
|
}
|
|
|
|
/**
|
|
* Render set of playlists out to Playlist Management Menu
|
|
* @param {Object} data - Data from server
|
|
* @param {String} location - Location to load from, either Channel or User
|
|
* @returns {Node} Rendered out playlist list
|
|
*/
|
|
renderPlaylists(data, location){
|
|
const playlists = [];
|
|
|
|
//For every playlist sent down from the server
|
|
for(let playlistIndex in data){
|
|
//Get playlist from data
|
|
const playlist = data[playlistIndex];
|
|
|
|
//Create a new playlist div
|
|
const playlistDiv = document.createElement('div');
|
|
//Set it's class
|
|
playlistDiv.classList.add('queue-playlist-div');
|
|
|
|
//Create span to hold playlist entry line contents
|
|
const playlistSpan = document.createElement('span');
|
|
//Set classes
|
|
playlistSpan.classList.add('queue-playlist-span');
|
|
|
|
//If this isn't our first rodeo
|
|
if(playlistIndex != 0){
|
|
//make note
|
|
playlistSpan.classList.add('not-first');
|
|
}
|
|
|
|
//assemble playlist entry line
|
|
playlistSpan.append(
|
|
this.renderLabels(playlist, location),
|
|
this.renderControls(playlist, location)
|
|
);
|
|
|
|
//assemble playlist div
|
|
playlistDiv.append(
|
|
playlistSpan,
|
|
this.renderMedia(playlist, location),
|
|
);
|
|
|
|
//add playlist div to playlists array
|
|
playlists.push(playlistDiv);
|
|
}
|
|
|
|
return playlists;
|
|
}
|
|
|
|
//aux rendering functions
|
|
/**
|
|
* Renders Playlist labels
|
|
* @param {Object} playlist - Playlist from server to render label for
|
|
* @param {String} location - Location of playlist (Channel or User)
|
|
* @returns {Node} Rendered out playlist label
|
|
*/
|
|
renderLabels(playlist, location){
|
|
//Create playlist label span
|
|
const playlistLabels = document.createElement('span');
|
|
//Set it's class
|
|
playlistLabels.classList.add('queue-playlist-labels-span');
|
|
|
|
//create playlist title span
|
|
const playlistTitleSpan = document.createElement('span');
|
|
//Set class
|
|
playlistTitleSpan.classList.add('queue-playlist-title-span', 'interactive');
|
|
|
|
//Create playlist title caret
|
|
const playlistTitleCaret = document.createElement('i');
|
|
|
|
//If this is supposed to be open
|
|
if(this.openMap[location].get(playlist.name)){
|
|
//Set class accordingly
|
|
playlistTitleSpan.classList.add('positive');
|
|
playlistTitleCaret.classList.add('bi-caret-down-fill');
|
|
//otherwise
|
|
}else{
|
|
//Set class accordingly
|
|
playlistTitleCaret.classList.add('bi-caret-right-fill');
|
|
}
|
|
|
|
//Create playlist title label
|
|
const playlistTitle = document.createElement('p');
|
|
//Set it's class
|
|
playlistTitle.classList.add('queue-playlist-title');
|
|
//Unescape Sanatized Enteties and safely inject as plaintext
|
|
playlistTitle.innerText = utils.unescapeEntities(playlist.name);
|
|
|
|
//Construct playlist title span
|
|
playlistTitleSpan.appendChild(playlistTitleCaret);
|
|
playlistTitleSpan.appendChild(playlistTitle);
|
|
|
|
//Create playlist count label
|
|
const playlistCount = document.createElement('p');
|
|
//Set it's class
|
|
playlistCount.classList.add('queue-playlist-count');
|
|
//List video count
|
|
playlistCount.innerText = `Count: ${playlist.media.length}`;
|
|
|
|
//Append items to playlist labels span
|
|
playlistLabels.appendChild(playlistTitleSpan);
|
|
playlistLabels.appendChild(playlistCount);
|
|
|
|
//Define input listeners
|
|
playlistTitleSpan.addEventListener('click', this.toggleMedia.bind(this));
|
|
|
|
return playlistLabels;
|
|
}
|
|
|
|
/**
|
|
* Renders out Playlist Controls
|
|
* @param {Object} playlist - Playlist from server to render label for
|
|
* @param {String} location - Location of playlist (Channel or User)
|
|
* @returns {Node} Rendered out playlist controls
|
|
*/
|
|
renderControls(playlist, location){
|
|
//Create playlist control span
|
|
const playlistControls = document.createElement('span');
|
|
//Set it's class
|
|
playlistControls.classList.add('queue-playlist-control-span');
|
|
//Set dataset
|
|
playlistControls.dataset['playlist'] = playlist.name;
|
|
playlistControls.dataset['location'] = location;
|
|
|
|
//Create queue all button
|
|
const playlistQueueRandomButton = document.createElement('button');
|
|
//Set it's classes
|
|
playlistQueueRandomButton.classList.add('queue-playlist-queue-random-button', 'queue-playlist-control');
|
|
//Inject text content
|
|
playlistQueueRandomButton.textContent = 'Random';
|
|
//Set title
|
|
playlistQueueRandomButton.title = 'Queue Random Item from Playlist';
|
|
|
|
//Create queue all button
|
|
const playlistQueueAllButton = document.createElement('button');
|
|
//Set it's classes
|
|
playlistQueueAllButton.classList.add('queue-playlist-queue-all-button', 'queue-playlist-control', 'not-first');
|
|
//Inject text content
|
|
playlistQueueAllButton.textContent = 'All';
|
|
//Set title
|
|
playlistQueueAllButton.title = 'Queue Entire Playlist';
|
|
|
|
//Create add from URL button
|
|
const playlistAddURLButton = document.createElement('button');
|
|
//Set it's classes
|
|
playlistAddURLButton.classList.add('queue-playlist-add-url-button', 'queue-playlist-control', 'positive-button', 'not-first');
|
|
//Set Tile
|
|
playlistAddURLButton.title = 'Add To Playlist From URL'
|
|
|
|
//Create playlist icons (we're using two so we're putting them inside the button :P)
|
|
const playlistAddIcon = document.createElement('i');
|
|
const playlistLinkIcon = document.createElement('i');
|
|
//set classes
|
|
playlistAddIcon.classList.add('bi-plus-lg');
|
|
playlistLinkIcon.classList.add('bi-link-45deg');
|
|
|
|
//Append icons to URL button
|
|
playlistAddURLButton.appendChild(playlistAddIcon);
|
|
playlistAddURLButton.appendChild(playlistLinkIcon);
|
|
|
|
//Create default titles button
|
|
const playlistDefaultTitlesButton = document.createElement('button');
|
|
//Set classes
|
|
playlistDefaultTitlesButton.classList.add('queue-playlist-add-url-button', 'queue-playlist-control', 'bi-tags-fill', 'positive-button', 'not-first');
|
|
//Set title
|
|
playlistDefaultTitlesButton.title = 'Change Default Titles'
|
|
//Set dataset
|
|
playlistDefaultTitlesButton.dataset['titles'] = JSON.stringify(playlist.defaultTitles);
|
|
|
|
//Create rename button
|
|
const playlistRenameButton = document.createElement('button');
|
|
//Set it's classes
|
|
playlistRenameButton.classList.add('queue-playlist-add-url-button', 'queue-playlist-control', 'bi-input-cursor-text', 'positive-button', 'not-first');
|
|
//Set title
|
|
playlistRenameButton.title = 'Rename Playlist'
|
|
|
|
//Create delete button
|
|
const playlistDeleteButton = document.createElement('button');
|
|
//Set it's classes
|
|
playlistDeleteButton.classList.add('queue-playlist-delete-button', 'queue-playlist-control', 'danger-button', 'bi-trash-fill', 'not-first');
|
|
//Set title
|
|
playlistDeleteButton.title = 'Delete Playlist'
|
|
|
|
//Append items to playlist control span
|
|
playlistControls.append(
|
|
playlistQueueRandomButton,
|
|
playlistQueueAllButton,
|
|
playlistAddURLButton,
|
|
playlistDefaultTitlesButton,
|
|
playlistRenameButton,
|
|
playlistDeleteButton
|
|
);
|
|
|
|
//Define input event listeners
|
|
playlistAddURLButton.addEventListener('click', this.addURL.bind(this));
|
|
playlistDefaultTitlesButton.addEventListener('click', this.editDefaultTitles.bind(this));
|
|
playlistRenameButton.addEventListener('click', this.renamePlaylist.bind(this));
|
|
playlistQueueRandomButton.addEventListener('click', this.queueRandom.bind(this));
|
|
playlistQueueAllButton.addEventListener('click', this.queueAll.bind(this));
|
|
playlistDeleteButton.addEventListener('click', this.deletePlaylist.bind(this));
|
|
|
|
return playlistControls;
|
|
}
|
|
|
|
/**
|
|
* Renders media object out for an entire playlist
|
|
* @param {Object} playlist - Playlist from server to render label for
|
|
* @param {String} location - Location of playlist (Channel or User)
|
|
* @returns {Node} Rendered out playlist
|
|
*/
|
|
renderMedia(playlist, location){
|
|
//Create media container div
|
|
const mediaContainer = document.createElement('div');
|
|
//Set classes
|
|
mediaContainer.classList.add('queue-playlist-media-container-div');
|
|
|
|
//If the playlist wasn't set to open in the open map
|
|
if(!this.openMap[location].get(playlist.name)){
|
|
//Auto-hide media container
|
|
mediaContainer.style.display = 'none';
|
|
}
|
|
|
|
//Set dataset
|
|
mediaContainer.dataset['playlist'] = playlist.name;
|
|
|
|
for(let mediaIndex in playlist.media){
|
|
//Grab media object from playlist
|
|
const media = playlist.media[mediaIndex];
|
|
|
|
//Sanatize title text
|
|
const title = utils.unescapeEntities(media.title);
|
|
|
|
//Create media div
|
|
const mediaDiv = document.createElement('div');
|
|
//Set class
|
|
mediaDiv.classList.add('queue-playlist-media-div');
|
|
//Inject title
|
|
mediaDiv.title = title;
|
|
|
|
//If this isn't our first rodeo
|
|
if(mediaIndex != 0){
|
|
mediaDiv.classList.add('not-first');
|
|
}
|
|
|
|
|
|
//Create media title
|
|
const mediaTitle = document.createElement('p');
|
|
//Set class
|
|
mediaTitle.classList.add('queue-playlist-media-title');
|
|
//Inject text content
|
|
mediaTitle.innerText = title;
|
|
|
|
//Append items to media div
|
|
mediaDiv.append(
|
|
mediaTitle,
|
|
this.renderMediaControls(media, playlist, location)
|
|
);
|
|
|
|
|
|
//Append media div to media container
|
|
mediaContainer.appendChild(mediaDiv);
|
|
}
|
|
|
|
//return media container
|
|
return mediaContainer;
|
|
}
|
|
|
|
/**
|
|
* Renders controls out for a single media entry within a playlist
|
|
* @param {Object} media - Media object from playlist to render controls for
|
|
* @param {Object} playlist - Playlist from server to render label for
|
|
* @param {String} location - Location of playlist (Channel or User)
|
|
* @returns {Node} Rendered out playlist
|
|
*/
|
|
renderMediaControls(media, playlist, location){
|
|
//Create media control span
|
|
const mediaControlSpan = document.createElement('span');
|
|
//Set it's class
|
|
mediaControlSpan.classList.add('queue-playlist-media-control-span');
|
|
//Set dataset
|
|
mediaControlSpan.dataset['playlist'] = playlist.name;
|
|
mediaControlSpan.dataset['uuid'] = media.uuid;
|
|
mediaControlSpan.dataset['location'] = location;
|
|
|
|
//Create Queue Media icon
|
|
const queueMediaIcon = document.createElement('i');
|
|
//set class
|
|
queueMediaIcon.classList.add('queue-playlist-control', 'queue-playlist-media-queue-icon', 'bi-play-circle');
|
|
//Set title
|
|
queueMediaIcon.title = (`Queue '${media.title}'`);
|
|
|
|
//Create delete media icon
|
|
const deleteMediaIcon = document.createElement('i');
|
|
//set class
|
|
deleteMediaIcon.classList.add('queue-playlist-control', 'queue-playlist-media-delete-icon', 'danger-text', 'bi-trash-fill');
|
|
//Set title
|
|
deleteMediaIcon.title = `Delete '${media.title}' from playlist '${playlist.name}'`;
|
|
|
|
//Append items to media control span
|
|
mediaControlSpan.appendChild(queueMediaIcon);
|
|
mediaControlSpan.appendChild(deleteMediaIcon);
|
|
|
|
//Handle input event listeners
|
|
queueMediaIcon.addEventListener('click', this.queueMedia.bind(this));
|
|
deleteMediaIcon.addEventListener('click', this.deleteMedia.bind(this));
|
|
|
|
//Return media control span
|
|
return mediaControlSpan;
|
|
}
|
|
|
|
/**
|
|
* Toggle Media List
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
toggleMedia(event){
|
|
//Grab playlist title caret
|
|
const playlistTitleCaret = event.target.querySelector('i');
|
|
//I hope my mother doesn't see this next line, god I hate dot crawling...
|
|
const mediaContainer = event.target.parentNode.parentNode.nextElementSibling;
|
|
|
|
//If the div is hidden
|
|
if(mediaContainer.style.display == 'none'){
|
|
//Light up the button
|
|
event.target.classList.add('positive');
|
|
//Flip the caret
|
|
playlistTitleCaret.classList.replace('bi-caret-right-fill', 'bi-caret-down-fill');
|
|
//Show the div
|
|
mediaContainer.style.display = '';
|
|
}else{
|
|
//Unlight the button
|
|
event.target.classList.remove('positive');
|
|
//Flip the caret
|
|
playlistTitleCaret.classList.replace('bi-caret-down-fill', 'bi-caret-right-fill');
|
|
//Hide the div
|
|
mediaContainer.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add URL to playlist
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
addURL(event){
|
|
new addURLPopup(
|
|
event,
|
|
event.target.parentNode.dataset['playlist'],
|
|
event.target.parentNode.dataset['location'],
|
|
this.client,
|
|
this.queuePanel.ownerDoc
|
|
);
|
|
}
|
|
|
|
//playlist control functions
|
|
|
|
/**
|
|
* Sends request to server to edit default titles
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
editDefaultTitles(event){
|
|
new defaultTitlesPopup(
|
|
event,
|
|
event.target.parentNode.dataset['playlist'],
|
|
JSON.parse(event.target.dataset['titles']),
|
|
event.target.parentNode.dataset['location'],
|
|
this.client,
|
|
this.queuePanel.ownerDoc
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Sends request to server to rename playlists
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
renamePlaylist(event){
|
|
new renamePopup(
|
|
event,
|
|
event.target.parentNode.dataset['playlist'],
|
|
this.client,
|
|
this.queuePanel.ownerDoc,
|
|
handleOpenedMedia.bind(this)
|
|
);
|
|
|
|
function handleOpenedMedia(newName){
|
|
//do an ugly dot crawl to get the media container div
|
|
const mediaContainer = event.target.parentNode.parentNode.nextElementSibling;
|
|
|
|
//If the media container is visible
|
|
if(mediaContainer.style.display != 'none'){
|
|
//Set openMap to new name indicating the new playlist has it's media opened
|
|
this.openMap[event.target.parentNode.dataset['location']] = newName;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sends request to server to queue all playlist items
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
queueAll(event){
|
|
this.client.socket.emit(`queue${event.target.parentNode.dataset['location']}Playlist`, {playlist: event.target.parentNode.dataset['playlist']});
|
|
}
|
|
|
|
/**
|
|
* Sends request to server to queue a playlist item
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
queueMedia(event){
|
|
this.client.socket.emit(`queueFrom${event.target.parentNode.dataset['location']}Playlist`,{playlist: event.target.parentNode.dataset['playlist'], uuid: event.target.parentNode.dataset['uuid']});
|
|
}
|
|
|
|
/**
|
|
* Sends request to server to queue a random playlist item
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
queueRandom(event){
|
|
this.client.socket.emit(`queueRandomFrom${event.target.parentNode.dataset['location']}Playlist`,{playlist: event.target.parentNode.dataset['playlist']});
|
|
}
|
|
|
|
/**
|
|
* Sends request to server to delete a playlist
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
deletePlaylist(event){
|
|
this.client.socket.emit(`delete${event.target.parentNode.dataset['location']}Playlist`, {playlist: event.target.parentNode.dataset['playlist']});
|
|
}
|
|
|
|
/**
|
|
* Sends request to server to delete a playlist item
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
deleteMedia(event ){
|
|
this.client.socket.emit(`delete${event.target.parentNode.dataset['location']}PlaylistMedia`, {playlist: event.target.parentNode.dataset['playlist'], uuid: event.target.parentNode.dataset['uuid']});
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Class representing pop-up dialogue for creating a new playlist
|
|
*/
|
|
class newPlaylistPopup{
|
|
/**
|
|
* Instantiates a New Playlist Popup
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
* @param {channel} client - Parent Client Management Object
|
|
* @param {Document} doc - Current owner documnet of the panel, so we know where to drop our pop-up
|
|
*/
|
|
constructor(event, client, doc){
|
|
/**
|
|
* Parent Client Management Object
|
|
*/
|
|
this.client = client;
|
|
|
|
//Create media popup and call async constructor when done
|
|
//unfortunately we cant call constructors asyncronously, and we cant call back to this from super, so we can't extend this as it stands :(
|
|
/**
|
|
* canopyUXUtils.popup() object
|
|
*/
|
|
this.popup = new canopyUXUtils.popup('/newPlaylist', true, this.asyncConstructor.bind(this), doc, false);
|
|
}
|
|
|
|
/**
|
|
* Continuation of object construction, called after child popup object construction
|
|
*/
|
|
asyncConstructor(){
|
|
this.name = this.popup.contentDiv.querySelector('#queue-create-playlist-popup-name');
|
|
this.defaultTitles = this.popup.contentDiv.querySelector('#queue-create-playlist-popup-default-titles');
|
|
this.location = this.popup.contentDiv.querySelector('#queue-create-playlist-popup-location');
|
|
this.saveButton = this.popup.contentDiv.querySelector('#queue-create-playlist-popup-save');
|
|
|
|
this.setupInput();
|
|
}
|
|
|
|
/**
|
|
* Defines input-related Event Handlers
|
|
*/
|
|
setupInput(){
|
|
//Setup input
|
|
this.saveButton.addEventListener('click', this.createPlaylist.bind(this));
|
|
this.popup.popupDiv.addEventListener('keydown', this.createPlaylist.bind(this));
|
|
}
|
|
|
|
/**
|
|
* Sends request to create a playlist off to the server
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
createPlaylist(event){
|
|
//If we clicked or hit enter
|
|
if(event.key == null || (event.key == "Enter" && this.defaultTitles !== this.popup.doc.activeElement)){
|
|
|
|
//Tell the server to create a new playlist
|
|
this.client.socket.emit(`create${this.location.value}Playlist`, {
|
|
playlist: this.name.value,
|
|
defaultTitles: this.defaultTitles.value.split('\n')
|
|
});
|
|
|
|
//Close the popup
|
|
this.popup.closePopup();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Class representing pop-up dialogue which adds media to a given playlist
|
|
*/
|
|
class addURLPopup{
|
|
/**
|
|
* Instantiates a new Add URL Pop-up
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
* @param {String} playlist - Playlist name
|
|
* @param {String} location - Location of playlist, either Channel or User
|
|
* @param {channel} client - Parent Client Management Object
|
|
* @param {Document} doc - Current owner documnet of the panel, so we know where to drop our pop-up
|
|
*/
|
|
constructor(event, playlist, location, client, doc){
|
|
/**
|
|
* Parent Client Management Object
|
|
*/
|
|
this.client = client;
|
|
|
|
/**
|
|
* Playlist Name
|
|
*/
|
|
this.playlist = playlist
|
|
|
|
/**
|
|
* Location of playlist, either Channel or User
|
|
*/
|
|
this.location = location;
|
|
|
|
//Create media popup and call async constructor when done
|
|
//unfortunately we cant call constructors asyncronously, and we cant call back to this from super, so we can't extend this as it stands :(
|
|
/**
|
|
* canopyUXUtils.popup() object
|
|
*/
|
|
this.popup = new canopyUXUtils.popup('/addToPlaylist', true, this.asyncConstructor.bind(this), doc);
|
|
}
|
|
|
|
/**
|
|
* Continuation of object construction, called after child popup object construction
|
|
*/
|
|
asyncConstructor(){
|
|
this.urlPrompt = this.popup.contentDiv.querySelector('#playlist-add-media-popup-prompt');
|
|
this.addButton = this.popup.contentDiv.querySelector('#playlist-add-media-popup-button');
|
|
|
|
this.setupInput();
|
|
}
|
|
|
|
/**
|
|
* Defines input-related Event Handlers
|
|
*/
|
|
setupInput(){
|
|
//Setup input
|
|
this.addButton.addEventListener('click', this.addToPlaylist.bind(this));
|
|
this.popup.popupDiv.addEventListener('keydown', this.addToPlaylist.bind(this));
|
|
}
|
|
|
|
/**
|
|
* Handles sending request to add to a playlist to the server
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
addToPlaylist(event){
|
|
//If we clicked or hit enter
|
|
if(event.key == null || event.key == "Enter"){
|
|
|
|
//Tell the server to add url to the playlist
|
|
this.client.socket.emit(`addTo${this.location}Playlist`, {
|
|
//this.client.socket.emit(`addToChannelPlaylist`, {
|
|
playlist: this.playlist,
|
|
url: this.urlPrompt.value
|
|
});
|
|
|
|
//Close the popup
|
|
this.popup.closePopup();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Class Representing popup dialogue for changing playlists defualt titles
|
|
*/
|
|
class defaultTitlesPopup{
|
|
/**
|
|
* Instantiates a new Default Titles Popup
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
* @param {String} playlist - Playlist name
|
|
* @param {String} titles - List of titles, denoted by newlines
|
|
* @param {String} location - Location of playlist, either Channel or User
|
|
* @param {channel} client - Parent Client Management Object
|
|
* @param {Document} doc - Current owner documnet of the panel, so we know where to drop our pop-up
|
|
*/
|
|
constructor(event, playlist, titles, location, client, doc){
|
|
/**
|
|
* Parent Client Management Object
|
|
*/
|
|
this.client = client;
|
|
|
|
/**
|
|
* Playlist Name
|
|
*/
|
|
this.playlist = playlist
|
|
|
|
/**
|
|
* Location of playlist, either Channel or User
|
|
*/
|
|
this.location = location;
|
|
|
|
/**
|
|
* Array of titles to set
|
|
*/
|
|
this.titles = titles.join('\n');
|
|
|
|
//Create media popup and call async constructor when done
|
|
//unfortunately we cant call constructors asyncronously, and we cant call back to this from super, so we can't extend this as it stands :(
|
|
/**
|
|
* canopyUXUtils.popup() object
|
|
*/
|
|
this.popup = new canopyUXUtils.popup('/playlistDefaultTitles', true, this.asyncConstructor.bind(this), doc, false);
|
|
}
|
|
|
|
/**
|
|
* Continuation of object construction, called after child popup object construction
|
|
*/
|
|
asyncConstructor(){
|
|
this.titlePrompt = this.popup.contentDiv.querySelector('#playlist-default-titles-popup-prompt');
|
|
this.titleButton = this.popup.contentDiv.querySelector('#playlist-default-media-popup-button');
|
|
|
|
this.titlePrompt.textContent = utils.unescapeEntities(this.titles);
|
|
this.setupInput();
|
|
}
|
|
|
|
/**
|
|
* Defines input-related Event Handlers
|
|
*/
|
|
setupInput(){
|
|
//Setup input
|
|
this.titleButton.addEventListener('click', this.changeDefaultTitles.bind(this));
|
|
this.popup.popupDiv.addEventListener('keydown', this.changeDefaultTitles.bind(this));
|
|
}
|
|
|
|
/**
|
|
* Handles sending request to change default titles of playlist to the server
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
changeDefaultTitles(event){
|
|
//If we clicked or hit enter while the prompt wasn't active
|
|
if(event.key == null || (event.key == "Enter" && this.titlePrompt !== this.popup.doc.activeElement)){
|
|
|
|
//Tell the server to change the titles
|
|
this.client.socket.emit(`changeDefaultTitles${this.location}Playlist`, {
|
|
playlist: this.playlist,
|
|
defaultTitles: this.titlePrompt.value.split('\n')
|
|
});
|
|
|
|
//Close the popup
|
|
this.popup.closePopup();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Class representing pop-up dialogue to rename a playlist
|
|
*/
|
|
class renamePopup{
|
|
/**
|
|
* Instantiates a new Rename Pop-up
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
* @param {String} playlist - Playlist name
|
|
* @param {channel} client - Parent Client Management Object
|
|
* @param {Document} doc - Current owner documnet of the panel, so we know where to drop our pop-up
|
|
* @param {Function} cb - Callback function, passed new name upon rename
|
|
*/
|
|
constructor(event, playlist, client, doc, cb){
|
|
/**
|
|
* Parent Client Management Object
|
|
*/
|
|
this.client = client;
|
|
|
|
/**
|
|
* Playlist Name
|
|
*/
|
|
this.playlist = playlist
|
|
|
|
/**
|
|
* Callback Function, passed new name upon rename
|
|
*/
|
|
this.cb = cb;
|
|
|
|
//Create media popup and call async constructor when done
|
|
//unfortunately we cant call constructors asyncronously, and we cant call back to this from super, so we can't extend this as it stands :(
|
|
/**
|
|
* canopyUXUtils.popup() object
|
|
*/
|
|
this.popup = new canopyUXUtils.popup('/renamePlaylist', true, this.asyncConstructor.bind(this), doc);
|
|
}
|
|
|
|
/**
|
|
* Continuation of object construction, called after child popup object construction
|
|
*/
|
|
asyncConstructor(){
|
|
this.renamePrompt = this.popup.contentDiv.querySelector('#playlist-rename-popup-prompt');
|
|
this.renameButton = this.popup.contentDiv.querySelector('#playlist-rename-popup-button');
|
|
|
|
this.setupInput();
|
|
}
|
|
|
|
/**
|
|
* Defines input-related Event Handlers
|
|
*/
|
|
setupInput(){
|
|
//Setup input
|
|
this.renameButton.addEventListener('click', this.renamePlaylist.bind(this));
|
|
this.popup.popupDiv.addEventListener('keydown', this.renamePlaylist.bind(this));
|
|
}
|
|
|
|
/**
|
|
* Handles sending request to rename playlist to the server
|
|
* @param {Event} event - Event passed down from Event Listener
|
|
*/
|
|
renamePlaylist(event){
|
|
//If we clicked or hit enter while the prompt wasn't active
|
|
if(event.key == null || event.key == "Enter"){
|
|
|
|
//Tell the server to change the titles
|
|
this.client.socket.emit('renameChannelPlaylist', {
|
|
playlist: this.playlist,
|
|
name: this.renamePrompt.value
|
|
});
|
|
|
|
//if CB is a function
|
|
if(typeof this.cb == 'function'){
|
|
//Hand it back the new name
|
|
this.cb(this.renamePrompt.value);
|
|
}
|
|
|
|
//Close the popup
|
|
this.popup.closePopup();
|
|
}
|
|
}
|
|
}</code></pre>
|
|
</article>
|
|
</section>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
<nav>
|
|
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="addURLPopup.html">addURLPopup</a></li><li><a href="cPanel.html">cPanel</a></li><li><a href="channel.html">channel</a></li><li><a href="chatBox.html">chatBox</a></li><li><a href="chatPostprocessor.html">chatPostprocessor</a></li><li><a href="clearPopup.html">clearPopup</a></li><li><a href="commandPreprocessor.html">commandPreprocessor</a></li><li><a href="commandProcessor.html">commandProcessor</a></li><li><a href="defaultTitlesPopup.html">defaultTitlesPopup</a></li><li><a href="emotePanel.html">emotePanel</a></li><li><a href="hlsBase.html">hlsBase</a></li><li><a href="hlsLiveStreamHandler.html">hlsLiveStreamHandler</a></li><li><a href="mediaHandler.html">mediaHandler</a></li><li><a href="newPlaylistPopup.html">newPlaylistPopup</a></li><li><a href="nullHandler.html">nullHandler</a></li><li><a href="panelObj.html">panelObj</a></li><li><a href="player.html">player</a></li><li><a href="playlistManager.html">playlistManager</a></li><li><a href="poppedPanel.html">poppedPanel</a></li><li><a href="queuePanel.html">queuePanel</a></li><li><a href="rawFileBase.html">rawFileBase</a></li><li><a href="rawFileHandler.html">rawFileHandler</a></li><li><a href="renamePopup.html">renamePopup</a></li><li><a href="reschedulePopup.html">reschedulePopup</a></li><li><a href="schedulePopup.html">schedulePopup</a></li><li><a href="settingsPanel.html">settingsPanel</a></li><li><a href="userList.html">userList</a></li><li><a href="youtubeEmbedHandler.html">youtubeEmbedHandler</a></li></ul><h3>Global</h3><ul><li><a href="global.html#onYouTubeIframeAPIReady">onYouTubeIframeAPIReady</a></li></ul>
|
|
</nav>
|
|
|
|
<br class="clear">
|
|
|
|
<footer>
|
|
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 4.0.4</a> on Sat Sep 06 2025 10:33:49 GMT-0400 (Eastern Daylight Time)
|
|
</footer>
|
|
|
|
<script> prettyPrint(); </script>
|
|
<script src="scripts/linenumber.js"> </script>
|
|
</body>
|
|
</html>
|