function setCopyStatus(message, isError) { const status = document.getElementById("copy-status"); if (!status) return; status.textContent = message; status.className = isError ? "copy-status error" : "copy-status success"; } function getNewsletterHtml() { const htmlSource = document.getElementById("newsletter-html-source"); const htmlDisplay = document.getElementById("newsletter-html-display"); return htmlSource?.value.trim() || htmlDisplay?.value.trim() || ""; } function getNewsletterOutlookHtml() { const outlookSource = document.getElementById("newsletter-outlook-source"); const outlook = outlookSource ? outlookSource.value.trim() : ""; if (outlook) return outlook; return getNewsletterHtml(); } function getNewsletterPlain() { const plainSource = document.getElementById("newsletter-plain-source"); return plainSource ? plainSource.value : ""; } function byteLength(text) { return new TextEncoder().encode(text).length; } function wrapCfHtml(html) { const fragmentStart = ""; const fragmentEnd = ""; const bodyMatch = html.match(/]*>([\s\S]*)<\/body>/i); const inner = bodyMatch ? bodyMatch[1].trim() : html; const documentHtml = `${fragmentStart}${inner}${fragmentEnd}`; const pad = (value) => String(value).padStart(10, "0"); const headerTemplate = "Version:0.9\r\n" + "StartHTML:0000000000\r\n" + "EndHTML:0000000000\r\n" + "StartFragment:0000000000\r\n" + "EndFragment:0000000000\r\n"; const startHtml = byteLength(headerTemplate); const endHtml = startHtml + byteLength(documentHtml); const startFragment = startHtml + byteLength(documentHtml.slice(0, documentHtml.indexOf(fragmentStart) + fragmentStart.length)); const endFragment = startHtml + byteLength(documentHtml.slice(0, documentHtml.indexOf(fragmentEnd))); return ( "Version:0.9\r\n" + `StartHTML:${pad(startHtml)}\r\n` + `EndHTML:${pad(endHtml)}\r\n` + `StartFragment:${pad(startFragment)}\r\n` + `EndFragment:${pad(endFragment)}\r\n` + documentHtml ); } function selectTextarea(textarea) { if (!textarea || !textarea.value) return false; textarea.scrollIntoView({ behavior: "smooth", block: "center" }); textarea.focus({ preventScroll: true }); textarea.select(); textarea.setSelectionRange(0, textarea.value.length); return true; } function copyFromTextarea(textarea) { if (!textarea || !textarea.value) return false; const wasReadonly = textarea.hasAttribute("readonly"); if (wasReadonly) textarea.removeAttribute("readonly"); selectTextarea(textarea); let ok = false; try { ok = document.execCommand("copy"); } catch (err) { console.warn("execCommand copy from textarea failed.", err); } if (wasReadonly) textarea.setAttribute("readonly", ""); if (!ok) { selectTextarea(textarea); } return ok; } function copyTextWithFallback(text) { if (!text) return false; const pageTextareas = [ document.getElementById("newsletter-html-display"), document.getElementById("newsletter-html-source"), document.getElementById("newsletter-outlook-source"), document.getElementById("newsletter-plain-source"), ]; for (const textarea of pageTextareas) { if (textarea && textarea.value === text && copyFromTextarea(textarea)) { return true; } } const textarea = document.createElement("textarea"); textarea.value = text; textarea.setAttribute("aria-hidden", "true"); textarea.style.cssText = "position:fixed;top:0;left:0;width:2px;height:2px;opacity:0;border:none;padding:0;"; document.body.appendChild(textarea); let ok = false; try { selectTextarea(textarea); ok = document.execCommand("copy"); } catch (err) { console.warn("execCommand copy from temporary textarea failed.", err); } finally { document.body.removeChild(textarea); } return ok; } let copyDialogKeyHandler = null; function closeCopyDialog() { const overlay = document.getElementById("copy-dialog-overlay"); if (overlay) overlay.remove(); if (copyDialogKeyHandler) { document.removeEventListener("keydown", copyDialogKeyHandler); copyDialogKeyHandler = null; } } function showCopyDialog(text, title) { closeCopyDialog(); const overlay = document.createElement("div"); overlay.id = "copy-dialog-overlay"; overlay.className = "copy-dialog-overlay"; overlay.innerHTML = ` `; document.body.appendChild(overlay); const textarea = overlay.querySelector(".copy-dialog-textarea"); textarea.value = text; selectTextarea(textarea); let copied = false; try { copied = document.execCommand("copy"); } catch (err) { console.warn("execCommand copy in dialog failed.", err); } if (copied) { setCopyStatus("HTML-Quelltext kopiert."); } else { setCopyStatus("Bitte im Fenster Strg+C drücken.", true); } overlay.querySelector("#copy-dialog-close-btn").addEventListener("click", closeCopyDialog); overlay.querySelector("#copy-dialog-select-btn").addEventListener("click", () => selectTextarea(textarea)); overlay.addEventListener("click", (event) => { if (event.target === overlay) closeCopyDialog(); }); copyDialogKeyHandler = (event) => { if (event.key === "Escape") closeCopyDialog(); }; document.addEventListener("keydown", copyDialogKeyHandler); } function copyViaIframe(html) { const iframe = document.createElement("iframe"); iframe.setAttribute("aria-hidden", "true"); iframe.style.cssText = "position:fixed;width:700px;height:500px;left:-9999px;top:0;border:none;"; document.body.appendChild(iframe); const doc = iframe.contentWindow.document; doc.open(); doc.write(html); doc.close(); const range = doc.createRange(); range.selectNodeContents(doc.body); const selection = iframe.contentWindow.getSelection(); selection.removeAllRanges(); selection.addRange(range); let ok = false; try { ok = doc.execCommand("copy"); } catch (err) { console.warn("execCommand copy via iframe failed.", err); } finally { selection.removeAllRanges(); document.body.removeChild(iframe); } return ok; } function copyHtmlWithFallback(html) { if (copyViaIframe(html)) return true; const container = document.createElement("div"); container.innerHTML = html; container.contentEditable = "true"; container.setAttribute("aria-hidden", "true"); container.style.cssText = "position:fixed;left:0;top:0;width:700px;max-height:1px;overflow:hidden;opacity:0;pointer-events:none;background:#FFFFFF;"; document.body.appendChild(container); const range = document.createRange(); range.selectNodeContents(container); const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); let ok = false; try { ok = document.execCommand("copy"); } catch (err) { console.warn("execCommand HTML copy failed.", err); } finally { selection.removeAllRanges(); document.body.removeChild(container); } return ok; } async function copyForOutlook() { const html = getNewsletterOutlookHtml(); const plain = getNewsletterPlain(); if (!html) { setCopyStatus("Bitte zuerst einen Newsletter generieren.", true); return; } if (copyHtmlWithFallback(html)) { setCopyStatus("Newsletter kopiert. In Outlook: Strg+V, dann ggf. Rechtsklick → „Format beibehalten“."); return; } const cfHtml = wrapCfHtml(html); if (navigator.clipboard && window.ClipboardItem && window.isSecureContext) { try { await navigator.clipboard.write([ new ClipboardItem({ "text/html": new Blob([cfHtml], { type: "text/html" }), "text/plain": new Blob([plain], { type: "text/plain" }), }), ]); setCopyStatus("Newsletter kopiert. In Outlook: Strg+V, dann ggf. Rechtsklick → „Format beibehalten“."); return; } catch (err) { console.warn("Clipboard API HTML copy failed.", err); } } setCopyStatus("Bitte „Im Browser öffnen“ nutzen und dort Strg+A → Strg+C.", true); } function copyHtmlSource() { const html = getNewsletterHtml(); if (!html) { setCopyStatus("Bitte zuerst einen Newsletter generieren.", true); return; } showCopyDialog(html, "HTML-Quelltext kopieren"); } function downloadHtml() { const html = getNewsletterOutlookHtml(); if (!html) { setCopyStatus("Bitte zuerst einen Newsletter generieren.", true); return; } downloadFile(html, `wiki-newsletter-${new Date().toISOString().slice(0, 10)}.html`, "text/html;charset=utf-8"); setCopyStatus("Outlook-HTML heruntergeladen."); } function downloadHtmlSource() { const html = getNewsletterHtml(); if (!html) { setCopyStatus("Bitte zuerst einen Newsletter generieren.", true); return; } downloadFile(html, `wiki-newsletter-source-${new Date().toISOString().slice(0, 10)}.html`, "text/html;charset=utf-8"); setCopyStatus("HTML-Quelltext heruntergeladen."); } function downloadFile(content, filename, mimeType) { const blob = new Blob([content], { type: mimeType }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); } function openOutlookInBrowser() { const html = getNewsletterOutlookHtml(); if (!html) { setCopyStatus("Bitte zuerst einen Newsletter generieren.", true); return; } const popup = window.open("", "_blank"); if (!popup) { setCopyStatus("Pop-up blockiert. Bitte Pop-ups erlauben oder „HTML herunterladen“ nutzen.", true); return; } popup.document.open(); popup.document.write(html); popup.document.close(); popup.document.title = "Wiki-Newsletter – zum Kopieren"; setCopyStatus("Newsletter im Browser geöffnet: Strg+A → Strg+C, dann in Outlook einfügen."); } document.addEventListener("DOMContentLoaded", () => { const outlookSource = document.getElementById("newsletter-outlook-source"); const htmlSource = document.getElementById("newsletter-html-source"); const frame = document.getElementById("newsletter-preview-frame"); const previewHtml = outlookSource?.value.trim() || htmlSource?.value.trim() || ""; if (frame && previewHtml) { frame.srcdoc = previewHtml; } const htmlDisplay = document.getElementById("newsletter-html-display"); htmlDisplay?.addEventListener("focus", () => selectTextarea(htmlDisplay)); htmlDisplay?.addEventListener("click", () => selectTextarea(htmlDisplay)); document.getElementById("copy-outlook-btn")?.addEventListener("click", copyForOutlook); document.getElementById("open-outlook-btn")?.addEventListener("click", openOutlookInBrowser); document.getElementById("copy-html-source-btn")?.addEventListener("click", copyHtmlSource); document.getElementById("copy-html-source-btn-2")?.addEventListener("click", copyHtmlSource); document.getElementById("download-html-btn")?.addEventListener("click", downloadHtml); document.getElementById("download-html-source-btn")?.addEventListener("click", downloadHtmlSource); });