const zone = document.getElementById('drop-zone'); const input = document.getElementById('fileInput'); const uploadBtn = document.getElementById('uploadBtn'); const cancelBtn = document.getElementById('cancelBtn'); const progressText = document.getElementById("progress-text"); const statsText = document.getElementById("stats-text"); const speedText = document.getElementById("speed-text"); const etaText = document.getElementById("eta-text"); const progressBar = document.getElementById("progress-bar"); const progressContainer = document.getElementById("progress-container"); let currentXhr = null; function formatBytes(bytes, decimals = 2) { if (bytes === 0) return '0 Bytes'; const k = 1024; const dm = decimals < 0 ? 0 : decimals; const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; } function formatTime(seconds) { if (!isFinite(seconds) || seconds < 0) return "--:--"; const h = Math.floor(seconds / 3600); const m = Math.floor((seconds % 3600) / 60); const s = Math.floor(seconds % 60); return [ h > 0 ? h : null, (h > 0 ? m.toString().padStart(2, '0') : m), s.toString().padStart(2, '0') ].filter(x => x !== null).join(':'); } zone.onclick = () => input.click(); zone.ondragover = (e) => { e.preventDefault(); zone.classList.add('active'); }; zone.ondragleave = () => zone.classList.remove('active'); zone.ondrop = (e) => { e.preventDefault(); zone.classList.remove('active'); if (e.dataTransfer.files.length) { input.files = e.dataTransfer.files; input.dispatchEvent(new Event('change')); } }; input.onchange = () => { if (input.files.length) { showFiles(input.files); uploadBtn.disabled = false; } else { uploadBtn.disabled = true; } }; function showFiles(fileList) { const files = Array.from(fileList || []); if (files.length === 0) return; const total = files.reduce((acc, f) => acc + f.size, 0); if (files.length === 1) { document.getElementById('dz-text').innerText = `${files[0].name} [${formatBytes(files[0].size)}]`; } else { document.getElementById('dz-text').innerText = `${files.length} FILES [${formatBytes(total)}] — will be zipped`; } } uploadBtn.onclick = () => { if (!input.files.length) return; if (input.files.length === 1) { handleUploadSingle(input.files[0]); } else { handleUploadMulti(input.files); } }; cancelBtn.onclick = (e) => { e.stopPropagation(); if (currentXhr) { currentXhr.abort(); alert("Upload cancelled."); location.reload(); } }; function commonFormData() { const fd = new FormData(); fd.append("once", document.getElementById("once").checked ? "true" : "false"); const hours = parseInt(document.getElementById("duration").value, 10); fd.append("duration", hours); return fd; } function startUploadUI() { uploadBtn.disabled = true; uploadBtn.innerText = "UPLOADING..."; cancelBtn.classList.remove('hidden'); progressContainer.classList.remove("hidden"); progressText.classList.remove("hidden"); statsText.classList.remove("hidden"); } function setupXHRHandlers(xhr) { let startTime = Date.now(); xhr.upload.onprogress = (e) => { if (e.lengthComputable) { const percent = Math.round((e.loaded / e.total) * 100); progressBar.style.width = percent + "%"; progressText.innerText = percent + "%"; const elapsedSeconds = (Date.now() - startTime) / 1000; if (elapsedSeconds > 0) { const bytesPerSecond = e.loaded / elapsedSeconds; const remainingBytes = e.total - e.loaded; const secondsRemaining = remainingBytes / bytesPerSecond; speedText.innerText = formatBytes(bytesPerSecond) + "/S"; etaText.innerText = formatTime(secondsRemaining); } } }; xhr.onload = () => { if (xhr.status >= 200 && xhr.status < 300) { try { const data = JSON.parse(xhr.responseText); if (data.error) throw new Error(data.error); window.location.href = "/f/" + data.view_key; } catch (err) { console.error("Invalid response:", xhr.responseText); alert("Server error"); } } else { alert("Upload failed"); } }; xhr.onerror = () => { if (xhr.statusText !== "abort") { alert("Upload failed"); location.reload(); } }; } function handleUploadSingle(file) { startUploadUI(); const fd = commonFormData(); fd.append("file", file); const xhr = new XMLHttpRequest(); currentXhr = xhr; setupXHRHandlers(xhr); xhr.open("POST", "/api/files/upload"); xhr.send(fd); } function handleUploadMulti(fileList) { startUploadUI(); const fd = commonFormData(); Array.from(fileList).forEach(f => fd.append("files", f)); const xhr = new XMLHttpRequest(); currentXhr = xhr; setupXHRHandlers(xhr); xhr.open("POST", "/api/files/upload-multi"); xhr.send(fd); } function copy(id) { const el = document.getElementById(id); el.select(); document.execCommand('copy'); }