diff --git a/assets/config.json b/assets/config.json
index c5ed8d1..ca26aea 100644
--- a/assets/config.json
+++ b/assets/config.json
@@ -1,12 +1,12 @@
{
"bundles": {
"app": "ts/app.ts",
- "fm": "js/fm.js",
- "url-shrinker": "js/url-shrinker.js",
+ "fm": "ts/fm.ts",
+ "url-shrinker": "ts/url-shrinker.ts",
"layout": "sass/layout.scss",
"error": "sass/error.scss",
"logo": "img/logo.svg",
"logo_png": "img/logox128.png",
"logo_png_xxl": "img/logox1024.png"
}
-}
\ No newline at end of file
+}
diff --git a/assets/js/fm.js b/assets/js/fm.js
deleted file mode 100644
index 354424d..0000000
--- a/assets/js/fm.js
+++ /dev/null
@@ -1,131 +0,0 @@
-document.addEventListener('DOMContentLoaded', () => {
- 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');
-
- const autogenUrlCheckbox = document.getElementById('field-autogen_url');
- const slugField = document.getElementById('field-slug');
-
- neverExpireCheckbox.addEventListener('change', () => {
- expireAfterDaysField.disabled = neverExpireCheckbox.checked;
- });
-
- autogenUrlCheckbox.addEventListener('change', () => {
- slugField.disabled = autogenUrlCheckbox.checked;
- });
-
- 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) {
- const time = new Date().getTime();
- if (this.lastTransferTime) {
- this.xferSpeed.push((loaded - this.lastLoaded) / (time - this.lastTransferTime));
-
- if (this.xferSpeed.length > 100) 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/js/url-shrinker.js b/assets/js/url-shrinker.js
deleted file mode 100644
index e1e10f5..0000000
--- a/assets/js/url-shrinker.js
+++ /dev/null
@@ -1,11 +0,0 @@
-document.addEventListener('DOMContentLoaded', () => {
- const form = document.getElementById('url-shrink-form');
- if (!form) return;
-
- const autogenUrlCheckbox = document.getElementById('field-autogen_url');
- const slugField = document.getElementById('field-slug');
-
- autogenUrlCheckbox.addEventListener('change', () => {
- slugField.disabled = autogenUrlCheckbox.checked;
- });
-});
diff --git a/assets/ts/fm.ts b/assets/ts/fm.ts
new file mode 100644
index 0000000..07d0134
--- /dev/null
+++ b/assets/ts/fm.ts
@@ -0,0 +1,171 @@
+import {applyFormMessages} from "./forms";
+
+document.addEventListener('DOMContentLoaded', () => {
+ const form = document.querySelector('#upload-form');
+ if (!form) return;
+ const upload = document.getElementById('file-upload');
+ const uploadLink = document.querySelector('#file-upload-link');
+
+ const uploadField = document.querySelector('#field-upload');
+
+ const neverExpireCheckbox = document.querySelector('#field-never_expire');
+ const expireAfterDaysField = document.querySelector('#field-expire_after_days');
+
+ const autogenUrlCheckbox = document.querySelector('#field-autogen_url');
+ const slugField = document.querySelector('#field-slug');
+
+ if (expireAfterDaysField) {
+ neverExpireCheckbox?.addEventListener('change', () => {
+ expireAfterDaysField.disabled = neverExpireCheckbox.checked;
+ });
+ }
+
+ if (slugField) {
+ autogenUrlCheckbox?.addEventListener('change', () => {
+ slugField.disabled = autogenUrlCheckbox.checked;
+ });
+ }
+
+ let uploadForm: UploadForm | undefined;
+
+ form.addEventListener('submit', e => {
+ e.preventDefault();
+ if (upload && uploadLink && uploadField && uploadField.files && (!uploadForm || uploadForm.isFinished())) {
+ uploadForm = new UploadForm(form, upload, uploadLink, uploadField.files[0].name);
+ uploadForm.updateView();
+ uploadForm.start();
+ }
+ });
+});
+
+
+const units = ['K', 'M', 'G', 'T'];
+
+class UploadForm {
+ private finished: boolean = false;
+ private readonly progressBar: HTMLElement | null;
+ private readonly progressBarContent: HTMLElement | null;
+ private readonly status: HTMLElement | null;
+ private speed: HTMLElement | null;
+ private xferSpeed: number[] = [];
+ private lastTransferTime: number | null = null;
+ private lastLoaded: number = 0;
+
+ private xhr: XMLHttpRequest;
+
+ public constructor(
+ private form: HTMLFormElement,
+ private upload: HTMLElement,
+ private uploadLink: HTMLLinkElement,
+ private fileName: string,
+ ) {
+ this.progressBar = this.upload.querySelector('.progress-bar');
+ this.progressBarContent = this.progressBar?.querySelector('.content') || null;
+ this.status = this.upload.querySelector('.status');
+ this.speed = this.status?.querySelector('.speed') || null;
+
+ if (!this.progressBar)
+ throw new Error('Invalid html');
+
+ this.xhr = new XMLHttpRequest();
+ this.xhr.responseType = 'json';
+ this.xhr.upload.addEventListener('progress', e => {
+ if (this.progressBar && this.progressBarContent) {
+ if (e.lengthComputable) {
+ this.progressBar.classList.remove('undefined');
+ const percent = (e.loaded / e.total * 100).toFixed(2) + '%';
+ this.progressBar.style.setProperty('--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;
+ const response = this.xhr.response;
+ console.log('done', response);
+ if (response.status === 'error') {
+ if (response.messages) {
+ this.restoreView();
+ applyFormMessages(this.form, response.messages);
+ }
+ } else if (response.url) {
+ if (this.status) {
+ this.status.innerHTML = 'Done!';
+ }
+ const uploadLinkContent = this.uploadLink.querySelector('.content');
+ if (uploadLinkContent) {
+ uploadLinkContent.innerText = response.url;
+ }
+ this.uploadLink.classList.remove('hidden');
+ } else {
+ window.location.reload();
+ }
+ });
+ this.xhr.addEventListener('error', (e) => {
+ this.finished = true;
+ console.error('error', e);
+ if (this.status) {
+ this.status.innerHTML = 'Error; upload was interrupted.';
+ }
+ });
+ }
+
+ public isFinished(): boolean {
+ return this.finished;
+ }
+
+ public updateView(): void {
+ const uploadName = this.upload.querySelector('.name');
+ if (uploadName) {
+ uploadName.innerText = this.fileName;
+ }
+ this.form.classList.add('hidden');
+ this.upload.classList.remove('hidden');
+ if (this.status) {
+ this.status.innerHTML = `Uploading @ --`;
+ this.speed = this.status.querySelector('.speed');
+ }
+ }
+
+ public restoreView(): void {
+ if (this.status) {
+ this.status.classList.add('hidden');
+ }
+ this.upload.classList.add('hidden');
+ this.form.classList.remove('hidden');
+ }
+
+ public start(): void {
+ const formData = new FormData(this.form);
+ this.xhr.open('POST', this.form.action);
+ this.xhr.send(formData);
+ }
+
+ public updateSpeed(loaded: number): void {
+ const time = new Date().getTime();
+ if (this.lastTransferTime) {
+ this.xferSpeed.push((loaded - this.lastLoaded) / (time - this.lastTransferTime));
+
+ if (this.xferSpeed.length > 100) 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++;
+ }
+
+ if (this.speed) {
+ this.speed.innerText = speed.toFixed(2) + units[unit] + 'Bps';
+ }
+ }
+ this.lastTransferTime = time;
+ this.lastLoaded = loaded;
+ }
+}
diff --git a/assets/ts/url-shrinker.ts b/assets/ts/url-shrinker.ts
new file mode 100644
index 0000000..439e3d6
--- /dev/null
+++ b/assets/ts/url-shrinker.ts
@@ -0,0 +1,13 @@
+document.addEventListener('DOMContentLoaded', () => {
+ const form = document.getElementById('url-shrink-form');
+ if (!form) return;
+
+ const autogenUrlCheckbox = document.querySelector('#field-autogen_url');
+ const slugField = document.querySelector('#field-slug');
+
+ if (slugField) {
+ autogenUrlCheckbox?.addEventListener('change', () => {
+ slugField.disabled = autogenUrlCheckbox.checked;
+ });
+ }
+});