/** * upload-progress.js * * Intercepts admin form submissions with files and submits via XMLHttpRequest. * Polls GET /admin/actions/upload-progress.php?token=xxx for server-side * processing progress (PeerTube uploads, file moves). * * Progress display: * 0%–25% : browser → server upload (XHR upload.progress) * 25%–99% : server processing (polled from progress endpoint) * 100% : response received — "Téléversé avec succès", then redirect */ (() => { 'use strict'; const FORMS = document.querySelectorAll('form[data-upload-progress]'); if (!FORMS.length) return; const POLL_INTERVAL = 400; const UPLOAD_CAP = 25; const PROCESSING_MAX = 99; const SUCCESS_DELAY = 800; for (const form of FORMS) { const progressWrap = form.querySelector('#upload-progress-wrap'); const progressBar = form.querySelector('#upload-progress-bar'); const progressLabel = form.querySelector('#upload-progress-label'); const progressFile = form.querySelector('#upload-progress-file'); const submitBtn = form.querySelector('button[type="submit"]'); const tokenInput = form.querySelector('input[name="progress_token"]'); if (!progressBar || !progressWrap) continue; function collectFileNames() { const names = []; const inputs = form.querySelectorAll('input[type="file"]'); for (const fi of inputs) { if (fi.files) { for (const f of fi.files) { if (f.name) names.push(f.name); } } } return names; } form.addEventListener('submit', function (e) { const fileNames = collectFileNames(); if (!fileNames.length) return; e.preventDefault(); const token = tokenInput ? tokenInput.value : ''; progressWrap.style.display = ''; progressBar.value = 0; progressBar.removeAttribute('data-complete'); progressLabel.textContent = 'Téléversement en cours…'; progressFile.textContent = fileNames.length === 1 ? fileNames[0] : fileNames.length + ' fichiers'; if (submitBtn) submitBtn.disabled = true; const fd = new FormData(form); const xhr = new XMLHttpRequest(); let uploadDone = false; let lastUploadPct = 0; let pollingTimer = null; /** Poll server-side progress */ function startPolling() { if (pollingTimer || !token) return; progressLabel.textContent = 'Traitement en cours…'; pollingTimer = setInterval(function () { fetch('/admin/actions/upload-progress.php?token=' + encodeURIComponent(token)) .then(function (r) { return r.json(); }) .then(function (data) { if (data && data.stage && data.stage !== 'upload') { const pct = Math.min(PROCESSING_MAX, Math.max(UPLOAD_CAP, data.pct || UPLOAD_CAP)); progressBar.value = pct; if (data.file) { progressFile.textContent = data.file; } } }) .catch(function () { /* ignore poll errors */ }); }, POLL_INTERVAL); } function stopPolling() { if (pollingTimer) { clearInterval(pollingTimer); pollingTimer = null; } } function finishSuccess() { stopPolling(); progressBar.value = 100; progressBar.setAttribute('data-complete', ''); progressLabel.textContent = 'Téléversé avec succès'; progressFile.textContent = ''; } // ── Upload phase (0% → UPLOAD_CAP) ── xhr.upload.addEventListener('progress', function (evt) { if (evt.lengthComputable) { const rawPct = Math.round((evt.loaded / evt.total) * 100); const scaled = Math.round((rawPct / 100) * UPLOAD_CAP); if (scaled > lastUploadPct) { lastUploadPct = scaled; progressBar.value = scaled; } } }); xhr.upload.addEventListener('loadend', function () { uploadDone = true; progressBar.value = UPLOAD_CAP; startPolling(); }); // ── Response handling ── xhr.addEventListener('readystatechange', function () { if (xhr.readyState !== XMLHttpRequest.DONE) return; stopPolling(); if (xhr.status >= 200 && xhr.status < 300) { finishSuccess(); setTimeout(function () { const finalUrl = xhr.responseURL || ''; if (finalUrl && finalUrl !== form.action) { window.location.href = finalUrl; } else { document.open(); document.write(xhr.responseText); document.close(); } }, SUCCESS_DELAY); } else { progressLabel.textContent = 'Erreur'; progressFile.textContent = 'Échec du téléversement'; document.open(); document.write(xhr.responseText); document.close(); } }); xhr.addEventListener('error', function () { stopPolling(); progressLabel.textContent = 'Erreur réseau'; progressFile.textContent = ''; if (submitBtn) submitBtn.disabled = false; }); xhr.addEventListener('abort', function () { stopPolling(); progressWrap.style.display = 'none'; if (submitBtn) submitBtn.disabled = false; }); xhr.open('POST', form.action, true); xhr.send(fd); }); } })();