This commit is contained in:
2026-01-21 00:15:32 +01:00
parent 9e67b8e30f
commit 5e76717a93
2 changed files with 70 additions and 26 deletions

View File

@@ -15,7 +15,6 @@ export function initEditor() {
const btnClear = document.getElementById('btn-clear-main');
const editorSelect = document.getElementById('editorTypeSelect');
const versionSelect = document.getElementById('versionSelect');
const btnRestoreVersion = document.getElementById('btn-restore-version');
const craftEditor = initCraftEditor();
  const prevDlg      = document.getElementById('previewDialog');
@@ -44,6 +43,9 @@ export function initEditor() {
let versionItems = [];
let savedSnapshot = '';
let lastVersionSelection = '';
let isDirty = false;
let dirtyCleanup = null;
let dialogCancelBound = false;
  const ok  = (m) => toast(m, true);
  const err = (m) => toast(m, false);
@@ -88,7 +90,7 @@ export function initEditor() {
function setVersionUiVisible(show) {
if (versionSelect) versionSelect.classList.toggle('hidden', !show);
if (btnRestoreVersion) btnRestoreVersion.classList.toggle('hidden', !show);
// restore button removed
}
setVersionUiVisible(false);
@@ -117,6 +119,38 @@ export function initEditor() {
};
}
function markDirty() {
isDirty = true;
}
function clearDirty() {
isDirty = false;
}
function attachGjsDirtyTracker(editor) {
if (!editor || typeof editor.on !== 'function') return () => {};
const onUpdate = () => markDirty();
editor.on('update', onUpdate);
editor.on('component:update', onUpdate);
return () => {
try {
editor.off('update', onUpdate);
editor.off('component:update', onUpdate);
} catch {}
};
}
function attachCraftDirtyTracker() {
const host = document.getElementById('craftEditor');
if (!host) return () => {};
const handler = () => markDirty();
const events = ['input', 'keydown', 'paste', 'drop'];
events.forEach(evt => host.addEventListener(evt, handler, true));
return () => {
events.forEach(evt => host.removeEventListener(evt, handler, true));
};
}
async function buildCurrentSnapshot() {
if (currentEditorType === 'craftjs') {
return buildSnapshot({
@@ -150,9 +184,11 @@ export function initEditor() {
function setSavedSnapshotFromData(payload) {
const fields = extractContentFields(payload);
savedSnapshot = buildSnapshot(fields);
clearDirty();
}
async function hasUnsavedChanges() {
if (isDirty) return true;
if (!savedSnapshot) return false;
const currentSnapshot = await buildCurrentSnapshot();
return currentSnapshot !== savedSnapshot;
@@ -245,6 +281,8 @@ export function initEditor() {
if (targetType === 'craftjs') {
craftEditor?.setContent(data.html || '', data.craftJson || '');
setSavedSnapshotFromData(payload);
if (dirtyCleanup) dirtyCleanup();
dirtyCleanup = attachCraftDirtyTracker();
return;
}
const editor = await waitForEditor(3000);
@@ -254,12 +292,16 @@ export function initEditor() {
const project = JSON.parse(jsonRaw);
editor.loadProjectData(project);
setSavedSnapshotFromData(payload);
if (dirtyCleanup) dirtyCleanup();
dirtyCleanup = attachGjsDirtyTracker(editor);
return;
} catch {}
}
const html = normalizeSnapshotValue(data.html);
editor.setComponents(html);
setSavedSnapshotFromData(payload);
if (dirtyCleanup) dirtyCleanup();
dirtyCleanup = attachGjsDirtyTracker(editor);
}
async function loadLatestContentFromServer() {
@@ -553,6 +595,8 @@ export function initEditor() {
if (editorType === 'craftjs') {
const craftHtml = extractCraftHtml(craftJson, fresh);
craftEditor?.setContent(craftHtml, craftJson);
if (dirtyCleanup) dirtyCleanup();
dirtyCleanup = attachCraftDirtyTracker();
hideVeil();
if (dlg && typeof dlg.showModal === 'function') dlg.showModal();
if (!looksCraftSerialized(craftJson) && craftEditor?.serializeFromHtml) {
@@ -633,10 +677,24 @@ export function initEditor() {
};
    // Jetzt den Editor-Core laden (erst NACH about:blank)
    iframe.src = `editor/editor-core.php?mode=${encodeURIComponent(current.section.slug)}&id=${current.id}&section_id=${current.section.id}&t=${Date.now()}`;
iframe.src = `editor/editor-core.php?mode=${encodeURIComponent(current.section.slug)}&id=${current.id}&section_id=${current.section.id}&t=${Date.now()}`;
    dlg?.showModal?.();
  }
dlg?.showModal?.();
if (dlg && !dialogCancelBound) {
dlg.addEventListener('cancel', async (ev) => {
ev.preventDefault();
await close();
});
dialogCancelBound = true;
}
waitForEditor(6000)
.then((ed) => {
if (dirtyCleanup) dirtyCleanup();
dirtyCleanup = attachGjsDirtyTracker(ed);
})
.catch(() => {});
}
// ---------- Speichern (DELEGIERT) ----------
// 🚨 KORRIGIERT: Delegiert Speichern an den iFrame, der die JSON-Daten holt!
@@ -740,6 +798,8 @@ export function initEditor() {
async function close() {
const decision = await confirmUnsavedChanges();
if (decision === 'cancel') return;
if (dirtyCleanup) dirtyCleanup();
dirtyCleanup = null;
// nächstes Öffnen invalidiert laufende asyncs
reqToken++;
@@ -754,9 +814,10 @@ export function initEditor() {
    iframe.src = 'about:blank#' + Date.now();
    // Kontext leeren
    current = null;
    window.__currentItemId = undefined;
    window.__currentEditorCtx = undefined;
current = null;
window.__currentItemId = undefined;
window.__currentEditorCtx = undefined;
clearDirty();
}
async function switchEditor(nextType) {
@@ -841,23 +902,7 @@ export function initEditor() {
versionSelect.value = previousSelection;
}
});
btnRestoreVersion && (btnRestoreVersion.onclick = async () => {
if (!current?.id) return;
const versionId = Number(versionSelect?.value || 0);
if (!versionId) {
err('Bitte eine Version auswählen');
return;
}
if (!confirm('Version wiederherstellen? Der aktuelle Stand wird überschrieben.')) return;
try {
const res = await apiAction('content_versions.restore', { method: 'POST', data: { id: versionId, content_id: current.id } });
if (!res?.ok) throw new Error(res?.error || 'Wiederherstellen fehlgeschlagen');
ok('Version wiederhergestellt');
await open({ id: current.id, name: current.name, section: current.section }, null, current.section);
} catch (e) {
err(e.message || 'Wiederherstellen fehlgeschlagen');
}
});
// restore button removed
window.AdminTestSend = window.AdminTestSend || {};
window.AdminTestSend.open = (opts = {}) => {