Spaces:
Running
Running
| // DOM elements | |
| const htmlEditor = document.getElementById('html-editor'); | |
| const cssEditor = document.getElementById('css-editor'); | |
| const jsEditor = document.getElementById('js-editor'); | |
| const imageUrlInput = document.getElementById('image-url'); | |
| const imageFileInput = document.getElementById('image-file'); | |
| const previewIframe = document.getElementById('preview-iframe'); | |
| const loadingSpinner = document.getElementById('loading-spinner'); | |
| const previewTab = document.getElementById('preview-tab'); | |
| const filesTab = document.getElementById('files-tab'); | |
| const previewView = document.getElementById('preview-view'); | |
| const filesView = document.getElementById('files-view'); | |
| // State | |
| let currentImageSrc = ''; | |
| let updateTimeout; | |
| // Debounce function | |
| function debounce(func, wait) { | |
| return function executedFunction(...args) { | |
| const later = () => { | |
| clearTimeout(updateTimeout); | |
| func(...args); | |
| }; | |
| clearTimeout(updateTimeout); | |
| updateTimeout = setTimeout(later, wait); | |
| }; | |
| } | |
| // Update preview function | |
| function updatePreview() { | |
| // Show loading spinner | |
| loadingSpinner.classList.remove('hidden'); | |
| // Get current code | |
| let html = htmlEditor.value; | |
| const css = cssEditor.value; | |
| const js = jsEditor.value; | |
| // Replace image src in HTML | |
| html = html.replace(/<img[^>]*id="preview-img"[^>]*>/g, `<img id="preview-img" src="${currentImageSrc}" alt="Your uploaded image">`); | |
| // Build complete HTML document | |
| const fullDoc = `<!DOCTYPE html> | |
| <html> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <style>${css}</style> | |
| </head> | |
| <body> | |
| ${html} | |
| <script>${js}<\/script> | |
| </body> | |
| </html>`; | |
| // Set iframe content | |
| previewIframe.srcdoc = fullDoc; | |
| // Hide spinner after a short delay | |
| setTimeout(() => { | |
| loadingSpinner.classList.add('hidden'); | |
| }, 200); | |
| } | |
| // Debounced update | |
| const debouncedUpdate = debounce(updatePreview, 300); | |
| // Tab switching function | |
| function switchTab(tabButton, viewElement) { | |
| // Remove active class from all tabs | |
| document.querySelectorAll('.tab-button').forEach(btn => btn.classList.remove('active')); | |
| // Add active class to clicked tab | |
| tabButton.classList.add('active'); | |
| // Remove active class from all views | |
| document.querySelectorAll('.view').forEach(view => view.classList.remove('active')); | |
| // Add active class to target view | |
| viewElement.classList.add('active'); | |
| } | |
| // Event listeners for tabs | |
| previewTab.addEventListener('click', () => switchTab(previewTab, previewView)); | |
| filesTab.addEventListener('click', () => switchTab(filesTab, filesView)); | |
| // Event listeners for image inputs | |
| imageUrlInput.addEventListener('input', (e) => { | |
| currentImageSrc = e.target.value; | |
| debouncedUpdate(); | |
| }); | |
| imageFileInput.addEventListener('change', (e) => { | |
| const file = e.target.files[0]; | |
| if (file) { | |
| const reader = new FileReader(); | |
| reader.onload = (event) => { | |
| currentImageSrc = event.target.result; | |
| debouncedUpdate(); | |
| }; | |
| reader.readAsDataURL(file); | |
| } | |
| }); | |
| // Editor input events | |
| [htmlEditor, cssEditor, jsEditor].forEach(editor => { | |
| editor.addEventListener('input', debouncedUpdate); | |
| }); | |
| // Initial preview update | |
| updatePreview(); |