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