I migrated the relevant parts of `editor.html` and `forums.go` (via `blogs.go`) into `project_edit.html` and `projects.go`. Let me know if you would prefer pulling the shared javascript into a shared file. Only some variable names were changed. I did not test the case where the upload fails. Co-authored-by: mark.dev <mark@mark.dev> Reviewed-on: hmn/hmn#77 Co-authored-by: mark.dev <mark.dev@noreply.localhost> Co-committed-by: mark.dev <mark.dev@noreply.localhost>
This commit is contained in:
parent
1806da0389
commit
a295d0ed52
|
@ -0,0 +1,239 @@
|
||||||
|
/* Requires base64.js
|
||||||
|
|
||||||
|
Usage: setupMarkdownUpload(eSubmit, eFileInput, eUploadBar, eText, doMarkdown, maxFileSize, uploadUrl)
|
||||||
|
eSubmit is the element of the button to submit/save the markdown changes. It
|
||||||
|
will be disabled and tell users files are uploading while uploading is
|
||||||
|
happening.
|
||||||
|
eFileInput is the <input type="file">
|
||||||
|
eUploadBar usually looks like
|
||||||
|
<div class="upload_bar flex-grow-1">
|
||||||
|
<div class="instructions">
|
||||||
|
Upload files by dragging & dropping, pasting, or <label class="pointer link" for="file_input">selecting</label> them.
|
||||||
|
</div>
|
||||||
|
<div class="progress flex">
|
||||||
|
<div class="progress_text mr3"></div>
|
||||||
|
<div class="progress_bar flex-grow-1 flex-shrink-1 pa1"><div class=""></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
eText is the text field that can be dropped into and is editing the markdown.
|
||||||
|
doMarkdown is the function returned by initLiveMarkdown.
|
||||||
|
maxFileSize
|
||||||
|
uploadUrl
|
||||||
|
*/
|
||||||
|
|
||||||
|
function setupMarkdownUpload(eSubmit, eFileInput, eUploadBar, eText, doMarkdown, maxFileSize, uploadUrl) {
|
||||||
|
const submitText = eSubmit.value;
|
||||||
|
const uploadProgress = eUploadBar.querySelector('.progress');
|
||||||
|
const uploadProgressText = eUploadBar.querySelector('.progress_text');
|
||||||
|
const uploadProgressBar = eUploadBar.querySelector('.progress_bar');
|
||||||
|
const uploadProgressBarFill = eUploadBar.querySelector('.progress_bar > div');
|
||||||
|
let fileCounter = 0;
|
||||||
|
let enterCounter = 0;
|
||||||
|
let uploadQueue = [];
|
||||||
|
let currentUpload = null;
|
||||||
|
let currentXhr = null;
|
||||||
|
let currentBatchSize = 0;
|
||||||
|
let currentBatchDone = 0;
|
||||||
|
|
||||||
|
eFileInput.addEventListener("change", function(ev) {
|
||||||
|
if (eFileInput.files.length > 0) {
|
||||||
|
importUserFiles(eFileInput.files);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
eText.addEventListener("dragover", function(ev) {
|
||||||
|
let effect = "none";
|
||||||
|
for (let i = 0; i < ev.dataTransfer.items.length; ++i) {
|
||||||
|
if (ev.dataTransfer.items[i].kind.toLowerCase() == "file") {
|
||||||
|
effect = "copy";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ev.dataTransfer.dropEffect = effect;
|
||||||
|
ev.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
eText.addEventListener("dragenter", function(ev) {
|
||||||
|
enterCounter++;
|
||||||
|
let droppable = false;
|
||||||
|
for (let i = 0; i < ev.dataTransfer.items.length; ++i) {
|
||||||
|
if (ev.dataTransfer.items[i].kind.toLowerCase() == "file") {
|
||||||
|
droppable = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (droppable) {
|
||||||
|
eText.classList.add("drop");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
eText.addEventListener("dragleave", function(ev) {
|
||||||
|
enterCounter--;
|
||||||
|
if (enterCounter == 0) {
|
||||||
|
eText.classList.remove("drop");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function makeUploadString(uploadNumber, filename) {
|
||||||
|
return `Uploading file #${uploadNumber}: \`${filename}\`...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
eText.addEventListener("drop", function(ev) {
|
||||||
|
enterCounter = 0;
|
||||||
|
eText.classList.remove("drop");
|
||||||
|
|
||||||
|
if (ev.dataTransfer && ev.dataTransfer.files) {
|
||||||
|
importUserFiles(ev.dataTransfer.files)
|
||||||
|
}
|
||||||
|
|
||||||
|
ev.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
eText.addEventListener("paste", function(ev) {
|
||||||
|
const files = ev.clipboardData?.files ?? [];
|
||||||
|
if (files.length > 0) {
|
||||||
|
importUserFiles(files)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function importUserFiles(files) {
|
||||||
|
let items = [];
|
||||||
|
for (let i = 0; i < files.length; ++i) {
|
||||||
|
let f = files[i];
|
||||||
|
if (f.size < maxFileSize) {
|
||||||
|
items.push({ file: f, error: null });
|
||||||
|
} else {
|
||||||
|
items.push({ file: null, error: `\`${f.name}\` is too big! Max size is ${maxFileSize} but the file is ${f.size}.` });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cursorStart = eText.selectionStart;
|
||||||
|
let cursorEnd = eText.selectionEnd;
|
||||||
|
|
||||||
|
let toInsert = "";
|
||||||
|
let linesToCursor = eText.value.substr(0, cursorStart).split("\n");
|
||||||
|
let cursorLine = linesToCursor[linesToCursor.length-1].trim();
|
||||||
|
if (cursorLine.length > 0) {
|
||||||
|
toInsert = "\n\n";
|
||||||
|
}
|
||||||
|
for (let i = 0; i < items.length; ++i) {
|
||||||
|
if (items[i].file) {
|
||||||
|
fileCounter++;
|
||||||
|
toInsert += makeUploadString(fileCounter, items[i].file.name) + "\n\n";
|
||||||
|
queueUpload(fileCounter, items[i].file);
|
||||||
|
} else {
|
||||||
|
toInsert += `${items[i].error}\n\n`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eText.value = eText.value.substring(0, cursorStart) + toInsert + eText.value.substring(cursorEnd, eText.value.length);
|
||||||
|
doMarkdown();
|
||||||
|
uploadNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceUploadString(upload, newString) {
|
||||||
|
let cursorStart = eText.selectionStart;
|
||||||
|
let cursorEnd = eText.selectionEnd;
|
||||||
|
let uploadString = makeUploadString(upload.uploadNumber, upload.file.name);
|
||||||
|
let insertIndex = eText.value.indexOf(uploadString)
|
||||||
|
eText.value = eText.value.replace(uploadString, newString);
|
||||||
|
if (cursorStart <= insertIndex + uploadString.length) {
|
||||||
|
eText.selectionStart = cursorStart;
|
||||||
|
} else {
|
||||||
|
eText.selectionStart = cursorStart - uploadString.length + newString.length;
|
||||||
|
}
|
||||||
|
if (cursorEnd <= insertIndex + uploadString.length) {
|
||||||
|
eText.selectionEnd = cursorEnd;
|
||||||
|
} else {
|
||||||
|
eText.selectionEnd = cursorEnd - uploadString.length + newString.length;
|
||||||
|
}
|
||||||
|
doMarkdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceUploadStringError(upload) {
|
||||||
|
replaceUploadString(upload, `There was a problem uploading your file \`${upload.file.name}\`.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function queueUpload(uploadNumber, file) {
|
||||||
|
uploadQueue.push({
|
||||||
|
uploadNumber: uploadNumber,
|
||||||
|
file: file
|
||||||
|
});
|
||||||
|
|
||||||
|
currentBatchSize++;
|
||||||
|
uploadProgressText.textContent = `Uploading files ${currentBatchDone+1}/${currentBatchSize}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function uploadDone(ev) {
|
||||||
|
try {
|
||||||
|
if (currentXhr.status == 200 && currentXhr.response) {
|
||||||
|
if (currentXhr.response.url) {
|
||||||
|
let url = currentXhr.response.url;
|
||||||
|
let newString = `[${currentUpload.file.name}](${url})`;
|
||||||
|
if (currentXhr.response.mime.startsWith("image")) {
|
||||||
|
newString = "!" + newString;
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceUploadString(currentUpload, newString);
|
||||||
|
} else if (currentXhr.response.error) {
|
||||||
|
replaceUploadString(currentUpload, `Upload failed for \`${currentUpload.file.name}\`: ${currentXhr.response.error}.`);
|
||||||
|
} else {
|
||||||
|
replaceUploadStringError(currentUpload);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
replaceUploadStringError(currentUpload);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
replaceUploadStringError(currentUpload);
|
||||||
|
}
|
||||||
|
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}`;
|
||||||
|
eUploadBar.classList.add("uploading");
|
||||||
|
uploadProgressBarFill.style.width = "0%";
|
||||||
|
eSubmit.disabled = true;
|
||||||
|
eSubmit.value = "Uploading files...";
|
||||||
|
|
||||||
|
try {
|
||||||
|
let utf8Filename = strToUTF8Arr(next.file.name);
|
||||||
|
let base64Filename = base64EncArr(utf8Filename);
|
||||||
|
// 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", base64Filename);
|
||||||
|
currentXhr.responseType = "json";
|
||||||
|
currentXhr.addEventListener("loadend", uploadDone);
|
||||||
|
currentXhr.send(next.file);
|
||||||
|
currentUpload = next;
|
||||||
|
} catch (err) {
|
||||||
|
replaceUploadStringError(next);
|
||||||
|
console.error(err);
|
||||||
|
uploadNext();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eSubmit.disabled = false;
|
||||||
|
eSubmit.value = submitText;
|
||||||
|
eUploadBar.classList.remove("uploading");
|
||||||
|
currentBatchSize = 0;
|
||||||
|
currentBatchDone = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
{{ template "markdown_previews.html" . }}
|
{{ template "markdown_previews.html" . }}
|
||||||
|
|
||||||
<script src="{{ static "js/base64.js" }}"></script>
|
<script src="{{ static "js/base64.js" }}"></script>
|
||||||
|
<script src="{{ static "js/markdown_upload.js" }}"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
#editor {
|
#editor {
|
||||||
|
@ -121,222 +122,14 @@
|
||||||
/*
|
/*
|
||||||
/ Asset upload
|
/ Asset upload
|
||||||
*/
|
*/
|
||||||
const submitButton = document.querySelector("#form input[type=submit]");
|
setupMarkdownUpload(
|
||||||
const submitText = submitButton.value;
|
document.querySelector("#form input[type=submit]"),
|
||||||
const fileInput = document.querySelector('#file_input');
|
document.querySelector('#file_input'),
|
||||||
const uploadBar = document.querySelector('.upload_bar');
|
document.querySelector('.upload_bar'),
|
||||||
const uploadProgress = document.querySelector('.upload_bar .progress');
|
textField,
|
||||||
const uploadProgressText = document.querySelector('.upload_bar .progress_text');
|
doMarkdown,
|
||||||
const uploadProgressBar = document.querySelector('.upload_bar .progress_bar');
|
maxFileSize,
|
||||||
const uploadProgressBarFill = document.querySelector('.upload_bar .progress_bar > div');
|
uploadUrl
|
||||||
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";
|
|
||||||
for (let i = 0; i < ev.dataTransfer.items.length; ++i) {
|
|
||||||
if (ev.dataTransfer.items[i].kind.toLowerCase() == "file") {
|
|
||||||
effect = "copy";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ev.dataTransfer.dropEffect = effect;
|
|
||||||
ev.preventDefault();
|
|
||||||
});
|
|
||||||
|
|
||||||
textField.addEventListener("dragenter", function(ev) {
|
|
||||||
enterCounter++;
|
|
||||||
let droppable = false;
|
|
||||||
for (let i = 0; i < ev.dataTransfer.items.length; ++i) {
|
|
||||||
if (ev.dataTransfer.items[i].kind.toLowerCase() == "file") {
|
|
||||||
droppable = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (droppable) {
|
|
||||||
textField.classList.add("drop");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
textField.addEventListener("dragleave", function(ev) {
|
|
||||||
enterCounter--;
|
|
||||||
if (enterCounter == 0) {
|
|
||||||
textField.classList.remove("drop");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function makeUploadString(uploadNumber, filename) {
|
|
||||||
return `Uploading file #${uploadNumber}: \`${filename}\`...`;
|
|
||||||
}
|
|
||||||
|
|
||||||
textField.addEventListener("drop", function(ev) {
|
|
||||||
enterCounter = 0;
|
|
||||||
textField.classList.remove("drop");
|
|
||||||
|
|
||||||
if (ev.dataTransfer && ev.dataTransfer.files) {
|
|
||||||
importUserFiles(ev.dataTransfer.files)
|
|
||||||
}
|
|
||||||
|
|
||||||
ev.preventDefault();
|
|
||||||
});
|
|
||||||
|
|
||||||
textField.addEventListener("paste", function(ev) {
|
|
||||||
const files = ev.clipboardData?.files ?? [];
|
|
||||||
if (files.length > 0) {
|
|
||||||
importUserFiles(files)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function importUserFiles(files) {
|
|
||||||
let items = [];
|
|
||||||
for (let i = 0; i < files.length; ++i) {
|
|
||||||
let f = files[i];
|
|
||||||
if (f.size < maxFileSize) {
|
|
||||||
items.push({ file: f, error: null });
|
|
||||||
} else {
|
|
||||||
items.push({ file: null, error: `\`${f.name}\` is too big! Max size is ${maxFileSize} but the file is ${f.size}.` });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let cursorStart = textField.selectionStart;
|
|
||||||
let cursorEnd = textField.selectionEnd;
|
|
||||||
|
|
||||||
let toInsert = "";
|
|
||||||
let linesToCursor = textField.value.substr(0, cursorStart).split("\n");
|
|
||||||
let cursorLine = linesToCursor[linesToCursor.length-1].trim();
|
|
||||||
if (cursorLine.length > 0) {
|
|
||||||
toInsert = "\n\n";
|
|
||||||
}
|
|
||||||
for (let i = 0; i < items.length; ++i) {
|
|
||||||
if (items[i].file) {
|
|
||||||
fileCounter++;
|
|
||||||
toInsert += makeUploadString(fileCounter, items[i].file.name) + "\n\n";
|
|
||||||
queueUpload(fileCounter, items[i].file);
|
|
||||||
} else {
|
|
||||||
toInsert += `${items[i].error}\n\n`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
textField.value = textField.value.substring(0, cursorStart) + toInsert + textField.value.substring(cursorEnd, textField.value.length);
|
|
||||||
doMarkdown();
|
|
||||||
uploadNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceUploadString(upload, newString) {
|
|
||||||
let cursorStart = textField.selectionStart;
|
|
||||||
let cursorEnd = textField.selectionEnd;
|
|
||||||
let uploadString = makeUploadString(upload.uploadNumber, upload.file.name);
|
|
||||||
let insertIndex = textField.value.indexOf(uploadString)
|
|
||||||
textField.value = textField.value.replace(uploadString, newString);
|
|
||||||
if (cursorStart <= insertIndex + uploadString.length) {
|
|
||||||
textField.selectionStart = cursorStart;
|
|
||||||
} else {
|
|
||||||
textField.selectionStart = cursorStart - uploadString.length + newString.length;
|
|
||||||
}
|
|
||||||
if (cursorEnd <= insertIndex + uploadString.length) {
|
|
||||||
textField.selectionEnd = cursorEnd;
|
|
||||||
} else {
|
|
||||||
textField.selectionEnd = cursorEnd - uploadString.length + newString.length;
|
|
||||||
}
|
|
||||||
doMarkdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceUploadStringError(upload) {
|
|
||||||
replaceUploadString(upload, `There was a problem uploading your file \`${upload.file.name}\`.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function queueUpload(uploadNumber, file) {
|
|
||||||
uploadQueue.push({
|
|
||||||
uploadNumber: uploadNumber,
|
|
||||||
file: file
|
|
||||||
});
|
|
||||||
|
|
||||||
currentBatchSize++;
|
|
||||||
uploadProgressText.textContent = `Uploading files ${currentBatchDone+1}/${currentBatchSize}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function uploadDone(ev) {
|
|
||||||
try {
|
|
||||||
if (currentXhr.status == 200 && currentXhr.response) {
|
|
||||||
if (currentXhr.response.url) {
|
|
||||||
let url = currentXhr.response.url;
|
|
||||||
let newString = `[${currentUpload.file.name}](${url})`;
|
|
||||||
if (currentXhr.response.mime.startsWith("image")) {
|
|
||||||
newString = "!" + newString;
|
|
||||||
}
|
|
||||||
|
|
||||||
replaceUploadString(currentUpload, newString);
|
|
||||||
} else if (currentXhr.response.error) {
|
|
||||||
replaceUploadString(currentUpload, `Upload failed for \`${currentUpload.file.name}\`: ${currentXhr.response.error}.`);
|
|
||||||
} else {
|
|
||||||
replaceUploadStringError(currentUpload);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
replaceUploadStringError(currentUpload);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
replaceUploadStringError(currentUpload);
|
|
||||||
}
|
|
||||||
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...";
|
|
||||||
|
|
||||||
try {
|
|
||||||
let utf8Filename = strToUTF8Arr(next.file.name);
|
|
||||||
let base64Filename = base64EncArr(utf8Filename);
|
|
||||||
// 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", base64Filename);
|
|
||||||
currentXhr.responseType = "json";
|
|
||||||
currentXhr.addEventListener("loadend", uploadDone);
|
|
||||||
currentXhr.send(next.file);
|
|
||||||
currentUpload = next;
|
|
||||||
} catch (err) {
|
|
||||||
replaceUploadStringError(next);
|
|
||||||
console.error(err);
|
|
||||||
uploadNext();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
submitButton.disabled = false;
|
|
||||||
submitButton.value = submitText;
|
|
||||||
uploadBar.classList.remove("uploading");
|
|
||||||
currentBatchSize = 0;
|
|
||||||
currentBatchDone = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
<script src="{{ static "js/tabs.js" }}"></script>
|
<script src="{{ static "js/tabs.js" }}"></script>
|
||||||
<script src="{{ static "js/image_selector.js" }}"></script>
|
<script src="{{ static "js/image_selector.js" }}"></script>
|
||||||
<script src="{{ static "js/templates.js" }}"></script>
|
<script src="{{ static "js/templates.js" }}"></script>
|
||||||
|
<script src="{{ static "js/base64.js" }}"></script>
|
||||||
|
<script src="{{ static "js/markdown_upload.js" }}"></script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
#desc-preview:empty::after {
|
#desc-preview:empty::after {
|
||||||
|
@ -12,6 +14,10 @@
|
||||||
color: var(--dimmer-color);
|
color: var(--dimmer-color);
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#description.drop {
|
||||||
|
box-shadow: inset 0px 0px 5px yellow;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
@ -156,6 +162,17 @@
|
||||||
<textarea id="description" class="w-100 h5 minh-5 mono lh-copy" name="description">
|
<textarea id="description" class="w-100 h5 minh-5 mono lh-copy" name="description">
|
||||||
{{- .ProjectSettings.Description -}}
|
{{- .ProjectSettings.Description -}}
|
||||||
</textarea>
|
</textarea>
|
||||||
|
<div class="flex justify-end items-center mt2">
|
||||||
|
<div class="upload_bar flex-grow-1">
|
||||||
|
<div class="instructions">
|
||||||
|
Upload files by dragging & dropping, pasting, or <label class="pointer link" for="file_input">selecting</label> them.
|
||||||
|
</div>
|
||||||
|
<div class="progress flex">
|
||||||
|
<div class="progress_text mr3"></div>
|
||||||
|
<div class="progress_bar flex-grow-1 flex-shrink-1 pa1"><div class=""></div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="b mt3 mb2">Preview:</div>
|
<div class="b mt3 mb2">Preview:</div>
|
||||||
<div id="desc-preview" class="w-100"></div>
|
<div id="desc-preview" class="w-100"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -196,6 +213,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<input type="file" multiple name="file_input" id="file_input" class="dn" />{{/* NOTE(mark): copied NOTE(asaf): Placing this outside the form to avoid submitting it to the server by accident */}}
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
let csrf = JSON.parse({{ csrftokenjs .Session }});
|
let csrf = JSON.parse({{ csrftokenjs .Session }});
|
||||||
|
@ -367,5 +385,18 @@
|
||||||
document.querySelector(".dark_logo")
|
document.querySelector(".dark_logo")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//////////////////
|
||||||
|
// Asset upload //
|
||||||
|
//////////////////
|
||||||
|
setupMarkdownUpload(
|
||||||
|
document.querySelector("#project_form [data-name=Description] input[type=submit]"),
|
||||||
|
document.querySelector('#file_input'),
|
||||||
|
document.querySelector('.upload_bar'),
|
||||||
|
description,
|
||||||
|
doMarkdown,
|
||||||
|
{{ .MaxFileSize }},
|
||||||
|
{{ .UploadUrl }}
|
||||||
|
);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
@ -402,6 +402,9 @@ type ProjectEditData struct {
|
||||||
|
|
||||||
APICheckUsernameUrl string
|
APICheckUsernameUrl string
|
||||||
LogoMaxFileSize int
|
LogoMaxFileSize int
|
||||||
|
|
||||||
|
MaxFileSize int
|
||||||
|
UploadUrl string
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProjectNew(c *RequestContext) ResponseData {
|
func ProjectNew(c *RequestContext) ResponseData {
|
||||||
|
@ -428,6 +431,9 @@ func ProjectNew(c *RequestContext) ResponseData {
|
||||||
|
|
||||||
APICheckUsernameUrl: hmnurl.BuildAPICheckUsername(),
|
APICheckUsernameUrl: hmnurl.BuildAPICheckUsername(),
|
||||||
LogoMaxFileSize: ProjectLogoMaxFileSize,
|
LogoMaxFileSize: ProjectLogoMaxFileSize,
|
||||||
|
|
||||||
|
MaxFileSize: AssetMaxSize(c.CurrentUser),
|
||||||
|
UploadUrl: c.UrlContext.BuildAssetUpload(),
|
||||||
}, c.Perf)
|
}, c.Perf)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
@ -552,6 +558,9 @@ func ProjectEdit(c *RequestContext) ResponseData {
|
||||||
|
|
||||||
APICheckUsernameUrl: hmnurl.BuildAPICheckUsername(),
|
APICheckUsernameUrl: hmnurl.BuildAPICheckUsername(),
|
||||||
LogoMaxFileSize: ProjectLogoMaxFileSize,
|
LogoMaxFileSize: ProjectLogoMaxFileSize,
|
||||||
|
|
||||||
|
MaxFileSize: AssetMaxSize(c.CurrentUser),
|
||||||
|
UploadUrl: c.UrlContext.BuildAssetUpload(),
|
||||||
}, c.Perf)
|
}, c.Perf)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue