/** * Briefvorlage-Editor: Minimal-WYSIWYG + Vorschau-Panel + Vorlagen-Loader * für Django Admin – keine externen Abhängigkeiten. */ (function () { "use strict"; // Warte auf DOM document.addEventListener("DOMContentLoaded", function () { var textareas = document.querySelectorAll("textarea.briefvorlage-textarea"); textareas.forEach(function (textarea) { initEditor(textarea); }); }); function initEditor(textarea) { var wrapper = document.createElement("div"); wrapper.style.cssText = "border:1px solid #ccc;border-radius:4px;overflow:hidden;margin-top:4px;"; // ---- Toolbar ---- var toolbar = document.createElement("div"); toolbar.style.cssText = "background:#f5f5f5;border-bottom:1px solid #ccc;padding:5px 8px;display:flex;flex-wrap:wrap;gap:4px;align-items:center;"; var buttons = [ { label: "B", cmd: "bold", title: "Fett (Strg+B)", style: "font-weight:bold;" }, { label: "I", cmd: "italic", title: "Kursiv (Strg+I)", style: "font-style:italic;" }, { label: "U", cmd: "underline", title: "Unterstrichen (Strg+U)", style: "text-decoration:underline;" }, { label: "¶", cmd: "insertParagraph", title: "Absatz einfügen" }, { label: "• Liste", cmd: "insertUnorderedList", title: "Aufzählung" }, { label: "1. Liste", cmd: "insertOrderedList", title: "Nummerierte Liste" }, ]; buttons.forEach(function (b) { var btn = document.createElement("button"); btn.type = "button"; btn.title = b.title; btn.innerHTML = b.label; btn.style.cssText = "padding:3px 8px;cursor:pointer;border:1px solid #ccc;border-radius:3px;background:#fff;font-size:13px;" + (b.style || ""); btn.addEventListener("click", function (e) { e.preventDefault(); editor.focus(); document.execCommand(b.cmd, false, null); syncToTextarea(); }); toolbar.appendChild(btn); }); // Trennlinie var sep = document.createElement("span"); sep.style.cssText = "border-left:1px solid #ccc;height:20px;margin:0 4px;"; toolbar.appendChild(sep); // Tab-Buttons: Editor / HTML / Vorschau var tabEditor = createTabBtn("Editor", true); var tabHtml = createTabBtn("HTML", false); var tabVorschau = createTabBtn("Vorschau", false); toolbar.appendChild(tabEditor); toolbar.appendChild(tabHtml); toolbar.appendChild(tabVorschau); // Vorlage-Loader (nur wenn BriefVorlage-API verfügbar) var sep2 = document.createElement("span"); sep2.style.cssText = "border-left:1px solid #ccc;height:20px;margin:0 4px;"; toolbar.appendChild(sep2); var vorlagenSelect = document.createElement("select"); vorlagenSelect.style.cssText = "font-size:12px;padding:2px 6px;border:1px solid #ccc;border-radius:3px;max-width:200px;"; var defaultOption = document.createElement("option"); defaultOption.value = ""; defaultOption.textContent = "– Vorlage laden –"; vorlagenSelect.appendChild(defaultOption); toolbar.appendChild(vorlagenSelect); // Vorlagen asynchron laden loadVorlagen(vorlagenSelect); var ladeBtn = document.createElement("button"); ladeBtn.type = "button"; ladeBtn.textContent = "Laden"; ladeBtn.title = "Ausgewählte Vorlage in den Editor laden"; ladeBtn.style.cssText = "padding:3px 8px;cursor:pointer;border:1px solid #0d6efd;border-radius:3px;background:#0d6efd;color:#fff;font-size:12px;"; ladeBtn.addEventListener("click", function (e) { e.preventDefault(); var val = vorlagenSelect.value; if (!val) return; var opt = vorlagenSelect.querySelector("option[value='" + val + "']"); if (!opt) return; var html = opt.dataset.briefvorlage || ""; var betreff = opt.dataset.betreff || ""; if (confirm("Vorlage \"" + opt.textContent + "\" laden?\nDer aktuelle Brieftext wird überschrieben.")) { editor.innerHTML = html; textarea.value = html; // Betreff-Feld befüllen falls vorhanden und nicht leer if (betreff) { var betreffField = document.getElementById("id_betreff"); if (betreffField && !betreffField.value) { betreffField.value = betreff; } } updatePreview(); } }); toolbar.appendChild(ladeBtn); // ---- Editor-Div (WYSIWYG) ---- var editor = document.createElement("div"); editor.contentEditable = "true"; editor.style.cssText = "min-height:300px;padding:12px;font-family:Times New Roman,serif;font-size:11pt;line-height:1.4;outline:none;background:#fff;"; editor.innerHTML = textarea.value; editor.addEventListener("input", syncToTextarea); editor.addEventListener("keyup", syncToTextarea); // ---- HTML-Textarea (Quelltext) ---- textarea.style.cssText += "display:none;width:100%;box-sizing:border-box;border:none;padding:12px;font-family:monospace;font-size:13px;"; textarea.addEventListener("input", function () { editor.innerHTML = textarea.value; updatePreview(); }); // ---- Vorschau-Panel ---- var preview = document.createElement("div"); preview.style.cssText = "display:none;min-height:300px;padding:12px;background:#fff;font-family:'Times New Roman',serif;font-size:11pt;line-height:1.4;"; // Tab-Logik function showTab(which) { editor.style.display = "none"; textarea.style.display = "none"; preview.style.display = "none"; tabEditor.style.background = "#f5f5f5"; tabHtml.style.background = "#f5f5f5"; tabVorschau.style.background = "#f5f5f5"; if (which === "editor") { editor.style.display = "block"; tabEditor.style.background = "#fff"; tabEditor.style.fontWeight = "bold"; } else if (which === "html") { textarea.style.display = "block"; tabHtml.style.background = "#fff"; tabHtml.style.fontWeight = "bold"; } else { preview.style.display = "block"; tabVorschau.style.background = "#fff"; tabVorschau.style.fontWeight = "bold"; updatePreview(); } } tabEditor.addEventListener("click", function (e) { e.preventDefault(); showTab("editor"); }); tabHtml.addEventListener("click", function (e) { e.preventDefault(); syncToTextarea(); showTab("html"); }); tabVorschau.addEventListener("click", function (e) { e.preventDefault(); showTab("vorschau"); }); // Zusammenbauen wrapper.appendChild(toolbar); wrapper.appendChild(editor); wrapper.appendChild(preview); // Textarea hinter Editor platzieren textarea.parentNode.insertBefore(wrapper, textarea); wrapper.appendChild(textarea); // Initial: Editor-Tab aktiv showTab("editor"); // ---- Hilfsfunktionen ---- function syncToTextarea() { textarea.value = editor.innerHTML; } function updatePreview() { // Platzhalter durch Beispielwerte ersetzen für Vorschau var html = textarea.value; var replacements = { "{{ anrede }}": "Frau", "{{ vorname }}": "Maria", "{{ nachname }}": "Mustermann", "{{ strasse }}": "Musterstraße 12", "{{ plz }}": "46499", "{{ ort }}": "Hamminkeln", "{{ datum }}": "Freitag, 17. April 2026", "{{ uhrzeit }}": "19:00 Uhr", "{{ veranstaltungsort }}": "Marienthaler Gasthof", "{{ gasthaus_adresse }}": "Pastor-Winkelmann-Str. 2, 46499 Hamminkeln", }; for (var key in replacements) { html = html.split(key).join(replacements[key]); } preview.innerHTML = html || "Kein Brieftext eingegeben."; } function createTabBtn(label, active) { var btn = document.createElement("button"); btn.type = "button"; btn.textContent = label; btn.style.cssText = "padding:3px 10px;cursor:pointer;border:1px solid #ccc;border-radius:3px;font-size:12px;background:" + (active ? "#fff" : "#f5f5f5") + ";"; if (active) btn.style.fontWeight = "bold"; return btn; } } function loadVorlagen(selectEl) { // Lese Vorlagen über einfachen Admin-API-Aufruf fetch("/admin/stiftung/briefvorlage/?format=json", { headers: { "X-Requested-With": "XMLHttpRequest" } }) .then(function (r) { return r.ok ? r.json() : null; }) .then(function (data) { if (!data || !data.results) return; data.results.forEach(function (v) { var opt = document.createElement("option"); opt.value = v.id || v.pk; opt.textContent = v.name || v.fields && v.fields.name; opt.dataset.briefvorlage = v.briefvorlage || v.fields && v.fields.briefvorlage || ""; opt.dataset.betreff = v.betreff || v.fields && v.fields.betreff || ""; selectEl.appendChild(opt); }); }) .catch(function () { // Kein API-Endpunkt – Vorlage-Loader deaktivieren }); // Alternativ: REST-API fetch("/api/v1/briefvorlagen/", { headers: { "X-Requested-With": "XMLHttpRequest" } }) .then(function (r) { return r.ok ? r.json() : null; }) .then(function (data) { if (!data) return; var results = Array.isArray(data) ? data : data.results; if (!results) return; // Bereits vorhandene Optionen nicht doppeln var existing = Array.from(selectEl.options).map(function (o) { return o.value; }); results.forEach(function (v) { var id = String(v.id || v.pk || ""); if (existing.includes(id)) return; var opt = document.createElement("option"); opt.value = id; opt.textContent = v.name; opt.dataset.briefvorlage = v.briefvorlage || ""; opt.dataset.betreff = v.betreff || ""; selectEl.appendChild(opt); }); }) .catch(function () { /* kein REST-Endpunkt */ }); } })();