diff --git a/www/doc/client/addURLPopup.html b/www/doc/client/addURLPopup.html index 92c5431..f37cb94 100644 --- a/www/doc/client/addURLPopup.html +++ b/www/doc/client/addURLPopup.html @@ -238,7 +238,7 @@
Source:
@@ -332,7 +332,7 @@
Source:
@@ -394,7 +394,7 @@
Source:
@@ -456,7 +456,7 @@
Source:
@@ -518,7 +518,7 @@
Source:
@@ -647,7 +647,7 @@
Source:
@@ -735,7 +735,7 @@
Source:
@@ -823,7 +823,7 @@
Source:
@@ -869,13 +869,13 @@
diff --git a/www/doc/client/cPanel.html b/www/doc/client/cPanel.html index 87ad8b4..1192d30 100644 --- a/www/doc/client/cPanel.html +++ b/www/doc/client/cPanel.html @@ -2596,13 +2596,13 @@
diff --git a/www/doc/client/channel.html b/www/doc/client/channel.html index 939c935..75abd0f 100644 --- a/www/doc/client/channel.html +++ b/www/doc/client/channel.html @@ -1248,13 +1248,13 @@
diff --git a/www/doc/client/channel.js.html b/www/doc/client/channel.js.html index 0504ca2..8df3ff0 100644 --- a/www/doc/client/channel.js.html +++ b/www/doc/client/channel.js.html @@ -283,13 +283,13 @@ const client = new channel();
diff --git a/www/doc/client/chat.js.html b/www/doc/client/chat.js.html index 83b5486..78164b3 100644 --- a/www/doc/client/chat.js.html +++ b/www/doc/client/chat.js.html @@ -656,13 +656,13 @@ L /**
diff --git a/www/doc/client/chatBox.html b/www/doc/client/chatBox.html index 83a0a6a..67c27d7 100644 --- a/www/doc/client/chatBox.html +++ b/www/doc/client/chatBox.html @@ -4644,13 +4644,13 @@ Also prevents horizontal scroll-bars from chat/window resizing
diff --git a/www/doc/client/chatPostprocessor.html b/www/doc/client/chatPostprocessor.html index 5d8916b..210e01a 100644 --- a/www/doc/client/chatPostprocessor.html +++ b/www/doc/client/chatPostprocessor.html @@ -1788,13 +1788,13 @@ Internal command used by several text filters to prevent code re-writes
diff --git a/www/doc/client/chatPostprocessor.js.html b/www/doc/client/chatPostprocessor.js.html index 3d4d1ee..76f0bcf 100644 --- a/www/doc/client/chatPostprocessor.js.html +++ b/www/doc/client/chatPostprocessor.js.html @@ -665,13 +665,13 @@ class chatPostprocessor{
diff --git a/www/doc/client/clearPopup.html b/www/doc/client/clearPopup.html new file mode 100644 index 0000000..17ca5ee --- /dev/null +++ b/www/doc/client/clearPopup.html @@ -0,0 +1,799 @@ + + + + + JSDoc: Class: clearPopup + + + + + + + + + + +
+ +

Class: clearPopup

+ + + + + + +
+ +
+ +

clearPopup(event, client, cb, doc)

+ +
Class represneting pop-up dialogue for clearing queue between a range of two dates
+ + +
+ +
+
+ + + + +

Constructor

+ + + +

new clearPopup(event, client, cb, doc)

+ + + + + + +
+ Instantiates a new queue Clear Popup +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
client + + +channel + + + + Parent Client Management Object
cb + + +function + + + + Callback function, passed upon pop-up creation
doc + + +Document + + + + Current owner documnet of the panel, so we know where to drop our pop-up
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

cb

+ + + + +
+ Callback function, passed upon pop-up creation +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

client

+ + + + +
+ Parent Client Management Object +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
+ canopyUXUtils.popup() object +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

asyncConstructor()

+ + + + + + +
+ Continuation of object construction, called after child popup object construction +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

clear(event)

+ + + + + + +
+ Handles sending request to clear playlist between two dates to the server +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

setupInput()

+ + + + + + +
+ Defines input-related Event Handlers +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/www/doc/client/commandPreprocessor.html b/www/doc/client/commandPreprocessor.html index 33199f2..49196d1 100644 --- a/www/doc/client/commandPreprocessor.html +++ b/www/doc/client/commandPreprocessor.html @@ -1906,13 +1906,13 @@
diff --git a/www/doc/client/commandPreprocessor.js.html b/www/doc/client/commandPreprocessor.js.html index 925e59c..6cc1133 100644 --- a/www/doc/client/commandPreprocessor.js.html +++ b/www/doc/client/commandPreprocessor.js.html @@ -365,13 +365,13 @@ class commandProcessor{
diff --git a/www/doc/client/commandProcessor.html b/www/doc/client/commandProcessor.html index e42636f..9b278c6 100644 --- a/www/doc/client/commandProcessor.html +++ b/www/doc/client/commandProcessor.html @@ -415,13 +415,13 @@
diff --git a/www/doc/client/cpanel.js.html b/www/doc/client/cpanel.js.html index ec70a43..85475ec 100644 --- a/www/doc/client/cpanel.js.html +++ b/www/doc/client/cpanel.js.html @@ -509,13 +509,13 @@ class poppedPanel{
diff --git a/www/doc/client/defaultTitlesPopup.html b/www/doc/client/defaultTitlesPopup.html index 833f323..a4612e6 100644 --- a/www/doc/client/defaultTitlesPopup.html +++ b/www/doc/client/defaultTitlesPopup.html @@ -261,7 +261,7 @@
Source:
@@ -355,7 +355,7 @@
Source:
@@ -417,7 +417,7 @@
Source:
@@ -479,7 +479,7 @@
Source:
@@ -541,7 +541,7 @@
Source:
@@ -603,7 +603,7 @@
Source:
@@ -683,7 +683,7 @@
Source:
@@ -820,7 +820,7 @@
Source:
@@ -908,7 +908,7 @@
Source:
@@ -954,13 +954,13 @@
diff --git a/www/doc/client/emotePanel.html b/www/doc/client/emotePanel.html index f7040de..51ae3df 100644 --- a/www/doc/client/emotePanel.html +++ b/www/doc/client/emotePanel.html @@ -2243,13 +2243,13 @@
diff --git a/www/doc/client/global.html b/www/doc/client/global.html index e837b79..04f0bc5 100644 --- a/www/doc/client/global.html +++ b/www/doc/client/global.html @@ -202,13 +202,13 @@
diff --git a/www/doc/client/hlsBase.html b/www/doc/client/hlsBase.html index 36b9e48..96474c8 100644 --- a/www/doc/client/hlsBase.html +++ b/www/doc/client/hlsBase.html @@ -2913,13 +2913,13 @@
diff --git a/www/doc/client/hlsLiveStreamHandler.html b/www/doc/client/hlsLiveStreamHandler.html index a5533b1..2215d4a 100644 --- a/www/doc/client/hlsLiveStreamHandler.html +++ b/www/doc/client/hlsLiveStreamHandler.html @@ -2890,13 +2890,13 @@
diff --git a/www/doc/client/index.html b/www/doc/client/index.html index b726445..014a751 100644 --- a/www/doc/client/index.html +++ b/www/doc/client/index.html @@ -81,13 +81,13 @@ This new codebase intends to solve the following issues with the current CyTube
diff --git a/www/doc/client/mediaHandler.html b/www/doc/client/mediaHandler.html index 7a4a2e5..a8ebff2 100644 --- a/www/doc/client/mediaHandler.html +++ b/www/doc/client/mediaHandler.html @@ -2695,13 +2695,13 @@
diff --git a/www/doc/client/mediaHandler.js.html b/www/doc/client/mediaHandler.js.html index 541407a..5eb03e5 100644 --- a/www/doc/client/mediaHandler.js.html +++ b/www/doc/client/mediaHandler.js.html @@ -829,13 +829,13 @@ class hlsLiveStreamHandler extends hlsBase{
diff --git a/www/doc/client/newPlaylistPopup.html b/www/doc/client/newPlaylistPopup.html index e1b91b7..53bdd5c 100644 --- a/www/doc/client/newPlaylistPopup.html +++ b/www/doc/client/newPlaylistPopup.html @@ -192,7 +192,7 @@
Source:
@@ -286,7 +286,7 @@
Source:
@@ -348,7 +348,7 @@
Source:
@@ -428,7 +428,7 @@
Source:
@@ -565,7 +565,7 @@
Source:
@@ -653,7 +653,7 @@
Source:
@@ -699,13 +699,13 @@
diff --git a/www/doc/client/nullHandler.html b/www/doc/client/nullHandler.html index d240d9a..d0ee7c4 100644 --- a/www/doc/client/nullHandler.html +++ b/www/doc/client/nullHandler.html @@ -2867,13 +2867,13 @@
diff --git a/www/doc/client/panelObj.html b/www/doc/client/panelObj.html index f9f9d2e..0d0161f 100644 --- a/www/doc/client/panelObj.html +++ b/www/doc/client/panelObj.html @@ -903,13 +903,13 @@
diff --git a/www/doc/client/panels_emotePanel.js.html b/www/doc/client/panels_emotePanel.js.html index d6e7a64..9cef131 100644 --- a/www/doc/client/panels_emotePanel.js.html +++ b/www/doc/client/panels_emotePanel.js.html @@ -347,13 +347,13 @@ class emotePanel extends panelObj{
diff --git a/www/doc/client/panels_queuePanel_playlistManager.js.html b/www/doc/client/panels_queuePanel_playlistManager.js.html index 02c2f22..cf51d00 100644 --- a/www/doc/client/panels_queuePanel_playlistManager.js.html +++ b/www/doc/client/panels_queuePanel_playlistManager.js.html @@ -88,6 +88,9 @@ class playlistManager{ 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"); @@ -974,13 +977,13 @@ class renamePopup{
diff --git a/www/doc/client/panels_queuePanel_queuePanel.js.html b/www/doc/client/panels_queuePanel_queuePanel.js.html new file mode 100644 index 0000000..88fb0ec --- /dev/null +++ b/www/doc/client/panels_queuePanel_queuePanel.js.html @@ -0,0 +1,1688 @@ + + + + + JSDoc: Source: panels/queuePanel/queuePanel.js + + + + + + + + + + +
+ +

Source: panels/queuePanel/queuePanel.js

+ + + + + + +
+
+
/*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 Queue Panel UX
+ */
+class queuePanel extends panelObj{
+    /**
+     * Instantiates a new Queue Panel object
+     * @param {channel} client - Parent Client Management Object
+     * @param {Document} panelDocument - Panel Document
+     */
+    constructor(client, panelDocument){
+        //Call derived constructor
+        super(client, "Media Schedule", "/panel/queue", panelDocument);
+
+        /**
+         * Current day, zero'd out to midnight
+         */
+        this.day = new Date();
+        //Zero out day to midnight
+        this.day.setHours(0,0,0,0);
+
+        /**
+         * Schedule time scale in seconds, defaults to 30 minutes
+         */
+        this.scale = 30 * 60;
+
+        /**
+         * Re-scale timer, counts down after re-sizing to clear re-size UI and show schedule again
+         */
+        this.rescaleTimer = null;
+
+        /**
+         * Enables auto-scroll on schedule UX
+         */
+        this.autoscroll = true;
+
+        /**
+         * Child Playlist Manager Object
+         */
+        this.playlistManager = new playlistManager(client, panelDocument, this);
+
+        //Define non-input event listeners
+        this.defineListeners();
+    }
+
+    docSwitch(){
+        //Clear timetips
+        this.killTimetips();
+
+        //Call derived doc switch function
+        super.docSwitch();
+
+        //Get queue div
+        this.queue = this.panelDocument.querySelector('#queue');
+        //Get queue container
+        this.queueContainer = this.queue.querySelector("#queue-container");
+        //Get queue marker contianer
+        this.queueMarkerContainer = this.queue.querySelector('#queue-marker-container');
+        //Get queue layout controller
+        this.queueLayoutController = this.panelDocument.querySelector('#queue-panel-layout-controller');
+        //Get queue control offset
+        this.queueControlOffset = this.panelDocument.querySelector('#queue-control-offset');
+        //Re-acquire time marker
+        this.timeMarker = this.panelDocument.querySelector('#time-marker');
+        //Dragscroll timer
+        this.dragScrollTimer = null;
+
+        //Get main control buttons
+        this.addMediaButton = this.panelDocument.querySelector('#queue-add-media');
+        this.goLiveButton = this.panelDocument.querySelector('#queue-go-live');
+        this.scrollLockButton = this.panelDocument.querySelector('#queue-scroll-lock');
+        this.queueDateButton = this.panelDocument.querySelector('#queue-date');
+        this.playlistMenuButton = this.panelDocument.querySelector("#queue-playlists");
+        this.clearMediaButton = this.panelDocument.querySelector('#queue-clear');
+        this.queueLockButton = this.panelDocument.querySelector('#queue-lock');
+
+        //Get control divs
+        this.addMediaDiv = this.panelDocument.querySelector('#queue-media-prompts');
+        this.goLiveDiv = this.panelDocument.querySelector('#queue-live-prompts');
+        this.queueDateDiv = this.panelDocument.querySelector('#queue-control-date');
+        this.playlistDiv = this.panelDocument.querySelector('#queue-playlist-prompts');
+
+        //Get control div elements
+        //Add Media
+        this.addMediaLinkPrompt = this.panelDocument.querySelector('#media-link-input');
+        this.addMediaNamePrompt = this.panelDocument.querySelector('#media-name-input');
+        this.queueLastButton = this.panelDocument.querySelector('#queue-last-button');
+        this.queueAtButton = this.panelDocument.querySelector('#queue-at-button');
+        //Go Live
+        this.goLiveNamePrompt = this.panelDocument.querySelector("#queue-live-prompts-name-input");
+        this.goLiveOverwriteButton = this.panelDocument.querySelector("#queue-live-prompts-overwrite-button");
+        this.goLivePushbackButton = this.panelDocument.querySelector("#queue-live-prompts-pushback-button");
+        //Date Queue date
+        this.queueDateDecrement = this.panelDocument.querySelector('#queue-control-date-decrement');
+        this.queueDateIncrement = this.panelDocument.querySelector('#queue-control-date-increment');
+        this.queueDatePrompt = this.panelDocument.querySelector('#queue-control-date-prompt');
+
+        //Display lock status
+        this.handleScheduleLock();
+
+        //Render out the queue
+        this.fullRender();
+
+        //Setup panel input
+        this.setupInput();
+
+        //Pass the new panelDoc and docSwitch() call down to child classes
+        this.playlistManager.panelDocument = this.panelDocument;
+        this.playlistManager.docSwitch();
+    }
+
+    closer(){
+        //Clear any remaining timers
+        clearTimeout(this.renderIntervalTimer);
+        //Clear timetips
+        this.killTimetips();
+    }
+
+    /**
+     * Handles Network-Related Event Listeners
+     */
+    defineListeners(){
+        //Render queue when we receive a new copy of the queue data from the server
+        //Render queue should be called within an arrow function so that it's called with default parameters, and not handed an event as a date
+        this.client.socket.on("clientMetadata", () => {this.renderQueue();});
+        this.client.socket.on("queue", () => {this.renderQueue();});
+        this.client.socket.on("start", this.handleStart.bind(this));
+        this.client.socket.on("end", this.handleEnd.bind(this));
+        this.client.socket.on("lock", this.handleScheduleLock.bind(this));
+        this.client.socket.on("error", this.handleQueueError.bind(this));
+    }
+
+    /**
+     * Handles Input-Related Event Listeners
+     */
+    setupInput(){
+        //Re-render queue and time-marker on window resize as time-relative absolute positioning will be absolutely thrown
+        this.ownerDoc.defaultView.addEventListener('resize', this.resizeRender.bind(this));
+
+        //queue
+        this.queue.addEventListener('wheel', this.scaleScroll.bind(this));
+
+        //control bar controls
+        this.addMediaButton.addEventListener('click', this.toggleAddMedia.bind(this));
+        this.goLiveButton.addEventListener('click', this.toggleGoLive.bind(this));
+        this.scrollLockButton.addEventListener('click', this.lockScroll.bind(this));
+        this.queueDateButton.addEventListener('click', this.toggleDateControl.bind(this));
+        this.playlistMenuButton.addEventListener('click', this.togglePlaylistDiv.bind(this));
+        this.clearMediaButton.addEventListener('click', this.clearMedia.bind(this));
+        this.queueLockButton.addEventListener('click', this.lockSchedule.bind(this));
+
+        //control bar divs
+        //Add Media
+        this.queueLastButton.addEventListener('click', this.queueLast.bind(this))
+        this.queueAtButton.addEventListener('click', this.queueAt.bind(this))
+        //Go Live
+        this.goLiveOverwriteButton.addEventListener('click', this.goLive.bind(this));
+        this.goLivePushbackButton.addEventListener('click', this.goLive.bind(this));
+        //Queue Date
+        this.queueDateDecrement.addEventListener('click', this.decrementDate.bind(this));
+        this.queueDateIncrement.addEventListener('click', this.incrementDate.bind(this));
+        this.queueDatePrompt.addEventListener('change', this.setQueueDate.bind(this));
+    }
+
+    /* socket.io listeners */
+    /**
+     * Handles call from server to start media
+     * @param {Object} data - Data from server
+     */
+    handleStart(data){
+        //If we're starting an HLS Livestream
+        if(data.media != null && data.media.type == 'livehls'){
+            //Hide the 'goLive' controls
+            this.goLiveDiv.style.display = 'none';
+        }
+
+        this.renderQueue();
+    }
+
+    /**
+     * Handles call from server to end media
+     */
+    handleEnd(){
+        //Reset Go Live button
+        this.goLiveButton.classList.replace('critical-danger-button', 'danger-button');
+        this.goLiveButton.classList.replace('bi-record', 'bi-record2');
+        this.goLiveButton.title = "Go Live";
+
+        this.renderQueue();
+    }
+
+    /**
+     * Handles call from server to lock schedule
+     */
+    handleScheduleLock(){
+        //Get queue lock button icon
+        const icon = this.queueLockButton.querySelector('i');
+
+        if(this.client.queueLock){
+            this.queueLockButton.classList.remove('positive-button', 'bi-unlock-fill');
+            this.queueLockButton.classList.add('danger-button', 'bi-lock-fill');
+        }else{
+            this.queueLockButton.classList.remove('danger-button', 'bi-lock-fill');
+            this.queueLockButton.classList.add('positive-button', 'bi-unlock-fill');
+        }
+    }
+
+    /**
+     * Handles queue-related error from the server
+     * @param {Object} data - Data from server
+     */
+    handleQueueError(data){
+        //Create a flag to reload the queue
+        let reloadQueue = false;
+        
+        //Check errors
+        for(let error of data.errors){
+            //If we have a queue error
+            if(error.type == "queue"){
+                //Throw the reload flag
+                reloadQueue = true;
+            }
+        }
+
+        //If we need to reload the queue
+        if(reloadQueue){
+            //Do so
+            this.renderQueue();
+        }
+    }
+
+    /* queue control button functions */
+
+    /**
+     * Toggles add media UX
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    toggleAddMedia(event){
+        //If the div is hidden
+        if(this.addMediaDiv.style.display == 'none'){
+            //Light up the button
+            this.addMediaButton.classList.add('positive-button');
+            //Show the div
+            this.addMediaDiv.style.display = '';
+        }else{
+            //Unlight the button
+            this.addMediaButton.classList.remove('positive-button');
+            //Hide the div
+            this.addMediaDiv.style.display = 'none';
+        }
+    }
+
+    /**
+     * Toggles Go-Live UX
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    toggleGoLive(event){
+        //If we're not livestreaming
+        if(client.player.mediaHandler.type != "livehls"){
+            //If the div is hidden
+            if(this.goLiveDiv.style.display == 'none'){
+                //Show the div
+                this.goLiveDiv.style.display = '';
+            }else{
+                //Hide the div
+                this.goLiveDiv.style.display = 'none';
+            }
+        //Otherwise
+        }else{
+            //Hide the div, if it isn't already
+            this.goLiveDiv.style.display = 'none';
+
+            //Stop the livestream
+            client.socket.emit('stop');
+        }
+
+    }
+
+    /**
+     * Handles sending request to server to start a live stream
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    goLive(event){
+        //Start a livestream
+        client.socket.emit('goLive',{title: this.goLiveNamePrompt.value, mode: event.target.dataset['mode']});
+    }
+
+    /**
+     * Locks schedule scroll to current time marker
+     * @param {Event} event - Event passed down from Event Listener
+     * @param {Boolean} jumpToDay - whether or not to jump schedule to the current day
+     */
+    lockScroll(event, jumpToDay = true){
+        //If we're supposed to jump to the current day
+        if(jumpToDay){
+            //Set schedule to current day
+            this.setDay();
+        }
+
+        //Enable scroll lock
+        this.autoscroll = true;
+
+        //If we have a time marker
+        if(this.timeMarker != null){
+            //Light the indicator
+            this.scrollLockButton.classList.add('positive-button');
+            //Render the marker early to insta-jump
+            this.renderTimeMarker();
+        }else{
+            //Unlight the indicator
+            this.scrollLockButton.classList.remove('positive-button');
+        }
+    }
+
+    /**
+     * Toggles schedule date control
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    toggleDateControl(event){
+        //If the div is hidden
+        if(this.queueDateDiv.style.display == 'none'){
+            //Light up the button
+            this.queueDateButton.classList.add('positive-button');
+
+            //Set date text
+            this.queueDatePrompt.valueAsDate = utils.ux.localizeDate(this.day);
+
+            //Show the div
+            this.queueDateDiv.style.display = '';
+        }else{
+            //Unlight the button
+            this.queueDateButton.classList.remove('positive-button');
+            //Hide the div
+            this.queueDateDiv.style.display = 'none';
+        }
+    }
+
+    /**
+     * Toggles playlist management UX
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    togglePlaylistDiv(event){
+        //If the div is hidden
+        if(this.playlistDiv.style.display == 'none'){
+            //Query playlists
+            client.socket.emit('getChannelPlaylists');
+
+            //Light up the button
+            this.playlistMenuButton.classList.add('positive-button');
+            //Show the div
+            this.playlistDiv.style.display = '';
+        }else{
+            //Unlight the button
+            this.playlistMenuButton.classList.remove('positive-button');
+            //Hide the div
+            this.playlistDiv.style.display = 'none';
+        }
+    }
+
+    /**
+     * Sends request to server to clear media between a range of two dates
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    clearMedia(event){
+        //Call up the popup
+        new clearPopup(event, this.client, null, this.ownerDoc);
+    }
+
+    /**
+     * Sends request to lock the schedule
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    lockSchedule(event){
+        client.socket.emit('lock');
+    }
+
+    /* add queue controls */
+    /**
+     * Sends request to queue current URL after the last item
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    queueLast(event){
+        //Send off the request
+        this.client.socket.emit("queue",{url:this.addMediaLinkPrompt.value, title:this.addMediaNamePrompt.value});
+
+        //Clear out prompts
+        this.addMediaLinkPrompt.value = '';
+        this.addMediaNamePrompt.value = '';
+    } 
+
+    /**
+     * Sends request to queue current URL at the current time
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    queueAt(event){
+        //Call up the popup
+        new schedulePopup(event, this.client, this.addMediaLinkPrompt.value, this.addMediaNamePrompt.value, null, this.ownerDoc);
+
+        //Clear out prompts
+        this.addMediaLinkPrompt.value = '';
+        this.addMediaNamePrompt.value = '';
+    }
+
+    /* set date controls */
+    /**
+     * Increments displayed schedule date
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    incrementDate(event){
+        //Increment day
+        this.day.setDate(this.day.getDate() + 1);
+        //Set day
+        this.setDay(this.day);
+    }
+
+    /**
+     * Decrements displayed schedule date
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    decrementDate(event){
+        //Decrement day
+        this.day.setDate(this.day.getDate() - 1);
+
+        //Set day
+        this.setDay(this.day);
+    }
+
+    /**
+     * Validates and  sets display schedule date from user input
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    setQueueDate(event){
+        //If we have a valid date
+        if(this.queueDatePrompt.valueAsDate != null){
+            //Set the day
+            this.setDay(utils.ux.normalizeDate(this.queueDatePrompt.valueAsDate));
+
+        }
+    }
+
+    /** 
+     * Directly sets schedule display date
+     * @param {Date} date - Date to set schedule to
+     */
+    setDay(date = new Date()){
+        //Set day
+        this.day = date;
+        //Zero out to midnight
+        this.day.setHours(0,0,0,0);
+
+        //Set prompt to current day
+        this.queueDatePrompt.valueAsDate = utils.ux.localizeDate(this.day);
+
+        //Re-render the queue
+        this.renderQueue();
+        //Re-render/hide the time marker
+        this.renderTimeMarker();
+
+        //If autoscroll is enabled
+        if(this.autoscroll){
+            //Simulate a button click to un/re-light the button and trigger a scroll when the date is set to today
+            this.lockScroll(null, false);
+        }
+    }
+
+    /**
+     * Handles time-scale changes on crtl+scroll
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    scaleScroll(event){
+        if(event.ctrlKey){
+            //I could sit here and work out why timetips break everything on scalescroll
+            //Then again you wouldn't want phantom timetips surviving the re-scale so you might as well get two birds stoned at once :P
+            this.killTimetips();
+
+            //Capture inverse scroll wheel direction
+            const scrollDirection = event.wheelDeltaY / Math.abs(event.wheelDeltaY) * -1;
+
+            //Default scale factor to 5 seconds
+            let scaleFactor = 5;
+
+            //Tried to do this with math but couldnt because im bad at math so heres the if statement of shame :(
+            if(this.scale >= 7200){
+                scaleFactor = 3600
+            }else if(this.scale >= 3600){
+                scaleFactor = scrollDirection > 0 ? 3600 : 1800;
+            }else if(this.scale >= 1800){
+                scaleFactor = scrollDirection > 0 ? 1800 : 900;
+            }else if(this.scale == 900){
+                scaleFactor = scrollDirection > 0 ? 900 : 300;
+            }else if(this.scale > 300){
+                //If we're above five minutes use five minutes
+                scaleFactor = 300;
+            }else if(this.scale == 300){
+                //If we're at five minutes scroll up by five minutes or scroll down to one minute
+                scaleFactor = scrollDirection > 0 ? 300 : 240;
+            }else if(this.scale == 60){
+                //If we're at one minutes scroll up by four minutes or scroll down by 10 seconds
+                scaleFactor = scrollDirection > 0 ? 240 : 10;
+            }else if(this.scale > 10){
+                scaleFactor = 10;
+            }else if(this.scale == 10){
+                scaleFactor = scrollDirection > 0 ? 10 : 5;
+            }
+
+            //Prevent page-wide zoom in/out
+            event.preventDefault();
+
+            //Clear out the queue UI
+            this.clearQueue();
+
+            //Calculate new scale
+            const newScale = this.scale + (scaleFactor * scrollDirection);
+
+            //Clamp scale between 10 seconds and half a day
+            this.scale = Math.max(5, Math.min(43200, newScale));
+
+            //If we have no scale label
+            if(this.scaleLabel == null){
+                //Make it
+                this.scaleLabel = document.createElement('p');
+                this.scaleLabel.id = 'queue-marker-scale-label';
+                this.queue.appendChild(this.scaleLabel);
+            }
+
+            //Set scale label text to humie readable time scale
+            this.scaleLabel.innerHTML = `Time Scale:<br>${utils.ux.humieFriendlyDuration(this.scale)}`
+
+            //Clear any previous rescale timer
+            clearTimeout(this.rescaleTimer);
+            //Set timeout to re-render after input stops
+            this.rescaleTimer = setTimeout(this.fullRender.bind(this), 500);
+        //Otherwise, if we're just scrolling
+        }else{
+            //If we're looking at today
+            if(utils.isSameDate(new Date(), this.day)){
+                //Unlock auto scroll
+                this.unlockScroll();
+            }
+        }
+    }
+
+    /**
+     * Un-locks scroll from curren time marker
+     */
+    unlockScroll(){
+        //Disable scroll lock
+        this.autoscroll = false;
+        //Unlight the indicator
+        this.scrollLockButton.classList.remove('positive-button');
+    } 
+
+    /**
+     * Handles re-rendering schedule upon window re-size
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    resizeRender(event){
+        const date = new Date();
+        this.renderQueue(date);
+        this.renderTimeMarker(date);
+    }
+
+    /**
+     * Clears out queue container for re-render
+     */
+    clearQueue(){
+        //If we have no body
+        if(this.ownerDoc.body == null){
+            //We have bigger issues
+            return;
+        }
+
+        //Clear out queue container
+        this.queueContainer.innerHTML = '';;
+        //Clear out queue marker container
+        this.queueMarkerContainer.innerHTML = '';
+        
+        //Grab all related tooltips
+        const tooltips = this.ownerDoc.body.querySelectorAll('.media-tooltip');
+
+        //clear them out since we're clearing out the elements with the event to remove them
+        //These should clear out on their own on mousemove but this makes things look a *little* prettier :)
+        for(let tooltip of tooltips){
+            tooltip.parentNode.remove();
+        }
+
+        //Clear any marker timers
+        clearTimeout(this.renderIntervalTimer);
+
+        //If we have an existing time marker
+        if(this.timeMarker != null){
+            //Clear it out
+            this.timeMarker.remove();
+            this.timeMarker = null;
+        }
+    }
+
+    /**
+     * 
+     * @param {Date} date - Current time, 
+     */
+    async fullRender(date = new Date()){
+        //Clear the queue
+        this.clearQueue();
+
+        //If we have a scale label
+        if(this.scaleLabel != null){
+            //Take it out
+            this.scaleLabel.remove();
+            this.scaleLabel = null;
+        }
+
+        //Render out time scale
+        this.renderQueueScale(date);
+
+        //Grab the first marker
+        let firstMarker = this.panelDocument.querySelector('.queue-marker-first');
+
+        //Loop until first marker is properly positioned
+        while(firstMarker.offsetTop > 0){
+            //wait a few frames so the scale can finish rendering, because dom function aren't async for some fucking reason
+            await utils.ux.awaitNextFrame();
+        }
+
+        //render the time marker w/ force scroll
+        this.renderTimeMarker(date, true);
+
+        //Kick off render interval
+        this.renderInterval(date);
+
+        //render out the queue
+        this.renderQueue(date);
+    }
+
+    /**
+     * Renders out schedule
+     * @param {Date} date - Date representing current time, defaults to new date object
+     */
+    renderQueue(date = new Date()){
+        //Clear out queue container
+        this.queueContainer.innerHTML = '';
+
+        //for every entry in the queue
+        for(let entry of this.client.queue){
+            //Check if item starts today
+            const startsToday = utils.isSameDate(this.day, new Date(entry[1].startTime));
+            //Check if item ends today
+            const endsToday = utils.isSameDate(this.day, new Date(entry[1].startTime + (entry[1].duration * 1000)));
+
+            //If the item either starts or ends today
+            var playsToday = (startsToday || endsToday);
+
+            //If the media neither starts nor ends today
+            if(!playsToday){
+                //set playsToday based on whether or not we're playing something fucking huge and it's covering all of today
+                playsToday = utils.dateWithinRange(new Date(entry[1].startTime), new Date(entry[1].startTime + (entry[1].duration * 1000)), this.day);
+            }
+
+            //If part of the current item plays today
+            if(playsToday){
+                //Create entry div
+                const entryDiv = document.createElement('div');
+                entryDiv.classList.add('queue-entry');
+
+                //For each property of the media object
+                for(let key of Object.keys(entry[1])){
+                    //add it to its given dataset
+                    entryDiv.dataset[key] = entry[1][key];
+                }
+
+                //If this item starts today
+                if(startsToday){
+                    //Place the top of the entry div based on time
+                    entryDiv.style.top = `${this.offsetByDate(new Date(entry[1].startTime))}px`;
+                }else{
+                    //Get dawn
+                    const dawn = new Date();
+                    dawn.setHours(0,0,0,0);
+
+                    //Place item beginning at dawn
+                    entryDiv.style.top = `${this.offsetByDate(dawn)}px`;
+
+                    //Apply style rules for items that starrted yesterday
+                    entryDiv.classList.add('started-yesterday');
+                }
+
+                //If the item ends today
+                if(endsToday){
+                    //Place the bottom of the entry div based on time
+                    entryDiv.style.bottom = `${this.offsetByDate(new Date(this.getMediaEnd(entry[1])), true)}px`;
+                }else{
+                    //Get midnight
+                    const dusk = new Date();
+                    dusk.setHours(23,59,59,999);
+
+                    //Place item beginning at dawn
+                    entryDiv.style.bottom = `${this.offsetByDate(dusk, true)}px`;
+
+                    //Run apply the rest of the styling rules
+                    entryDiv.classList.add('ends-tomorrow');
+                }
+
+                //If we started in the middle of the video
+                if(entry[1].startTimeStamp > 0){
+                    entryDiv.classList.add('started-late');
+                }
+
+                //If we ended early
+                if(entry[1].earlyEnd != null){
+                    entryDiv.classList.add('ended-early');
+                }
+
+                //Create entry title
+                const entryTitle = document.createElement('p');
+                entryTitle.textContent = utils.unescapeEntities(entry[1].title);
+
+                //Tooltip generation
+                //tooltip div
+                const tooltipDiv = document.createElement('div');
+                tooltipDiv.classList.add('media-tooltip');
+
+                //tooltip components
+                //For each string
+                for(let string of [
+                    `Title: ${entry[1].title}`,
+                    `File Name: ${entry[1].fileName}`,
+                    `Source: ${entry[1].type}`,
+                    `Duration: ${utils.ux.humieFriendlyDuration(entry[1].duration)}`,
+                    `Start Time: ${new Date(entry[1].startTime).toLocaleString()}${entry[1].startTimeStamp == 0 ? '' : ' (Started Late)'}`,
+                    `End Time: ${new Date(this.getMediaEnd(entry[1])).toLocaleString()}${entry[1].earlyEnd == null ? '' : ' (Ended Early)'}`
+                ]){
+                    //Create a 'p' node
+                    const component = document.createElement('p');
+                    //Fill it with the string
+                    component.textContent = utils.unescapeEntities(string);
+                    //Append it to the tooltip div
+                    tooltipDiv.append(component);
+                }
+
+                //Setup media tooltip
+                entryDiv.addEventListener('mouseenter',(event)=>{
+                    //If we're not dragging
+                    if(event.target.dataset['drag'] != 'true'){
+                        //Display tooltip
+                        utils.ux.displayTooltip(event, tooltipDiv, false, null, true, this.ownerDoc);
+                    }
+                });
+
+                //Create context menu map
+                const menuMap = new Map();
+                const now = new Date();
+
+                //If the item hasn't started yet
+                if(entry[1].startTime > now.getTime()){
+                    //Add 'Play' option to context menu
+                    menuMap.set("Play now", ()=>{this.client.socket.emit('move', {uuid: entry[1].uuid})});
+                    //Add 'Move To...' option to context menu
+                    menuMap.set("Move To...", (event)=>{new reschedulePopup(event, this.client, entry[1], null, this.ownerDoc)});
+                //Otherwise, if the item is currently playing (confirm with UUID since time might not always be reliable, such as during livestreams)
+                }else if(entry[1].uuid == this.client.player.mediaHandler.nowPlaying.uuid){
+                    //Add 'Stop' option to context menu
+                    menuMap.set("Stop", ()=>{this.client.socket.emit('stop')});
+                    //Add the Now Playing glow, not the prettiest place to add this, but why let a good conditional go to waste?
+                    entryDiv.classList.add('now-playing');
+                //Otherwise, if the item has been archived
+                }else{
+                    entryDiv.classList.add('archived');
+                }
+
+                //Add 'Delete' option to context menu
+                menuMap.set("Delete", ()=>{this.client.socket.emit('delete', {uuid: entry[1].uuid})})
+                //Add 'New Tab' option to context menu
+                menuMap.set("Open in New Tab", ()=>{window.open(entry[1].url, '_blank').focus();})
+                //Add 'Copy URL' option to context menu
+                menuMap.set("Copy URL", ()=>{navigator.clipboard.writeText(entry[1].url);})
+
+                //If the item hasn't yet ended
+                if(this.getMediaEnd(entry[1]) > now.getTime()){
+                    //Setup drag n drop
+                    entryDiv.addEventListener('mousedown', clickEntry.bind(this));
+                }
+
+                //Setup context menu
+                entryDiv.addEventListener('contextmenu', (event)=>{
+                    //If we're not dragging
+                    if(event.target.dataset['drag'] != 'true'){
+                        //Display context menu
+                        utils.ux.displayContextMenu(event, '', menuMap, this.ownerDoc);
+                    }
+                });
+                
+                //Append entry elements
+                entryDiv.append(entryTitle);
+
+                //Append entry
+                this.queueContainer.append(entryDiv);
+            }
+
+        }
+
+        //Render out any playing livestreams
+        this.renderLiveStream(date);
+
+        function clickEntry(event){
+            //If it's not a left click
+            if(event.buttons != 1){
+                //fuck off
+                return;
+            }
+
+            //Grab existing height
+            let height = event.target.offsetHeight;
+            let cutoffOffset = 0;
+
+            //If the item got cut-off at the bottom
+            if(event.target.classList.contains("ends-tomorrow") || event.target.classList.contains("ended-early")){
+                //Calculate height from duration
+                height = this.offsetByMilliseconds(Number(event.target.dataset['duration']) * 1000);
+            //If the item got cut-off at the top
+            }else if(event.target.classList.contains('started-yesterday') || event.target.classList.contains("started-late")){
+                //Keep old height for now
+                const oldHeight = height;
+
+                //Calculate height from duration
+                height = this.offsetByMilliseconds(Number(event.target.dataset['duration']) * 1000);
+
+                //Calculate the mouse offset needed to keep it properly placed relative to the original click point
+                cutoffOffset = height - oldHeight;
+            }
+
+            //Remove any cut-off borders
+            event.target.classList.remove('ends-tomorrow', 'started-yesterday', 'ended-early', 'started-late');
+
+
+            //If we havent set height or width
+            if(event.target.style.height == ""){
+                //Preserve calculated entry height
+                event.target.style.height = `${height}px`;
+            }
+
+            //Add set dragging CSS class to target
+            event.target.classList.add('dragging-queue-entry');
+
+            //enable drag on target dataset
+            event.target.dataset['drag'] = true;
+
+            //Kill existing timetips
+            this.killTimetips();
+
+            //Create a tooltip to show the time we're dragging to
+            const timetip = new canopyUXUtils.tooltip('', false, null, this.ownerDoc);
+            timetip.tooltip.classList.add('media-tooltip','media-timetip');
+            timetip.tooltip.addEventListener('mousemove', this.killTimetips.bind(this));
+
+
+            //Drag entry with mouse
+            this.ownerDoc.body.addEventListener('mousemove', (nestedEvent)=>{(dragEntry.bind(this))(nestedEvent, event.target, timetip)});
+
+            //Drop on mouse up
+            this.ownerDoc.body.addEventListener('mouseup', (nestedEvent)=>{(dropEntry.bind(this))(nestedEvent, event.target)});
+
+            //Disable selection on body
+            this.ownerDoc.body.style.userSelect = 'none';
+
+            //Save top of target relative to window minus the mouse position as our drag offset
+            event.target.dataset['dragoffset'] = (event.target.offsetTop + this.ownerDoc.defaultView.scrollY) - event.clientY - cutoffOffset;
+
+            //Call the drag entry function to move the entry on click without re-writing the wheel
+            (dragEntry.bind(this))(event, event.target, timetip);
+            
+            //Start dragscroll loop
+            this.dragScrollTimer = setInterval(()=>{(dragScroll.bind(this))(event.target)}, 10);
+        }
+
+        function dragScroll(target){
+            //Stop timeout loop
+            if(!target.isConnected || target.dataset['drag'] != "true"){
+                //Clear the interval
+                clearInterval(this.dragScrollTimer);
+                //Fuck off and die!
+                return;
+            }
+
+            //Set minimum distance to detect
+            const detectionDistance = 70;
+            //Set value to devide distance from edge during scroll speed calculation
+            const speedDevider = 6;
+
+            //Get top boundaries distance from the top relative to the scroll top and set as top input
+            let topInput = target.offsetTop - this.queueLayoutController.scrollTop;
+            //Get bottom boundaries distance from the top relative to the scroll top and set as bottom input
+            let bottomInput = this.queueContainer.offsetHeight - ((target.offsetTop + target.offsetHeight) + (this.queueLayoutController.scrollTopMax - this.queueLayoutController.scrollTop));
+
+            //If the item we're dragging is fackin uge'
+            if(target.offsetHeight > (this.queueLayoutController.offsetHeight - ((detectionDistance * 2) + 20))){
+                //AND THEY FUCKING SAID YOU COULDN'T GET MOUSE POS OUTSIDE OF AN EVENT WITHOUT :HOVER TRICKS EAT MY FUCKING ASS
+                topInput = Math.round(target.offsetTop - Number(target.dataset['dragoffset']) - (this.queueLayoutController.getBoundingClientRect().top + this.queueControlOffset.offsetHeight));
+                bottomInput = this.queueLayoutController.offsetHeight - (topInput + this.queueControlOffset.offsetHeight);
+            }
+
+            //If the top of the entry is within five pixels of the top of the parent and we have room to scroll up
+            if(topInput < detectionDistance && this.queueLayoutController.scrollTop > 0){
+                //Unlock auto scroll
+                this.unlockScroll();
+
+                //Filter out less than 0 from relative entry top to calculate speed
+                const speed = Math.round(((detectionDistance) - (topInput < 0 ? 0 : topInput)) / speedDevider);
+
+                //Scroll queue by distance to top
+                this.queueLayoutController.scrollBy(0, -speed);
+
+                //Add scroll amount to drag offset to keep entry aligned with mouse
+                target.dataset['dragoffset'] = Number(target.dataset['dragoffset']) - speed
+                //Move entry by speed to match new drag offset
+                target.style.top = `${target.offsetTop - speed}px`
+            //Otherwise if the bottom of the entry is within five pixels the bottom of the parent and we have room to scroll down
+            }else if(bottomInput < (detectionDistance) && this.queueLayoutController.scrollTop < this.queueLayoutController.scrollTopMax){
+                //Unlock auto scroll
+                this.unlockScroll();
+
+                //Calculate speed by distance to bottom
+                const offsetBottom = bottomInput;
+                //Filter out less than 0, reverse the range, and apply scroll dampen to avoid scrolling off the edge
+                const speed = Math.round((detectionDistance - (offsetBottom < 0 ? 0 : offsetBottom)) / speedDevider)
+
+                //Scroll queue by calculated speed
+                this.queueLayoutController.scrollBy(0, speed);
+
+                //Subtract speed from drag offset to keep aligned with mouse
+                target.dataset['dragoffset'] = Number(target.dataset['dragoffset']) + speed;
+                //Move entry by speed to match new drag offset
+                target.style.top = `${target.offsetTop + speed}px`
+            }
+        }
+
+        function dragEntry(event, target, timetip){
+                //Gross but works :P
+                if(!target.isConnected || target.dataset['drag'] != "true"){
+                    return;
+                }
+
+                //Get current start time
+                const start = this.dateByOffset(target.offsetTop);
+
+                //Position timetip
+                timetip.moveToMouse(event);
+
+                //Inject timetip label
+                //Normally wouldn't do innerHTML but these values are calculated serverside and it saves us making a <br> element
+                timetip.tooltip.innerHTML = [
+                    `Start Time: ${utils.ux.timeStringFromDate(start, true)}`,
+                    `End Time: ${utils.ux.timeStringFromDate(new Date(start.getTime() + (target.dataset['duration'] * 1000)), true)}`
+                ].join('<br>');
+
+                //Calculate offset from rest of window
+                const windowOffset = this.queueContainer.offsetTop + this.ownerDoc.defaultView.scrollY;
+
+                //Move the entry to the mouse offset by the target nodes height and the queue layouts scroll
+                const entryTop = event.clientY + Number(target.dataset['dragoffset']) - windowOffset;
+                
+                //Set target vertical position
+                target.style.top = `${entryTop}px`;
+        }
+
+        function dropEntry(event, target){
+            //Gross but works :P
+            if(!target.isConnected || target.dataset['drag'] != "true"){
+                return;
+            }
+
+            //Asynchronously send move command item by calculated time to server
+            this.client.socket.emit('move', {uuid: target.dataset['uuid'], start: this.dateByOffset(target.offsetTop).getTime()});
+
+            //allow selection on body
+            this.ownerDoc.body.style.userSelect = 'none';
+
+            //kill timetips
+            this.killTimetips();
+
+            //Finish dragging
+            target.dataset['drag'] = false;
+        }
+
+    }
+
+    /**
+     * Kills off hung tooltips
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    killTimetips(event){
+        //If we have an event and it's holding any mouse buttons
+        if(event != null && event.buttons != 0){
+            //Fuck off and die
+            return;
+        }
+
+        //Find any existing timetips
+        for(const timetip of this.ownerDoc.querySelectorAll('.media-timetip')){
+            //nukem
+            timetip.remove();
+        }
+    }
+
+    /**
+     * Render call called at 1-second intervals, handles time and livestream markers
+     * @param {Date} date - Date representing current time, defaults to new date object
+     */
+    renderInterval(date = new Date()){
+        this.renderTimeMarker(date);
+        this.renderLiveStream(date, true);
+
+
+        //Clear update timer
+        clearTimeout(this.renderIntervalTimer);
+
+        //Set timer to update marker every second
+        this.renderIntervalTimer = setTimeout(this.renderInterval.bind(this), 1000);
+    }
+
+    /**
+     * Renders current time marker on to the schedule
+     * @param {Date} date - Date representing current time, defaults to new date object
+     * @param {Boolean} forceScroll - Whether or not to scroll the schedule on increment
+     */
+    renderTimeMarker(date = new Date(), forceScroll = false){
+        //Calculate marker position by date
+        const markerPosition = Math.round(this.offsetByDate(date));
+
+        //If markers are null
+        if(markerPosition == null){
+            //Try again in a second since the user probably just popped the panel or something :P
+            return;
+        }
+
+        //If we're not looking at today
+        if(!utils.isSameDate(this.day, new Date())){
+            //If we still have at time marker
+            if(this.timeMarker != null){
+                this.timeMarker.remove();
+                this.timeMarker = null
+            }
+        
+            //Stop here
+            return;
+        }
+
+        //if we need to make the time marker
+        if(this.timeMarker == null){
+            //Create the time marker
+            this.timeMarker = document.createElement('span');
+            //Add time marker class
+            this.timeMarker.id = 'time-marker';
+            //Append time marker
+            this.queue.appendChild(this.timeMarker);
+        } 
+
+        //Set time marker position
+        this.timeMarker.style.top = `${markerPosition}px`;
+
+        //If the panel document isn't null (we're not actively switching panels)
+        if(this.panelDocument != null && (this.autoscroll || forceScroll)){
+            //Get height difference between window and queue layout controller
+            const docDifference = this.ownerDoc.defaultView.innerHeight - this.queueLayoutController.getBoundingClientRect().height;
+            //Calculate scroll target by body difference and marker position
+            const scrollTarget = (markerPosition - (this.queueLayoutController.getBoundingClientRect().height - docDifference) / 2) + docDifference;
+            //Calculate scroll behavior by distance
+            const scrollBehavior = Math.abs(scrollTarget - this.queueLayoutController.scrollTop) > 10 ? "smooth" : "instant";
+        
+
+            //Scroll to the marker
+            this.queueLayoutController.scroll({
+                left: 0, 
+                top: scrollTarget,
+                behavior: scrollBehavior
+            });
+        }
+    }
+
+    /**
+     * Renders queue scale markers
+     * @param {Date} inputDate - Date representing current time, defaults to new date object
+     */
+    renderQueueScale(inputDate = new Date()){
+        //Clear out queue marker container
+        this.queueMarkerContainer.innerHTML = '';       
+
+        //Make sure we don't modify the date we're handed
+        const date = structuredClone(inputDate);
+
+        //Zero out time to midnight on the morning of the input date
+        date.setHours(0,0,0,0);
+
+        //Store epoch of current date at midnight for later user
+        const dateEpoch = date.getTime();
+
+        //Create array to hold entries for post processing
+        const entries = [];
+
+        //while we've counted less than the amount of time in the day, count up by scale
+        for(let time = 0; time <= 86400; time += this.scale){
+            //Get index of current chunk by dividing time by scale
+            const index = time / this.scale;
+
+            //Set time by scale, we could do this against this.scale and date.getTime(), but this seemed cleaner :P
+            date.setTime(dateEpoch + (time * 1000))
+
+            //Create marker span
+            const markerDiv = document.createElement('div');
+            markerDiv.classList.add('queue-marker');
+
+            //Create marker line (unfortunately <hr> tags don't seem to play nice with parents who have display:flex)
+            const marker = document.createElement('span');
+            marker.classList.add('queue-marker');
+
+            //If it's even/zero
+            if(index % 2 == 0){
+                //Create marker label
+                const markerLabel = document.createElement('p');
+
+                //If scale is over a minute then we don't need to display seconds
+                markerLabel.textContent = utils.ux.timeStringFromDate(date, this.scale < 60) 
+
+                //Add marker label to marker span    
+                markerDiv.appendChild(markerLabel);
+            }
+
+            //Append marker to marker span
+            markerDiv.appendChild(marker);
+
+            //Append marker span to queue container
+            this.queueMarkerContainer.appendChild(markerDiv);
+            //Add it to our postprocessing list
+            entries.push(markerDiv);
+        }
+
+        //If we made anything (should always be true :P)
+        if(entries.length > 0){
+            //Set the margin for the first queue marker
+            entries[0].classList.add('queue-marker-first');
+
+            //Set the margin for the last queue marker
+            entries[entries.length - 1].classList.add('queue-marker-last');
+        }
+    }
+
+    /**
+     * Renders live-stream marker
+     * @param {Date} inputDate - Date representing current time, defaults to new date object
+     * @param {Boolean} intervalRun - Whether or not this was run by renderInterval, defaults to false
+     */
+    renderLiveStream(date = new Date(), intervalRun = false){
+        //Grab all live queue entries
+        const staleEntry = this.queueContainer.querySelector('.queue-entry.live');
+
+        //If we're not livestreaming
+        if(client.player.mediaHandler.type != "livehls"){
+            //If we have a stale entry
+            if(staleEntry != null){
+                //Remove stale entry since we're no longer streaming
+                staleEntry.remove();
+            }
+
+            //Fuck off and die
+            return;
+        }
+
+        //If we haven't updated the Go-Live button yet
+        //This kinda needs to be done here since this might be opened mid-stream :P
+        if(this.goLiveButton.title != "Stop Stream"){
+            this.goLiveButton.classList.replace('danger-button', 'critical-danger-button');
+            this.goLiveButton.title = "Stop Stream";
+        }
+        
+
+        //If this was called by the timer function, and not some other queue-rendering related function
+        if(intervalRun){
+            //Though there is reason to run an if statement here since we dont want the latter over-writing the former every run...
+            //If we're showing the regular icon
+            if(this.goLiveButton.classList.contains('bi-record2')){
+                //Animated it with the other live icon
+                this.goLiveButton.classList.replace('bi-record2', 'bi-record');
+            //otherwise
+            }else{
+                //Show the standard one we always do
+                this.goLiveButton.classList.replace('bi-record', 'bi-record2');
+            }
+        }
+
+        //Grab currently playing media
+        const nowPlaying = client.player.mediaHandler.nowPlaying;
+
+        //If we don't have a good stale entry to re-use
+        if(staleEntry == null || staleEntry.dataset.uuid != nowPlaying.uuid){
+            //If it wasn't null but just old
+            if(staleEntry != null){
+                //Nukem
+                staleEntry.remove();
+            }
+
+            //Create entry div
+            const entryDiv = document.createElement('div');
+            entryDiv.classList.add('queue-entry', 'live');
+
+            //Iterate through nowPlaying's properties
+            for(let key of Object.keys(nowPlaying)){
+                //Add them to the entry div's dataset
+                entryDiv.dataset[key] = nowPlaying[key];
+            }
+
+            //Convert start epoch to JS date object
+            const started = new Date(nowPlaying.startTime);
+
+            //If this started today
+            if(utils.isSameDate(this.day, started)){
+                //Set entryDiv top-border location based on start time
+                entryDiv.style.top = `${this.offsetByDate(started)}px`
+            }else{
+                //Get dawn
+                const dawn = new Date();
+                dawn.setHours(0,0,0,0)
+
+                //Place item at the beginning of dawn
+                entryDiv.style.top = `${this.offsetByDate(dawn)}px`;
+
+                //Apply rest of the styling rules for items that started yestarday
+                entryDiv.classList.add('started-yesterday')
+            }
+
+            //Create entry title
+            const entryTitle = document.createElement('p');
+            entryTitle.textContent = utils.unescapeEntities(nowPlaying.title);
+
+            //Set entry div bottom-border location based on current time, round to match time marker
+            entryDiv.style.bottom = `${Math.round(this.offsetByDate(date, true))}px`
+
+            //Assembly entryDiv
+            entryDiv.appendChild(entryTitle);
+
+            //Setup media tooltip
+            entryDiv.addEventListener('mouseenter',(event)=>{
+                //Construct tooltip on mouseover to re-calculate duration for live media
+                const tooltipDiv = buildTooltip();
+
+                //Display tooltip
+                utils.ux.displayTooltip(event, tooltipDiv, false, null, true, this.ownerDoc);
+            });
+
+            const menuMap = new Map([
+                ["Stop", ()=>{this.client.socket.emit('stop');}],
+                ["Delete", ()=>{this.client.socket.emit('delete', {uuid: nowPlaying.uuid});}],
+                ["Open in New Tab", ()=>{window.open(nowPlaying.url, '_blank').focus();}],
+                ["Copy URL", ()=>{navigator.clipboard.writeText(nowPlaying.url);}],
+
+            ]);
+
+            //Setup context menu
+            entryDiv.addEventListener('contextmenu', (event)=>{
+                //Display context menu
+                utils.ux.displayContextMenu(event, '', menuMap, this.ownerDoc);
+            });
+
+            //Append entry div to queue container
+            this.queueContainer.appendChild(entryDiv);
+        }else{
+            //Update existing entry, round offset to match time marker
+            staleEntry.style.bottom = `${Math.round(this.offsetByDate(date, true))}px`
+        }
+
+        //Keep tooltip date seperate so it re-calculates live duration properly
+        function buildTooltip(date = new Date()){
+            //Tooltip generation
+            //tooltip div
+            const tooltipDiv = document.createElement('div');
+            tooltipDiv.classList.add('media-tooltip');
+
+            //tooltip components
+            const tooltipStrings = [
+                `Title: ${nowPlaying.title}`,
+                `File Name: ${nowPlaying.fileName}`,
+                `Source: HLS Livestream (${nowPlaying.url})`,
+                `Duration: ${utils.ux.humieFriendlyDuration((date.getTime() - nowPlaying.startTime) / 1000)}`,
+                `Start Time: ${new Date(nowPlaying.startTime).toLocaleString()}`,
+            ];
+
+            //For each string in the tooltip
+            for(const string of tooltipStrings){
+
+                //Create a 'p' node
+                const component = document.createElement('p');
+                //Fill it with the string
+                component.textContent = utils.unescapeEntities(string);
+
+                //Append it to the tooltip div
+                tooltipDiv.append(component);
+            }
+
+            //Create End Date
+            const source = document.createElement('p');
+            const liveSpan = document.createElement('span');
+
+            //Fill end date label
+            source.textContent = "End Time: "
+            liveSpan.textContent = 'LIVE'
+
+            //Set class
+            liveSpan.classList.add('critical-danger-text');
+
+            //Assemble end date
+            source.appendChild(liveSpan);
+            tooltipDiv.appendChild(source);           
+
+            return tooltipDiv;
+        }
+    }
+
+    /**
+     * Calculate schedule offset from top or bottom of div from date
+     * @param {Date} date - Date to calculate from, defaults to now
+     * @param {Boolean} bottomOffset - Whether or not offset should be calculated from top or bottom
+     * @returns {Number} Offset from top/bottom of div in PX relative to time markers, calculated from given date
+     */
+    offsetByDate(date = new Date(), bottomOffset = false){
+        //Pull start of day epoch from given date, make sure to use a new date object so we don't fuck up any date objects passed to us
+        const dayEpoch = new Date(date).setHours(0,0,0,0);
+        //Get difference between now and day epoch to get time since the start of the current day in milliseconds
+        const curTime = date.getTime() - dayEpoch;
+
+        //Calculate the offset from todays milliseconds
+        return this.offsetByMilliseconds(curTime, bottomOffset);
+    }
+
+    /**
+     * Calculates date by offset pixels relative to schedule div top
+     * @param {Number} input - Pixels date is away from schedule div top
+     * @returns {Date} Date object representing date which was calculated from given pixel offset
+     */
+    dateByOffset(input = 0){
+        //Get markers
+        const markers = this.panelDocument.querySelectorAll('span.queue-marker');
+        //get range of position from top to bottom marker
+        const range = [markers[0].offsetTop, markers[markers.length - 1].offsetTop]
+        //get max offset relative to markers
+        const offsetMax = range[1] - range[0];
+
+        //input offset + relative difference between top marker and parent devided by offset max
+        //save as 'float' between 0 and 1
+        const relativeInput = ((input - range[0]) / offsetMax);
+        
+        //Get the currently viewed day
+        const date = new Date(this.day);
+
+        //Convert our 'float' from 0-1 to a time between 0-24
+        date.setHours(0,0,0,relativeInput * 86400000);
+
+        //return our date
+        return date;
+    }
+
+    /**
+     * Calculate schedule offset from top or bottom of div from JS Epoch (millis)
+     * @param {Number} input - JS Epoch (millis) to calculate from, defaults to 0
+     * @param {Boolean} bottomOffset - Whether or not offset should be calculated from top or bottom
+     * @returns {Number} Offset from top/bottom of div in PX relative to time markers, calculated from given date
+     */
+    offsetByMilliseconds(input = 0, bottomOffset = false){
+        //Convert amount of milliseconds to a float, 0 representing the start of the day and 1 representing the end.
+        //Make sure to reverse it if bottomOffset is set to true
+        const timeFloat = bottomOffset ? 1 - (input / 86400000) : input / 86400000;
+
+        //Get queue markers
+        const markers = this.panelDocument.querySelectorAll('span.queue-marker');
+
+        //If the marker is null for some reason
+        if(markers[0] == null){
+
+            return null;
+        }
+
+        //Get marker position range
+        const range = [markers[0].offsetTop, markers[markers.length - 1].offsetTop]
+        //Get maximum position relative to the range itself
+        const offsetMax = range[1] - range[0];
+        //return position relative to parent
+        return (offsetMax * timeFloat) + range[0];
+    }
+
+    /**
+     * Returns media end time
+     * @param {Object} media - media object to get end time from
+     * @returns {Number} Media end time as JS Epoch (millis)
+     */
+    getMediaEnd(media){
+        //If we have an early end
+        if(media.earlyEnd != null){
+            return media.startTime + (media.earlyEnd * 1000);
+        //Otherwise
+        }else{
+            return media.startTime + ((media.duration - media.startTimeStamp) * 1000);
+        }
+    }
+}
+
+/**
+ * Class representing pop-up dialogue to schedule a piece of media
+ */
+class schedulePopup{
+    /**
+     * Instantiates a new schedule media Pop-up
+     * @param {Event} event - Event passed down from Event Listener
+     * @param {channel} client - Parent Client Management Object
+     * @param {String} url - URL/link to media to queue
+     * @param {String} title - Title of media to queue
+     * @param {Function} cb - Callback function, passed upon pop-up creation
+     * @param {Document} doc - Current owner documnet of the panel, so we know where to drop our pop-up
+     */
+    constructor(event, client, url, title, cb, doc){
+        /**
+         * Parent Client Management Object
+         */
+        this.client = client;
+        
+        /**
+         * URL/Link to media to queue
+         */
+        this.url = url;
+        
+        /**
+         * Title of media to queue
+         */
+        this.title = title;
+        
+        /**
+         * Callback function, passed upon pop-up creation
+         */
+        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('/scheduleMedia', true, this.asyncConstructor.bind(this), doc);
+    }
+
+    /**
+     * Continuation of object construction, called after child popup object construction
+     */
+    asyncConstructor(){
+        //Grab required UI elements
+        this.scheduleButton = this.popup.contentDiv.querySelector('#schedule-media-popup-schedule-button');
+        this.datePrompt = this.popup.contentDiv.querySelector('#schedule-media-popup-time-prompt');
+
+        //getCurrentDate
+        const curDate = new Date();
+        //Zero out time to the nearest minute
+        curDate.setSeconds(0,0);
+        //Set the date prompt to the next minute, adjusted to display local time
+        this.datePrompt.valueAsDate = utils.ux.localizeDate(curDate);
+
+        //Setup input
+        this.setupInput();
+
+        //If we have a function
+        if(typeof cb == "function"){
+            //Call any callbacks we where given
+            this.cb();
+        }
+    }
+
+    /**
+     * Defines input-related Event Handlers
+     */
+    setupInput(){
+        //Setup input
+        this.scheduleButton.addEventListener('click', this.schedule.bind(this));
+        this.popup.popupDiv.addEventListener('keydown', this.schedule.bind(this));
+    }
+
+    /**
+     * Handles sending request to schedule item to the queue
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    schedule(event){
+        //If we clicked or hit enter
+        if(event.key == null || event.key == "Enter"){
+            //Get localized input date
+            const inputDate = utils.ux.normalizeDate(this.datePrompt.valueAsDate);
+
+            //Tell the server to move the media
+            this.client.socket.emit("queue",{url: this.url, title: this.title, start: inputDate.getTime()});
+
+            //Close the popup
+            this.popup.closePopup();
+        }
+    }
+}
+
+/**
+ * Class representing pop-up dialogue for reschedule queue media
+ * @extends schedulePopup
+ */
+class reschedulePopup extends schedulePopup{
+    /**
+     * Instantiates a new schedule media Pop-up
+     * @param {Event} event - Event passed down from Event Listener
+     * @param {channel} client - Parent Client Management Object
+     * @param {Object} media - Media object to re-schedule
+     * @param {Function} cb - Callback function, passed upon pop-up creation
+     * @param {Document} doc - Current owner documnet of the panel, so we know where to drop our pop-up
+     */
+    constructor(event, client, media, cb, doc){
+        //Call derived constructor
+        super(event, client, null, null, cb, doc);
+
+        /**
+         * Media Object to Re-Schedule
+         */
+        this.media = media;
+    }
+
+    schedule(event){
+        //If we clicked or hit enter
+        if(event.key == null || event.key == "Enter"){
+            //Get localized input date
+            const inputDate = utils.ux.normalizeDate(this.datePrompt.valueAsDate);
+
+            //Tell the server to move the media
+            this.client.socket.emit('move', {uuid: this.media.uuid, start: inputDate.getTime()});
+
+            //Close the popup
+            this.popup.closePopup();
+        }
+    }
+}
+
+/**
+ * Class represneting pop-up dialogue for clearing queue between a range of two dates
+ */
+class clearPopup{
+    /**
+     * Instantiates a new queue Clear Popup
+     * @param {Event} event - Event passed down from Event Listener
+     * @param {channel} client - Parent Client Management Object
+     * @param {Function} cb - Callback function, passed upon pop-up creation
+     * @param {Document} doc - Current owner documnet of the panel, so we know where to drop our pop-up
+     */
+    constructor(event, client, cb, doc){
+        /**
+         * Parent Client Management Object
+         */
+        this.client = client;
+
+        /**
+         * Callback function, passed upon pop-up creation
+         */
+        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('/clearMedia', true, this.asyncConstructor.bind(this), doc);
+    }
+
+    /**
+     * Continuation of object construction, called after child popup object construction
+     */
+    asyncConstructor(){
+        //Grab required UI elements
+        this.clearButton = this.popup.contentDiv.querySelector('#clear-media-popup-clear-button');
+        this.startDatePrompt = this.popup.contentDiv.querySelector('#clear-media-popup-start-time-prompt');
+        this.endDatePrompt = this.popup.contentDiv.querySelector('#clear-media-popup-end-time-prompt');
+
+        //getCurrentDate
+        const curDate = new Date();
+        //Zero out current time to the nearest minute
+        curDate.setSeconds(0,0);
+        //Set the start date prompt to the next minute, adjusted to display local time
+        this.startDatePrompt.valueAsDate = utils.ux.localizeDate(curDate);
+        //Add 30 minutes
+        curDate.setMinutes(curDate.getMinutes() + 30);
+        //Set the end date prompt to 30 minutes in the futre, adjusted to display local time
+        this.endDatePrompt.valueAsDate = utils.ux.localizeDate(curDate);
+
+        //Setup input
+        this.setupInput();
+
+        //If we have a function
+        if(typeof cb == "function"){
+            //Call any callbacks we where given
+            this.cb();
+        }
+    }
+
+    /**
+     * Defines input-related Event Handlers
+     */
+    setupInput(){
+        //Setup input
+        this.clearButton.addEventListener('click', this.clear.bind(this));
+        this.popup.popupDiv.addEventListener('keydown', this.clear.bind(this));
+    }
+
+    /**
+     * Handles sending request to clear playlist between two dates to the server
+     * @param {Event} event - Event passed down from Event Listener
+     */
+    clear(event){
+        //If we clicked or hit enter
+        if(event.key == null || event.key == "Enter"){
+            //Get localized input date
+            const inputStartDate = utils.ux.normalizeDate(this.startDatePrompt.valueAsDate);
+            const inputEndDate = utils.ux.normalizeDate(this.endDatePrompt.valueAsDate);
+
+            //Tell the server to clear media between the input range
+            this.client.socket.emit("clear",{start: inputStartDate.getTime(), end: inputEndDate.getTime()});
+
+            //Close the popup
+            this.popup.closePopup();
+        }
+    }
+}
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/www/doc/client/panels_settingsPanel.js.html b/www/doc/client/panels_settingsPanel.js.html index 6b2601f..be0bdba 100644 --- a/www/doc/client/panels_settingsPanel.js.html +++ b/www/doc/client/panels_settingsPanel.js.html @@ -97,13 +97,13 @@ class settingsPanel extends panelObj{
diff --git a/www/doc/client/player.html b/www/doc/client/player.html index 89d5b92..010c7aa 100644 --- a/www/doc/client/player.html +++ b/www/doc/client/player.html @@ -3368,13 +3368,13 @@ Might seem weird to keep this here instead of the HLS handler, but remember we m
diff --git a/www/doc/client/player.js.html b/www/doc/client/player.js.html index 02bacb1..433f762 100644 --- a/www/doc/client/player.js.html +++ b/www/doc/client/player.js.html @@ -468,13 +468,13 @@ class player{
diff --git a/www/doc/client/playlistManager.html b/www/doc/client/playlistManager.html index 9f37da6..c5d099d 100644 --- a/www/doc/client/playlistManager.html +++ b/www/doc/client/playlistManager.html @@ -141,7 +141,7 @@ -queuePanel +queuePanel @@ -601,7 +601,7 @@
Source:
@@ -738,7 +738,7 @@
Source:
@@ -963,7 +963,7 @@
Source:
@@ -1100,7 +1100,95 @@
Source:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

docSwitch()

+ + + + + + +
+ Handles Up-stream Document/Panel Changes from the parent Queue Panel object +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
@@ -1237,7 +1325,7 @@
Source:
@@ -1374,7 +1462,7 @@
Source:
@@ -1511,7 +1599,7 @@
Source:
@@ -1648,7 +1736,7 @@
Source:
@@ -1785,7 +1873,7 @@
Source:
@@ -1922,7 +2010,7 @@
Source:
@@ -2082,7 +2170,7 @@
Source:
@@ -2264,7 +2352,7 @@
Source:
@@ -2446,7 +2534,7 @@
Source:
@@ -2651,7 +2739,7 @@
Source:
@@ -2833,7 +2921,7 @@
Source:
@@ -2992,7 +3080,7 @@
Source:
@@ -3080,7 +3168,7 @@
Source:
@@ -3217,7 +3305,7 @@
Source:
@@ -3354,7 +3442,7 @@
Source:
@@ -3491,7 +3579,7 @@
Source:
@@ -3537,13 +3625,13 @@
diff --git a/www/doc/client/poppedPanel.html b/www/doc/client/poppedPanel.html index bf8865d..6f39b48 100644 --- a/www/doc/client/poppedPanel.html +++ b/www/doc/client/poppedPanel.html @@ -1436,13 +1436,13 @@
diff --git a/www/doc/client/queuePanel.html b/www/doc/client/queuePanel.html new file mode 100644 index 0000000..cee7e1f --- /dev/null +++ b/www/doc/client/queuePanel.html @@ -0,0 +1,5322 @@ + + + + + JSDoc: Class: queuePanel + + + + + + + + + + +
+ +

Class: queuePanel

+ + + + + + +
+ +
+ +

queuePanel(client, panelDocument)

+ +
Class representing Queue Panel UX
+ + +
+ +
+
+ + + + +

Constructor

+ + + +

new queuePanel(client, panelDocument)

+ + + + + + +
+ Instantiates a new Queue Panel object +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
client + + +channel + + + + Parent Client Management Object
panelDocument + + +Document + + + + Panel Document
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

autoscroll

+ + + + +
+ Enables auto-scroll on schedule UX +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

day

+ + + + +
+ Current day, zero'd out to midnight +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

playlistManager

+ + + + +
+ Child Playlist Manager Object +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

rescaleTimer

+ + + + +
+ Re-scale timer, counts down after re-sizing to clear re-size UI and show schedule again +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

scale

+ + + + +
+ Schedule time scale in seconds, defaults to 30 minutes +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

clearMedia(event)

+ + + + + + +
+ Sends request to server to clear media between a range of two dates +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

clearQueue()

+ + + + + + +
+ Clears out queue container for re-render +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

dateByOffset(input) → {Date}

+ + + + + + +
+ Calculates date by offset pixels relative to schedule div top +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
input + + +Number + + + + + + 0 + + Pixels date is away from schedule div top
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+ Date object representing date which was calculated from given pixel offset +
+ + + +
+
+ Type +
+
+ +Date + + +
+
+ + + + + + + + + + + + + +

decrementDate(event)

+ + + + + + +
+ Decrements displayed schedule date +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

defineListeners()

+ + + + + + +
+ Handles Network-Related Event Listeners +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

(async) fullRender(date)

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
date + + +Date + + + + Current time,
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

getMediaEnd(media) → {Number}

+ + + + + + +
+ Returns media end time +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
media + + +Object + + + + media object to get end time from
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+ Media end time as JS Epoch (millis) +
+ + + +
+
+ Type +
+
+ +Number + + +
+
+ + + + + + + + + + + + + +

goLive(event)

+ + + + + + +
+ Handles sending request to server to start a live stream +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

handleEnd()

+ + + + + + +
+ Handles call from server to end media +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

handleQueueError(data)

+ + + + + + +
+ Handles queue-related error from the server +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +Object + + + + Data from server
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

handleScheduleLock()

+ + + + + + +
+ Handles call from server to lock schedule +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

handleStart(data)

+ + + + + + +
+ Handles call from server to start media +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +Object + + + + Data from server
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

incrementDate(event)

+ + + + + + +
+ Increments displayed schedule date +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

killTimetips(event)

+ + + + + + +
+ Kills off hung tooltips +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

lockSchedule(event)

+ + + + + + +
+ Sends request to lock the schedule +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

lockScroll(event, jumpToDay)

+ + + + + + +
+ Locks schedule scroll to current time marker +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
event + + +Event + + + + + + Event passed down from Event Listener
jumpToDay + + +Boolean + + + + + + true + + whether or not to jump schedule to the current day
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

offsetByDate(date, bottomOffset) → {Number}

+ + + + + + +
+ Calculate schedule offset from top or bottom of div from date +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
date + + +Date + + + + + + Date to calculate from, defaults to now
bottomOffset + + +Boolean + + + + + + false + + Whether or not offset should be calculated from top or bottom
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+ Offset from top/bottom of div in PX relative to time markers, calculated from given date +
+ + + +
+
+ Type +
+
+ +Number + + +
+
+ + + + + + + + + + + + + +

offsetByMilliseconds(input, bottomOffset) → {Number}

+ + + + + + +
+ Calculate schedule offset from top or bottom of div from JS Epoch (millis) +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
input + + +Number + + + + + + 0 + + JS Epoch (millis) to calculate from, defaults to 0
bottomOffset + + +Boolean + + + + + + false + + Whether or not offset should be calculated from top or bottom
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+ Offset from top/bottom of div in PX relative to time markers, calculated from given date +
+ + + +
+
+ Type +
+
+ +Number + + +
+
+ + + + + + + + + + + + + +

queueAt(event)

+ + + + + + +
+ Sends request to queue current URL at the current time +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

queueLast(event)

+ + + + + + +
+ Sends request to queue current URL after the last item +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

renderInterval(date)

+ + + + + + +
+ Render call called at 1-second intervals, handles time and livestream markers +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
date + + +Date + + + + Date representing current time, defaults to new date object
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

renderLiveStream(inputDate, intervalRun)

+ + + + + + +
+ Renders live-stream marker +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
inputDate + + +Date + + + + Date representing current time, defaults to new date object
intervalRun + + +Boolean + + + + Whether or not this was run by renderInterval, defaults to false
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

renderQueue(date)

+ + + + + + +
+ Renders out schedule +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
date + + +Date + + + + Date representing current time, defaults to new date object
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

renderQueueScale(inputDate)

+ + + + + + +
+ Renders queue scale markers +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
inputDate + + +Date + + + + Date representing current time, defaults to new date object
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

renderTimeMarker(date, forceScroll)

+ + + + + + +
+ Renders current time marker on to the schedule +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
date + + +Date + + + + + + Date representing current time, defaults to new date object
forceScroll + + +Boolean + + + + + + false + + Whether or not to scroll the schedule on increment
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

resizeRender(event)

+ + + + + + +
+ Handles re-rendering schedule upon window re-size +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

scaleScroll(event)

+ + + + + + +
+ Handles time-scale changes on crtl+scroll +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

setDay(date)

+ + + + + + +
+ Directly sets schedule display date +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
date + + +Date + + + + Date to set schedule to
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

setQueueDate(event)

+ + + + + + +
+ Validates and sets display schedule date from user input +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

setupInput()

+ + + + + + +
+ Handles Input-Related Event Listeners +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

toggleAddMedia(event)

+ + + + + + +
+ Toggles add media UX +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

toggleDateControl(event)

+ + + + + + +
+ Toggles schedule date control +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

toggleGoLive(event)

+ + + + + + +
+ Toggles Go-Live UX +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

togglePlaylistDiv(event)

+ + + + + + +
+ Toggles playlist management UX +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

unlockScroll()

+ + + + + + +
+ Un-locks scroll from curren time marker +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/www/doc/client/rawFileBase.html b/www/doc/client/rawFileBase.html index 30eca5c..c28becc 100644 --- a/www/doc/client/rawFileBase.html +++ b/www/doc/client/rawFileBase.html @@ -2908,13 +2908,13 @@
diff --git a/www/doc/client/rawFileHandler.html b/www/doc/client/rawFileHandler.html index d4800dd..a130f13 100644 --- a/www/doc/client/rawFileHandler.html +++ b/www/doc/client/rawFileHandler.html @@ -2890,13 +2890,13 @@
diff --git a/www/doc/client/renamePopup.html b/www/doc/client/renamePopup.html index 95ad387..d3afa2d 100644 --- a/www/doc/client/renamePopup.html +++ b/www/doc/client/renamePopup.html @@ -238,7 +238,7 @@
Source:
@@ -332,7 +332,7 @@
Source:
@@ -394,7 +394,7 @@
Source:
@@ -456,7 +456,7 @@
Source:
@@ -518,7 +518,7 @@
Source:
@@ -598,7 +598,7 @@
Source:
@@ -735,7 +735,7 @@
Source:
@@ -823,7 +823,7 @@
Source:
@@ -869,13 +869,13 @@
diff --git a/www/doc/client/reschedulePopup.html b/www/doc/client/reschedulePopup.html new file mode 100644 index 0000000..94fb5a3 --- /dev/null +++ b/www/doc/client/reschedulePopup.html @@ -0,0 +1,1059 @@ + + + + + JSDoc: Class: reschedulePopup + + + + + + + + + + +
+ +

Class: reschedulePopup

+ + + + + + +
+ +
+ +

reschedulePopup(event, client, media, cb, doc)

+ +
Class representing pop-up dialogue for reschedule queue media
+ + +
+ +
+
+ + + + +

Constructor

+ + + +

new reschedulePopup(event, client, media, cb, doc)

+ + + + + + +
+ Instantiates a new schedule media Pop-up +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
client + + +channel + + + + Parent Client Management Object
media + + +Object + + + + Media object to re-schedule
cb + + +function + + + + Callback function, passed upon pop-up creation
doc + + +Document + + + + Current owner documnet of the panel, so we know where to drop our pop-up
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + +

Extends

+ + + + + + + + + + + + + + + + + + + + +

Members

+ + + +

cb

+ + + + +
+ Callback function, passed upon pop-up creation +
+ + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

client

+ + + + +
+ Parent Client Management Object +
+ + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

media

+ + + + +
+ Media Object to Re-Schedule +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
+ canopyUXUtils.popup() object +
+ + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

title

+ + + + +
+ Title of media to queue +
+ + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

url

+ + + + +
+ URL/Link to media to queue +
+ + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

asyncConstructor()

+ + + + + + +
+ Continuation of object construction, called after child popup object construction +
+ + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

schedule(event)

+ + + + + + +
+ Handles sending request to schedule item to the queue +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

setupInput()

+ + + + + + +
+ Defines input-related Event Handlers +
+ + + + + + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/www/doc/client/schedulePopup.html b/www/doc/client/schedulePopup.html new file mode 100644 index 0000000..0c053a5 --- /dev/null +++ b/www/doc/client/schedulePopup.html @@ -0,0 +1,969 @@ + + + + + JSDoc: Class: schedulePopup + + + + + + + + + + +
+ +

Class: schedulePopup

+ + + + + + +
+ +
+ +

schedulePopup(event, client, url, title, cb, doc)

+ +
Class representing pop-up dialogue to schedule a piece of media
+ + +
+ +
+
+ + + + +

Constructor

+ + + +

new schedulePopup(event, client, url, title, cb, doc)

+ + + + + + +
+ Instantiates a new schedule media Pop-up +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
client + + +channel + + + + Parent Client Management Object
url + + +String + + + + URL/link to media to queue
title + + +String + + + + Title of media to queue
cb + + +function + + + + Callback function, passed upon pop-up creation
doc + + +Document + + + + Current owner documnet of the panel, so we know where to drop our pop-up
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +

Members

+ + + +

cb

+ + + + +
+ Callback function, passed upon pop-up creation +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

client

+ + + + +
+ Parent Client Management Object +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
+ canopyUXUtils.popup() object +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

title

+ + + + +
+ Title of media to queue +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + +

url

+ + + + +
+ URL/Link to media to queue +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + +

Methods

+ + + + + + + +

asyncConstructor()

+ + + + + + +
+ Continuation of object construction, called after child popup object construction +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

schedule(event)

+ + + + + + +
+ Handles sending request to schedule item to the queue +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
event + + +Event + + + + Event passed down from Event Listener
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

setupInput()

+ + + + + + +
+ Defines input-related Event Handlers +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/www/doc/client/settingsPanel.html b/www/doc/client/settingsPanel.html index 602065a..8372d57 100644 --- a/www/doc/client/settingsPanel.html +++ b/www/doc/client/settingsPanel.html @@ -1150,13 +1150,13 @@
diff --git a/www/doc/client/userList.html b/www/doc/client/userList.html index 113b5fd..240eb75 100644 --- a/www/doc/client/userList.html +++ b/www/doc/client/userList.html @@ -1185,13 +1185,13 @@
diff --git a/www/doc/client/userlist.js.html b/www/doc/client/userlist.js.html index 0502398..7a25e12 100644 --- a/www/doc/client/userlist.js.html +++ b/www/doc/client/userlist.js.html @@ -246,13 +246,13 @@ class userList{
diff --git a/www/doc/client/youtubeEmbedHandler.html b/www/doc/client/youtubeEmbedHandler.html index 14d4cdd..0f43fa4 100644 --- a/www/doc/client/youtubeEmbedHandler.html +++ b/www/doc/client/youtubeEmbedHandler.html @@ -2885,13 +2885,13 @@
diff --git a/www/doc/server/activeChannel.html b/www/doc/server/activeChannel.html index 2981333..b627572 100644 --- a/www/doc/server/activeChannel.html +++ b/www/doc/server/activeChannel.html @@ -786,7 +786,7 @@
diff --git a/www/doc/server/app_channel_activeChannel.js.html b/www/doc/server/app_channel_activeChannel.js.html index 8c64334..0416210 100644 --- a/www/doc/server/app_channel_activeChannel.js.html +++ b/www/doc/server/app_channel_activeChannel.js.html @@ -196,7 +196,7 @@ module.exports = activeChannel;
diff --git a/www/doc/server/app_channel_channelManager.js.html b/www/doc/server/app_channel_channelManager.js.html index df13b07..d735a64 100644 --- a/www/doc/server/app_channel_channelManager.js.html +++ b/www/doc/server/app_channel_channelManager.js.html @@ -347,7 +347,7 @@ module.exports = channelManager;
diff --git a/www/doc/server/app_channel_chat.js.html b/www/doc/server/app_channel_chat.js.html index 6f7d25d..1e4952e 100644 --- a/www/doc/server/app_channel_chat.js.html +++ b/www/doc/server/app_channel_chat.js.html @@ -81,7 +81,7 @@ module.exports = chat;
diff --git a/www/doc/server/app_channel_chatBuffer.js.html b/www/doc/server/app_channel_chatBuffer.js.html index facfe82..b6e068c 100644 --- a/www/doc/server/app_channel_chatBuffer.js.html +++ b/www/doc/server/app_channel_chatBuffer.js.html @@ -178,7 +178,7 @@ module.exports = chatBuffer;
diff --git a/www/doc/server/app_channel_chatHandler.js.html b/www/doc/server/app_channel_chatHandler.js.html index 550a91d..2fdd6ec 100644 --- a/www/doc/server/app_channel_chatHandler.js.html +++ b/www/doc/server/app_channel_chatHandler.js.html @@ -376,7 +376,7 @@ module.exports = chatHandler;
diff --git a/www/doc/server/app_channel_commandPreprocessor.js.html b/www/doc/server/app_channel_commandPreprocessor.js.html index e1cf92e..a9e0b48 100644 --- a/www/doc/server/app_channel_commandPreprocessor.js.html +++ b/www/doc/server/app_channel_commandPreprocessor.js.html @@ -473,7 +473,7 @@ module.exports = commandPreprocessor;
diff --git a/www/doc/server/app_channel_connectedUser.js.html b/www/doc/server/app_channel_connectedUser.js.html index 63c8cee..2c71b12 100644 --- a/www/doc/server/app_channel_connectedUser.js.html +++ b/www/doc/server/app_channel_connectedUser.js.html @@ -334,7 +334,7 @@ module.exports = connectedUser;
diff --git a/www/doc/server/app_channel_media_media.js.html b/www/doc/server/app_channel_media_media.js.html index e21100b..c6d3c26 100644 --- a/www/doc/server/app_channel_media_media.js.html +++ b/www/doc/server/app_channel_media_media.js.html @@ -83,7 +83,7 @@ module.exports = media;
diff --git a/www/doc/server/app_channel_media_playlistHandler.js.html b/www/doc/server/app_channel_media_playlistHandler.js.html index 504eb56..919fa37 100644 --- a/www/doc/server/app_channel_media_playlistHandler.js.html +++ b/www/doc/server/app_channel_media_playlistHandler.js.html @@ -1180,7 +1180,7 @@ module.exports = playlistHandler;
diff --git a/www/doc/server/app_channel_media_queue.js.html b/www/doc/server/app_channel_media_queue.js.html index 9028c9e..bdacf29 100644 --- a/www/doc/server/app_channel_media_queue.js.html +++ b/www/doc/server/app_channel_media_queue.js.html @@ -1795,7 +1795,7 @@ module.exports = queue;
diff --git a/www/doc/server/app_channel_media_queuedMedia.js.html b/www/doc/server/app_channel_media_queuedMedia.js.html index 9a714d8..7ddc804 100644 --- a/www/doc/server/app_channel_media_queuedMedia.js.html +++ b/www/doc/server/app_channel_media_queuedMedia.js.html @@ -165,7 +165,7 @@ module.exports = queuedMedia;
diff --git a/www/doc/server/app_channel_tokebot.js.html b/www/doc/server/app_channel_tokebot.js.html index 3cd8f2b..1f8a045 100644 --- a/www/doc/server/app_channel_tokebot.js.html +++ b/www/doc/server/app_channel_tokebot.js.html @@ -273,7 +273,7 @@ module.exports = tokebot;
diff --git a/www/doc/server/channelManager.html b/www/doc/server/channelManager.html index e191877..0f5fc9d 100644 --- a/www/doc/server/channelManager.html +++ b/www/doc/server/channelManager.html @@ -1991,7 +1991,7 @@
diff --git a/www/doc/server/chat.html b/www/doc/server/chat.html index f6cf2ce..c0e99fb 100644 --- a/www/doc/server/chat.html +++ b/www/doc/server/chat.html @@ -329,7 +329,7 @@
diff --git a/www/doc/server/chatBuffer.html b/www/doc/server/chatBuffer.html index af34fbe..c8c390e 100644 --- a/www/doc/server/chatBuffer.html +++ b/www/doc/server/chatBuffer.html @@ -829,7 +829,7 @@ Left here since it seems like good form anywho, since this would be a private, o
diff --git a/www/doc/server/chatHandler.html b/www/doc/server/chatHandler.html index 30d7724..e89a68b 100644 --- a/www/doc/server/chatHandler.html +++ b/www/doc/server/chatHandler.html @@ -3686,7 +3686,7 @@
diff --git a/www/doc/server/commandPreprocessor.html b/www/doc/server/commandPreprocessor.html index dc4c15a..4da5243 100644 --- a/www/doc/server/commandPreprocessor.html +++ b/www/doc/server/commandPreprocessor.html @@ -1246,7 +1246,7 @@ These arrays are used to handle further command/chat processing
diff --git a/www/doc/server/commandProcessor.html b/www/doc/server/commandProcessor.html index b841a59..217bc3d 100644 --- a/www/doc/server/commandProcessor.html +++ b/www/doc/server/commandProcessor.html @@ -1831,7 +1831,7 @@
diff --git a/www/doc/server/connectedUser.html b/www/doc/server/connectedUser.html index 5957cb4..c67fe94 100644 --- a/www/doc/server/connectedUser.html +++ b/www/doc/server/connectedUser.html @@ -1879,7 +1879,7 @@ Having to crawl through these sockets is that. Because the other ways seem more
diff --git a/www/doc/server/global.html b/www/doc/server/global.html index 26ef9e7..8c2d71d 100644 --- a/www/doc/server/global.html +++ b/www/doc/server/global.html @@ -7377,7 +7377,7 @@ Warns server admin against unsafe config options.
diff --git a/www/doc/server/index.html b/www/doc/server/index.html index 22ed415..85fe372 100644 --- a/www/doc/server/index.html +++ b/www/doc/server/index.html @@ -87,7 +87,7 @@ This new codebase intends to solve the following issues with the current CyTube
diff --git a/www/doc/server/media.html b/www/doc/server/media.html index 13be405..578a2d9 100644 --- a/www/doc/server/media.html +++ b/www/doc/server/media.html @@ -352,7 +352,7 @@
diff --git a/www/doc/server/playlistHandler.html b/www/doc/server/playlistHandler.html index 61f1ac8..c544c67 100644 --- a/www/doc/server/playlistHandler.html +++ b/www/doc/server/playlistHandler.html @@ -5108,7 +5108,7 @@
diff --git a/www/doc/server/queue.html b/www/doc/server/queue.html index 0e0ea9d..d0b5249 100644 --- a/www/doc/server/queue.html +++ b/www/doc/server/queue.html @@ -5805,7 +5805,7 @@ Called auto-magically by the Synchronization Timer
diff --git a/www/doc/server/queuedMedia.html b/www/doc/server/queuedMedia.html index 78fcb9b..b1943d8 100644 --- a/www/doc/server/queuedMedia.html +++ b/www/doc/server/queuedMedia.html @@ -936,7 +936,7 @@
diff --git a/www/doc/server/schemas_channel_channelBanSchema.js.html b/www/doc/server/schemas_channel_channelBanSchema.js.html index 5f9adab..85ed105 100644 --- a/www/doc/server/schemas_channel_channelBanSchema.js.html +++ b/www/doc/server/schemas_channel_channelBanSchema.js.html @@ -101,7 +101,7 @@ module.exports = channelBanSchema;
diff --git a/www/doc/server/schemas_channel_channelPermissionSchema.js.html b/www/doc/server/schemas_channel_channelPermissionSchema.js.html index e7db35e..f333bae 100644 --- a/www/doc/server/schemas_channel_channelPermissionSchema.js.html +++ b/www/doc/server/schemas_channel_channelPermissionSchema.js.html @@ -169,7 +169,7 @@ module.exports = channelPermissionSchema;
diff --git a/www/doc/server/schemas_channel_channelSchema.js.html b/www/doc/server/schemas_channel_channelSchema.js.html index 99ffb23..747a402 100644 --- a/www/doc/server/schemas_channel_channelSchema.js.html +++ b/www/doc/server/schemas_channel_channelSchema.js.html @@ -934,7 +934,7 @@ module.exports = mongoose.model("channel", channelSchema);
diff --git a/www/doc/server/schemas_channel_chatSchema.js.html b/www/doc/server/schemas_channel_chatSchema.js.html index b29d51c..abe226c 100644 --- a/www/doc/server/schemas_channel_chatSchema.js.html +++ b/www/doc/server/schemas_channel_chatSchema.js.html @@ -96,7 +96,7 @@ module.exports = chatSchema;
diff --git a/www/doc/server/schemas_channel_media_mediaSchema.js.html b/www/doc/server/schemas_channel_media_mediaSchema.js.html index f024dec..691feec 100644 --- a/www/doc/server/schemas_channel_media_mediaSchema.js.html +++ b/www/doc/server/schemas_channel_media_mediaSchema.js.html @@ -96,7 +96,7 @@ module.exports = mediaSchema;
diff --git a/www/doc/server/schemas_channel_media_playlistMediaSchema.js.html b/www/doc/server/schemas_channel_media_playlistMediaSchema.js.html index ed20518..e01c115 100644 --- a/www/doc/server/schemas_channel_media_playlistMediaSchema.js.html +++ b/www/doc/server/schemas_channel_media_playlistMediaSchema.js.html @@ -124,7 +124,7 @@ module.exports = mediaSchema.discriminator('saved', playlistMediaProperties); diff --git a/www/doc/server/schemas_channel_media_playlistSchema.js.html b/www/doc/server/schemas_channel_media_playlistSchema.js.html index 91dc507..b6a48ed 100644 --- a/www/doc/server/schemas_channel_media_playlistSchema.js.html +++ b/www/doc/server/schemas_channel_media_playlistSchema.js.html @@ -174,7 +174,7 @@ module.exports = playlistSchema;
diff --git a/www/doc/server/schemas_channel_media_queuedMediaSchema.js.html b/www/doc/server/schemas_channel_media_queuedMediaSchema.js.html index 652848d..c23211b 100644 --- a/www/doc/server/schemas_channel_media_queuedMediaSchema.js.html +++ b/www/doc/server/schemas_channel_media_queuedMediaSchema.js.html @@ -113,7 +113,7 @@ module.exports = mediaSchema.discriminator('queued', queuedProperties); diff --git a/www/doc/server/schemas_emoteSchema.js.html b/www/doc/server/schemas_emoteSchema.js.html index 8ffcdc0..442d3fd 100644 --- a/www/doc/server/schemas_emoteSchema.js.html +++ b/www/doc/server/schemas_emoteSchema.js.html @@ -164,7 +164,7 @@ module.exports = mongoose.model("emote", emoteSchema);
diff --git a/www/doc/server/schemas_flairSchema.js.html b/www/doc/server/schemas_flairSchema.js.html index 2335427..819824b 100644 --- a/www/doc/server/schemas_flairSchema.js.html +++ b/www/doc/server/schemas_flairSchema.js.html @@ -118,7 +118,7 @@ module.exports = mongoose.model("flair", flairSchema);
diff --git a/www/doc/server/schemas_permissionSchema.js.html b/www/doc/server/schemas_permissionSchema.js.html index 350ea00..9152edb 100644 --- a/www/doc/server/schemas_permissionSchema.js.html +++ b/www/doc/server/schemas_permissionSchema.js.html @@ -356,7 +356,7 @@ module.exports = mongoose.model("permissions", permissionSchema);
diff --git a/www/doc/server/schemas_statSchema.js.html b/www/doc/server/schemas_statSchema.js.html index ac632f9..2010006 100644 --- a/www/doc/server/schemas_statSchema.js.html +++ b/www/doc/server/schemas_statSchema.js.html @@ -240,7 +240,7 @@ module.exports = mongoose.model("statistics", statSchema);
diff --git a/www/doc/server/schemas_tokebot_tokeCommandSchema.js.html b/www/doc/server/schemas_tokebot_tokeCommandSchema.js.html index cdf42f1..daa18c1 100644 --- a/www/doc/server/schemas_tokebot_tokeCommandSchema.js.html +++ b/www/doc/server/schemas_tokebot_tokeCommandSchema.js.html @@ -160,7 +160,7 @@ module.exports = mongoose.model("tokeCommand", tokeCommandSchema);
diff --git a/www/doc/server/schemas_user_emailChangeSchema.js.html b/www/doc/server/schemas_user_emailChangeSchema.js.html index 20b38b1..1a1592d 100644 --- a/www/doc/server/schemas_user_emailChangeSchema.js.html +++ b/www/doc/server/schemas_user_emailChangeSchema.js.html @@ -222,7 +222,7 @@ module.exports = mongoose.model("emailChange", emailChangeSchema);
diff --git a/www/doc/server/schemas_user_passwordResetSchema.js.html b/www/doc/server/schemas_user_passwordResetSchema.js.html index 813b93c..4768a5c 100644 --- a/www/doc/server/schemas_user_passwordResetSchema.js.html +++ b/www/doc/server/schemas_user_passwordResetSchema.js.html @@ -198,7 +198,7 @@ module.exports = mongoose.model("passwordReset", passwordResetSchema);
diff --git a/www/doc/server/schemas_user_userBanSchema.js.html b/www/doc/server/schemas_user_userBanSchema.js.html index 18f6dd4..3606178 100644 --- a/www/doc/server/schemas_user_userBanSchema.js.html +++ b/www/doc/server/schemas_user_userBanSchema.js.html @@ -521,7 +521,7 @@ module.exports = mongoose.model("userBan", userBanSchema);
diff --git a/www/doc/server/schemas_user_userSchema.js.html b/www/doc/server/schemas_user_userSchema.js.html index b871d4d..c717362 100644 --- a/www/doc/server/schemas_user_userSchema.js.html +++ b/www/doc/server/schemas_user_userSchema.js.html @@ -888,7 +888,7 @@ module.exports.userModel = mongoose.model("user", userSchema);
diff --git a/www/doc/server/tokebot.html b/www/doc/server/tokebot.html index 4ed8aee..45a896d 100644 --- a/www/doc/server/tokebot.html +++ b/www/doc/server/tokebot.html @@ -841,7 +841,7 @@ I would now, but I don't want to break shit in a comment-only commit.
diff --git a/www/doc/server/utils_altchaUtils.js.html b/www/doc/server/utils_altchaUtils.js.html index 0658a00..492ca5b 100644 --- a/www/doc/server/utils_altchaUtils.js.html +++ b/www/doc/server/utils_altchaUtils.js.html @@ -118,7 +118,7 @@ module.exports.verify = async function(payload, uniqueSecret = ''){
diff --git a/www/doc/server/utils_configCheck.js.html b/www/doc/server/utils_configCheck.js.html index 343bc7a..c642d74 100644 --- a/www/doc/server/utils_configCheck.js.html +++ b/www/doc/server/utils_configCheck.js.html @@ -108,7 +108,7 @@ module.exports.securityCheck = function(){
diff --git a/www/doc/server/utils_hashUtils.js.html b/www/doc/server/utils_hashUtils.js.html index a626089..0fb01b8 100644 --- a/www/doc/server/utils_hashUtils.js.html +++ b/www/doc/server/utils_hashUtils.js.html @@ -103,7 +103,7 @@ module.exports.hashIP = function(ip){
diff --git a/www/doc/server/utils_linkUtils.js.html b/www/doc/server/utils_linkUtils.js.html index c3eb85e..3716202 100644 --- a/www/doc/server/utils_linkUtils.js.html +++ b/www/doc/server/utils_linkUtils.js.html @@ -146,7 +146,7 @@ module.exports.markLink = async function(link){
diff --git a/www/doc/server/utils_loggerUtils.js.html b/www/doc/server/utils_loggerUtils.js.html index e8e9296..40ed5cf 100644 --- a/www/doc/server/utils_loggerUtils.js.html +++ b/www/doc/server/utils_loggerUtils.js.html @@ -207,7 +207,7 @@ module.exports.errorMiddleware = function(err, req, res, next){
diff --git a/www/doc/server/utils_mailUtils.js.html b/www/doc/server/utils_mailUtils.js.html index 4ec3345..991adcb 100644 --- a/www/doc/server/utils_mailUtils.js.html +++ b/www/doc/server/utils_mailUtils.js.html @@ -140,7 +140,7 @@ module.exports.sendAddressVerification = async function(requestDB, userDB, newEm
diff --git a/www/doc/server/utils_media_internetArchiveUtils.js.html b/www/doc/server/utils_media_internetArchiveUtils.js.html index 863a13d..1c949a8 100644 --- a/www/doc/server/utils_media_internetArchiveUtils.js.html +++ b/www/doc/server/utils_media_internetArchiveUtils.js.html @@ -154,7 +154,7 @@ module.exports.fetchMetadata = async function(fullID, title){
diff --git a/www/doc/server/utils_media_yanker.js.html b/www/doc/server/utils_media_yanker.js.html index a53ee59..d310e11 100644 --- a/www/doc/server/utils_media_yanker.js.html +++ b/www/doc/server/utils_media_yanker.js.html @@ -193,7 +193,7 @@ module.exports.getMediaType = async function(url){
diff --git a/www/doc/server/utils_media_ytdlpUtils.js.html b/www/doc/server/utils_media_ytdlpUtils.js.html index 0dfa945..23afb14 100644 --- a/www/doc/server/utils_media_ytdlpUtils.js.html +++ b/www/doc/server/utils_media_ytdlpUtils.js.html @@ -186,7 +186,7 @@ async function ytdlpFetch(link, format = 'b'){
diff --git a/www/doc/server/utils_regexUtils.js.html b/www/doc/server/utils_regexUtils.js.html index f13d80d..2b7433d 100644 --- a/www/doc/server/utils_regexUtils.js.html +++ b/www/doc/server/utils_regexUtils.js.html @@ -69,7 +69,7 @@ module.exports.escapeRegex = function(string){
diff --git a/www/doc/server/utils_scheduler.js.html b/www/doc/server/utils_scheduler.js.html index c02d279..8f35179 100644 --- a/www/doc/server/utils_scheduler.js.html +++ b/www/doc/server/utils_scheduler.js.html @@ -105,7 +105,7 @@ module.exports.kickoff = function(){
diff --git a/www/doc/server/utils_sessionUtils.js.html b/www/doc/server/utils_sessionUtils.js.html index 50dd1bc..e94d4ae 100644 --- a/www/doc/server/utils_sessionUtils.js.html +++ b/www/doc/server/utils_sessionUtils.js.html @@ -236,7 +236,7 @@ module.exports.maxAttempts = maxAttempts;
diff --git a/www/js/channel/panels/queuePanel/playlistManager.js b/www/js/channel/panels/queuePanel/playlistManager.js index 7989bc3..57e4022 100644 --- a/www/js/channel/panels/queuePanel/playlistManager.js +++ b/www/js/channel/panels/queuePanel/playlistManager.js @@ -60,6 +60,9 @@ class playlistManager{ 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"); diff --git a/www/js/channel/panels/queuePanel/queuePanel.js b/www/js/channel/panels/queuePanel/queuePanel.js index 49a8540..9bb91f4 100644 --- a/www/js/channel/panels/queuePanel/queuePanel.js +++ b/www/js/channel/panels/queuePanel/queuePanel.js @@ -13,26 +13,45 @@ 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 .*/ + +/** + * Class representing Queue Panel UX + */ class queuePanel extends panelObj{ + /** + * Instantiates a new Queue Panel object + * @param {channel} client - Parent Client Management Object + * @param {Document} panelDocument - Panel Document + */ constructor(client, panelDocument){ //Call derived constructor super(client, "Media Schedule", "/panel/queue", panelDocument); - //Current day + /** + * Current day, zero'd out to midnight + */ this.day = new Date(); //Zero out day to midnight this.day.setHours(0,0,0,0); - //Store releative scale of items in seconds, defaulting to 30 minute chunks + /** + * Schedule time scale in seconds, defaults to 30 minutes + */ this.scale = 30 * 60; - //Create variable to hold rescale timer + /** + * Re-scale timer, counts down after re-sizing to clear re-size UI and show schedule again + */ this.rescaleTimer = null; - //Autoscroll boolean + /** + * Enables auto-scroll on schedule UX + */ this.autoscroll = true; - //Setup child classes + /** + * Child Playlist Manager Object + */ this.playlistManager = new playlistManager(client, panelDocument, this); //Define non-input event listeners @@ -112,7 +131,9 @@ class queuePanel extends panelObj{ this.killTimetips(); } - + /** + * Handles Network-Related Event Listeners + */ defineListeners(){ //Render queue when we receive a new copy of the queue data from the server //Render queue should be called within an arrow function so that it's called with default parameters, and not handed an event as a date @@ -124,6 +145,9 @@ class queuePanel extends panelObj{ this.client.socket.on("error", this.handleQueueError.bind(this)); } + /** + * Handles Input-Related Event Listeners + */ setupInput(){ //Re-render queue and time-marker on window resize as time-relative absolute positioning will be absolutely thrown this.ownerDoc.defaultView.addEventListener('resize', this.resizeRender.bind(this)); @@ -154,6 +178,10 @@ class queuePanel extends panelObj{ } /* socket.io listeners */ + /** + * Handles call from server to start media + * @param {Object} data - Data from server + */ handleStart(data){ //If we're starting an HLS Livestream if(data.media != null && data.media.type == 'livehls'){ @@ -164,7 +192,10 @@ class queuePanel extends panelObj{ this.renderQueue(); } - handleEnd(data){ + /** + * Handles call from server to end media + */ + handleEnd(){ //Reset Go Live button this.goLiveButton.classList.replace('critical-danger-button', 'danger-button'); this.goLiveButton.classList.replace('bi-record', 'bi-record2'); @@ -173,6 +204,9 @@ class queuePanel extends panelObj{ this.renderQueue(); } + /** + * Handles call from server to lock schedule + */ handleScheduleLock(){ //Get queue lock button icon const icon = this.queueLockButton.querySelector('i'); @@ -186,6 +220,10 @@ class queuePanel extends panelObj{ } } + /** + * Handles queue-related error from the server + * @param {Object} data - Data from server + */ handleQueueError(data){ //Create a flag to reload the queue let reloadQueue = false; @@ -207,6 +245,11 @@ class queuePanel extends panelObj{ } /* queue control button functions */ + + /** + * Toggles add media UX + * @param {Event} event - Event passed down from Event Listener + */ toggleAddMedia(event){ //If the div is hidden if(this.addMediaDiv.style.display == 'none'){ @@ -222,6 +265,10 @@ class queuePanel extends panelObj{ } } + /** + * Toggles Go-Live UX + * @param {Event} event - Event passed down from Event Listener + */ toggleGoLive(event){ //If we're not livestreaming if(client.player.mediaHandler.type != "livehls"){ @@ -244,11 +291,20 @@ class queuePanel extends panelObj{ } + /** + * Handles sending request to server to start a live stream + * @param {Event} event - Event passed down from Event Listener + */ goLive(event){ //Start a livestream client.socket.emit('goLive',{title: this.goLiveNamePrompt.value, mode: event.target.dataset['mode']}); } + /** + * Locks schedule scroll to current time marker + * @param {Event} event - Event passed down from Event Listener + * @param {Boolean} jumpToDay - whether or not to jump schedule to the current day + */ lockScroll(event, jumpToDay = true){ //If we're supposed to jump to the current day if(jumpToDay){ @@ -271,6 +327,10 @@ class queuePanel extends panelObj{ } } + /** + * Toggles schedule date control + * @param {Event} event - Event passed down from Event Listener + */ toggleDateControl(event){ //If the div is hidden if(this.queueDateDiv.style.display == 'none'){ @@ -290,6 +350,10 @@ class queuePanel extends panelObj{ } } + /** + * Toggles playlist management UX + * @param {Event} event - Event passed down from Event Listener + */ togglePlaylistDiv(event){ //If the div is hidden if(this.playlistDiv.style.display == 'none'){ @@ -308,16 +372,28 @@ class queuePanel extends panelObj{ } } + /** + * Sends request to server to clear media between a range of two dates + * @param {Event} event - Event passed down from Event Listener + */ clearMedia(event){ //Call up the popup new clearPopup(event, this.client, null, this.ownerDoc); } + /** + * Sends request to lock the schedule + * @param {Event} event - Event passed down from Event Listener + */ lockSchedule(event){ client.socket.emit('lock'); } /* add queue controls */ + /** + * Sends request to queue current URL after the last item + * @param {Event} event - Event passed down from Event Listener + */ queueLast(event){ //Send off the request this.client.socket.emit("queue",{url:this.addMediaLinkPrompt.value, title:this.addMediaNamePrompt.value}); @@ -327,6 +403,10 @@ class queuePanel extends panelObj{ this.addMediaNamePrompt.value = ''; } + /** + * Sends request to queue current URL at the current time + * @param {Event} event - Event passed down from Event Listener + */ queueAt(event){ //Call up the popup new schedulePopup(event, this.client, this.addMediaLinkPrompt.value, this.addMediaNamePrompt.value, null, this.ownerDoc); @@ -337,6 +417,10 @@ class queuePanel extends panelObj{ } /* set date controls */ + /** + * Increments displayed schedule date + * @param {Event} event - Event passed down from Event Listener + */ incrementDate(event){ //Increment day this.day.setDate(this.day.getDate() + 1); @@ -344,6 +428,10 @@ class queuePanel extends panelObj{ this.setDay(this.day); } + /** + * Decrements displayed schedule date + * @param {Event} event - Event passed down from Event Listener + */ decrementDate(event){ //Decrement day this.day.setDate(this.day.getDate() - 1); @@ -352,6 +440,10 @@ class queuePanel extends panelObj{ this.setDay(this.day); } + /** + * Validates and sets display schedule date from user input + * @param {Event} event - Event passed down from Event Listener + */ setQueueDate(event){ //If we have a valid date if(this.queueDatePrompt.valueAsDate != null){ @@ -361,6 +453,10 @@ class queuePanel extends panelObj{ } } + /** + * Directly sets schedule display date + * @param {Date} date - Date to set schedule to + */ setDay(date = new Date()){ //Set day this.day = date; @@ -382,6 +478,10 @@ class queuePanel extends panelObj{ } } + /** + * Handles time-scale changes on crtl+scroll + * @param {Event} event - Event passed down from Event Listener + */ scaleScroll(event){ if(event.ctrlKey){ //I could sit here and work out why timetips break everything on scalescroll @@ -455,6 +555,9 @@ class queuePanel extends panelObj{ } } + /** + * Un-locks scroll from curren time marker + */ unlockScroll(){ //Disable scroll lock this.autoscroll = false; @@ -462,12 +565,19 @@ class queuePanel extends panelObj{ this.scrollLockButton.classList.remove('positive-button'); } + /** + * Handles re-rendering schedule upon window re-size + * @param {Event} event - Event passed down from Event Listener + */ resizeRender(event){ const date = new Date(); this.renderQueue(date); this.renderTimeMarker(date); } + /** + * Clears out queue container for re-render + */ clearQueue(){ //If we have no body if(this.ownerDoc.body == null){ @@ -500,6 +610,10 @@ class queuePanel extends panelObj{ } } + /** + * + * @param {Date} date - Current time, + */ async fullRender(date = new Date()){ //Clear the queue this.clearQueue(); @@ -533,6 +647,10 @@ class queuePanel extends panelObj{ this.renderQueue(date); } + /** + * Renders out schedule + * @param {Date} date - Date representing current time, defaults to new date object + */ renderQueue(date = new Date()){ //Clear out queue container this.queueContainer.innerHTML = ''; @@ -880,6 +998,10 @@ class queuePanel extends panelObj{ } + /** + * Kills off hung tooltips + * @param {Event} event - Event passed down from Event Listener + */ killTimetips(event){ //If we have an event and it's holding any mouse buttons if(event != null && event.buttons != 0){ @@ -894,6 +1016,10 @@ class queuePanel extends panelObj{ } } + /** + * Render call called at 1-second intervals, handles time and livestream markers + * @param {Date} date - Date representing current time, defaults to new date object + */ renderInterval(date = new Date()){ this.renderTimeMarker(date); this.renderLiveStream(date, true); @@ -906,6 +1032,11 @@ class queuePanel extends panelObj{ this.renderIntervalTimer = setTimeout(this.renderInterval.bind(this), 1000); } + /** + * Renders current time marker on to the schedule + * @param {Date} date - Date representing current time, defaults to new date object + * @param {Boolean} forceScroll - Whether or not to scroll the schedule on increment + */ renderTimeMarker(date = new Date(), forceScroll = false){ //Calculate marker position by date const markerPosition = Math.round(this.offsetByDate(date)); @@ -960,6 +1091,10 @@ class queuePanel extends panelObj{ } } + /** + * Renders queue scale markers + * @param {Date} inputDate - Date representing current time, defaults to new date object + */ renderQueueScale(inputDate = new Date()){ //Clear out queue marker container this.queueMarkerContainer.innerHTML = ''; @@ -1023,6 +1158,11 @@ class queuePanel extends panelObj{ } } + /** + * Renders live-stream marker + * @param {Date} inputDate - Date representing current time, defaults to new date object + * @param {Boolean} intervalRun - Whether or not this was run by renderInterval, defaults to false + */ renderLiveStream(date = new Date(), intervalRun = false){ //Grab all live queue entries const staleEntry = this.queueContainer.querySelector('.queue-entry.live'); @@ -1188,6 +1328,12 @@ class queuePanel extends panelObj{ } } + /** + * Calculate schedule offset from top or bottom of div from date + * @param {Date} date - Date to calculate from, defaults to now + * @param {Boolean} bottomOffset - Whether or not offset should be calculated from top or bottom + * @returns {Number} Offset from top/bottom of div in PX relative to time markers, calculated from given date + */ offsetByDate(date = new Date(), bottomOffset = false){ //Pull start of day epoch from given date, make sure to use a new date object so we don't fuck up any date objects passed to us const dayEpoch = new Date(date).setHours(0,0,0,0); @@ -1198,6 +1344,11 @@ class queuePanel extends panelObj{ return this.offsetByMilliseconds(curTime, bottomOffset); } + /** + * Calculates date by offset pixels relative to schedule div top + * @param {Number} input - Pixels date is away from schedule div top + * @returns {Date} Date object representing date which was calculated from given pixel offset + */ dateByOffset(input = 0){ //Get markers const markers = this.panelDocument.querySelectorAll('span.queue-marker'); @@ -1220,6 +1371,12 @@ class queuePanel extends panelObj{ return date; } + /** + * Calculate schedule offset from top or bottom of div from JS Epoch (millis) + * @param {Number} input - JS Epoch (millis) to calculate from, defaults to 0 + * @param {Boolean} bottomOffset - Whether or not offset should be calculated from top or bottom + * @returns {Number} Offset from top/bottom of div in PX relative to time markers, calculated from given date + */ offsetByMilliseconds(input = 0, bottomOffset = false){ //Convert amount of milliseconds to a float, 0 representing the start of the day and 1 representing the end. //Make sure to reverse it if bottomOffset is set to true @@ -1242,6 +1399,11 @@ class queuePanel extends panelObj{ return (offsetMax * timeFloat) + range[0]; } + /** + * Returns media end time + * @param {Object} media - media object to get end time from + * @returns {Number} Media end time as JS Epoch (millis) + */ getMediaEnd(media){ //If we have an early end if(media.earlyEnd != null){ @@ -1253,22 +1415,51 @@ class queuePanel extends panelObj{ } } +/** + * Class representing pop-up dialogue to schedule a piece of media + */ class schedulePopup{ + /** + * Instantiates a new schedule media Pop-up + * @param {Event} event - Event passed down from Event Listener + * @param {channel} client - Parent Client Management Object + * @param {String} url - URL/link to media to queue + * @param {String} title - Title of media to queue + * @param {Function} cb - Callback function, passed upon pop-up creation + * @param {Document} doc - Current owner documnet of the panel, so we know where to drop our pop-up + */ constructor(event, client, url, title, cb, doc){ - //Set Client + /** + * Parent Client Management Object + */ this.client = client; - //Set link + + /** + * URL/Link to media to queue + */ this.url = url; - //Set title + + /** + * Title of media to queue + */ this.title = title; - //Set callback + + /** + * Callback function, passed upon pop-up creation + */ 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('/scheduleMedia', true, this.asyncConstructor.bind(this), doc); } + /** + * Continuation of object construction, called after child popup object construction + */ asyncConstructor(){ //Grab required UI elements this.scheduleButton = this.popup.contentDiv.querySelector('#schedule-media-popup-schedule-button'); @@ -1291,12 +1482,19 @@ class schedulePopup{ } } + /** + * Defines input-related Event Handlers + */ setupInput(){ //Setup input this.scheduleButton.addEventListener('click', this.schedule.bind(this)); this.popup.popupDiv.addEventListener('keydown', this.schedule.bind(this)); } + /** + * Handles sending request to schedule item to the queue + * @param {Event} event - Event passed down from Event Listener + */ schedule(event){ //If we clicked or hit enter if(event.key == null || event.key == "Enter"){ @@ -1312,12 +1510,26 @@ class schedulePopup{ } } +/** + * Class representing pop-up dialogue for reschedule queue media + * @extends schedulePopup + */ class reschedulePopup extends schedulePopup{ + /** + * Instantiates a new schedule media Pop-up + * @param {Event} event - Event passed down from Event Listener + * @param {channel} client - Parent Client Management Object + * @param {Object} media - Media object to re-schedule + * @param {Function} cb - Callback function, passed upon pop-up creation + * @param {Document} doc - Current owner documnet of the panel, so we know where to drop our pop-up + */ constructor(event, client, media, cb, doc){ //Call derived constructor super(event, client, null, null, cb, doc); - //Set media + /** + * Media Object to Re-Schedule + */ this.media = media; } @@ -1336,18 +1548,39 @@ class reschedulePopup extends schedulePopup{ } } +/** + * Class represneting pop-up dialogue for clearing queue between a range of two dates + */ class clearPopup{ + /** + * Instantiates a new queue Clear Popup + * @param {Event} event - Event passed down from Event Listener + * @param {channel} client - Parent Client Management Object + * @param {Function} cb - Callback function, passed upon pop-up creation + * @param {Document} doc - Current owner documnet of the panel, so we know where to drop our pop-up + */ constructor(event, client, cb, doc){ - //Set Client + /** + * Parent Client Management Object + */ this.client = client; - //Set callback + + /** + * Callback function, passed upon pop-up creation + */ 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('/clearMedia', true, this.asyncConstructor.bind(this), doc); } + /** + * Continuation of object construction, called after child popup object construction + */ asyncConstructor(){ //Grab required UI elements this.clearButton = this.popup.contentDiv.querySelector('#clear-media-popup-clear-button'); @@ -1375,12 +1608,19 @@ class clearPopup{ } } + /** + * Defines input-related Event Handlers + */ setupInput(){ //Setup input this.clearButton.addEventListener('click', this.clear.bind(this)); this.popup.popupDiv.addEventListener('keydown', this.clear.bind(this)); } + /** + * Handles sending request to clear playlist between two dates to the server + * @param {Event} event - Event passed down from Event Listener + */ clear(event){ //If we clicked or hit enter if(event.key == null || event.key == "Enter"){