/** Download-URL (href der API) → dieselbe URL mit ?inline=1 für Anzeige */ export function hrefToInlineView(href) { if (!href) return href; const u = new URL(href, window.location.origin); u.searchParams.set('inline', '1'); return u.pathname + u.search + u.hash; } const TEXT_PREVIEW_MAX = 512 * 1024; /** @param {string} mime @param {string} fileName */ export function attachmentPreviewKind(mime, fileName) { const m = (mime || '').toLowerCase().trim(); const ext = (fileName || '').split('.').pop()?.toLowerCase() || ''; if (m.startsWith('image/')) return 'image'; if (m === 'application/pdf' || ext === 'pdf') return 'pdf'; if (m.startsWith('video/')) return 'video'; if (m.startsWith('audio/')) return 'audio'; if ( m.startsWith('text/') || m === 'application/json' || m === 'application/xml' || ['csv', 'json', 'xml', 'txt', 'log', 'md', 'svg'].includes(ext) ) { return 'text'; } return 'other'; } let dialogEl; let bodyEl; let titleEl; let downloadLink; function setPageScrollLocked(locked) { if (locked) { document.documentElement.style.overflow = 'hidden'; document.body.style.overflow = 'hidden'; } else { document.documentElement.style.overflow = ''; document.body.style.overflow = ''; } } function ensureDialog() { if (dialogEl) return; dialogEl = document.createElement('dialog'); dialogEl.className = 'attachment-preview-dialog'; dialogEl.setAttribute('aria-modal', 'true'); dialogEl.innerHTML = `
`; document.body.appendChild(dialogEl); bodyEl = dialogEl.querySelector('.attachment-preview-body'); titleEl = dialogEl.querySelector('.attachment-preview-title'); downloadLink = dialogEl.querySelector('.attachment-preview-download'); dialogEl.querySelector('.attachment-preview-close').addEventListener('click', () => { dialogEl.close(); }); dialogEl.addEventListener('click', (e) => { if (e.target === dialogEl) dialogEl.close(); }); dialogEl.addEventListener('close', () => { if (bodyEl) bodyEl.innerHTML = ''; setPageScrollLocked(false); }); } /** * Klick-Delegation: Links mit .js-attachment-preview öffnen das Modal. */ export function bindAttachmentPreview(root = document.body) { ensureDialog(); root.addEventListener('click', (e) => { const a = e.target.closest('a.js-attachment-preview'); if (!a) return; e.preventDefault(); openAttachmentPreview(a); }); } /** * @param {HTMLAnchorElement} a */ export async function openAttachmentPreview(a) { ensureDialog(); const name = a.getAttribute('data-name') || a.textContent?.trim() || 'Datei'; const mime = a.getAttribute('data-mime') || ''; const rawHref = a.getAttribute('href') || ''; const viewUrl = hrefToInlineView(rawHref); const kind = attachmentPreviewKind(mime, name); titleEl.textContent = name; downloadLink.href = rawHref; downloadLink.setAttribute('download', name); bodyEl.className = 'attachment-preview-body attachment-preview-body--scroll'; bodyEl.innerHTML = ''; try { if (kind === 'image') { bodyEl.innerHTML = ''; const img = document.createElement('img'); img.className = 'attachment-preview-img'; img.alt = name; img.src = viewUrl; img.referrerPolicy = 'same-origin'; bodyEl.appendChild(img); } else if (kind === 'pdf') { bodyEl.className = 'attachment-preview-body attachment-preview-body--embed'; bodyEl.innerHTML = ''; const iframe = document.createElement('iframe'); iframe.className = 'attachment-preview-iframe'; iframe.title = name; iframe.src = viewUrl; bodyEl.appendChild(iframe); } else if (kind === 'video') { bodyEl.innerHTML = ''; const v = document.createElement('video'); v.className = 'attachment-preview-video'; v.controls = true; v.playsInline = true; v.src = viewUrl; bodyEl.appendChild(v); } else if (kind === 'audio') { bodyEl.innerHTML = ''; const v = document.createElement('audio'); v.className = 'attachment-preview-audio'; v.controls = true; v.src = viewUrl; bodyEl.appendChild(v); } else if (kind === 'text') { const res = await fetch(viewUrl, { credentials: 'include' }); if (!res.ok) throw new Error('Laden fehlgeschlagen'); let text = await res.text(); if (text.length > TEXT_PREVIEW_MAX) { text = `${text.slice(0, TEXT_PREVIEW_MAX)}\n\n… (gekürzt)`; } bodyEl.innerHTML = ''; const pre = document.createElement('pre'); pre.className = 'attachment-preview-text'; pre.textContent = text; bodyEl.appendChild(pre); } else { bodyEl.innerHTML = 'Für diesen Dateityp gibt es keine eingebaute Vorschau. Nutzen Sie „Herunterladen“.
'; } } catch (err) { bodyEl.className = 'attachment-preview-body attachment-preview-body--scroll'; bodyEl.innerHTML = `Vorschau konnte nicht geladen werden: ${err.message || err}
`; } if (typeof dialogEl.showModal === 'function') { setPageScrollLocked(true); dialogEl.showModal(); } }