diff --git a/src/app/channel/media/queue.js b/src/app/channel/media/queue.js index 7e6b9f2..d593f78 100644 --- a/src/app/channel/media/queue.js +++ b/src/app/channel/media/queue.js @@ -55,16 +55,22 @@ module.exports = class{ async queueURL(socket, data){ try{ //Set url - const url = encodeURI(data.url); + var url = data.url; //pull URL and start time from data //let {url, start, title} = data; //If we where given a bad URL if(!validator.isURL(url)){ - //Bitch, moan, complain... - loggerUtils.socketErrorHandler(socket, "Bad URL!", "validation"); - //and ignore it! - return; + //Attempt to fix the situation by encoding it + url = encodeURI(url); + + //If it's still bad + if(!validator.isURL(url)){ + //Bitch, moan, complain... + loggerUtils.socketErrorHandler(socket, "Bad URL!", "validation"); + //and ignore it! + return; + } } //If the title is too long diff --git a/src/utils/media/internetArchiveUtils.js b/src/utils/media/internetArchiveUtils.js index 32b68ee..f0750a9 100644 --- a/src/utils/media/internetArchiveUtils.js +++ b/src/utils/media/internetArchiveUtils.js @@ -74,7 +74,7 @@ module.exports.fetchMetadata = async function(link){ function compatibilityFilter(file){ //return true for all files that match for web-safe formats - return file.format == "h.264" || file.format == "Ogg Video" || file.format.match("MPEG4"); + return file.format == "h.264 IA" || file.format == "h.264" || file.format == "Ogg Video" || file.format.match("MPEG4"); } function pathFilter(file){ diff --git a/www/css/channel.css b/www/css/channel.css index 04a8200..d502e40 100644 --- a/www/css/channel.css +++ b/www/css/channel.css @@ -108,6 +108,10 @@ div#chat-panel-main-div{ width: 0.6em; } +#chat-panel-users-drag-handle{ + z-index: 2; +} + #chat-panel-multipanel-div{ height: 100%; } diff --git a/www/css/panel/queue.css b/www/css/panel/queue.css index e28a2b9..be7b4dc 100644 --- a/www/css/panel/queue.css +++ b/www/css/panel/queue.css @@ -96,11 +96,18 @@ div.queue-entry{ right: 0; left: 0; cursor:grab; + display: flex; + justify-content: center; + align-items: center; } -div.queue-entry a{ - position: relative; +div.queue-entry p{ z-index: 2; + pointer-events: none; +} + +div.dragging-queue-entry{ + z-index: 3; } #time-marker{ diff --git a/www/js/channel/panels/queuePanel.js b/www/js/channel/panels/queuePanel.js index 4f8ddb2..aadcc7c 100644 --- a/www/js/channel/panels/queuePanel.js +++ b/www/js/channel/panels/queuePanel.js @@ -413,10 +413,11 @@ class queuePanel extends panelObj{ //Create entry div const entryDiv = document.createElement('div'); entryDiv.classList.add('queue-entry'); + entryDiv.dataset['uuid'] = entry[1].uuid; //If this item starts today if(startsToday){ - //Place entry div based on time + //Place the top of the entry div based on time entryDiv.style.top = `${this.offsetByDate(new Date(entry[1].startTime))}px`; }else{ //Get dawn @@ -426,13 +427,13 @@ class queuePanel extends panelObj{ //Place item beginning at dawn entryDiv.style.top = `${this.offsetByDate(dawn)}px`; - //Run entry from top + //Run apply the rest of the styling rules entryDiv.classList.add('started-before-today'); } //If the item ends today if(endsToday){ - //Place entry div based on time + //Place the bottom of the entry div based on time entryDiv.style.bottom = `${this.offsetByDate(new Date(entry[1].startTime + (entry[1].duration * 1000)), true)}px`; }else{ //Get midnight @@ -442,7 +443,7 @@ class queuePanel extends panelObj{ //Place item beginning at dawn entryDiv.style.bottom = `${this.offsetByDate(dusk, true)}px`; - //Run entry to bottom + //Run apply the rest of the styling rules entryDiv.classList.add('ends-after-today'); } @@ -455,44 +456,32 @@ class queuePanel extends panelObj{ const tooltipDiv = document.createElement('div'); tooltipDiv.classList.add('media-tooltip'); - //tooltip title - const tooltipTitle = document.createElement('p'); - tooltipTitle.textContent = `Title: ${entry[1].title}`; - - //tooltip filename - const tooltipFilename = document.createElement('p'); - tooltipFilename.textContent = `File Name: ${entry[1].fileName}`; - - //tooltip source - const tooltipSource = document.createElement('p'); - tooltipSource.textContent = `Source: ${entry[1].type}`; - - //tooltip duration - const tooltipDuration = document.createElement('p'); - tooltipDuration.textContent = `Duration: ${entry[1].duration}`; - - //tooltip start - const tooltipStart = document.createElement('p'); - tooltipStart.textContent = `Start Time: ${new Date(entry[1].startTime).toLocaleString()}`; - - //tooltip end - const tooltipEnd = document.createElement('p'); - tooltipEnd.textContent = `End Time: ${new Date(entry[1].startTime + (entry[1].duration * 1000)).toLocaleString()}`; - - //append components - for(let component of [ - tooltipTitle, - tooltipFilename, - tooltipSource, - tooltipDuration, - tooltipStart, - tooltipEnd + //tooltip components + //For each string + for(let string of [ + `Title: ${entry[1].title}`, + `File Name: ${entry[1].fileName}`, + `Source: ${entry[1].type}`, + `Duration: ${entry[1].duration}`, + `Start Time: ${new Date(entry[1].startTime).toLocaleString()}`, + `End Time: ${new Date(entry[1].startTime + (entry[1].duration * 1000)).toLocaleString()}` ]){ + //Create a 'p' node + const component = document.createElement('p'); + //Fill it with the string + component.textContent = string; + //Append it to the tooltip div tooltipDiv.append(component); } //Setup media tooltip - entryDiv.addEventListener('mouseenter',(event)=>{utils.ux.displayTooltip(event, tooltipDiv, false, null, true, this.ownerDoc);}); + 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); + } + }); //context menu const menuMap = new Map([ @@ -503,8 +492,17 @@ class queuePanel extends panelObj{ ["Copy URL", ()=>{navigator.clipboard.writeText(entry[1].url);}], ]); + //Setup drag n drop + entryDiv.addEventListener('mousedown', clickEntry.bind(this)); + //Setup context menu - entryDiv.addEventListener('contextmenu', (event)=>{utils.ux.displayContextMenu(event, '', menuMap, this.ownerDoc);}); + 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); @@ -514,6 +512,74 @@ class queuePanel extends panelObj{ } } + function clickEntry(event){ + //If it's not a left click + if(event.buttons != 1){ + //fuck off + return; + } + + //Create variables to hold width and height + const height = event.target.offsetHeight; + + //If we havent set height or width + if(event.target.style.height == ""){ + //Preserve calculated entry height + event.target.style.height = `${height}px`; + } + + event.target.classList.add('dragging-queue-entry'); + + event.target.dataset['drag'] = true; + + //Drag entry with mouse + this.ownerDoc.body.addEventListener('mousemove', (nestedEvent)=>{(dragEntry.bind(this))(nestedEvent, event.target)}); + + //Drop on moust 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.getBoundingClientRect().top + this.ownerDoc.defaultView.scrollY) - event.clientY); + event.target.dataset['dragoffset'] = event.clientY - (event.target.getBoundingClientRect().top + this.ownerDoc.defaultView.scrollY); + + //Call the drag entry function to move the entry on click without re-writing the wheel + (dragEntry.bind(this))(event, event.target); + } + + function dragEntry(event, target){ + //Gross but works :P + if(!target.isConnected || target.dataset['drag'] != "true"){ + return; + } + + console.log("fuck") + //Calculate offset from rest of window + const windowOffset = this.queueContainer.getBoundingClientRect().top + this.ownerDoc.defaultView.scrollY; + + //Move the entry to the mouse offset by the target nodes height and the queue layouts scroll + target.style.top = `${event.clientY - Number(target.dataset['dragoffset']) - windowOffset}px`; + } + + function dropEntry(event, target){ + //Gross but works :P + if(!target.isConnected){ + 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'; + + + //Finish dragging + target.dataset['drag'] = false; + } + class reschedulePopup{ constructor(event, client, media, cb){ //Set Client @@ -742,4 +808,26 @@ class queuePanel extends panelObj{ //return position relative to parent return (offsetMax * timeFloat) + range[0]; } + + 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 current date + const date = new Date(); + + //Convert our 'float' from 0-1 to a time between 0-24 + date.setHours(0,0,0,relativeInput * 86400000); + + //return our date + return date; + } } \ No newline at end of file diff --git a/www/js/utils.js b/www/js/utils.js index eb3cbe6..4ef54fc 100644 --- a/www/js/utils.js +++ b/www/js/utils.js @@ -122,7 +122,7 @@ class canopyUXUtils{ if(soft){ //remove the tooltip on context menu open - event.target.addEventListener('click', tooltip.remove.bind(tooltip)); + event.target.addEventListener('mousedown', tooltip.remove.bind(tooltip)); event.target.addEventListener('contextmenu', tooltip.remove.bind(tooltip)); }