From 363f62ae5f1255ac2580bdf32ae7b98b78cee5a3 Mon Sep 17 00:00:00 2001 From: imperatormk Date: Wed, 25 Mar 2020 00:29:08 +0100 Subject: [PATCH] Reworked upload box (#2) --- static/close-icon.png | Bin 0 -> 343 bytes static/upload-icon.png | Bin 0 -> 3251 bytes static/upload.css | 158 +++++++++++++++++++ static/upload.js | 346 ++++++++++++++++++++++++++--------------- templates/base.html | 3 +- templates/upload.html | 63 ++++---- 6 files changed, 409 insertions(+), 161 deletions(-) create mode 100644 static/close-icon.png create mode 100644 static/upload-icon.png create mode 100644 static/upload.css diff --git a/static/close-icon.png b/static/close-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..72dd33dc999330dd1b01271a0c585efe95a05ac4 GIT binary patch literal 343 zcmV-d0jU0oP);2_Xz#7!#|`HC|>sI5x_81b@!p_>Uy8TftP|(h5A^ug`9P zCI91gCr=9b;ws>O1*AwM0k0#L3V2nT%99GD0>%o!j!+4#of#5e2!T4Nl!Vk5LZBV< zToO|7qjh4*B$fb@*w$?WQ=qr2P)caQ!xi9=uz+_XK=p(byeolGGH#RiMh3hG+N5nl z3wU=0dI>`jpyalYa63Z1h7HriBcN6ia0NI3IW}7^G!kGETbETr1IX%A6EiJy8X%mt z!*!5JE+xHT$NjXzo7i|`oVq80X^FrFsQ|dZ)9|Ulv_xQo|5Kn8lsja=#n=5}LW5cr pKYz2Xs-!kfDxj_0D!=dwd;!T65I{E`!yy0w002ovPDHLkV1iRuip&52 literal 0 HcmV?d00001 diff --git a/static/upload-icon.png b/static/upload-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3f28e28246e153524e46b63b5ed0f42891c5df56 GIT binary patch literal 3251 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 Kp;hb%O#TOqL%47N literal 0 HcmV?d00001 diff --git a/static/upload.css b/static/upload.css new file mode 100644 index 0000000..c2ded20 --- /dev/null +++ b/static/upload.css @@ -0,0 +1,158 @@ +.upload-container { + display: inline-block; + max-width: 100%; + margin-top: 50px; +} + +.upload-wrapper { + padding: 20px; + background-color: #282a2e; + border: 1px solid #1e1e1e; + border-radius: 20px; + font-family: 'Trebuchet MS', Arial, sans-serif; +} + +.upload-form { + border: 5px dashed #676867; + border-radius: 10px; + cursor: pointer; +} + +.upload-box { + display: flex; + flex-direction: column; + height: 100%; + width: 100%; + justify-content: center; + align-items: center; + min-width: 300px; + min-height: 300px; +} + +.upload-box span { + color: #c5c8c6; + font-weight: bold; +} + +.upload-details { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 0 0 20px 0; +} + +.upload-details > * { + margin-top: 20px; +} + +.upload-details span { + color: #c5c8c6; + font-weight: bold; +} + +.upload-file { + display: none; + width: 100%; + padding: 0 20px; + justify-content: space-between; + align-items: center; +} + +.upload-message { + white-space: normal; + padding: 0 20px; + word-wrap: break-word; + max-width: 300px; +} + +.upload-message.error { + color: #e82e57; +} + +.upload-button-wrapper { + display: none; + width: 100%; + padding: 0 20px; +} + +.upload-button { + border: 0; + box-shadow: none; + border-radius: 0px; + + width: 100%; + padding: 15px; + background: #191919 !important; + font-family: 'Trebuchet MS', Arial, sans-serif; + color: #c5c8c6; + border-radius: 10px; + cursor: pointer; +} + +.upload-button.transparent { + background: transparent !important; + padding: 10px; +} + +.upload-progress-container { + display: none; + width: 100%; + padding: 0 20px; +} + +.upload-progress { + height: 30px; + background: linear-gradient(45deg, #c7c7c7, #ae81ff); + border-radius: 5px; + display: flex; + justify-content: center; + align-items: center; +} + +.upload-progress-label { + color: #282a2e !important; +} + +/* SPINNER */ + +.loader, +.loader:after { + border-radius: 50%; + width: 3em; + height: 3em; +} +.loader { + font-size: 10px; + position: relative; + text-indent: -9999em; + border-top: 0.5em solid rgba(255, 255, 255, 0.2); + border-right: 0.5em solid rgba(255, 255, 255, 0.2); + border-bottom: 0.5em solid rgba(255, 255, 255, 0.2); + border-left: 0.5em solid #ffffff; + -webkit-transform: translateZ(0); + -ms-transform: translateZ(0); + transform: translateZ(0); + -webkit-animation: load8 1.1s infinite linear; + animation: load8 1.1s infinite linear; +} +@-webkit-keyframes load8 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} +@keyframes load8 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } +} \ No newline at end of file diff --git a/static/upload.js b/static/upload.js index 7353c47..6bf1aa0 100644 --- a/static/upload.js +++ b/static/upload.js @@ -1,145 +1,237 @@ // common variables -var iBytesUploaded = 0; -var iBytesTotal = 0; -var iPreviousBytesLoaded = 0; -var iMaxFilesize = 104857600; // 100MB -var oTimer = 0; -var sResultFileSize = ''; -function secondsToTime(secs) { // we will use this function to convert seconds in normal time format - var hr = Math.floor(secs / 3600); - var min = Math.floor((secs - (hr * 3600))/60); - var sec = Math.floor(secs - (hr * 3600) - (min * 60)); - if (hr < 10) {hr = "0" + hr; } - if (min < 10) {min = "0" + min;} - if (sec < 10) {sec = "0" + sec;} - if (hr) {hr = "00";} - return hr + ':' + min + ':' + sec; -}; -function bytesToSize(bytes) { - var sizes = ['Bytes', 'KB', 'MB']; - if (bytes == 0) return 'n/a'; - var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); - return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i]; -}; -function fileSelected() { - // hide different warnings - document.getElementById('upload_response').style.display = 'none'; - document.getElementById('error').style.display = 'none'; - document.getElementById('error2').style.display = 'none'; - document.getElementById('abort').style.display = 'none'; - document.getElementById('warnsize').style.display = 'none'; - // get selected file element - var oFile = document.getElementById('image_file').files[0]; - // filter for image files - var rFilter = /^(image\/bmp|image\/gif|image\/jpeg|image\/png|image\/tiff)$/i; - if (! rFilter.test(oFile.type)) { - document.getElementById('error').style.display = 'block'; - return; - } - // little test for filesize - if (oFile.size > iMaxFilesize) { - document.getElementById('warnsize').style.display = 'block'; - return; - } - // get preview element - var oImage = document.getElementById('preview'); - // prepare HTML5 FileReader - var oReader = new FileReader(); - oReader.onload = function(e){ - // e.target.result contains the DataURL which we will use as a source of the image - oImage.src = e.target.result; - oImage.onload = function () { // binding onload event - // we are going to display some custom image information here - sResultFileSize = bytesToSize(oFile.size); - document.getElementById('fileinfo').style.display = 'block'; - document.getElementById('filename').innerHTML = 'Name: ' + oFile.name; - document.getElementById('filesize').innerHTML = 'Size: ' + sResultFileSize; - document.getElementById('filetype').innerHTML = 'Type: ' + oFile.type; - document.getElementById('filedim').innerHTML = 'Dimension: ' + oImage.naturalWidth + ' x ' + oImage.naturalHeight; - }; - }; - // read selected file as DataURL - oReader.readAsDataURL(oFile); +let iBytesUploaded = 0 +let iBytesTotal = 0 +let iPreviousBytesLoaded = 0 +let iMaxFilesize = 104857600 // 100MB +let timer = 0 +let uploadInProgress = 'n/a' +let isProcessing = false +let file = null + +/* CACHED ELEMENTS */ + +const uploadForm = document.getElementById('upload-form') +const videoInput = document.getElementById('video-input') +const uploadMessageLabel = document.getElementById('upload-message') +const uploadFileContainer = document.getElementById('upload-file') +const uploadFilenameLabel = document.getElementById('upload-filename') +const uploadButtonWrapper = document.getElementById('upload-button-wrapper') +const uploadButton = document.getElementById('upload-button') +const uploadProgressContainer = document.getElementById('upload-progress-container') +const uploadProgressBar = document.getElementById('upload-progress') +const uploadProgressLabel = document.getElementById('upload-progress-label') +const uploadStopped = document.getElementById('upload-stopped') +const uploadStarted = document.getElementById('upload-started') + +/* HELPERS */ + +const setProgress = (_progress) => { + uploadProgressContainer.style.display = _progress > 0 ? 'flex' : 'none' + uploadProgressBar.style.width = `${_progress}%` + uploadProgressLabel.innerText = _progress >= 15 ? `${_progress}%` : '' } -function startUploading() { - // cleanup all temp states - iPreviousBytesLoaded = 0; - document.getElementById('upload_response').style.display = 'none'; - document.getElementById('error').style.display = 'none'; - document.getElementById('error2').style.display = 'none'; - document.getElementById('abort').style.display = 'none'; - document.getElementById('warnsize').style.display = 'none'; - document.getElementById('progress_percent').innerHTML = ''; - var oProgress = document.getElementById('progress'); - oProgress.style.display = 'block'; - oProgress.style.width = '0px'; - // get form data for POSTing - //var vFD = document.getElementById('upload_form').getFormData(); // for FF3 - var vFD = new FormData(document.getElementById('upload_form')); - // create XMLHttpRequest object, adding few event listeners, and POSTing our data - var oXHR = new XMLHttpRequest(); - oXHR.upload.addEventListener('progress', uploadProgress, false); - oXHR.addEventListener('load', uploadFinish, false); - oXHR.addEventListener('error', uploadError, false); - oXHR.addEventListener('abort', uploadAbort, false); - oXHR.open('POST', '/upload'); - oXHR.send(vFD); - // set inner timer - oTimer = setInterval(doInnerUpdates, 300); + +const setMessage = (_message, isError) => { + uploadMessageLabel.style.display = _message ? 'block' : 'none' + uploadMessageLabel.innerHTML = _message + if (isError) { + uploadMessageLabel.classList.add('error') + } else { + uploadMessageLabel.classList.remove('error') + } } -function doInnerUpdates() { // we will use this function to display upload speed - var iCB = iBytesUploaded; - var iDiff = iCB - iPreviousBytesLoaded; + +const setUploadState = (_uploadInProgress) => { + uploadInProgress = _uploadInProgress + + uploadStarted.style.display = _uploadInProgress ? 'inline-block' : 'none' + uploadStopped.style.display = _uploadInProgress ? 'none' : 'block' + + if (_uploadInProgress) { + uploadButton.classList.add('transparent') + timer = setInterval(doInnerUpdates, 300) + } else { + uploadButton.classList.remove('transparent') + clearInterval(timer) + } +} + +const secondsToTime = (secs) => { + let hr = Math.floor(secs / 3600) + let min = Math.floor((secs - (hr * 3600)) / 60) + let sec = Math.floor(secs - (hr * 3600) - (min * 60)) + if (hr < 10) hr = `0${hr}` + if (min < 10) min = `0${min}` + if (sec < 10) sec = `0${sec}` + if (hr) hr = '00' + return `${hr}:${min}:${sec}` +} + +const bytesToSize = (bytes) => { + const sizes = ['Bytes', 'KB', 'MB'] + if (bytes == 0) return 'n/a' + const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))) + return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i] +} + +const determineDragAndDropCapable = () => { + const div = document.createElement('div') + return (('draggable' in div) + || ('ondragstart' in div && 'ondrop' in div)) + && 'FormData' in window + && 'FileReader' in window +} + +/* MAIN */ + +document.addEventListener('DOMContentLoaded', () => { + ['drag', 'dragstart', 'dragend', 'dragover', 'dragenter', 'dragleave'] + .forEach((evt) => { + uploadForm.addEventListener(evt, (e) => { + e.preventDefault() + e.stopPropagation() + }) + }) + + uploadForm.addEventListener('drop', (e) => { + console.log(111) + e.preventDefault() + e.stopPropagation() + + const _file = e.dataTransfer.files[0] + if (!_file) return false + fileSelected(_file) + + return false + }) +}, false) + +const labelClicked = (e) => { + if (uploadInProgress === true) { + e.preventDefault() + return false + } +} + +const fileSelected = (_file) => { + file = _file || videoInput.files[0] + if (_file) videoInput.value = '' + if (!file) return + + if (file.size > iMaxFilesize) { + setMessage('Your file is very big. We can\'t accept it. Please select more small file.', true) + return + } + + setMessage('') + setProgress(0) + + const filename = file.name.length <= 20 ? file.name : `${file.name.substring(0, 14)}...${file.name.substring(file.name.length - 3)}` + uploadFilenameLabel.innerText = filename + uploadFileContainer.style.display = 'flex' + + uploadButtonWrapper.style.display = 'block' + setUploadState(false) +} + +const removeFile = (e, keepMessage) => { + if (e) e.preventDefault() + if (uploadInProgress === true) return + + uploadFileContainer.style.display = 'none' + uploadButtonWrapper.style.display = 'none' + videoInput.value = '' + file = null + if (!keepMessage) setMessage('No file selected') + + return false +} + +const startUploading = () => { + if (uploadInProgress === true) return + if (!file) return + + isProcessing = false + iPreviousBytesLoaded = 0 + setMessage('') + setProgress(0) + setUploadState(true) + + const formData = new FormData() + formData.append('video_file', file) + const xhr = new XMLHttpRequest() + + xhr.upload.addEventListener('progress', uploadProgress, false) + xhr.addEventListener('load', uploadFinish, false) + xhr.addEventListener('error', uploadError, false) + xhr.addEventListener('abort', uploadAbort, false) + + xhr.open('POST', '/upload') + xhr.send(formData) + + timer = setInterval(doInnerUpdates, 300) +} + +const doInnerUpdates = () => { // we will use this function to display upload speed + if (isProcessing) { + clearInterval(timer) + return + } + + let iDiff = iBytesUploaded - iPreviousBytesLoaded // if nothing new loaded - exit if (iDiff == 0) - return; - iPreviousBytesLoaded = iCB; - iDiff = iDiff * 2; - var iBytesRem = iBytesTotal - iPreviousBytesLoaded; - var secondsRemaining = iBytesRem / iDiff; + return + iPreviousBytesLoaded = iBytesUploaded + iDiff = iDiff * 2 + const iBytesRem = iBytesTotal - iPreviousBytesLoaded + const secondsRemaining = iBytesRem / iDiff // update speed info - var iSpeed = iDiff.toString() + 'B/s'; + let iSpeed = iDiff.toString() + 'B/s' if (iDiff > 1024 * 1024) { - iSpeed = (Math.round(iDiff * 100/(1024*1024))/100).toString() + 'MB/s'; + iSpeed = (Math.round(iDiff * 100/(1024*1024))/100).toString() + 'MB/s' } else if (iDiff > 1024) { - iSpeed = (Math.round(iDiff * 100/1024)/100).toString() + 'KB/s'; + iSpeed = (Math.round(iDiff * 100/1024)/100).toString() + 'KB/s' } - document.getElementById('speed').innerHTML = iSpeed; - document.getElementById('remaining').innerHTML = '| ' + secondsToTime(secondsRemaining); + + const speedMessage = `${iSpeed} | ${secondsToTime(secondsRemaining)}` + setMessage(speedMessage) } + function uploadProgress(e) { // upload process in progress if (e.lengthComputable) { - iBytesUploaded = e.loaded; - iBytesTotal = e.total; - var iPercentComplete = Math.round(e.loaded * 100 / e.total); - var iBytesTransfered = bytesToSize(iBytesUploaded); - document.getElementById('progress_percent').innerHTML = iPercentComplete.toString() + '%'; - document.getElementById('progress').style.width = (iPercentComplete * 4).toString() + 'px'; - document.getElementById('b_transfered').innerHTML = iBytesTransfered; - if (iPercentComplete == 100) { - var oUploadResponse = document.getElementById('upload_response'); - oUploadResponse.innerHTML = '

Please wait...processing

'; - oUploadResponse.style.display = 'block'; + iBytesUploaded = e.loaded + iBytesTotal = e.total + + const iPercentComplete = Math.round(iBytesUploaded / iBytesTotal * 100) + setProgress(iPercentComplete) + if (iPercentComplete === 100) { + isProcessing = true + setMessage('Processing video... please wait') } } else { - document.getElementById('progress').innerHTML = 'unable to compute'; + setMessage('Unable to compute progress.') } } -function uploadFinish(e) { // upload successfully finished - var oUploadResponse = document.getElementById('upload_response'); - oUploadResponse.innerHTML = e.target.responseText; - oUploadResponse.style.display = 'block'; - document.getElementById('progress_percent').innerHTML = '100%'; - document.getElementById('progress').style.width = '400px'; - document.getElementById('filesize').innerHTML = sResultFileSize; - document.getElementById('remaining').innerHTML = '| 00:00:00'; - clearInterval(oTimer); + +const uploadFinish = (e) => { // upload successfully finished + const message = e.target.responseText + const isSuccess = e.target.status < 400 + + setProgress(isSuccess ? 100 : 0) + setMessage(message, !isSuccess) + setUploadState(false) + if (isSuccess) removeFile(null, true) } -function uploadError(e) { // upload error - document.getElementById('error2').style.display = 'block'; - clearInterval(oTimer); -} -function uploadAbort(e) { // upload abort - document.getElementById('abort').style.display = 'block'; - clearInterval(oTimer); + +const uploadError = () => { // upload error + setMessage('An error occurred while uploading the file.', true) + setProgress(0) + setUploadState(false) } + +const uploadAbort = () => { // upload abort + setMessage('The upload has been canceled by the user or the browser dropped the connection.', true) + setProgress(0) + setUploadState(false) +} \ No newline at end of file diff --git a/templates/base.html b/templates/base.html index 854a845..a156f3b 100644 --- a/templates/base.html +++ b/templates/base.html @@ -6,6 +6,7 @@ + {{ template "stylesheets" . }} {{ template "css" . }} @@ -14,7 +15,7 @@
{{template "content" .}} diff --git a/templates/upload.html b/templates/upload.html index 3701eec..1716b65 100644 --- a/templates/upload.html +++ b/templates/upload.html @@ -1,38 +1,35 @@ {{define "content"}} -
-
-
- +
+
{{end}} {{define "scripts"}}