/** * JSON-Editor für die Template-Bearbeitung. * * Generiert ein Formular aus JSON-Daten und extrahiert * die Werte zurück in ein JSON-Objekt. */ let editContainerRef = null; let currentIndent = 2; /** * Öffne Edit-Modal für eine Template-Datei * @param {string} path - Pfad zur Template-Datei */ function editModalContent(path) { window.currentEditTemplate = path; const title = path.split('/').pop(); document.getElementById('edit-title').textContent = `Template bearbeiten: ${title}`; window._wasViewModalOpen = document.getElementById('modal').classList.contains('active'); // Inhalt laden und editierbare Formulare abhängig vom Dateityp erstellen fetch(path) .then(r => r.text()) .then(content => { const editContainer = document.getElementById('edit-content-content'); editContainer.innerHTML = ''; if (path.endsWith('.json')) { try { const jsonData = JSON.parse(content); // Original-Indent erkennen const indentMatch = content.match(/^\s{2,8}/m); currentIndent = indentMatch ? indentMatch[0].length : 2; createJsonEditUI(editContainer, jsonData, path); } catch (e) { // Falls JSON ungültig, als Text bearbeiten createTextEditUI(editContainer, content); } } else { // Markdown als einfaches Textfeld createTextEditUI(editContainer, content); } document.getElementById('edit-modal').classList.add('active'); }) .catch(e => showToast(`✗ Fehler beim Laden: ${e.message}`)); } /** * Erstelle ein Textarea für die Bearbeitung * @param {HTMLElement} container - Ziel-Container * @param {string} content - Dateiinhalt */ function createTextEditUI(container, content) { const textarea = document.createElement('textarea'); textarea.id = 'edit-textarea'; textarea.value = content; textarea.style.cssText = 'width: 100%; min-height: 300px; background: var(--bg-input); color: var(--text-primary); border: 1px solid var(--border); border-radius: 4px; padding: 10px; font-family: var(--mono); font-size: 13px;'; textarea.spellcheck = false; container.appendChild(textarea); } /** * Erstelle ein Formular aus JSON-Daten * @param {HTMLElement} container - Ziel-Container * @param {Object} jsonData - Zu bearbeitende JSON-Daten * @param {string} editPathHint - Pfad-Hinweis */ function createJsonEditUI(container, jsonData, editPathHint = '') { editContainerRef = container; container.innerHTML = ''; const formDiv = document.createElement('div'); formDiv.style.cssText = 'display: flex; flex-direction: column; gap: 16px; padding: 8px; background: var(--bg-card); border-radius: 4px; margin: 0; min-height: 300px; overflow-y: auto;'; // Rekursive Funktion zum Erstellen von Eingabefeldern für alle Properties function buildJsonForm(data, prefix = '', level = 0, targetElement = null) { if (typeof data !== 'object' || data === null) return; const container = targetElement || document.getElementById('edit-content-content'); if (!container) return; Object.keys(data).forEach(key => { const fullKey = prefix ? `${prefix}.${key}` : key; const value = data[key]; const isObject = typeof value === 'object' && value !== null && !Array.isArray(value); const isArray = Array.isArray(value); // Für Objekte: Rekursiv alle inneren Properties anzeigen if (isObject) { const fieldContainer = document.createElement('div'); fieldContainer.style.cssText = 'background: #1a1a1a; padding: 12px; border-radius: 4px; border-left: 3px solid #4CAF50;'; const label = document.createElement('label'); label.htmlFor = `edit-${fullKey.replace(/[.\s]/g, '-')}`; label.textContent = fullKey + ' (Objekt)'; label.style.cssText = 'font-weight: 600; color: #4CAF50; margin-bottom: 8px; display: block; font-size: 14px;'; fieldContainer.appendChild(label); // Rekursiv innere Properties in den Container einfügen const innerContainer = document.createElement('div'); innerContainer.style.cssText = 'padding-left: 12px; margin-top: 4px;'; buildJsonForm(value, fullKey, level + 1, innerContainer); fieldContainer.appendChild(innerContainer); container.appendChild(fieldContainer); return; } // Für Arrays: Rekursiv jedes Element als eigenes Feld anzeigen if (isArray) { const arrayContainer = document.createElement('div'); arrayContainer.style.cssText = 'background: #1a1a1a; padding: 12px; border-radius: 4px; border-left: 3px solid #2196F3;'; const arrayLabel = document.createElement('label'); arrayLabel.htmlFor = `edit-${fullKey.replace(/[.\s]/g, '-')}`; arrayLabel.textContent = fullKey + ' (Array) - ' + value.length + ' Elemente'; arrayLabel.style.cssText = 'font-weight: 600; color: #2196F3; margin-bottom: 8px; display: block; font-size: 14px;'; arrayContainer.appendChild(arrayLabel); // Für jedes Array-Element ein separates Eingabefeld const arrayItemsContainer = document.createElement('div'); arrayItemsContainer.style.cssText = 'margin-top: 4px; padding-left: 12px;'; value.forEach((item, index) => { const itemKey = `${fullKey}[${index}]`; const itemContainer = document.createElement('div'); itemContainer.style.cssText = 'background: #2a2a2a; padding: 8px; margin: 4px 0; border-radius: 3px; border-left: 2px solid #FF9800;'; const itemLabel = document.createElement('label'); itemLabel.htmlFor = `edit-${itemKey.replace(/[.\s]/g, '-')}`; itemLabel.textContent = `Element [${index}]`; itemLabel.style.cssText = 'font-weight: 500; color: #FF9800; margin-bottom: 4px; display: block; font-size: 13px;'; itemContainer.appendChild(itemLabel); // Prüfen, ob das Array-Element selbst ein Objekt ist if (typeof item === 'object' && item !== null) { const innerObjContainer = document.createElement('div'); innerObjContainer.style.cssText = 'padding-left: 12px; margin-top: 4px;'; buildJsonForm(item, itemKey, level + 1, innerObjContainer); itemContainer.appendChild(innerObjContainer); } else { const itemFieldContainer = document.createElement('div'); itemFieldContainer.style.cssText = 'background: var(--bg-input); padding: 8px; border-radius: 3px; margin-bottom: 4px;'; const input = document.createElement('input'); const type = typeof item === 'boolean' ? 'checkbox' : typeof item === 'number' ? 'number' : 'text'; input.type = type; input.value = item !== null && item !== undefined ? item : ''; input.dataset.key = itemKey; input.dataset.type = typeof item; input.dataset.arrayIndex = index; if (type === 'checkbox') input.checked = item; else input.value = String(item !== null && item !== undefined ? item : ''); input.style.cssText = 'width: 100%; padding: 8px; background: #222222; color: #ffffff; border: 1px solid #555; border-radius: 3px; font-family: var(--mono); font-size: 13px;'; itemFieldContainer.appendChild(input); itemContainer.appendChild(itemFieldContainer); } arrayItemsContainer.appendChild(itemContainer); }); arrayContainer.appendChild(arrayItemsContainer); container.appendChild(arrayContainer); return; } // Für primitive Werte: Standard-Eingabefeld erstellen const fieldContainer = document.createElement('div'); fieldContainer.style.cssText = 'background: var(--bg-input); padding: 8px; border-radius: 4px; border: 1px solid transparent;'; const label = document.createElement('label'); label.htmlFor = `edit-${fullKey.replace(/[.\s]/g, '-')}`; label.textContent = fullKey; label.style.cssText = 'font-weight: 600; color: var(--text-primary); display: block; margin-bottom: 4px; font-size: 14px; margin-top: 0;'; fieldContainer.appendChild(label); const type = typeof value === 'boolean' ? 'checkbox' : typeof value === 'number' ? 'number' : 'text'; const input = document.createElement('input'); input.type = type; input.value = value !== null && value !== undefined ? value : ''; input.dataset.key = fullKey; input.dataset.type = typeof value; if (type === 'checkbox') input.checked = value; else input.value = String(value !== null && value !== undefined ? value : ''); input.style.cssText = 'width: 100%; padding: 8px; background: #222222; color: #ffffff; border: 1px solid #555; border-radius: 3px; font-family: var(--mono); font-size: 13px;'; fieldContainer.appendChild(input); container.appendChild(fieldContainer); }); } buildJsonForm(jsonData, '', 0, formDiv); container.appendChild(formDiv); } /** * Extrahiere den Wert aus einem Input-Element * @param {HTMLElement} input - Input-Element * @returns {*} Extrahierter Wert */ function extractInputValue(input) { if (input.type === 'checkbox') { return input.checked; } const value = input.value; try { // Nur wenn der Input-Typ "text" ist und Wert JSON-ähnlich formatiert if (input.type === 'text' && value.length > 0) { if ((value.startsWith('{') && value.endsWith('}')) || (value.startsWith('[') && value.endsWith(']'))) { try { return JSON.parse(value); } catch (parseErr) { // Kein gültiges JSON, behalte String-Wert } } } if (input.dataset.type === 'number') { return Number(value); } return value; } catch (e) { return value; } } /** * Extrahiere JSON aus dem Formular-Container * @param {HTMLElement} formDiv - Formular-Container * @returns {Object} Extrahiertes JSON-Objekt */ function extractJsonFromForm(formDiv) { // Map: full key -> { value, type } const inputEntries = []; const inputs = formDiv.querySelectorAll('[data-key]'); inputs.forEach(input => { const key = input.dataset.key || ''; inputEntries.push({ key, value: extractInputValue(input) }); }); // Sort by key to ensure stable parent-first construction inputEntries.sort((a,b) => a.key.localeCompare(b.key)); const result = {}; inputEntries.forEach(({ key, value }) => { const parts = key.split('.'); let cur = result; for (let i = 0; i < parts.length; i++) { const part = parts[i]; const isLast = i === parts.length - 1; const m = part.match(/^([^\[\]]+)\[(\d+)\]$/); if (m && !isLast) { const base = m[1]; const idx = Number(m[2]); if (!cur[base]) cur[base] = []; if (!cur[base][idx]) cur[base][idx] = {}; cur = cur[base][idx]; } else if (m && isLast) { // Array leaf const base = m[1]; const idx = Number(m[2]); if (!cur[base]) cur[base] = []; cur[base][idx] = value; } else if (isLast) { cur[part] = value; } else { if (!cur[part]) cur[part] = {}; cur = cur[part]; } } }); return result; } // Export for api.js (global scope, loaded before api.js) // editModalContent, createTextEditUI, createJsonEditUI, extractInputValue, extractJsonFromForm // sind als globale Funktionen verfügbar