diff --git a/static/js/magazine.js b/static/js/magazine.js index 2fca30f..f54d087 100644 --- a/static/js/magazine.js +++ b/static/js/magazine.js @@ -114,23 +114,44 @@ function magShowDroppedFile(file) { if (noteEl) noteEl.textContent = file ? file.name : MAG_DROPZONE_DEFAULT_NOTE; } +let _magDropGuardInstalled = false; + function setupMagDropzone() { const zone = $('mag-dropzone'); const input = $('mag-new-file'); - if (!zone || !input || zone.dataset.magDropzoneWired) return; + if (!zone || !input) return; + + // A page-wide safety net: if a drop ever lands outside the zone (or the + // zone's own handler below fails to intercept it for any reason), the + // browser's native behaviour is to navigate to/open the dropped file -- + // confirmed live as exactly what happened before this existed. Installed + // once regardless of which section is active, since a stray drop can land + // anywhere on the page. + if (!_magDropGuardInstalled) { + _magDropGuardInstalled = true; + window.addEventListener('dragover', (e) => { e.preventDefault(); }); + window.addEventListener('drop', (e) => { e.preventDefault(); }); + } + + if (zone.dataset.magDropzoneWired) return; zone.dataset.magDropzoneWired = '1'; - zone.addEventListener('dragover', (e) => { - if (e.dataTransfer && Array.from(e.dataTransfer.types || []).includes('Files')) { - e.preventDefault(); - zone.classList.add('dragover'); - } - }); + + // dragenter AND dragover both need preventDefault() -- some browsers only + // arm the element as a valid drop target once dragenter itself has been + // cancelled, not just the dragover events that follow it. Not gated on + // dataTransfer.types: during a real OS file drag, Chromium does not + // always populate that list synchronously on every one of these events, + // and gating on it here (confirmed live) silently skipped preventDefault + // often enough that neither the hover highlight nor the drop interception + // ever fired -- the browser fell through to opening the file itself. + const armDropTarget = (e) => { e.preventDefault(); zone.classList.add('dragover'); }; + zone.addEventListener('dragenter', armDropTarget); + zone.addEventListener('dragover', armDropTarget); zone.addEventListener('dragleave', () => zone.classList.remove('dragover')); zone.addEventListener('drop', (e) => { - if (!(e.dataTransfer && Array.from(e.dataTransfer.types || []).includes('Files'))) return; e.preventDefault(); zone.classList.remove('dragover'); - const file = e.dataTransfer.files?.[0]; + const file = e.dataTransfer?.files?.[0]; if (!file) return; if (file.type !== 'application/pdf' && !/\.pdf$/i.test(file.name)) { return say('Only PDF files are supported', 'err');