summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorimperatormk <darko.simonovski@hotmail.com>2020-03-25 00:29:08 +0100
committerGitHub <noreply@github.com>2020-03-25 09:29:08 +1000
commit363f62ae5f1255ac2580bdf32ae7b98b78cee5a3 (patch)
tree8c6ea1928736e39376f5c9ffa45464102a798237
parent5803bcb6fc5179c26c8b908eeb48fe923e543550 (diff)
Reworked upload box (#2)
-rw-r--r--static/close-icon.pngbin0 -> 343 bytes
-rw-r--r--static/upload-icon.pngbin0 -> 3251 bytes
-rw-r--r--static/upload.css158
-rw-r--r--static/upload.js342
-rw-r--r--templates/base.html3
-rw-r--r--templates/upload.html63
6 files changed, 407 insertions, 159 deletions
diff --git a/static/close-icon.png b/static/close-icon.png
new file mode 100644
index 0000000..72dd33d
--- /dev/null
+++ b/static/close-icon.png
Binary files differ
diff --git a/static/upload-icon.png b/static/upload-icon.png
new file mode 100644
index 0000000..3f28e28
--- /dev/null
+++ b/static/upload-icon.png
Binary files differ
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;
+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}%` : ''
+}
+
+const setMessage = (_message, isError) => {
+ uploadMessageLabel.style.display = _message ? 'block' : 'none'
+ uploadMessageLabel.innerHTML = _message
+ if (isError) {
+ uploadMessageLabel.classList.add('error')
+ } else {
+ uploadMessageLabel.classList.remove('error')
+ }
+}
+
+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
}
- // little test for filesize
- if (oFile.size > iMaxFilesize) {
- document.getElementById('warnsize').style.display = 'block';
- return;
+}
+
+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
}
- // 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);
+
+ 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)
}
-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 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
}
-function doInnerUpdates() { // we will use this function to display upload speed
- var iCB = iBytesUploaded;
- var iDiff = iCB - iPreviousBytesLoaded;
+
+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 = '<h1>Please wait...processing</h1>';
- 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);
-}
-function uploadError(e) { // upload error
- document.getElementById('error2').style.display = 'block';
- 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 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 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico">
<link rel="stylesheet" type="text/css" href="/static/theme.css">
+ <link rel="stylesheet" type="text/css" href="/static/upload.css">
{{ template "stylesheets" . }}
{{ template "css" . }}
@@ -14,7 +15,7 @@
<body>
<nav>
<a href="/">Tube</a>
- <a class="centered" href="/upload">Upload</a>
+ <a class="centered" style="text-indent: 0;" href="/upload">Upload</a>
</nav>
<main>
{{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"}}
- <div id="upload">
- <form id="upload_form" enctype="multipart/form-data" method="POST" action="/upload">
- <div>
- <label class="filebutton">
- Select a video to upload...
- <span><input type="file" name="video_file" id="video_file" accept="video/*" onchange="fileSelected();" /></span>
- </label>
+ <div style="text-align: center;">
+ <label class="upload-container" onclick="labelClicked(event)">
+ <div class="upload-wrapper">
+ <form id="upload-form" class="upload-form" enctype="multipart/form-data" method="POST" action="/upload">
+ <input id="video-input" type="file" accept="video/*" onchange="fileSelected()" style="display: none;"/>
+ <div class="upload-box">
+ <img width="100" src="/static/upload-icon.png"/>
+ <span>Click to browse or drop file here</span>
+ </div>
+ <div class="upload-details">
+ <div id="upload-file" class="upload-file">
+ <span id="upload-filename"></span>
+ <img width="20" src="/static/close-icon.png" onclick="removeFile(event)"/>
+ </div>
+ <span id="upload-message" class="upload-message">No file selected</span>
+ <div id="upload-button-wrapper" class="upload-button-wrapper">
+ <button id="upload-button" class="upload-button" onclick="startUploading()" type="button">
+ <span id="upload-stopped">Upload</span>
+ <span id="upload-started" class="loader" style="display: none;"></span>
+ </button>
+ </div>
+ <div id="upload-progress-container" class="upload-progress-container">
+ <div id="upload-progress" class="upload-progress">
+ <span id="upload-progress-label" class="upload-progress-label"></span>
+ </div>
+ <div style="flex-grow: 1;"></div>
+ </div>
+ </div>
+ </form>
</div>
-
- <div>
- <input type="button" value="Upload" onclick="startUploading()" />
- </div>
-
- <div id="error">You should select valid image files only!</div>
- <div id="error2">An error occurred while uploading the file</div>
- <div id="abort">The upload has been canceled by the user or the browser dropped the connection</div>
- <div id="warnsize">Your file is very big. We can't accept it. Please select more small file</div>
-
- <div id="progress_info">
- <div id="progress"></div>
- <div id="progress_percent">&nbsp;</div>
- <div class="clear_both"></div>
- <div>
- <div id="speed">&nbsp;</div>
- <div id="remaining">&nbsp;</div>
- <div id="b_transfered">&nbsp;</div>
- <div class="clear_both"></div>
- </div>
-
- <div id="upload_response">
- <div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div>
- </div>
- </div>
- </form>
+ </label>
</div>
{{end}}
{{define "scripts"}}