Re: Mobirise CMS 4.0
Verfasst: So 21. Jun 2026, 09:11
Code: Alles auswählen
// ===== cms-core =====
// Geschrieben von den KI´s Gemini, Claude und Chat GPT
// Idee und Autor: Volker Niederastroth
// Version vom 21.06.26 09:11 Uhr
document.execCommand("defaultParagraphSeparator", false, "p");
// Verhindert, dass eingebettete Videos (YouTube/Vimeo-iframes) beim Zurück-Navigieren
// einfach weiterlaufen, weil der Browser die Seite aus dem Back-Forward-Cache (bfcache)
// im exakt eingefrorenen Zustand wiederherstellt (inkl. "läuft gerade").
window.addEventListener('pageshow', function(e) {
if (e.persisted) {
document.querySelectorAll('iframe').forEach(function(f) {
var src = f.getAttribute('src');
if (src && (src.includes('youtube.com') || src.includes('youtu.be') || src.includes('vimeo.com'))) {
f.setAttribute('src', 'about:blank');
setTimeout(function(){ f.setAttribute('src', src); }, 10); // about:blank zwingt echten Reload, statt no-op bei identischem src
}
});
}
});
function formatText(cmd) { document.execCommand(cmd, false, null); }
function formatTextColor(color) { document.execCommand('foreColor', false, color); }
function formatTextSize(size) { if(size) document.execCommand('fontSize', false, size); }
function clearTextFormatAndClose() { document.querySelector('#text-editor-tools').style.display = 'none'; window.getSelection().removeAllRanges(); }
window.setAllVideosAutoplay = function(state) {
document.querySelectorAll('video').forEach(function(v) {
if (state) {
v.setAttribute('autoplay', 'autoplay');
v.setAttribute('muted', 'muted');
v.setAttribute('loop', 'loop');
v.removeAttribute('controls');
v.muted = true;
v.play().catch(function(){});
} else {
v.removeAttribute('autoplay');
v.removeAttribute('muted');
v.removeAttribute('loop');
v.setAttribute('controls', 'controls');
v.muted = false;
v.pause();
}
});
alert('Autoplay für alle Videos: ' + (state ? 'AN' : 'AUS'));
};
window.repairNestedEditables = function() {
var count = 0;
document.querySelectorAll('[data-auto-id]').forEach(function(el) {
var nested = el.querySelectorAll('[contenteditable]');
if (nested.length > 1) {
// Tiefstes verschachteltes Element finden -> dessen Inhalt ist der "echte" Text
var deepest = nested[nested.length - 1];
var innerHtml = deepest.innerHTML;
var first = nested[0];
first.outerHTML = innerHtml; // alle verschachtelten Wrapper entfernen, nur Inhalt behalten
count++;
}
});
alert(count + ' Element(e) bereinigt. Jetzt bitte auf "💾 Diese Seite speichern" klicken, um den sauberen Zustand zu sichern.');
};
function addTextLink() {
var sel = window.getSelection(); if (!sel.toString().trim()) return;
var anchorA = sel.anchorNode.parentNode.closest('a');
var cur = anchorA ? anchorA.getAttribute('href') : "";
var url = prompt("Ziel-URL:", cur || "");
if (url && url.trim() !== "") {
var finalUrl = url.trim();
if (/^www\./i.test(finalUrl)) { finalUrl = "https://" + finalUrl; }
else if (!/^https?:\/\//i.test(finalUrl) && !/^\//.test(finalUrl) && !/^#/.test(finalUrl) && !/\.html$/i.test(finalUrl)) { finalUrl = "https://" + finalUrl; }
document.execCommand("createLink", false, finalUrl);
var newA = sel.anchorNode.parentNode.closest('a');
if (newA) {
newA.classList.add('text-primary');
var t = prompt("In neuem Tab öffnen?\n'y' = JA | 'n' = NEIN:", newA.getAttribute('target') === '_blank' ? 'y' : 'n');
if (t !== null) { if (t.toLowerCase() === 'y') newA.setAttribute('target', '_blank'); else newA.removeAttribute('target'); }
}
}
}
document.addEventListener("DOMContentLoaded", function() {
try { initCMS(); } catch(e) { console.error("CMS Startfehler:", e); document.documentElement.classList.add('cms-ready'); }
});
function initCMS() {
var handlerUrl = 'save_content.php';
var overlay = document.querySelector('.global-admin-overlay');
var closeBtn = document.querySelector('.global-admin-close');
var loginForm = document.querySelector('#global-login-form');
var loginFormEl = document.querySelector('#volkers-ajax-login-form');
var loginPw = document.querySelector('#global-admin-password');
var panel = document.querySelector('#global-admin-panel');
var saveBtn = document.querySelector('#global-save-btn');
var logoutBtn = document.querySelector('#global-logout-btn');
var toolbar = document.querySelector('#text-editor-tools');
var fileUploader = document.querySelector('#cms-file-uploader');
var blockColorpicker = document.querySelector('#cms-block-colorpicker');
var backupList = document.querySelector('#global-backup-list');
var restoreBtn = document.querySelector('#global-restore-btn');
var stickySelect = document.querySelector('#global-menu-sticky-select');
var navColorsSection = document.getElementById('cms-nav-colors-section');
var navLinkColorPicker = document.getElementById('cms-nav-link-color');
var navBtnColorPicker = document.getElementById('cms-nav-btn-color');
var navBrandColorPicker = document.getElementById('cms-nav-brand-color');
var navBgColorPicker = document.getElementById('cms-nav-bg-color');
var overlayPanel = document.getElementById('cms-overlay-panel');
var overlayColorPicker = document.getElementById('cms-overlay-color-picker');
var overlayOpacityRange = document.getElementById('cms-overlay-opacity-range');
var overlayOpacityLabel = document.getElementById('cms-overlay-opacity-label');
var overlayApplyBtn = document.getElementById('cms-overlay-apply');
var overlayCloseBtn = document.getElementById('cms-overlay-close');
var pageName = window.location.pathname.split("/").pop().replace(".html", "") || "index";
var jsonUrl = 'content_' + pageName + '.json';
var jsonGlobalUrl = 'content_global_nav.json';
var jsonFooterUrl = 'content_global_footer.json';
var editableTexts = []; var editableMedias = []; var editableLinks = []; var editableColors = [];
var isAdminActive = false; var activeMediaElementToUpload = null; var activeSectionToColor = null;
var menuElementNode = null;
var activeOverlaySection = null;
// ===== DOWNGRADE-SCHALTER =====
// false = Menü & Footer sind NICHT editierbar (kein Text, kein Bild, keine Farbe, keine Menüfarben-Buttons)
// true = volle Funktion wie bisher
var ALLOW_MENU_FOOTER_EDIT = true;
var sections = document.querySelectorAll('section, nav, footer');
sections.forEach(function(section, sectionIndex) {
if (!section) return;
var isMenu = (sectionIndex === 0 || section.tagName === 'NAV' || section.classList.contains('menu') || section.classList.contains('navbar'));
var isFooter = (sectionIndex === (sections.length - 1) || section.tagName === 'FOOTER' || section.classList.contains('footer'));
// Downgrade: Menü/Footer-Sektion komplett überspringen -> keine Buttons, kein editierbarer Text/Bild/Farbe
if (!ALLOW_MENU_FOOTER_EDIT && (isMenu || isFooter)) {
if (isMenu) menuElementNode = section; // bleibt für Sticky-Logik nötig, aber ohne Edit-Buttons
return;
}
var scopeType = 'page'; var scopeKey = 'block_' + sectionIndex + '_';
if (isMenu) { scopeType = 'menu'; scopeKey = 'global_nav_'; menuElementNode = section; }
else if (isFooter) { scopeType = 'footer'; scopeKey = 'global_footer_'; }
section.setAttribute('data-block-idx', sectionIndex);
section.classList.add('cms-bg-section-active');
var controlContainer = document.createElement('div');
controlContainer.className = 'cms-control-container';
section.appendChild(controlContainer);
var computedBg = window.getComputedStyle(section).backgroundImage;
var hasBgImage = (computedBg && computedBg !== 'none' && computedBg.includes('url('));
if (hasBgImage) {
var cleanUrl = computedBg.replace(/url\(['"]?(.*?)['"]?\)/, '$1');
section.setAttribute('data-original-bg', cleanUrl);
var editBgBtn = document.createElement('button'); editBgBtn.type = 'button'; editBgBtn.className = 'cms-bg-edit-trigger'; editBgBtn.innerText = '🖼️ Bild';
editBgBtn.setAttribute('data-bg-id', scopeKey + 'bg_image'); editBgBtn.setAttribute('data-cms-scope', scopeType);
controlContainer.appendChild(editBgBtn);
editableMedias.push({ el: editBgBtn, type: 'section-bg', parentSection: section });
}
var editColorBtn = document.createElement('button'); editColorBtn.type = 'button'; editColorBtn.className = 'cms-color-edit-trigger'; editColorBtn.innerText = '🎨 Farbe';
editColorBtn.setAttribute('data-color-id', scopeKey + 'bg_color'); editColorBtn.setAttribute('data-cms-scope', scopeType);
controlContainer.appendChild(editColorBtn);
editableColors.push({ el: editColorBtn, parentSection: section });
var textSelectors = ['.mbr-section-title','.mbr-section-subtitle','.card-title','.panel-title-edit','.item-title','.item-title a','.item-subtitle','.icon-title','.icon-text','.mbr-text','p'];
textSelectors.forEach(function(selector) {
section.querySelectorAll(selector).forEach(function(el, elIndex) {
if (!el || el.hasAttribute('data-auto-id') || el.tagName === 'IMG' || el.tagName === 'VIDEO' || el.tagName === 'IFRAME') return;
el.setAttribute('data-auto-id', scopeKey + selector.replace('.','').replace(' ','_') + '_' + elIndex);
el.setAttribute('data-cms-scope', scopeType); editableTexts.push(el);
});
});
section.querySelectorAll('.btn, .nav-link, .navbar-brand, .navbar-logo a, .navbar-caption, footer a').forEach(function(link, linkIndex) {
if (!link || link.closest('.global-admin-overlay')) return;
link.setAttribute('data-link-id', scopeKey + 'link_' + linkIndex);
link.setAttribute('data-cms-scope', scopeType); editableLinks.push(link);
});
section.querySelectorAll('img, video, iframe, [data-bg-video]').forEach(function(media, mediaIndex) {
// Nur Bilder innerhalb der Modals/Lightbox komplett überspringen
if (media.closest('.modal.mbr-slider')) return;
var mediaType = media.tagName.toLowerCase();
if (media.hasAttribute('data-bg-video')) { mediaType = 'bg-video'; }
// Markierung für alle Nicht-Lightbox-Bilder
media.setAttribute('data-media-id', scopeKey + mediaType + '_' + mediaIndex);
media.setAttribute('data-cms-scope', scopeType);
// Jetzt wird jedes Bild/Video, das kein Lightbox-Slider-Element ist, editierbar gemacht
if (mediaType === 'video' || mediaType === 'iframe' || mediaType === 'bg-video') {
if (mediaType === 'video') { media.pause(); }
var wrapper = document.createElement('div'); wrapper.className = 'cms-video-wrapper';
media.parentNode.insertBefore(wrapper, media); wrapper.appendChild(media);
var shield = document.createElement('div'); shield.className = 'cms-video-shield';
shield.setAttribute('data-target-id', scopeKey + mediaType + '_' + mediaIndex); wrapper.appendChild(shield);
editableMedias.push({ el: media, shield: shield, type: mediaType });
} else {
// Hier fehlte in deiner Version evtl. die Zuweisung für normale Bilder
editableMedias.push({ el: media, type: mediaType });
}
});
});
// --- Lightbox-Galerie: Vorschaubild & Popup-Carousel-Bild koppeln ---
var lightboxPairs = new WeakMap(); // thumbnailImg -> carouselImg
(function setupLightboxPairs() {
document.querySelectorAll('.modal.mbr-slider').forEach(function(modal) {
var carousel = modal.querySelector('.carousel[id]');
if (!carousel) return;
var carouselId = carousel.id;
var slideImgs = carousel.querySelectorAll('.carousel-item img');
document.querySelectorAll(
'[data-bs-target="#' + carouselId + '"][data-slide-to], ' +
'[data-target="#' + carouselId + '"][data-slide-to]'
).forEach(function(thumb) {
if (thumb.tagName !== 'IMG') return;
var idx = parseInt(thumb.getAttribute('data-bs-slide-to') || thumb.getAttribute('data-slide-to'), 10);
var slideImg = slideImgs[idx];
if (slideImg) lightboxPairs.set(thumb, slideImg);
});
});
})();
// Setzt eine neue Bild-URL auf ein <img> und hält ein eventuell gekoppeltes Lightbox-Bild synchron.
// Sucht innerhalb eines Links/Textelements das tatsächliche Text-Element (span/b/strong/i),
// überspringt dabei aber Icon-Elemente (Mobirise-Iconfont, FontAwesome, Bootstrap-Icons etc.),
// damit Icons nicht versehentlich als "Text-Ziel" missbraucht werden (führte zu verschachtelten/doppelten Icons).
// Gibt null zurück, wenn kein eigener Text-Wrapper existiert (Text liegt dann direkt als Textknoten im Element).
function getTextEditTarget(el) {
var candidates = el.querySelectorAll('span, b, strong, i');
for (var i = 0; i < candidates.length; i++) {
var c = candidates[i];
if (/icon|mbri|fa-|bi-/i.test(c.className)) continue;
return c;
}
return null;
}
// Liest den sichtbaren Text eines Links aus, ohne Icon-Inhalte mitzunehmen.
function getLinkText(el) {
var t = getTextEditTarget(el);
if (t) return t.innerText;
var txt = '';
el.childNodes.forEach(function(n){ if (n.nodeType === 3) txt += n.textContent; });
return txt.trim();
}
// Setzt den sichtbaren Text eines Links, OHNE vorhandene Icon-Elemente zu entfernen.
function setLinkText(el, text) {
var t = getTextEditTarget(el);
if (t) { t.innerText = text; return; }
// Kein Text-Wrapper vorhanden: nur reine Textknoten entfernen/ersetzen, Icon-Elemente bleiben unangetastet.
Array.prototype.slice.call(el.childNodes).forEach(function(n){ if (n.nodeType === 3) el.removeChild(n); });
el.appendChild(document.createTextNode(text));
}
function setImageSrcSynced(imgEl, newSrc) {
if (!imgEl) return;
imgEl.setAttribute('src', newSrc);
var pairedImg = lightboxPairs.get(imgEl);
if (pairedImg) pairedImg.setAttribute('src', newSrc);
}
function loadJsonData(url) { return fetch(url + '?v=' + Date.now()).then(function(r){ if(r.ok) return r.json(); }).catch(function(){ return null; }); }
Promise.all([loadJsonData(jsonUrl), loadJsonData(jsonGlobalUrl), loadJsonData(jsonFooterUrl)]).then(function(results) {
var d = Object.assign({}, results[0]||{}, results[1]||{}, results[2]||{});
editableTexts.forEach(function(el) {
var key = el.getAttribute('data-auto-id');
if (d[key]) { el.innerHTML = d[key]; }
});
editableColors.forEach(function(item) {
if (d[item.el.getAttribute('data-color-id')]) item.parentSection.style.backgroundColor = d[item.el.getAttribute('data-color-id')];
});
if (menuElementNode) {
var sticky = d['cms_menu_sticky_style'] || 'sticky';
if (sticky === 'static') {
menuElementNode.style.setProperty('position', 'relative', 'important');
menuElementNode.style.setProperty('top', 'auto', 'important');
menuElementNode.classList.remove('cms-force-sticky', 'fixed-top', 'navbar-fixed-top');
menuElementNode.classList.add('cms-force-static');
if(stickySelect) stickySelect.value = 'static';
} else {
menuElementNode.style.setProperty('position', 'fixed', 'important');
menuElementNode.style.setProperty('top', '0', 'important');
menuElementNode.classList.remove('cms-force-static');
menuElementNode.classList.add('cms-force-sticky');
if(stickySelect) stickySelect.value = 'sticky';
}
}
if (menuElementNode) {
if (d['cms_nav_link_color']) {
menuElementNode.style.setProperty('--nav-link-color', d['cms_nav_link_color']);
}
if (d['cms_nav_btn_color']) {
menuElementNode.style.setProperty('--nav-btn-color', d['cms_nav_btn_color']);
}
if (d['cms_nav_brand_color']) {
menuElementNode.style.setProperty('--nav-brand-color', d['cms_nav_brand_color']);
}
if (d['cms_nav_bg_color']) {
menuElementNode.style.setProperty('--nav-bg-color', d['cms_nav_bg_color']);
}
}
editableMedias.forEach(function(item) {
if (item.type === 'section-bg') {
var key = item.el.getAttribute('data-bg-id');
var bgUrl = d[key] || item.parentSection.getAttribute('data-original-bg');
if (bgUrl) { item.parentSection.classList.add('cms-has-bg'); item.parentSection.style.setProperty('--cms-bg-image',"url('"+bgUrl+"')"); item.parentSection.style.backgroundImage='none'; }
var opVal = (d[key+'_opacity'] !== undefined) ? d[key+'_opacity'] : "1";
item.parentSection.style.setProperty('--cms-bg-opacity', opVal);
var idx = item.parentSection.getAttribute('data-block-idx');
if (d['overlay_color_'+idx]) { item.parentSection.style.setProperty('--cms-bg-overlay-color', d['overlay_color_'+idx]); item.parentSection.classList.add('cms-has-bg-overlay'); }
if (d['overlay_opacity_'+idx] !== undefined) { item.parentSection.style.setProperty('--cms-bg-overlay-opacity', d['overlay_opacity_'+idx]); }
}
else if (item.type === 'bg-video') { var key = item.el.getAttribute('data-media-id'); if(d[key]) item.el.setAttribute('data-bg-video', d[key]); }
else if (item.type === 'video') {
var key = item.el.getAttribute('data-media-id');
if (d[key] && typeof d[key] === 'object') {
item.el.setAttribute('src', d[key].src);
if (d[key].autoplay === true || d[key].autoplay === 'true') {
item.el.setAttribute('autoplay','autoplay'); item.el.setAttribute('muted','muted'); item.el.setAttribute('loop','loop'); item.el.removeAttribute('controls');
item.el.muted = true; setTimeout(function(){ item.el.play().catch(function(){}); }, 150);
} else {
item.el.removeAttribute('autoplay'); item.el.removeAttribute('muted'); item.el.removeAttribute('loop'); item.el.setAttribute('controls','controls'); item.el.muted = false;
item.el.pause(); // Stoppt ein bereits durch Race-Condition gestartetes Autoplay
}
} else if (d[key]) { item.el.setAttribute('src', d[key]); }
}
else { var key = item.el.getAttribute('data-media-id'); if(d[key]) setImageSrcSynced(item.el, d[key]); }
});
editableLinks.forEach(function(el) {
var key = el.getAttribute('data-link-id');
if (d[key]) {
if (d[key].url) el.setAttribute('href', d[key].url);
if (d[key].target) el.setAttribute('target', d[key].target); else el.removeAttribute('target');
if (!el.querySelector('img') && d[key].text) { setLinkText(el, d[key].text); }
}
});
document.documentElement.classList.add('cms-ready');
}).catch(function() { document.documentElement.classList.add('cms-ready'); });
if (stickySelect) {
stickySelect.addEventListener('change', function() {
if (!menuElementNode) return;
if (stickySelect.value === 'static') {
menuElementNode.classList.remove('cms-force-sticky'); menuElementNode.classList.add('cms-force-static');
menuElementNode.classList.remove('fixed-top','navbar-fixed-top');
} else {
menuElementNode.classList.remove('cms-force-static'); menuElementNode.classList.add('cms-force-sticky');
}
});
}
var tapCount = 0; var tapTimer = null;
document.addEventListener('touchstart', function(e) {
if (e.target.closest('.global-admin-box')) return;
tapCount++; clearTimeout(tapTimer);
if (tapCount === 4) { overlay.style.display = 'flex'; tapCount = 0; }
tapTimer = setTimeout(function(){ tapCount = 0; }, 1000);
});
document.addEventListener('dblclick', function(e) { if (e.ctrlKey && !e.target.closest('.global-admin-box')) { overlay.style.display = 'flex'; } });
closeBtn.addEventListener('click', function() { overlay.style.display = 'none'; });
if (loginFormEl) { loginFormEl.addEventListener('submit', function(e) { e.preventDefault(); executeLogin(); }); }
function executeLogin() {
fetch(handlerUrl, { method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded'}, body:'action=login&password='+encodeURIComponent(loginPw.value), credentials:'include' })
.then(function(r){ return r.json(); }).then(function(data) {
if (data && data.success) { loginForm.style.display='none'; panel.style.display='block'; isAdminActive=true; document.body.classList.add('admin-active'); enablePageEditing(true); fetchBackupList(); }
else { alert('Falsches Passwort!'); }
});
}
function fetchBackupList() {
fetch(handlerUrl, { method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded'}, body:'action=list_backups&prefix=content_'+pageName, credentials:'include' })
.then(function(r){ return r.json(); }).then(function(backups) {
backupList.innerHTML = '<option value="" selected disabled>Wähle ein Backup...</option>';
if (backups) backups.forEach(function(b){ var o=document.createElement('option'); o.value=b.file; o.innerText=b.label; backupList.appendChild(o); });
});
}
restoreBtn.addEventListener('click', function() {
if (!backupList.value || !confirm('Backup einspielen?')) return;
fetch(handlerUrl, { method:'POST', headers:{'Content-Type':'application/x-www-form-urlencoded'}, body:'action=restore_backup&backup_file='+encodeURIComponent(backupList.value), credentials:'include' })
.then(function(r){ return r.json(); }).then(function(d){ if(d.success) window.location.reload(); });
});
function handleColorInteraction(e) { e.preventDefault(); activeSectionToColor = e.currentTarget.parentElement.parentElement; blockColorpicker.click(); }
blockColorpicker.addEventListener('input', function() { if (activeSectionToColor) activeSectionToColor.style.backgroundColor = blockColorpicker.value; });
overlayOpacityRange.addEventListener('input', function() {
overlayOpacityLabel.textContent = overlayOpacityRange.value + '%';
if (activeOverlaySection) {
activeOverlaySection.style.setProperty('--cms-bg-overlay-color', overlayColorPicker.value);
activeOverlaySection.style.setProperty('--cms-bg-overlay-opacity', overlayOpacityRange.value / 100);
activeOverlaySection.classList.add('cms-has-bg-overlay');
}
});
overlayColorPicker.addEventListener('input', function() {
if (activeOverlaySection) {
activeOverlaySection.style.setProperty('--cms-bg-overlay-color', overlayColorPicker.value);
activeOverlaySection.classList.add('cms-has-bg-overlay');
}
});
overlayApplyBtn.addEventListener('click', function() {
overlayPanel.style.display = 'none';
activeOverlaySection = null;
});
overlayCloseBtn.addEventListener('click', function() {
overlayPanel.style.display = 'none';
activeOverlaySection = null;
});
function openOverlayPanel(sTarget, triggerBtn) {
activeOverlaySection = sTarget;
var curColor = sTarget.style.getPropertyValue('--cms-bg-overlay-color') || '#000000';
var curOp = parseFloat(sTarget.style.getPropertyValue('--cms-bg-overlay-opacity') || '0');
overlayColorPicker.value = curColor;
overlayOpacityRange.value = Math.round(curOp * 100);
overlayOpacityLabel.textContent = Math.round(curOp * 100) + '%';
var rect = triggerBtn.getBoundingClientRect();
overlayPanel.style.left = Math.min(rect.left, window.innerWidth - 220) + 'px';
overlayPanel.style.top = (rect.bottom + 6) + 'px';
overlayPanel.style.display = 'block';
}
function applyNavLinkColor(color) {
if (menuElementNode) menuElementNode.style.setProperty('--nav-link-color', color);
}
function applyNavBtnColor(color) {
if (menuElementNode) menuElementNode.style.setProperty('--nav-btn-color', color);
}
function applyNavBrandColor(color) {
if (menuElementNode) menuElementNode.style.setProperty('--nav-brand-color', color);
}
function applyNavBgColor(color) {
if (menuElementNode) menuElementNode.style.setProperty('--nav-bg-color', color);
}
function initNavColorPickers() {
if (!menuElementNode) return;
var currentLinkColor = menuElementNode.style.getPropertyValue('--nav-link-color');
var currentBtnColor = menuElementNode.style.getPropertyValue('--nav-btn-color');
var currentBrandColor = menuElementNode.style.getPropertyValue('--nav-brand-color');
var currentBgColor = menuElementNode.style.getPropertyValue('--nav-bg-color');
if (!currentLinkColor || currentLinkColor === 'inherit') currentLinkColor = '#ffffff';
if (!currentBtnColor || currentBtnColor === 'inherit') currentBtnColor = '#ffffff';
if (!currentBrandColor || currentBrandColor === 'inherit') currentBrandColor = '#ffffff';
if (!currentBgColor || currentBgColor === 'inherit') currentBgColor = '#1e222b';
if (navLinkColorPicker) navLinkColorPicker.value = currentLinkColor.trim();
if (navBtnColorPicker) navBtnColorPicker.value = currentBtnColor.trim();
if (navBrandColorPicker) navBrandColorPicker.value = currentBrandColor.trim();
if (navBgColorPicker) navBgColorPicker.value = currentBgColor.trim();
navColorsSection.style.display = 'block';
}
if (navLinkColorPicker) {
navLinkColorPicker.addEventListener('input', function() { applyNavLinkColor(navLinkColorPicker.value); });
navLinkColorPicker.addEventListener('change', function() { applyNavLinkColor(navLinkColorPicker.value); });
}
if (navBtnColorPicker) {
navBtnColorPicker.addEventListener('input', function() { applyNavBtnColor(navBtnColorPicker.value); });
navBtnColorPicker.addEventListener('change', function() { applyNavBtnColor(navBtnColorPicker.value); });
}
if (navBrandColorPicker) {
navBrandColorPicker.addEventListener('input', function() { applyNavBrandColor(navBrandColorPicker.value); });
navBrandColorPicker.addEventListener('change', function() { applyNavBrandColor(navBrandColorPicker.value); });
}
if (navBgColorPicker) {
navBgColorPicker.addEventListener('input', function() { applyNavBgColor(navBgColorPicker.value); });
navBgColorPicker.addEventListener('change', function() { applyNavBgColor(navBgColorPicker.value); });
}
function enablePageEditing(active) {
if (active) {
editableTexts.forEach(function(el) { el.contentEditable = true; el.classList.add('global-editing-active'); });
editableMedias.forEach(function(item) {
if (item.type === 'section-bg') { item.el.addEventListener('click', handleMediaInteraction); }
else if (item.shield) { item.shield.style.display = 'block'; item.shield.addEventListener('click', handleMediaInteraction); }
else { item.el.classList.add('global-img-editable'); item.el.addEventListener('click', handleMediaInteraction); }
});
document.querySelectorAll('.cms-control-container').forEach(function(c){ c.style.display='flex'; });
editableColors.forEach(function(item) { item.el.addEventListener('click', handleColorInteraction); });
editableLinks.forEach(function(el) { el.classList.add('cms-link-editable'); });
initNavColorPickers();
document.addEventListener('selectionchange', handleTextSelection);
} else {
document.querySelectorAll('.global-editing-active, .cms-link-editable, [contenteditable], a, h4, h5, h6').forEach(function(el) {
el.contentEditable = false;
el.removeAttribute('contenteditable');
el.blur();
el.classList.remove('global-editing-active','cms-link-editable');
if (el.tagName.toLowerCase() === 'a') el.onclick = null;
});
editableMedias.forEach(function(item) { if(item.shield){ item.shield.style.display='none'; } else { item.el.classList.remove('global-img-editable'); } });
document.querySelectorAll('.cms-control-container').forEach(function(c){ c.style.display='none'; });
navColorsSection.style.display = 'none';
overlayPanel.style.display = 'none';
toolbar.style.display = 'none';
document.removeEventListener('selectionchange', handleTextSelection);
}
}
window.addEventListener('mousedown', function(e) {
if (!isAdminActive) return;
if (e.target.closest('#cms-overlay-panel') || e.target.closest('#cms-nav-colors-section')) return;
var linkEl = e.target.closest('.cms-link-editable');
if (linkEl) { e.preventDefault(); e.stopPropagation(); linkEl.onclick = function(ev){ ev.preventDefault(); return false; }; handleLinkClickDirect(linkEl); }
}, true);
function handleTextSelection() {
var sel = window.getSelection();
if (sel.rangeCount > 0 && sel.toString().trim().length > 0) {
var rect = sel.getRangeAt(0).getBoundingClientRect();
toolbar.style.display = 'flex'; toolbar.style.top = (rect.top + window.scrollY - 45) + 'px'; toolbar.style.left = (rect.left + window.scrollX) + 'px';
} else { toolbar.style.display = 'none'; }
}
function showVideoMenu(el, anchorEl) {
var old = document.getElementById('cms-video-menu'); if (old) old.remove();
var menu = document.createElement('div');
menu.id = 'cms-video-menu';
menu.style.cssText = 'position:fixed;z-index:999999;background:#1e222b;border:1px solid rgba(255,255,255,0.15);border-radius:10px;padding:10px;box-shadow:0 4px 16px rgba(0,0,0,0.6);font-family:sans-serif;min-width:220px;';
var rect = anchorEl.getBoundingClientRect();
var top = rect.top + rect.height/2 - 80; if (top < 10) top = 10; if (top > window.innerHeight - 200) top = window.innerHeight - 200;
var left = rect.left + rect.width/2 - 110; if (left < 10) left = 10; if (left > window.innerWidth - 230) left = window.innerWidth - 230;
menu.style.top = top + 'px'; menu.style.left = left + 'px';
function btn(label, handler) {
var b = document.createElement('button'); b.type = 'button'; b.innerText = label;
b.style.cssText = 'display:block;width:100%;text-align:left;background:#16181b;color:#fff;border:1px solid #3a3f47;padding:8px 10px;border-radius:6px;margin-bottom:6px;font-size:13px;cursor:pointer;';
b.addEventListener('click', function(ev){ ev.stopPropagation(); menu.remove(); handler(); });
return b;
}
menu.appendChild(btn('📤 MP4 hochladen', function(){ activeMediaElementToUpload = el; fileUploader.click(); }));
menu.appendChild(btn('🔗 YouTube-Link einfügen', function(){
var rawInput = prompt("YouTube-Link einfügen:", el.getAttribute('src') || el.getAttribute('data-bg-video') || '');
if (rawInput !== null && rawInput.trim() !== '') {
var finalUrl = rawInput.trim();
if (finalUrl.includes('<iframe') && finalUrl.includes('src=')) { var m = finalUrl.match(/src=["'](.*?)["']/); if(m) finalUrl=m[1]; }
if (el.hasAttribute('data-bg-video')) el.setAttribute('data-bg-video', finalUrl); else el.setAttribute('src', finalUrl);
}
}));
menu.appendChild(btn('⏯️ Autoplay AN/AUS', function(){ setVideoAutoplayState(el, confirm("Autoplay AN?\n[OK]=JA | [Abbrechen]=NEIN")); }));
menu.appendChild(btn('✏️ Manueller Pfad', function(){
var src = prompt("Pfad:", el.getAttribute('src')||'');
if(src&&src.trim()) { if(el.hasAttribute('data-bg-video')) el.setAttribute('data-bg-video',src.trim()); else el.setAttribute('src',src.trim()); setVideoAutoplayState(el, confirm("Autoplay?")); }
}));
var cancelBtn = document.createElement('button'); cancelBtn.type='button'; cancelBtn.innerText='✕ Abbrechen';
cancelBtn.style.cssText = 'display:block;width:100%;text-align:center;background:none;color:#aaa;border:1px solid #3a3f47;padding:6px 10px;border-radius:6px;font-size:12px;cursor:pointer;';
cancelBtn.addEventListener('click', function(ev){ ev.stopPropagation(); menu.remove(); });
menu.appendChild(cancelBtn);
document.body.appendChild(menu);
setTimeout(function(){
document.addEventListener('click', function closeOnOutside(ev){
if (!menu.contains(ev.target)) { menu.remove(); document.removeEventListener('click', closeOnOutside); }
});
}, 0);
}
function showImageMenu(el, anchorEl) {
var old = document.getElementById('cms-image-menu'); if (old) old.remove();
var menu = document.createElement('div');
menu.id = 'cms-image-menu';
menu.style.cssText = 'position:fixed;z-index:999999;background:#1e222b;border:1px solid rgba(255,255,255,0.15);border-radius:10px;padding:10px;box-shadow:0 4px 16px rgba(0,0,0,0.6);font-family:sans-serif;min-width:220px;';
var rect = anchorEl.getBoundingClientRect();
var top = rect.top + rect.height/2 - 60; if (top < 10) top = 10; if (top > window.innerHeight - 160) top = window.innerHeight - 160;
var left = rect.left + rect.width/2 - 110; if (left < 10) left = 10; if (left > window.innerWidth - 230) left = window.innerWidth - 230;
menu.style.top = top + 'px'; menu.style.left = left + 'px';
function btn(label, handler) {
var b = document.createElement('button'); b.type = 'button'; b.innerText = label;
b.style.cssText = 'display:block;width:100%;text-align:left;background:#16181b;color:#fff;border:1px solid #3a3f47;padding:8px 10px;border-radius:6px;margin-bottom:6px;font-size:13px;cursor:pointer;';
b.addEventListener('click', function(ev){ ev.stopPropagation(); menu.remove(); handler(); });
return b;
}
menu.appendChild(btn('📤 Bild hochladen', function(){ activeMediaElementToUpload = el; fileUploader.click(); }));
menu.appendChild(btn('✏️ Manueller Pfad', function(){
var src = prompt("Pfad:", el.getAttribute('src')||'');
if (src !== null && src.trim() !== '') setImageSrcSynced(el, src.trim());
}));
var cancelBtn = document.createElement('button'); cancelBtn.type='button'; cancelBtn.innerText='✕ Abbrechen';
cancelBtn.style.cssText = 'display:block;width:100%;text-align:center;background:none;color:#aaa;border:1px solid #3a3f47;padding:6px 10px;border-radius:6px;font-size:12px;cursor:pointer;';
cancelBtn.addEventListener('click', function(ev){ ev.stopPropagation(); menu.remove(); });
menu.appendChild(cancelBtn);
document.body.appendChild(menu);
setTimeout(function(){
document.addEventListener('click', function closeOnOutside(ev){
if (!menu.contains(ev.target)) { menu.remove(); document.removeEventListener('click', closeOnOutside); }
});
}, 0);
}
function handleMediaInteraction(e) {
e.preventDefault();
e.stopPropagation();
if (e.stopImmediatePropagation) e.stopImmediatePropagation();
var clickedEl = e.currentTarget;
var el = clickedEl.classList.contains('cms-video-shield') ? clickedEl.parentNode.querySelector('video, iframe, [data-bg-video]') : clickedEl;
if (!el) { console.error('CMS: Ziel-Medienelement nicht gefunden für', clickedEl); return; }
activeMediaElementToUpload = el;
var isBgTrigger = clickedEl.classList.contains('cms-bg-edit-trigger');
var isVideoElement = (el && (el.tagName==='VIDEO' || el.tagName==='IFRAME' || el.hasAttribute('data-bg-video')));
if (isBgTrigger) {
var sTarget = clickedEl.parentElement.parentElement;
var mode = prompt("Hintergrundbild-Optionen:\n'1' = Bild hochladen/ändern\n'2' = Deckkraft einstellen\n'3' = Overlay-Farbe wählen", "1");
if (mode === "2") {
var curOp = sTarget.style.getPropertyValue('--cms-bg-opacity') || "1";
var newOp = prompt("Deckkraft (0–100):\n100 = voll sichtbar | 20 = fast transparent", Math.round(curOp * 100));
if (newOp !== null && !isNaN(newOp)) { sTarget.style.setProperty('--cms-bg-opacity', Math.min(Math.max(parseInt(newOp)/100,0),1)); }
return;
}
if (mode === "3") {
openOverlayPanel(sTarget, clickedEl);
return;
}
if (confirm("Neues Bild hochladen?\n[Abbrechen] für manuelle Pfadeingabe.")) { activeMediaElementToUpload = clickedEl; fileUploader.click(); }
else { var src = prompt("Pfad eintragen:"); if (src !== null && src.trim() !== '') { sTarget.classList.add('cms-has-bg'); sTarget.style.setProperty('--cms-bg-image',"url('"+src.trim()+"')"); sTarget.style.backgroundImage='none'; } }
return;
}
if (isVideoElement) {
showVideoMenu(el, clickedEl);
return;
} else {
showImageMenu(el, clickedEl);
}
}
function setVideoAutoplayState(videoEl, shouldAutoplay) {
if (!videoEl || videoEl.tagName !== 'VIDEO') { alert("Gilt nur für MP4-Videos."); return; }
if (shouldAutoplay) {
videoEl.setAttribute('autoplay','autoplay');
videoEl.setAttribute('muted','muted');
videoEl.setAttribute('loop','loop');
videoEl.removeAttribute('controls');
videoEl.muted = true;
videoEl.play().catch(function(){});
} else {
videoEl.removeAttribute('autoplay');
videoEl.removeAttribute('muted');
videoEl.removeAttribute('loop');
videoEl.setAttribute('controls','controls');
videoEl.muted = false;
videoEl.pause();
}
var mediaId = videoEl.getAttribute('data-media-id');
if (mediaId) videoEl.setAttribute('data-autoplay-state', shouldAutoplay ? '1' : '0');
alert(shouldAutoplay ? 'Autoplay AKTIVIERT!' : 'Autoplay AUSGESCHALTET!');
}
fileUploader.addEventListener('change', function(e) {
console.log("Upload gestartet..."); // Debug-Log
if (!e.target.files[0] || !activeMediaElementToUpload) {
console.log("Abbruch: Keine Datei oder kein Ziel-Element.");
return;
}
var file = e.target.files[0];
var formData = new FormData();
formData.append('action','upload');
formData.append('file', file);
fetch(handlerUrl, {
method:'POST',
body:formData,
credentials:'include'
})
.then(function(r) {
if (!r.ok) throw new Error('Server-Fehler: ' + r.status);
return r.json();
})
.then(function(d) {
console.log("Server-Antwort erhalten:", d); // Debug-Log
if (d.success) {
var newPath = d.filepath || d.path || d.url || d.filename; // save_content.php liefert "filepath"
var el = activeMediaElementToUpload;
if (el) {
if (el.classList && el.classList.contains('cms-bg-edit-trigger')) {
// Hintergrundbild eines Abschnitts
var sTarget = el.parentElement.parentElement;
sTarget.classList.add('cms-has-bg');
sTarget.style.setProperty('--cms-bg-image', "url('" + newPath + "')");
sTarget.style.backgroundImage = 'none';
} else if (el.tagName === 'VIDEO') {
el.setAttribute('src', newPath);
} else if (el.hasAttribute('data-bg-video')) {
el.setAttribute('data-bg-video', newPath);
} else {
// normales <img> inkl. Lightbox-Vorschaubild -> Lightbox-Bild mitsynchronisieren
setImageSrcSynced(el, newPath);
}
}
alert('Erfolgreich hochgeladen!');
fileUploader.value = ''; // Reset für das nächste Mal
activeMediaElementToUpload = null;
} else {
alert('Upload-Fehler: ' + d.error);
}
})
.catch(function(err) {
console.error("Upload-Fehler:", err);
alert('Ein Fehler ist aufgetreten: ' + err.message);
});
});
function handleLinkClickDirect(el) {
var txt = prompt("Text:", getLinkText(el));
var url = prompt("Ziel-URL:", el.getAttribute('href') || '');
if (txt !== null && url !== null) {
var finalUrl = url.trim();
if (finalUrl !== "") {
if (/^www\./i.test(finalUrl)) { finalUrl = "https://" + finalUrl; }
else if (!/^https?:\/\//i.test(finalUrl) && !/^\//.test(finalUrl) && !/^#/.test(finalUrl) && !/\.html$/i.test(finalUrl)) { finalUrl = "https://" + finalUrl; }
}
if (!el.querySelector('img')) setLinkText(el, txt);
el.setAttribute('href', finalUrl);
var t = prompt("In neuem Tab?\n'y'=JA | 'n'=NEIN:", el.getAttribute('target')==='_blank' ? 'y' : 'n');
if (t !== null) { if (t.toLowerCase()==='y') el.setAttribute('target','_blank'); else el.removeAttribute('target'); }
}
el.onclick = null;
}
if (saveBtn) {
saveBtn.addEventListener('click', function() {
var pLoad = {}, gLoad = {}, fLoad = {};
if (stickySelect) gLoad['cms_menu_sticky_style'] = stickySelect.value;
if (navLinkColorPicker) gLoad['cms_nav_link_color'] = navLinkColorPicker.value;
if (navBtnColorPicker) gLoad['cms_nav_btn_color'] = navBtnColorPicker.value;
if (navBrandColorPicker) gLoad['cms_nav_brand_color'] = navBrandColorPicker.value;
if (navBgColorPicker) gLoad['cms_nav_bg_color'] = navBgColorPicker.value;
editableTexts.forEach(function(el) {
var k = el.getAttribute('data-auto-id'), s = el.getAttribute('data-cms-scope');
var c = el.cloneNode(true);
c.classList.remove('global-editing-active'); c.removeAttribute('contenteditable');
c.querySelectorAll('[contenteditable]').forEach(function(sub){ sub.removeAttribute('contenteditable'); sub.classList.remove('cms-link-editable','global-editing-active'); });
var html = c.innerHTML.trim();
if(s==='menu') gLoad[k]=html; else if(s==='footer') fLoad[k]=html; else pLoad[k]=html;
});
editableColors.forEach(function(item) {
var k = item.el.getAttribute('data-color-id'), s = item.el.getAttribute('data-cms-scope');
var v = item.parentSection.style.backgroundColor;
if(v && v!=='') { if(s==='menu') gLoad[k]=v; else if(s==='footer') fLoad[k]=v; else pLoad[k]=v; }
});
editableMedias.forEach(function(item) {
var s = item.el.getAttribute('data-cms-scope');
if (item.type === 'section-bg') {
var k = item.el.getAttribute('data-bg-id');
var rawImg = item.parentSection.style.getPropertyValue('--cms-bg-image');
var path = rawImg ? rawImg.replace(/url\(['"]?(.*?)['"]?\)/,'$1') : "";
var opVal = item.parentSection.style.getPropertyValue('--cms-bg-opacity') || "1";
var idx = item.parentSection.getAttribute('data-block-idx');
var ovColor = item.parentSection.style.getPropertyValue('--cms-bg-overlay-color');
var ovOpacity = item.parentSection.style.getPropertyValue('--cms-bg-overlay-opacity');
if (ovColor) {
if(s==='menu') gLoad['overlay_color_'+idx]=ovColor;
else if(s==='footer') fLoad['overlay_color_'+idx]=ovColor;
else pLoad['overlay_color_'+idx]=ovColor;
}
if (ovOpacity !== '') {
if(s==='menu') gLoad['overlay_opacity_'+idx]=ovOpacity;
else if(s==='footer') fLoad['overlay_opacity_'+idx]=ovOpacity;
else pLoad['overlay_opacity_'+idx]=ovOpacity;
}
if(s==='menu') { gLoad[k]=path; gLoad[k+'_opacity']=opVal; }
else if(s==='footer') { fLoad[k]=path; fLoad[k+'_opacity']=opVal; }
else { pLoad[k]=path; pLoad[k+'_opacity']=opVal; }
} else if (item.type === 'bg-video') {
var k = item.el.getAttribute('data-media-id'), path = item.el.getAttribute('data-bg-video');
if(s==='menu') gLoad[k]=path; else if(s==='footer') fLoad[k]=path; else pLoad[k]=path;
} else if (item.type === 'video') {
var k = item.el.getAttribute('data-media-id');
var videoData = {
src: item.el.getAttribute('src') || '',
autoplay: item.el.hasAttribute('autoplay')
};
if(s==='menu') gLoad[k]=videoData; else if(s==='footer') fLoad[k]=videoData; else pLoad[k]=videoData;
} else {
var k = item.el.getAttribute('data-media-id'), path = item.el.getAttribute('src');
if(s==='menu') gLoad[k]=path; else if(s==='footer') fLoad[k]=path; else pLoad[k]=path;
}
});
editableLinks.forEach(function(el) {
var k = el.getAttribute('data-link-id'), s = el.getAttribute('data-cms-scope');
var data = { text: el.querySelector('img') ? "" : getLinkText(el), url: el.getAttribute('href'), target: el.getAttribute('target')||'' };
if(s==='menu') gLoad[k]=data; else if(s==='footer') fLoad[k]=data; else pLoad[k]=data;
});
function send(f,p){ return fetch(handlerUrl,{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'action=save&filename='+encodeURIComponent(f)+'&data='+encodeURIComponent(JSON.stringify(p)),credentials:'include'}); }
Promise.all([send(jsonUrl,pLoad), send(jsonGlobalUrl,gLoad), send(jsonFooterUrl,fLoad)]).then(function(){
if (!ALLOW_MENU_FOOTER_EDIT) {
alert('Gespeichert!\n\nHinweis: Menü und Footer wurden NICHT gespeichert (Bearbeitung ist aktuell auf dieser Seite gesperrt).');
} else {
alert('Gespeichert!');
}
fetchBackupList();
});
});
}
if (logoutBtn) {
logoutBtn.addEventListener('click', function() {
fetch(handlerUrl,{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'action=logout',credentials:'include'})
.then(function(){ document.body.classList.remove('admin-active'); window.location.reload(); });
});
}
}