// File Editor Module window.fileEditor = { currentFile: null, currentContent: null, modifiedFiles: new Set(), init() { // Set up edit button handlers document.getElementById('editProjectBtn').addEventListener('click', () => { this.openEditor('project', 'Project Configuration (project.json)', JSON.stringify(window.timelineEngine.config, null, 2)); }); document.getElementById('editSvgBtn').addEventListener('click', () => { this.openEditor('svg', 'SVG Template', window.timelineEngine.template); }); document.getElementById('editCssBtn').addEventListener('click', () => { this.openEditor('css', 'Stylesheet', window.timelineEngine.cssData); }); document.getElementById('editCsvBtn').addEventListener('click', () => { this.openEditor('csv', 'CSV Data', window.timelineEngine.csvData); }); // Set up save changes button document.getElementById('saveChanges').addEventListener('click', () => { this.saveAllChanges(); }); // Close modal on background click document.getElementById('editorModal').addEventListener('click', (e) => { if (e.target.id === 'editorModal') { this.closeEditor(); } }); // Keyboard shortcut: Escape to close document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && document.getElementById('editorModal').style.display === 'block') { this.closeEditor(); } }); console.log('File editor initialized'); }, openEditor(fileType, title, content) { if (!content) { alert(`No ${title} loaded. Please load a file first.`); return; } this.currentFile = fileType; this.currentContent = content; document.getElementById('editorTitle').textContent = `Edit ${title}`; document.getElementById('editorTextarea').value = content; document.getElementById('editorModal').style.display = 'block'; console.log(`Opened editor for ${fileType}`); }, closeEditor() { document.getElementById('editorModal').style.display = 'none'; this.currentFile = null; this.currentContent = null; }, applyChanges() { const newContent = document.getElementById('editorTextarea').value; // Validate based on file type if (this.currentFile === 'project' || this.currentFile === 'json') { try { JSON.parse(newContent); } catch (e) { alert(`Invalid JSON: ${e.message}\n\nPlease fix the syntax errors before applying.`); return; } } // Check if content actually changed if (newContent === this.currentContent) { console.log('No changes detected'); this.closeEditor(); return; } // Apply changes based on file type console.log(`Applying changes to ${this.currentFile}`); try { switch (this.currentFile) { case 'project': const cfg = JSON.parse(newContent); window.timelineEngine.config = cfg; // Update UI document.getElementById("projectName").textContent = cfg.name || "Timeline"; document.getElementById("projectSubtitle").textContent = cfg.description || "Project configuration updated."; // Show field mappings window.timelineEngine.showFieldMappings(); // Regenerate timeline if we have CSV data if (window.timelineEngine.csvData) { window.timelineEngine.processCsv(window.timelineEngine.csvData); } break; case 'svg': window.timelineEngine.template = newContent; window.timelineEngine.showTemplateFields(); // Regenerate timeline if we have CSV data if (window.timelineEngine.csvData) { window.timelineEngine.processCsv(window.timelineEngine.csvData); } else { window.timelineEngine.showTemplatePreview(); } break; case 'css': window.timelineEngine.cssData = newContent; const blob = new Blob([newContent], { type: "text/css" }); document.getElementById("dynamicCss").href = URL.createObjectURL(blob); break; case 'csv': window.timelineEngine.csvData = newContent; window.timelineEngine.processCsv(newContent); break; } // Mark file as modified this.modifiedFiles.add(this.currentFile); this.updateModifiedBadges(); this.updateSaveButton(); console.log(`✅ Changes applied to ${this.currentFile}`); this.closeEditor(); } catch (error) { console.error('Error applying changes:', error); alert(`Error applying changes: ${error.message}`); } }, updateModifiedBadges() { // Remove all existing badges document.querySelectorAll('.modified-badge').forEach(badge => badge.remove()); // Add badges to modified files this.modifiedFiles.forEach(fileType => { let statusElement; switch (fileType) { case 'project': statusElement = document.getElementById('projectFile'); break; case 'svg': statusElement = document.getElementById('svgFile'); break; case 'css': statusElement = document.getElementById('cssFile'); break; case 'csv': statusElement = document.getElementById('csvFile'); break; } if (statusElement && !statusElement.querySelector('.modified-badge')) { const badge = document.createElement('span'); badge.className = 'modified-badge'; badge.textContent = 'MODIFIED'; statusElement.appendChild(badge); } }); }, updateSaveButton() { const saveBtn = document.getElementById('saveChanges'); if (this.modifiedFiles.size > 0) { saveBtn.disabled = false; saveBtn.style.opacity = '1'; } else { saveBtn.disabled = true; saveBtn.style.opacity = '0.6'; } }, saveAllChanges() { if (this.modifiedFiles.size === 0) { alert('No files have been modified.'); return; } const filesToSave = Array.from(this.modifiedFiles); console.log('Saving modified files:', filesToSave); filesToSave.forEach(fileType => { let content, filename, mimeType; switch (fileType) { case 'project': content = JSON.stringify(window.timelineEngine.config, null, 2); filename = 'project.json'; mimeType = 'application/json'; break; case 'svg': content = window.timelineEngine.template; filename = 'template-v2.svg'; mimeType = 'image/svg+xml'; break; case 'css': content = window.timelineEngine.cssData; filename = 'style.css'; mimeType = 'text/css'; break; case 'csv': content = window.timelineEngine.csvData; filename = 'sample.csv'; mimeType = 'text/csv'; break; default: console.warn(`Unknown file type: ${fileType}`); return; } // Create and trigger download const blob = new Blob([content], { type: mimeType }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; a.click(); URL.revokeObjectURL(url); console.log(`✅ Saved ${filename}`); }); // Clear modified state after saving setTimeout(() => { if (confirm(`${filesToSave.length} file(s) downloaded.\n\nClear modified state?`)) { this.modifiedFiles.clear(); this.updateModifiedBadges(); this.updateSaveButton(); } }, 500); }, enableEditButton(fileType) { let btnId; switch (fileType) { case 'project': btnId = 'editProjectBtn'; break; case 'svg': btnId = 'editSvgBtn'; break; case 'css': btnId = 'editCssBtn'; break; case 'csv': btnId = 'editCsvBtn'; break; default: return; } const btn = document.getElementById(btnId); if (btn) { btn.disabled = false; } } };