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; + }); + } +});