// WebUI Static JavaScript Utilities // Simple helper functions shared across templates /** * Escape HTML to prevent XSS attacks when displaying user content */ function escapeHtml(text) { if (typeof text !== 'string') return ""; var e = document.createElement('div'); try { e.textContent = text; return e.innerHTML; } catch (err) { return String(text).replace(/[&<>"']/g, function(m) { switch (m) { case '&': return '&'; case '<': return '<'; case '>': return '>'; case '"': return '"'; case "'": return '''; default: return m; } }); } } /** * Format number with thousands separators */ function formatNumber(num) { if (num === null || num === undefined) return "N/A"; return new Intl.NumberFormat().format(Math.floor(num)); } /** * Show loading spinner */ function showLoading(elementId) { var el = document.getElementById(elementId); if (el) { el.innerHTML = '
Loading...
'; } } /** * Hide loading spinner */ function hideLoading(elementId) { var el = document.getElementById(elementId); if (el) { el.innerHTML = ""; } } /** * Create a toast notification */ function showToast(message, type) { var toast = document.createElement('div'); toast.className = 'toast ' + (type || 'info'); toast.textContent = message; toast.style.cssText = 'position:fixed;bottom:20px;right:20px;' + 'padding:12px 20px;border-radius:4px;margin-bottom:10px;' + 'background:#333;color:white;font-size:0.9rem;z-index:1000'; document.body.appendChild(toast); setTimeout(function() { toast.style.opacity = '0'; setTimeout(function() { toast.remove(); }, 200); }, 3000); } /** * Show error notification */ function showError(message) { showToast("Error: " + message, "error"); } /** * Show success notification */ function showSuccess(message) { showToast("Success: " + message, "success"); } /** * Make an API request with error handling */ async function apiRequest(endpoint, method = 'GET', data = null) { const config = window.webuiConfig; let url = config.apiUrl; if (!url.endsWith('/')) url += '/'; url += endpoint; const headers = {}; if (config.apiKey) { headers['X-API-Key'] = config.apiKey; } try { let response; if (method === 'POST') { response = await fetch(url, { method: method, headers: headers, body: JSON.stringify(data) }); } else { response = await fetch(url, { method: method, headers: headers }); } if (!response.ok) { throw new Error(response.statusText); } const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { return await response.json(); } else { return await response.text(); } } catch (err) { console.error('API request failed:', err); throw err; } } /** * Initialize tooltips if using them */ function initTooltips() { // Add tooltip functionality here if needed } /** * Debounce function for input handling */ function debounce(func, wait) { var timeout; return function executedFunction(...args) { var later = function() { clearTimeout(timeout); func.apply(this, args); }; timeout = setTimeout(later, wait); }; } // Export to window for use in templates window.escapeHtml = escapeHtml; window.formatNumber = formatNumber; window.showToast = showToast; window.showError = showError; window.showSuccess = showSuccess;