/* /editor/bridge-core.js — Loader + Orchestrator (FINAL & LOG-KONTROLLIERT) */ (function () { // --- Initialisierung BridgeParts (B) und Plugin-Registry --- if (!window.BridgeParts) window.BridgeParts = {}; const B = window.BridgeParts; // ---------------------------------------------------------------------- // 🎯 LOKALE LOG-KONFIGURATION & WRAPPER // ---------------------------------------------------------------------- const PluginName = 'bridge-core'; // Setzen Sie dies auf 'false', um alle Logs NUR für dieses Plugin zu deaktivieren. if (B.LOG_CONFIG) { B.LOG_CONFIG.PLUGINS[PluginName] = true; // bridge-core spezifisch deaktivieren (optional) } /** * NEUER LOKALER WRAPPER, der die zentrale B.log Funktion verwendet. */ const log = (type, message, color = '#1E90FF', logType = 'info', force = false) => { // Loggt NUR, wenn B.log verfügbar ist (aus general-functions.js). if (typeof B.log === 'function') { B.log(PluginName, `[${type}] ${message}`, color, logType, force); } // Ansonsten wird NICHTS geloggt, bis general-functions.js geladen ist. }; // ---------------------------------------------------------------------- // 🛑 GLOBALER LOG ZUR BESTÄTIGUNG DER SKRIPT-AUSFÜHRUNG // log('START', `SKRIPT-AUSFÜHRUNG GESTARTET.`, '#DC143C', 'info', true); // DEAKTIVIERT/IGNORIERT DURCH FEHLENDEN B.log // ---------------------------------------------------------------------- // 🛑 KONFIGURATION: NEWSLETTER-PRESET-TOGGLE // ---------------------------------------------------------------------- const LOAD_NEWSLETTER_PRESET = false; // <<< KRITISCHER FIX: Auf FALSE gesetzt, um den "defaults" Konflikt zu beheben! // ---------------------------------------------------------------------- if (window.__bridgeCoreInitialized) { log('INIT ABORT', 'Bridge Core wurde bereits initialisiert.', 'orange'); return; } window.__bridgeCoreInitialized = true; // --- Initialisierung BridgeParts (B) und Plugin-Registry --- B.BASE_PATH_BRIDGE = '../assets/js/bridge/'; B.BASE_PATH_CONFIG = B.BASE_PATH_BRIDGE; const apiFallback = '/api.php'; B.API_BASE = B.API_BASE || apiFallback; B.API_KERNEL_URL = B.API_KERNEL_URL || B.API_BASE; B.GrapesJSPlugins = []; B.registerGrapesJSPlugin = (name, pluginFn) => { B.GrapesJSPlugins.push({ name, pluginFn }); log('PLUGIN REGISTER', `Plugin zur Registry hinzugefügt: ${name}`, 'yellow'); }; // --- DEBUG-HELPER UND LOADER-HELPER --- const badgeSay = (text, type = 'info') => { const b=document.getElementById('badge'); if (!b) return; b.textContent = text; switch(type) { case 'ok': b.style.background = '#dcfce7'; b.style.color = '#15803d'; b.style.borderColor = '#bbf7d0'; break; case 'error': b.style.background = '#fee2e2'; b.style.color = '#7f1d1d'; b.style.borderColor = '#fecaca'; break; default: b.style.background = '#eef2ff'; b.style.color = '#1e3a8a'; b.style.borderColor = '#c7d2fe'; } }; function loadScript(url, done) { const filename = url.split('/').pop(); var s = document.createElement('script'); s.src = url + (url.indexOf('?') === -1 ? '?v=' : '&v=') + Date.now(); s.async = false; s.onload = function(){ log('LOAD SUCCESS', `Skript geladen: ${filename}`, 'green'); try { done && done(); } catch(e){ if (e.message.includes('setting getter-only property "defaults"')) { log('RUNTIME WARNING', `IGNORIERE Block-Konflikt in ${filename}: ${e.message}`, 'orange', 'warn'); } else { // 🛑 KORREKTUR: force: true explizit auf false setzen (oder weglassen) log('RUNTIME ERROR', `Fehler in Callback nach ${filename}: ${e.message}`, 'red', 'error', false); } } }; s.onerror = function(){ // 🛑 KORREKTUR: force: true explizit auf false setzen (oder weglassen) log('LOAD FAILED', `Skript FEHLT oder Pfad falsch: ${filename}`, 'red', 'error', false); badgeSay(`Ladefehler: ${filename}`, 'error'); try { done && done(); } catch(e){} }; document.head.appendChild(s); } /** * HILFSFUNKTION: Wandelt den Dateinamen (z.B. blocks-standard.js) in den globalen * Objektnamen (z.B. BridgeBlocksStandard) um. */ function getPluginObjectName(fileName) { // 1. Entferne Dateiendung (.js) let name = fileName.replace('.js', ''); // 'blocks-standard' // 2. Teile in Bestandteile zerlegen und den ersten Buchstaben groß schreiben const parts = name.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)); // ['Blocks', 'Standard'] // 3. Mit 'Bridge' prefixen und zusammenfügen return 'Bridge' + parts.join(''); // 'BridgeBlocksStandard' } // 🛑 NEUE FUNKTION: Erstellt alle in der Konfiguration definierten Kategorien. function ensureConfiguredCategories(editor) { const bm = editor.BlockManager; // HINWEIS: B.CATEGORY_CONFIG wird in category-config.js befüllt (muss vorher geladen werden) const config = window.BridgeParts?.CATEGORY_CONFIG || {}; Object.keys(config) .sort((a, b) => (config[a].ord || 999) - (config[b].ord || 999)) .forEach(catId => { const catConf = config[catId]; // Category wird nur erstellt, wenn sie noch nicht existiert if (!bm.getCategories().get(catId)) { bm.getCategories().add({ id: catId, label: catConf.label, open: catConf.open !== false, order: catConf.ord || 999 }); log('CAT INIT', `Kategorie '${catId}' explizit erstellt.`, 'green'); } }); } function loadBridgeParts(cb){ const base = B.BASE_PATH_BRIDGE; // 🛑 LOKALES LOGGING ENTFERNT, DA ES VOR B.log LIEGT. // log('LOAD START', 'Starte Laden der modularen Bridge-Teile (Geordnet).'); const coreFiles = [ base + 'general-functions.js', // <<< RE-AKTIVIERT: Für B.log base + 'category-config.js', // <<< RE-AKTIVIERT: Für B.CATEGORY_CONFIG (und damit API-Flexibilität) base + 'library-parts.js', base + 'categorization-master.js', base + 'categorization-cleanup.js', ]; const initialLoadList = [...coreFiles]; function recursiveLoader(list, index = 0) { if (index >= list.length) { log('LOAD END', 'Initial-Bridge-Skripte geladen.', 'green'); const config = window.BridgeParts?.CATEGORY_CONFIG || {}; let allBlockFiles = []; // Dynamisches Sammeln der Block-Dateien aus der Config Object.keys(config) .sort((a, b) => (config[a].ord || 999) - (config[b].ord || 999)) .forEach(key => { // Sammelt alle Dateien, egal ob sync oder async if (Array.isArray(config[key].files)) { allBlockFiles.push(...config[key].files.map(file => base + file)); } }); // Duplikate entfernen (falls eine Datei in mehreren Kategorien gelistet ist) allBlockFiles = Array.from(new Set(allBlockFiles)); function loadBlockFiles(blockIndex = 0) { if (blockIndex >= allBlockFiles.length) { log('LOAD END', 'Alle Blöcke geladen.', 'green'); return cb && cb(B); } log('LOADING BLOCKS', `Lade Block-Skript [${blockIndex + 1}/${allBlockFiles.length}]: ${allBlockFiles[blockIndex].split('/').pop()}`); loadScript(allBlockFiles[blockIndex], function(){ loadBlockFiles(blockIndex + 1); }); } loadBlockFiles(); return; } // 🛑 LOKALES LOGGING ENTFERNT, DA ES VOR B.log LIEGT. log('LOADING CORE', `Lade Skript [${index + 1}/${initialLoadList.length}]: ${list[index].split('/').pop()}`); // Loggt ab dem 3. Skript, da general-functions.js an 2. Stelle geladen wird. loadScript(list[index], function(){ recursiveLoader(list, index + 1); }); } recursiveLoader(initialLoadList); } try { parent.postMessage({ source:'bridge', type:'boot' }, '*'); } catch {} var MODE = (window.__editorMode || 'templates').toLowerCase(); const replaceReferenceLibrary = (editor, ref, mode) => { (window.BridgeParts?.addReferenceLibrary || (()=>{}))(editor, ref, mode); }; const upsertCustomForBothCats = (editor, payload) => { (window.BridgeParts?.upsertCustomForBothCats || (()=>{}))(editor, payload); }; // --- Init & Events (Plugin integriert) --------------------------------------------- loadBridgeParts(function(B){ log('INIT START', 'Alle Bridge-Teile geladen, starte GrapesJS-Initialisierung.', 'orange'); if (typeof grapesjs === 'undefined' || !grapesjs.init) { // 🛑 KORREKTUR: force: true explizit auf false setzen (oder weglassen) log('CRITICAL ERROR', 'Das globale Objekt grapesjs ist nicht verfügbar! Laden von grapes.min.js ist fehlgeschlagen.', 'red', 'error', false); badgeSay('Fehler: GrapesJS nicht geladen!', 'error'); return; } // 🛑 KRITISCHER FIX TEIL 1: Registriere alle gesammelten Bridge-Plugins global. if (typeof grapesjs.plugins.add === 'function') { B.GrapesJSPlugins.forEach(p => { grapesjs.plugins.add(p.name, p.pluginFn); log('PLUGIN ACTIVATION', `GrapesJS Plugin global bereitgestellt: ${p.name}`, 'lime'); }); } else { // 🛑 KORREKTUR: force: true explizit auf false setzen (oder weglassen) log('PLUGIN ERROR', `GrapesJS Plugin-API (grapesjs.plugins.add) fehlt. Plugins können nicht registriert werden.`, 'red', 'error', false); } // 🛑 KRITISCHER FIX: Safety Plugin MUSS die fehlenden Views Panels hinzufügen. function safetyPlugin(editor){ const pn = editor.Panels, orig = pn.getButton.bind(pn); pn.getButton = (pid, id) => orig(pid, id) || { set(){}, get(){ return null; } }; // Fügen Sie das Panel 'views' hinzu, wenn es fehlt if(!pn.getPanel('views')) { pn.addPanel({ id: 'views', el: '.gjs-pn-views' }); log('PANEL FIX', "Das 'views' Panel wurde nachträglich hinzugefügt.", 'yellow', 'warn'); } // Stellen Sie sicher, dass der Block Manager in den Views-Container rendert editor.Config.blockManager = editor.Config.blockManager || {}; editor.Config.blockManager.appendTo = editor.Config.blockManager.appendTo || '.gjs-blocks'; // Der fehlerhafte Timeout-Block wurde entfernt. } let pluginsList = [ safetyPlugin, // 🛑 KRITISCHE ERGÄNZUNG: Aktiviert das registrierte API-Plugin 'bridge-blocks-api', 'bridge-categorization-master', 'bridge-categorization-cleanup', ]; if (LOAD_NEWSLETTER_PRESET) { pluginsList.push('gjs-preset-newsletter'); } const storageConf = { autoload: false, autosave: false, }; var ed = grapesjs.init({ container: '#gjs', height: '100vh', noticeOnUnload: 0, // 🛑 KRITISCHE KORREKTUR: storageManager aktivieren und konfigurieren storageManager: storageConf, plugins: pluginsList, pluginsOpts: {}, // 🛑 KRITISCHE ERGÄNZUNG: Verhindert das automatische Ausblenden leerer Kategorien blockManager: { hideEmpty: false } }); window.__gjs = ed; // 🛑 KRITISCHE KORREKTUR 1: Explizite Erstellung aller konfigurierten Kategorien ensureConfiguredCategories(ed); // 🛑 KRITISCHE KORREKTUR 2: Sofortige Label-Korrektur // Überschreibt den potenziell falschen, durch GrapesJS gesetzten Label-Namen Object.keys(B.CATEGORY_CONFIG || {}).forEach(catId => { const expectedLabel = B.CATEGORY_CONFIG[catId].label; const categoryModel = ed.BlockManager.getCategories().get(catId); if (categoryModel && categoryModel.get('label') !== expectedLabel) { // Setzen ohne das 'change:label' Event auszulösen (optional, aber sauber) categoryModel.set('label', expectedLabel, { silent: true }); log('LABEL FIX', `Kategorie '${catId}' Label auf korrigiert: '${expectedLabel}'`, 'yellow', 'warn'); } }); // --------------------------------------------------- B.ensureViews && B.ensureViews(ed); log('BLOCK REGISTER', 'Registriere Bridge Blöcke, um Preset-Defaults zu überschreiben.', 'purple'); // 🛑 DYNAMISCHE AKTIVIERUNG DER SYNCHRONEN BLÖCKE (Ersetzt die fixen Aufrufe) if (B.CATEGORY_CONFIG && ed) { log('DYNAMIC ACTIVATION', 'Starte Aktivierung synchroner Block-Plugins (via Config).', 'purple'); // Iteriere über die konfigurierten Kategorien Object.keys(B.CATEGORY_CONFIG).forEach(catId => { const config = B.CATEGORY_CONFIG[catId]; // Verarbeite nur SYNCHRONE Plugins, die Dateien angeben if (config.registration_mode === 'sync' && Array.isArray(config.files)) { config.files.forEach(fileName => { // Korrigierte Funktion liefert jetzt z.B. 'BridgeBlocksCustom' const objectName = getPluginObjectName(fileName); const plugin = window[objectName]; // Prüfen, ob das Skript geladen wurde und die Register-Funktion vorhanden ist if (plugin && typeof plugin.register === 'function') { log('DYNAMIC ACTIVATION', `Registriere sync Plugin: ${objectName} (${fileName})`, 'lime'); try { plugin.register(ed); } catch(e) { log('DYNAMIC ACTIVATION ERROR', `Fehler beim Registrieren von ${objectName}: ${e.message}`, 'red', 'error'); } } else { log('DYNAMIC ACTIVATION WARNING', `Sync Plugin Objekt oder .register() Methode nicht gefunden: ${objectName} (${fileName})`, 'orange', 'warn'); } }); } }); } // --------------------------------------------------- log('INIT API', 'API-Elemente werden nun durch das Plugin bridge-blocks-api geladen. (ASYNCHRON)', 'orange'); // ---------------------------------------------------------------------- // DEBUGGING: ZÄHLE REKURSIVE EVENTS // ---------------------------------------------------------------------- let eventCounts = {}; let isParsing = false; const MAX_CALLS = 1000; const debugEvents = [ 'component:add', 'component:update', 'change:components', 'block:add', 'change:attributes', 'comp:update:status' ]; const debugListener = (event, model) => { if (!isParsing) return; if (!eventCounts[event]) { eventCounts[event] = 0; } eventCounts[event]++; if (eventCounts[event] === MAX_CALLS + 1) { // Diese kritischen Debug-Meldungen bleiben DIREKT im console-Objekt, // da sie immer sichtbar sein müssen, um Endlosschleifen zu erkennen. console.error(`%c[DEBUG RECURSION ALARM] 🚨 Event '${event}' hat den Grenzwert von ${MAX_CALLS} überschritten!`, 'color:red; font-size: 1.1em; font-weight: bold;'); } if (eventCounts[event] > MAX_CALLS && eventCounts[event] < (MAX_CALLS + 10)) { const type = (model && typeof model.get === 'function') ? model.get('type') : 'N/A'; const parentType = (model && typeof model.parent === 'function' && model.parent()) ? model.parent().get('type') : 'N/A'; // Diese bleiben console.log aus demselben Grund console.log(`%c [RECURSION SOURCE] Event: ${event}, Type: ${type}, Parent: ${parentType}`, 'color: #8b0000;'); } }; ed.on('load', function() { debugEvents.forEach(event => ed.on(event, (model) => debugListener(event, model))); setTimeout(() => { (B.waitForBlocks ? B.waitForBlocks(ed) : Promise.resolve()).then(function(){ try { log('CORE WARN', 'Führe finalen, verzögerten Cleanup-Lauf durch (2000ms).', 'orange', 'warn'); B.normalizeCategories && B.normalizeCategories(ed); B.renderBlocks && B.renderBlocks(ed); } catch(e) { log('CORE ERROR', `Finaler Cleanup-Fehler: ${e.message}`, 'red', 'error'); } }); }, 2000); }, { once: true }); // ---------------------------------------------------------------------- // MESSAGE HANDLER // ---------------------------------------------------------------------- window.addEventListener('message', async function(ev){ var data = ev.data || {}; if (data.source !== 'admin') return; if (data.type === 'init'){ B.ensureViews && B.ensureViews(ed); var html = (data.html || '').trim(); var hasJson = !!data.hasJson; if (!html) html = '
Neues DokumentInhalt ... |