diff --git a/assets/js/fm.js b/assets/js/fm.js
index 8fb2c06..c867d91 100644
--- a/assets/js/fm.js
+++ b/assets/js/fm.js
@@ -1,5 +1,10 @@
document.addEventListener('DOMContentLoaded', () => {
- if (!document.getElementById('upload-form')) return;
+ const form = document.getElementById('upload-form');
+ if (!form) return;
+ const upload = document.getElementById('file-upload');
+ const uploadLink = document.getElementById('file-upload-link');
+
+ const uploadField = document.getElementById('field-upload');
const neverExpireCheckbox = document.getElementById('field-never_expire');
const expireAfterDaysField = document.getElementById('field-expire_after_days');
@@ -14,4 +19,114 @@ document.addEventListener('DOMContentLoaded', () => {
autogenUrlCheckbox.addEventListener('change', () => {
slugField.disabled = autogenUrlCheckbox.checked;
});
-});
\ No newline at end of file
+
+ let uploadForm;
+
+ form.addEventListener('submit', e => {
+ e.preventDefault();
+ if (!uploadForm || uploadForm.isFinished()) {
+ uploadForm = new UploadForm(form, upload, uploadLink, uploadField.files[0].name);
+ uploadForm.updateView();
+ uploadForm.start();
+ }
+ });
+});
+
+function UploadForm(form, upload, uploadLink, fileName) {
+ this.form = form;
+ this.upload = upload;
+ this.uploadLink = uploadLink;
+ this.fileName = fileName;
+ this.progressBar = this.upload.querySelector('.progress-bar');
+ this.progressBarContent = this.progressBar.querySelector('.content');
+ this.status = this.upload.querySelector('.status');
+ this.speed = this.status.querySelector('.speed');
+ this.finished = false;
+
+ this.xferSpeed = [];
+ this.lastTransferTime = null;
+
+ this.xhr = new XMLHttpRequest();
+ this.xhr.responseType = 'json';
+ this.xhr.upload.addEventListener('progress', e => {
+ if (e.lengthComputable) {
+ this.progressBar.classList.remove('undefined');
+ let percent = ((e.loaded / e.total) * 100).toFixed(2) + '%';
+ this.progressBar.style = `--progress: ${percent}`;
+ this.progressBarContent.innerText = percent;
+ this.updateSpeed(e.loaded);
+ } else {
+ this.progressBar.classList.add('undefined');
+ }
+ });
+ this.xhr.upload.addEventListener('loadstart', () => {
+ this.status.classList.remove('hidden');
+ });
+ this.xhr.addEventListener('load', () => {
+ this.finished = true;
+ let response = this.xhr.response;
+ console.log('done', response);
+ if (response.status === 'error') {
+ if (response.messages) {
+ this.restoreView();
+ window.applyFormMessages(this.form, response.messages);
+ }
+ } else if (response.url) {
+ this.status.innerHTML = 'Done!';
+ this.uploadLink.querySelector('.content').innerText = response.url;
+ this.uploadLink.classList.remove('hidden');
+ } else {
+ window.location.reload();
+ }
+ });
+ this.xhr.addEventListener('error', (e) => {
+ this.finished = true;
+ console.error('error', e);
+ this.status.innerHTML = 'Error; upload was interrupted.';
+ });
+}
+
+UploadForm.prototype.isFinished = function () {
+ return this.finished;
+}
+
+UploadForm.prototype.updateView = function () {
+ this.upload.querySelector('.name').innerText = this.fileName;
+ this.form.classList.add('hidden');
+ this.upload.classList.remove('hidden');
+ this.status.innerHTML = `Uploading @ --`;
+ this.speed = this.status.querySelector('.speed');
+}
+
+UploadForm.prototype.restoreView = function () {
+ this.status.classList.add('hidden');
+ this.upload.classList.add('hidden');
+ this.form.classList.remove('hidden');
+};
+
+UploadForm.prototype.start = function () {
+ const formData = new FormData(this.form);
+ this.xhr.open('POST', this.form.action);
+ this.xhr.send(formData);
+}
+
+const units = ['K', 'M', 'G', 'T'];
+UploadForm.prototype.updateSpeed = function (loaded) {
+ console.log(this.xferSpeed);
+ const time = new Date().getTime();
+ if (this.lastTransferTime) {
+ this.xferSpeed.push((loaded - this.lastLoaded) / (time - this.lastTransferTime));
+
+ if (this.xferSpeed.length > 10) this.xferSpeed = this.xferSpeed.slice(1);
+
+ let speed = this.xferSpeed.reduce((v, c) => v + c) / this.xferSpeed.length;
+ let unit = 0;
+ while (speed >= 1000 && unit < units.length - 1) {
+ speed /= 1000;
+ unit++;
+ }
+ this.speed.innerText = (speed).toFixed(2) + units[unit] + 'Bps';
+ }
+ this.lastTransferTime = time;
+ this.lastLoaded = loaded;
+}
diff --git a/assets/sass/app.scss b/assets/sass/app.scss
index 933fea1..0a709c7 100644
--- a/assets/sass/app.scss
+++ b/assets/sass/app.scss
@@ -1 +1,2 @@
-@import "layout";
\ No newline at end of file
+@import "layout";
+@import "fm";
\ No newline at end of file
diff --git a/assets/sass/fm.scss b/assets/sass/fm.scss
new file mode 100644
index 0000000..6f5df35
--- /dev/null
+++ b/assets/sass/fm.scss
@@ -0,0 +1,11 @@
+@import "vars";
+
+#file-upload {
+ padding: 8px;
+ background: $infoColor;
+ border-radius: 5px;
+
+ .name, .status {
+ text-align: center;
+ }
+}
\ No newline at end of file
diff --git a/views/file-upload.njk b/views/file-upload.njk
index 0d6a36b..ad798ce 100644
--- a/views/file-upload.njk
+++ b/views/file-upload.njk
@@ -14,7 +14,8 @@
diff --git a/yarn.lock b/yarn.lock
index 0587f97..0f4dd94 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -9925,9 +9925,9 @@ widest-line@^3.1.0:
string-width "^4.0.0"
wms-core@^0:
- version "0.8.10"
- resolved "https://registry.toot.party/wms-core/-/wms-core-0.8.10.tgz#8db64926f9bd9eaa61556ad98dcd215361b16950"
- integrity sha512-1GJkpOX/efoDGK9JOwHnBkD55Q6dEKTQsdHpgMmF3g0h7XxJFAAsZTv0tOyfYHIEpJo5mNdR/+mzdZTPsgtAdw==
+ version "0.8.11"
+ resolved "https://registry.toot.party/wms-core/-/wms-core-0.8.11.tgz#7b33600e3dff864f71e5a8bd57e185cf324b0332"
+ integrity sha512-We+PYb+D80gVj/VWUUYDazZCf/BiEjybi32Z51BEM73ihcmFuCOnD9FUq4Oy3MK/Vl6wVctWdapIb2jVnrSPug==
dependencies:
"@types/express" "^4.17.6"
"@types/express-session" "^1.17.0"