140 lines
5.5 KiB
JavaScript
140 lines
5.5 KiB
JavaScript
//console.log("editor.js wurde erfolgreich geladen!");
|
||
|
||
function initEditor() {
|
||
const form = document.getElementById("editor-form");
|
||
|
||
if (!form) {
|
||
console.error("editor.js abgebrochen: Formular nicht gefunden!");
|
||
return;
|
||
} else {
|
||
console.log("Formular gefunden und editor.js initialisiert:", form);
|
||
}
|
||
|
||
const container = document.getElementById("block-container");
|
||
const plusButton = document.getElementById("plus-button");
|
||
const popup = document.getElementById("block-popup");
|
||
|
||
// Fortlaufender Zähler für eindeutige Block-Indizes. Wird nie wiederverwendet
|
||
// (auch nicht nach dem Löschen eines Blocks), damit sich neue und übrig
|
||
// gebliebene Blöcke beim finalen Submit nie einen Namen teilen.
|
||
let blockIndex = container.querySelectorAll(".editor-block").length;
|
||
|
||
// Ohne JS sind Textblock-/Bild-Button im Pop-up immer sichtbar und ganz normale
|
||
// Submit-Buttons. Erst mit JS blenden wir das Pop-up standardmäßig aus und
|
||
// steuern die Sichtbarkeit über den Plus-Button.
|
||
popup.classList.add("hidden");
|
||
|
||
plusButton.addEventListener("click", () => {
|
||
popup.classList.toggle("hidden");
|
||
});
|
||
|
||
// Klick auf "Textblock" / "Bild einfügen": lokal im DOM anlegen statt zum
|
||
// Server zu submitten (entlastet den Server, kein Page-Reload nötig).
|
||
popup.querySelectorAll('[name="editor_action"]').forEach(btn => {
|
||
btn.addEventListener("click", function (e) {
|
||
e.preventDefault();
|
||
const type = this.getAttribute("data-type");
|
||
addBlockElement(type, "");
|
||
popup.classList.add("hidden");
|
||
});
|
||
});
|
||
|
||
// Bereits vom Server gerenderte Blöcke (z.B. beim Bearbeiten eines bestehenden
|
||
// Artikels oder nach einem Validierungsfehler) ebenfalls mit JS-Verhalten ausstatten.
|
||
container.querySelectorAll(".editor-block").forEach(blockDiv => {
|
||
bindDeleteButton(blockDiv);
|
||
bindImageInput(blockDiv);
|
||
});
|
||
|
||
// Erstellt einen neuen Block inkl. echter, benannter Formularfelder
|
||
// (blocks[i][type], blocks[i][text] bzw. blocks[i][image]). Diese Felder werden
|
||
// beim finalen Submit ganz normal vom Browser als multipart/form-data verschickt –
|
||
// es ist kein manuelles Zusammenbauen von JSON beim Absenden mehr nötig.
|
||
function addBlockElement(type, value = "") {
|
||
const index = blockIndex++;
|
||
|
||
const blockDiv = document.createElement("div");
|
||
blockDiv.classList.add("editor-block", "article-editor-scope");
|
||
blockDiv.setAttribute("data-index", String(index));
|
||
|
||
const typeInput = document.createElement("input");
|
||
typeInput.type = "hidden";
|
||
typeInput.name = `blocks[${index}][type]`;
|
||
typeInput.value = type;
|
||
blockDiv.appendChild(typeInput);
|
||
|
||
if (type === "text") {
|
||
const textarea = document.createElement("textarea");
|
||
textarea.name = `blocks[${index}][text]`;
|
||
textarea.placeholder = "Schreibe deinen Textblock...";
|
||
textarea.value = value;
|
||
blockDiv.appendChild(textarea);
|
||
} else if (type === "image") {
|
||
const fileInput = document.createElement("input");
|
||
fileInput.type = "file";
|
||
fileInput.name = `blocks[${index}][image]`;
|
||
fileInput.accept = "image/*";
|
||
blockDiv.appendChild(fileInput);
|
||
}
|
||
|
||
const deleteBtn = document.createElement("button");
|
||
deleteBtn.type = "submit";
|
||
deleteBtn.name = "editor_action";
|
||
deleteBtn.value = `delete_block:${index}`;
|
||
deleteBtn.classList.add("delete-block-btn");
|
||
deleteBtn.innerHTML = "✕";
|
||
blockDiv.appendChild(deleteBtn);
|
||
|
||
container.appendChild(blockDiv);
|
||
|
||
bindDeleteButton(blockDiv);
|
||
bindImageInput(blockDiv);
|
||
}
|
||
|
||
// Löschen-Button eines Blocks lokal abfangen: entfernt den Block nur aus dem DOM,
|
||
// statt das Formular zum Server zu senden.
|
||
function bindDeleteButton(blockDiv) {
|
||
const btn = blockDiv.querySelector(".delete-block-btn");
|
||
if (!btn) return;
|
||
btn.addEventListener("click", (e) => {
|
||
e.preventDefault();
|
||
blockDiv.remove();
|
||
});
|
||
}
|
||
|
||
// Zeigt bei Auswahl einer Bilddatei sofort eine Vorschau an. Rein optisch –
|
||
// der eigentliche Datei-Upload läuft nativ über das <input type="file">.
|
||
function bindImageInput(blockDiv) {
|
||
const fileInput = blockDiv.querySelector('input[type="file"]');
|
||
if (!fileInput) return;
|
||
|
||
let imgPreview = blockDiv.querySelector(".block-image-preview");
|
||
if (!imgPreview) {
|
||
imgPreview = document.createElement("img");
|
||
imgPreview.classList.add("block-image-preview");
|
||
imgPreview.style.maxWidth = "200px";
|
||
imgPreview.style.display = "none";
|
||
imgPreview.style.marginTop = "10px";
|
||
blockDiv.insertBefore(imgPreview, fileInput.nextSibling);
|
||
}
|
||
|
||
fileInput.addEventListener("change", function () {
|
||
if (this.files && this.files[0]) {
|
||
const reader = new FileReader();
|
||
reader.onload = (e) => {
|
||
imgPreview.src = e.target.result;
|
||
imgPreview.style.display = "block";
|
||
};
|
||
reader.readAsDataURL(this.files[0]);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// SICHERER START: Prüft, ob das HTML bereits bereit ist
|
||
if (document.readyState === "loading") {
|
||
document.addEventListener("DOMContentLoaded", initEditor);
|
||
} else {
|
||
// Falls das DOM schon fertig geladen ist, führen wir es direkt aus
|
||
initEditor();
|
||
} |