diff --git a/public/style.css b/public/style.css
index 6c1f0fb..5fa0228 100644
--- a/public/style.css
+++ b/public/style.css
@@ -7216,16 +7216,16 @@ body {
line-height: 1.5;
font-weight: 400; }
-a {
+a, .link {
color: #666;
color: var(--link-color);
border-bottom-color: #666;
border-bottom-color: var(--link-border-color);
border-bottom: none;
text-decoration: none; }
- a:hover {
+ a:hover, .link:hover {
/* text-decoration:underline; */ }
- a.external::after {
+ a.external::after, .link.external::after {
font-family: "icons";
content: " 1";
vertical-align: middle; }
@@ -9434,3 +9434,17 @@ span.icon-rss::before {
.notice-failure {
background-color: #b42222;
background-color: var(--notice-failure-color); }
+
+.upload_bar.uploading .instructions, .upload_bar:not(.uploading) .progress {
+ display: none; }
+
+.upload_bar .progress_bar {
+ border: 2px solid;
+ border-color: #666;
+ border-color: var(--link-color);
+ padding: 2px; }
+
+.upload_bar .progress_bar > div {
+ background-color: #666;
+ background-color: var(--link-color);
+ height: 100%; }
diff --git a/src/rawdata/scss/_core.scss b/src/rawdata/scss/_core.scss
index 1f6d552..20dceaa 100644
--- a/src/rawdata/scss/_core.scss
+++ b/src/rawdata/scss/_core.scss
@@ -31,7 +31,7 @@ body {
font-weight: 400;
}
-a {
+a, .link {
@include usevar(color, link-color);
@include usevar(border-bottom-color, link-border-color);
diff --git a/src/rawdata/scss/_progress_bar.scss b/src/rawdata/scss/_progress_bar.scss
new file mode 100644
index 0000000..1a86f1d
--- /dev/null
+++ b/src/rawdata/scss/_progress_bar.scss
@@ -0,0 +1,15 @@
+.upload_bar.uploading .instructions, .upload_bar:not(.uploading) .progress {
+ display: none;
+}
+
+.upload_bar .progress_bar {
+ border: 2px solid;
+ @include usevar('border-color', 'link-color');
+ padding: 2px;
+}
+
+.upload_bar .progress_bar > div {
+ @include usevar('background-color', 'link-color');
+ height: 100%;
+}
+
diff --git a/src/rawdata/scss/style.scss b/src/rawdata/scss/style.scss
index 617e220..0aa6480 100644
--- a/src/rawdata/scss/style.scss
+++ b/src/rawdata/scss/style.scss
@@ -25,3 +25,4 @@
@import 'timeline';
@import 'carousel';
@import 'notices';
+@import 'progress_bar';
diff --git a/src/templates/src/editor.html b/src/templates/src/editor.html
index 63e5dcd..a569bfa 100644
--- a/src/templates/src/editor.html
+++ b/src/templates/src/editor.html
@@ -53,8 +53,17 @@
*/}}
-
-
+
+
+
+ Upload files by dragging & dropping, pasting, or them.
+
+
+
+
{{ if .IsEditing }}
@@ -104,6 +113,7 @@
+
{{/* NOTE(asaf): Placing this outside the form to avoid submitting it to the server by accident */}}
@@ -185,7 +195,30 @@
window.localStorage.removeItem(storageKey);
});
+ /*
+ / Asset upload
+ */
+ const submitButton = document.querySelector("#form input[type=submit]");
+ const submitText = submitButton.value;
+ const fileInput = document.querySelector('#file_input');
+ const uploadBar = document.querySelector('.upload_bar');
+ const uploadProgress = document.querySelector('.upload_bar .progress');
+ const uploadProgressText = document.querySelector('.upload_bar .progress_text');
+ const uploadProgressBar = document.querySelector('.upload_bar .progress_bar');
+ const uploadProgressBarFill = document.querySelector('.upload_bar .progress_bar > div');
let fileCounter = 0;
+ let enterCounter = 0;
+ let uploadQueue = [];
+ let currentUpload = null;
+ let currentXhr = null;
+ let currentBatchSize = 0;
+ let currentBatchDone = 0;
+
+ fileInput.addEventListener("change", function(ev) {
+ if (fileInput.files.length > 0) {
+ importUserFiles(fileInput.files);
+ }
+ });
textField.addEventListener("dragover", function(ev) {
let effect = "none";
@@ -199,8 +232,6 @@
ev.preventDefault();
});
- let enterCounter = 0;
-
textField.addEventListener("dragenter", function(ev) {
enterCounter++;
let droppable = false;
@@ -230,9 +261,23 @@
enterCounter = 0;
textField.classList.remove("drop");
+ if (ev.dataTransfer && ev.dataTransfer.files) {
+ importUserFiles(ev.dataTransfer.files)
+ }
+
+ ev.preventDefault();
+ });
+
+ textField.addEventListener("paste", function(ev) {
+ if (ev.clipboardData && ev.clipboardData.files) {
+ importUserFiles(ev.clipboardData.files)
+ }
+ });
+
+ function importUserFiles(files) {
let items = [];
- for (let i = 0; i < ev.dataTransfer.files.length; ++i) {
- let f = ev.dataTransfer.files[i];
+ for (let i = 0; i < files.length; ++i) {
+ let f = files[i];
if (f.size < maxFileSize) {
items.push({ file: f, error: null });
} else {
@@ -243,7 +288,12 @@
let cursorStart = textField.selectionStart;
let cursorEnd = textField.selectionEnd;
- let toInsert = "\n";
+ let toInsert = "";
+ let linesToCursor = textField.value.substr(0, cursorStart).split("\n");
+ let cursorLine = linesToCursor[linesToCursor.length-1].trim();
+ if (cursorLine.length > 0) {
+ toInsert = "\n";
+ }
for (let i = 0; i < items.length; ++i) {
if (items[i].file) {
fileCounter++;
@@ -256,9 +306,7 @@
textField.value = textField.value.substring(0, cursorStart) + toInsert + textField.value.substring(cursorEnd, textField.value.length);
doMarkdown();
-
- ev.preventDefault();
- });
+ }
function replaceUploadString(upload, newString) {
let cursorStart = textField.selectionStart;
@@ -279,15 +327,14 @@
doMarkdown();
}
- let uploadQueue = [];
- let currentUpload = null;
- let currentXhr = null;
-
function startUpload(uploadNumber, file) {
uploadQueue.push({
uploadNumber: uploadNumber,
file: file
});
+
+ currentBatchSize++;
+ uploadProgressText.textContent = `Uploading files ${currentBatchDone+1}/${currentBatchSize}`;
uploadNext();
}
@@ -316,21 +363,42 @@
}
currentUpload = null;
currentXhr = null;
+ currentBatchDone++;
uploadNext();
}
+ function updateUploadProgress(ev) {
+ if (ev.lengthComputable) {
+ let progress = ev.loaded / ev.total;
+ uploadProgressBarFill.style.width = Math.floor(progress * 100) + "%";
+ }
+ }
+
function uploadNext() {
if (currentUpload == null) {
next = uploadQueue.shift();
if (next) {
+ uploadProgressText.textContent = `Uploading files ${currentBatchDone+1}/${currentBatchSize}`;
+ uploadBar.classList.add("uploading");
+ uploadProgressBarFill.style.width = "0%";
+ submitButton.disabled = true;
+ submitButton.value = "Uploading files...";
+
// NOTE(asaf): We use XHR because fetch can't do upload progress reports. Womp womp. https://youtu.be/Pubd-spHN-0?t=2
currentXhr = new XMLHttpRequest();
+ currentXhr.upload.addEventListener("progress", updateUploadProgress);
currentXhr.open("POST", uploadUrl, true);
currentXhr.setRequestHeader("Hmn-Upload-Filename", next.file.name);
currentXhr.responseType = "json";
currentXhr.addEventListener("loadend", uploadDone);
currentXhr.send(next.file);
currentUpload = next;
+ } else {
+ submitButton.disabled = false;
+ submitButton.value = submitText;
+ uploadBar.classList.remove("uploading");
+ currentBatchSize = 0;
+ currentBatchDone = 0;
}
}
}