From 87f093f99a6b230c78e5608fd46e1c65a92e2f5e Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Mon, 8 Dec 2025 00:35:25 +0100 Subject: [PATCH] ycysd --- public/assets/js/bridge/blocks-custom.js | 267 ++++++------ public/editor/bridge-core (Kopie).js | 496 ----------------------- 2 files changed, 134 insertions(+), 629 deletions(-) delete mode 100644 public/editor/bridge-core (Kopie).js diff --git a/public/assets/js/bridge/blocks-custom.js b/public/assets/js/bridge/blocks-custom.js index 7e18066..b9622e5 100644 --- a/public/assets/js/bridge/blocks-custom.js +++ b/public/assets/js/bridge/blocks-custom.js @@ -61,7 +61,8 @@ return res.json(); }) .then(data => { - placeholderSchemaStore.tables = Array.isArray(data?.tables) ? data.tables : []; + const tbls = data && Array.isArray(data.tables) ? data.tables : []; + placeholderSchemaStore.tables = tbls; return placeholderSchemaStore.tables; }) .catch(err => { @@ -98,143 +99,143 @@ const PlaceholderModel = BaseModel.extend({ defaults: { name: 'Placeholder', - tagName: 'span', - droppable: false, - attributes: { - 'data-placeholder-type': 'custom', - 'data-placeholder-key': 'UEBERSCHRIFT', + tagName: 'span', + droppable: false, + attributes: { + 'data-placeholder-type': 'custom', + 'data-placeholder-key': 'UEBERSCHRIFT', + }, + traits: [ + { + type: 'select', + name: 'data-placeholder-type', + label: 'Typ', + options: [ + { id: 'custom', label: 'Allgemein' }, + { id: 'database', label: 'Datenbank' }, + ], + changeProp: true, }, - traits: [ - { - type: 'select', - name: 'data-placeholder-type', - label: 'Typ', - options: [ - { id: 'custom', label: 'Allgemein' }, - { id: 'database', label: 'Datenbank' }, - ], - changeProp: true, - }, - { - type: 'text', - name: 'data-placeholder-key', - label: 'Bezeichner', - placeholder: 'UEBERSCHRIFT', - changeProp: true, - }, - { - type: 'select', - name: 'data-placeholder-table', - label: 'Tabelle', - options: [], - changeProp: true, - }, - { - type: 'select', - name: 'data-placeholder-column', - label: 'Feld', - options: [], - changeProp: true, - }, - ], - }, - - init() { - this.listenTo(this, 'change:attributes', this.updatePlaceholderState); - this.updatePlaceholderState(); - fetchPlaceholderSchema() - .then(() => this.updateSchemaTraits()) - .catch(() => this.updateSchemaTraits([])); - }, - - updatePlaceholderState() { - const attrs = this.getAttributes(); - const type = attrs['data-placeholder-type'] || 'custom'; - if (type === 'database' && placeholderSchemaStore.tables.length === 0) { - this.addAttributes({ 'data-placeholder-type': 'custom' }); - } - this.updateTraitVisibility(); - this.updateSchemaTraits(); - this.updateLabel(); - }, - - updateTraitVisibility() { - const attrs = this.getAttributes(); - const type = attrs['data-placeholder-type'] || 'custom'; - const isDb = type === 'database'; - const tableTrait = getTraitByName(this, 'data-placeholder-table'); - const columnTrait = getTraitByName(this, 'data-placeholder-column'); - const keyTrait = getTraitByName(this, 'data-placeholder-key'); - - if (tableTrait?.view?.el) { - tableTrait.view.el.style.display = isDb ? '' : 'none'; - } - if (columnTrait?.view?.el) { - columnTrait.view.el.style.display = isDb ? '' : 'none'; - } - if (keyTrait?.view?.el) { - keyTrait.view.el.style.display = isDb ? 'none' : ''; - } - }, - - updateSchemaTraits(tablesOverride) { - const tables = Array.isArray(tablesOverride) ? tablesOverride : placeholderSchemaStore.tables; - const tableTrait = getTraitByName(this, 'data-placeholder-table'); - const columnTrait = getTraitByName(this, 'data-placeholder-column'); - const loading = !tablesOverride && placeholderSchemaStore.promise && !tables.length; - - if (tableTrait) { - let opts; - if (tables.length) { - opts = tables.map(tbl => ({ id: tbl.name, label: tbl.name })); - } else if (loading) { - opts = [{ id: '', label: 'Tabellen werden geladen…', disabled: true }]; - } else { - opts = [{ id: '', label: 'Keine Tabellen verfügbar', disabled: true }]; - } - setTraitOptions(tableTrait, opts); - } - - if (columnTrait) { - const attrs = this.getAttributes(); - const tableName = (attrs['data-placeholder-table'] || '').toLowerCase(); - const table = tables.find(tbl => tbl.name.toLowerCase() === tableName); - const colOpts = table - ? table.columns.map(col => ({ id: col.name, label: `${col.name} (${col.type})` })) - : [{ id: '', label: table ? 'Keine Felder' : 'Feld wählen', disabled: !table }]; - setTraitOptions(columnTrait, colOpts); - } - }, - - updateLabel() { - const attrs = this.getAttributes(); - const type = attrs['data-placeholder-type'] || 'custom'; - let label; - if (type === 'database') { - const table = attrs['data-placeholder-table'] || 'TABELLE'; - const column = attrs['data-placeholder-column'] || 'FELD'; - label = `${table}.${column}`.toUpperCase(); - } else { - label = (attrs['data-placeholder-key'] || 'PLATZHALTER').toUpperCase(); - } - const text = `{{${label}}}`; - const comps = this.components(); - if (comps.length === 1 && comps.at(0).is('textnode')) { - comps.at(0).set('content', text); - } else { - comps.reset([{ type: 'textnode', content: text }]); - } - }, + { + type: 'text', + name: 'data-placeholder-key', + label: 'Bezeichner', + placeholder: 'UEBERSCHRIFT', + changeProp: true, + }, + { + type: 'select', + name: 'data-placeholder-table', + label: 'Tabelle', + options: [], + changeProp: true, + }, + { + type: 'select', + name: 'data-placeholder-column', + label: 'Feld', + options: [], + changeProp: true, + }, + ], }, - }, { - isComponent(el) { - if (el && el.hasAttribute && el.hasAttribute('data-placeholder-type')) { - return { type: PLACEHOLDER_COMPONENT }; + + init() { + this.listenTo(this, 'change:attributes', this.updatePlaceholderState); + this.updatePlaceholderState(); + fetchPlaceholderSchema() + .then(() => this.updateSchemaTraits()) + .catch(() => this.updateSchemaTraits([])); + }, + + updatePlaceholderState() { + const attrs = this.getAttributes(); + const type = attrs['data-placeholder-type'] || 'custom'; + if (type === 'database' && placeholderSchemaStore.tables.length === 0) { + this.addAttributes({ 'data-placeholder-type': 'custom' }); } - return false; + this.updateTraitVisibility(); + this.updateSchemaTraits(); + this.updateLabel(); }, + + updateTraitVisibility() { + const attrs = this.getAttributes(); + const type = attrs['data-placeholder-type'] || 'custom'; + const isDb = type === 'database'; + const tableTrait = getTraitByName(this, 'data-placeholder-table'); + const columnTrait = getTraitByName(this, 'data-placeholder-column'); + const keyTrait = getTraitByName(this, 'data-placeholder-key'); + + if (tableTrait && tableTrait.view && tableTrait.view.el) { + tableTrait.view.el.style.display = isDb ? '' : 'none'; + } + if (columnTrait && columnTrait.view && columnTrait.view.el) { + columnTrait.view.el.style.display = isDb ? '' : 'none'; + } + if (keyTrait && keyTrait.view && keyTrait.view.el) { + keyTrait.view.el.style.display = isDb ? 'none' : ''; + } + }, + + updateSchemaTraits(tablesOverride) { + const tables = Array.isArray(tablesOverride) ? tablesOverride : placeholderSchemaStore.tables; + const tableTrait = getTraitByName(this, 'data-placeholder-table'); + const columnTrait = getTraitByName(this, 'data-placeholder-column'); + const loading = !tablesOverride && placeholderSchemaStore.promise && !tables.length; + + if (tableTrait) { + let opts; + if (tables.length) { + opts = tables.map(function (tbl) { return { id: tbl.name, label: tbl.name }; }); + } else if (loading) { + opts = [{ id: '', label: 'Tabellen werden geladen…', disabled: true }]; + } else { + opts = [{ id: '', label: 'Keine Tabellen verfügbar', disabled: true }]; + } + setTraitOptions(tableTrait, opts); + } + + if (columnTrait) { + const attrs = this.getAttributes(); + const tableName = (attrs['data-placeholder-table'] || '').toLowerCase(); + const table = tables.find(function (tbl) { return tbl.name.toLowerCase() === tableName; }); + const colOpts = table + ? table.columns.map(function (col) { return { id: col.name, label: col.name + ' (' + col.type + ')' }; }) + : [{ id: '', label: table ? 'Keine Felder' : 'Feld wählen', disabled: !table }]; + setTraitOptions(columnTrait, colOpts); + } + }, + + updateLabel() { + const attrs = this.getAttributes(); + const type = attrs['data-placeholder-type'] || 'custom'; + let label; + if (type === 'database') { + const table = attrs['data-placeholder-table'] || 'TABELLE'; + const column = attrs['data-placeholder-column'] || 'FELD'; + label = (table + '.' + column).toUpperCase(); + } else { + label = (attrs['data-placeholder-key'] || 'PLATZHALTER').toUpperCase(); + } + const text = '{{' + label + '}}'; + const comps = this.components(); + const onlyText = comps.length === 1 && comps.at(0).is('textnode'); + if (onlyText) { + comps.at(0).set('content', text); + } else { + comps.reset([{ type: 'textnode', content: text }]); + } + } }); + PlaceholderModel.isComponent = function (el) { + if (el && el.hasAttribute && el.hasAttribute('data-placeholder-type')) { + return { type: PLACEHOLDER_COMPONENT }; + } + return false; + }; + const PlaceholderView = BaseView.extend({ render() { BaseView.prototype.render.apply(this, arguments); @@ -291,7 +292,7 @@ const tableName = firstTable.name || 'tabelle'; const columns = Array.isArray(firstTable.columns) ? firstTable.columns : []; const firstColumn = columns.length ? columns[0].name : 'feld'; - const placeholderLabel = `${tableName}.${firstColumn}`.toUpperCase(); + const placeholderLabel = (tableName + '.' + firstColumn).toUpperCase(); addOnce(bm, 'cust-placeholder-db', { id: 'cust-placeholder-db', label: '🗄️ Placeholder (DB)', diff --git a/public/editor/bridge-core (Kopie).js b/public/editor/bridge-core (Kopie).js deleted file mode 100644 index 7ef4623..0000000 --- a/public/editor/bridge-core (Kopie).js +++ /dev/null @@ -1,496 +0,0 @@ -/* /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] = false; // bridge-core spezifisch deaktivieren (optional) - } - - /** - * NEUER LOKALER WRAPPER, der die zentrale B.log Funktion verwendet. - * Der unformatierte Fallback WURDE ENTFERNT, da er das console.log erzeugt hat. - * Die ersten kritischen Logs WURDEN EBENFALLS ENTFERNT, da sie vor B.log lagen. - */ - 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 - // Dieser erste Log-Aufruf wird nun still ignoriert, da B.log noch fehlt. - // Er wird durch den SUCCESS-Log der general-functions.js ersetzt. - // 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; - - 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; - 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 + 'category-config.js', - // base + 'general-functions.js', - 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'); - } - - var ed = grapesjs.init({ - container: '#gjs', - height: '100vh', - storageManager: false, - 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.', '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(); - if (!html) html = '

Neues Dokument

Inhalt ...

'; - - const applySnips = function(arr){ - const list = (Array.isArray(arr)?arr:[]).map(s => ({ id:s.id, name:s.name, html: s.html || s.content || '' })); - - B.replaceSnippetBlocks && B.replaceSnippetBlocks(ed, list); - - upsertCustomForBothCats(ed, { - ref: (data.ref && (Array.isArray(data.ref.sections) || Array.isArray(data.ref.blocks))) ? { - sections: data.ref.sections || [], - blocks: data.ref.blocks || [] - } : { sections: [], blocks: [] }, - snippets: list - }); - - setTimeout(() => { - try { - // Erneutes Normalisieren nach Laden der Snippets (falls nötig) - B.normalizeCategories && B.normalizeCategories(ed); - B.ensureViews && B.ensureViews(ed); - B.renderBlocks && B.renderBlocks(ed); - log('CORE WARN', 'normalize/render nach applySnips ausgeführt (1ms).', 'orange', 'warn'); - } catch(e) { - log('CORE ERROR', `applySnips-Cleanup-Fehler: ${e.message}`, 'red', 'error'); - } - }, 1); - - }; - - if (Array.isArray(data.snippets) && data.snippets.length) applySnips(data.snippets); - else (B.fetchSnippets ? B.fetchSnippets() : Promise.resolve([])).then(applySnips); - - if (data.ref && (Array.isArray(data.ref.sections) || Array.isArray(data.ref.blocks))) { - replaceReferenceLibrary(ed, { - sections: data.ref.sections || [], - blocks: data.ref.blocks || [] - }, MODE); - } - - // Finaler Aufruf nachrichtengesteuert (konsolidiert) - setTimeout(() => { - (B.waitForBlocks ? B.waitForBlocks(ed) : Promise.resolve()).then(function(){ - try { - log('CORE WARN', 'Führe nachrichtengesteuerten Final-Cleanup-Lauf durch (100ms).', 'orange', 'warn'); - - if (!ed.__contentLoaded) { - window.__GJS_IS_PARSING = true; - isParsing = true; - eventCounts = {}; - - try { - ed.setComponents(html); - } catch (e) { - log('SET COMPONENTS FAILED', `setComponents Fehler: ${e.message}. Aufgerufene Event-Zähler: ${JSON.stringify(eventCounts)}`, 'red', 'error'); - // console.table(eventCounts); bleibt eine direkte Debug-Ausgabe - throw e; - } finally { - window.__GJS_IS_PARSING = false; - isParsing = false; - log('CONTENT', 'HTML-Inhalt in den Editor geladen (FINAL FIX).', 'orange'); - - B.normalizeCategories && B.normalizeCategories(ed); - B.renderBlocks && B.renderBlocks(ed); - } - - ed.__contentLoaded = true; - - } else { - B.normalizeCategories && B.normalizeCategories(ed); - B.renderBlocks && B.renderBlocks(ed); - } - - } catch(e) { - log('CORE ERROR', `Nachrichten-Final-Cleanup-Fehler: ${e.message}. Event-Zähler (im Log-Objekt): ${JSON.stringify(eventCounts)}`, 'red', 'error'); - } - }); - }, 100); - - - try { var b=ed.Panels.getButton('views','open-blocks'); b && b.set('active',true); } catch {} - badgeSay('Inhalt geladen','ok'); - setTimeout(function(){ badgeSay('bereit'); }, 1200); - } - }, false); - - try { B.send && B.send('core-ready', { mode: MODE }); } catch {} - try { var bd=document.getElementById('badge'); if (bd) bd.remove(); } catch {} - }); - - window.onerror = function(message, source, lineno, colno, error) { - // Diese kritische Funktion MUSS console.error verwenden. - console.error(`%c[${PluginName} - GLOBAL ERROR] Uncaught JS Error: ${message} (Quelle: ${source}:${lineno})`, 'color:red; font-weight:bold;'); - return false; - }; - -})();