// UnitForge Main JavaScript // Handles general functionality across the application class UnitForge { constructor() { this.baseUrl = window.location.origin; this.apiUrl = `${this.baseUrl}/api`; this.init(); } init() { this.setupEventListeners(); this.initializeTooltips(); } setupEventListeners() { // Upload modal functionality const fileInput = document.getElementById('fileInput'); if (fileInput) { fileInput.addEventListener('change', this.handleFileSelect.bind(this)); } // Copy to clipboard functionality document.addEventListener('click', (e) => { if (e.target.matches('[data-copy]') || e.target.closest('[data-copy]')) { const target = e.target.matches('[data-copy]') ? e.target : e.target.closest('[data-copy]'); this.copyToClipboard(target.dataset.copy); } }); } initializeTooltips() { // Initialize Bootstrap tooltips const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); tooltipTriggerList.map(function (tooltipTriggerEl) { return new bootstrap.Tooltip(tooltipTriggerEl); }); } // File upload handling handleFileSelect(event) { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { this.displayFileContent(e.target.result, file.name); }; reader.readAsText(file); } displayFileContent(content, filename) { // This will be overridden in specific pages console.log('File loaded:', filename, content); } // API calls async apiCall(endpoint, options = {}) { const url = `${this.apiUrl}${endpoint}`; const defaultOptions = { headers: { 'Content-Type': 'application/json', }, }; const finalOptions = { ...defaultOptions, ...options }; try { const response = await fetch(url, finalOptions); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.detail || `HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } catch (error) { console.error('API call failed:', error); throw error; } } async validateUnitFile(content, filename = null) { return await this.apiCall('/validate', { method: 'POST', body: JSON.stringify({ content: content, filename: filename }) }); } async uploadUnitFile(file) { const formData = new FormData(); formData.append('file', file); const response = await fetch(`${this.apiUrl}/upload`, { method: 'POST', body: formData }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.detail || `Upload failed: ${response.statusText}`); } return await response.json(); } async downloadUnitFile(content, filename) { return await this.apiCall('/download', { method: 'POST', body: JSON.stringify({ content: content, filename: filename }) }); } async getTemplates() { return await this.apiCall('/templates'); } async getTemplate(name) { return await this.apiCall(`/templates/${name}`); } async generateFromTemplate(templateName, parameters, filename = null) { return await this.apiCall('/generate', { method: 'POST', body: JSON.stringify({ template_name: templateName, parameters: parameters, filename: filename }) }); } // Utility functions async copyToClipboard(text) { try { await navigator.clipboard.writeText(text); this.showToast('Copied to clipboard!', 'success'); } catch (err) { // Fallback for older browsers const textArea = document.createElement('textarea'); textArea.value = text; textArea.style.position = 'fixed'; textArea.style.left = '-999999px'; textArea.style.top = '-999999px'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { document.execCommand('copy'); this.showToast('Copied to clipboard!', 'success'); } catch (err) { this.showToast('Failed to copy to clipboard', 'error'); } document.body.removeChild(textArea); } } showToast(message, type = 'info', duration = 3000) { // Create toast container if it doesn't exist let toastContainer = document.getElementById('toast-container'); if (!toastContainer) { toastContainer = document.createElement('div'); toastContainer.id = 'toast-container'; toastContainer.className = 'position-fixed top-0 end-0 p-3'; toastContainer.style.zIndex = '9999'; document.body.appendChild(toastContainer); } // Create toast element const toastId = `toast-${Date.now()}`; const toastHtml = ` `; toastContainer.insertAdjacentHTML('beforeend', toastHtml); // Initialize and show toast const toastElement = document.getElementById(toastId); const toast = new bootstrap.Toast(toastElement, { autohide: true, delay: duration }); toast.show(); // Remove toast element after it's hidden toastElement.addEventListener('hidden.bs.toast', () => { toastElement.remove(); }); } getToastIcon(type) { const icons = { success: 'check-circle', error: 'exclamation-circle', warning: 'exclamation-triangle', info: 'info-circle' }; return icons[type] || 'info-circle'; } getToastColor(type) { const colors = { success: 'success', error: 'danger', warning: 'warning', info: 'info' }; return colors[type] || 'info'; } formatValidationResults(validation) { if (!validation) return ''; let html = ''; if (validation.valid) { html += `
Unit file is valid!
`; } if (validation.errors && validation.errors.length > 0) { html += '
'; html += `
Errors (${validation.errors.length})
`; validation.errors.forEach(error => { const location = error.section + (error.key ? `.${error.key}` : ''); html += `
[${location}] ${error.message}
`; }); html += '
'; } if (validation.warnings && validation.warnings.length > 0) { html += '
'; html += `
Warnings (${validation.warnings.length})
`; validation.warnings.forEach(warning => { const location = warning.section + (warning.key ? `.${warning.key}` : ''); html += `
[${location}] ${warning.message}
`; }); html += '
'; } return html; } downloadTextFile(content, filename) { const blob = new Blob([content], { type: 'text/plain' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } showLoading(element, message = 'Loading...') { if (typeof element === 'string') { element = document.getElementById(element); } if (element) { element.innerHTML = `
${message}

${message}

`; } } hideLoading(element) { if (typeof element === 'string') { element = document.getElementById(element); } if (element) { element.innerHTML = ''; } } } // Global functions for HTML onclick handlers function showUploadModal() { const modal = new bootstrap.Modal(document.getElementById('uploadModal')); modal.show(); } function showCliModal() { const modal = new bootstrap.Modal(document.getElementById('cliModal')); modal.show(); } async function validateFile() { const fileInput = document.getElementById('fileInput'); const file = fileInput.files[0]; if (!file) { unitforge.showToast('Please select a file first', 'warning'); return; } try { const result = await unitforge.uploadUnitFile(file); // Display results const resultsDiv = document.getElementById('uploadResults'); const outputDiv = document.getElementById('validationOutput'); if (resultsDiv && outputDiv) { outputDiv.innerHTML = unitforge.formatValidationResults(result.validation); resultsDiv.classList.remove('d-none'); } if (result.validation.valid) { unitforge.showToast('File validation completed successfully!', 'success'); } else { unitforge.showToast(`Validation found ${result.validation.errors.length} error(s)`, 'warning'); } } catch (error) { unitforge.showToast(`Validation failed: ${error.message}`, 'error'); } } // Initialize the application const unitforge = new UnitForge(); // Export for use in other modules window.UnitForge = UnitForge; window.unitforge = unitforge;