/* ELYSIA MARKDOWN STUDIO v1.0 - Utility Functions Toast, modals, storage, helpers */ const Utils = { // Toast Notifications toast: { show(message, type = "info", duration = 3000) { const container = document.getElementById("toast-container"); const toast = document.createElement("div"); toast.className = `toast ${type}`; const icons = { success: "✅", error: "❌", warning: "⚠️", info: "ℹ️", loading: "⏳" }; toast.innerHTML = ` ${icons[type]} ${message} `; container.appendChild(toast); const close = () => { toast.style.animation = "slideOut 0.3s ease"; setTimeout(() => toast.remove(), 300); }; toast.querySelector(".toast-close").onclick = close; if (duration > 0) setTimeout(close, duration); return toast; }, success: (msg, duration) => Utils.toast.show(msg, "success", duration), error: (msg, duration) => Utils.toast.show(msg, "error", duration), warning: (msg, duration) => Utils.toast.show(msg, "warning", duration), info: (msg, duration) => Utils.toast.show(msg, "info", duration) }, // Modal Management modal: { open(modalId) { const modal = document.getElementById(modalId); if (modal) { modal.classList.add("active"); document.body.style.overflow = "hidden"; } }, close(modalId) { const modal = document.getElementById(modalId); if (modal) { modal.classList.remove("active"); document.body.style.overflow = ""; } }, init() { document.querySelectorAll(".modal").forEach(modal => { modal.addEventListener("click", e => { if (e.target === modal) { Utils.modal.close(modal.id); } }); }); document.querySelectorAll(".modal-close, [data-modal]").forEach(btn => { btn.addEventListener("click", () => { const modalId = btn.getAttribute("data-modal"); if (modalId) Utils.modal.close(modalId); }); }); } }, // Local Storage Wrapper (with encryption for sensitive data) storage: { // Simple XOR encryption (basic obfuscation - better than plaintext) _encrypt(text) { const key = "ElysiaStudio2025"; // Simple key for obfuscation let encrypted = ""; for (let i = 0; i < text.length; i++) { encrypted += String.fromCharCode(text.charCodeAt(i) ^ key.charCodeAt(i % key.length)); } return btoa(encrypted); // Base64 encode }, _decrypt(encrypted) { try { const decoded = atob(encrypted); const key = "ElysiaStudio2025"; let decrypted = ""; for (let i = 0; i < decoded.length; i++) { decrypted += String.fromCharCode(decoded.charCodeAt(i) ^ key.charCodeAt(i % key.length)); } return decrypted; } catch { return null; } }, get(key, defaultValue = null) { try { const value = localStorage.getItem(key); if (!value) return defaultValue; // Decrypt API key if it's stored if (key === "apiKey") { const decrypted = this._decrypt(value); return decrypted || defaultValue; } return JSON.parse(value); } catch { return defaultValue; } }, set(key, value) { try { // Encrypt API key before storing if (key === "apiKey" && value) { localStorage.setItem(key, this._encrypt(value)); return true; } localStorage.setItem(key, JSON.stringify(value)); return true; } catch { return false; } }, remove(key) { localStorage.removeItem(key); }, clear() { localStorage.clear(); } }, // Format Date/Time formatDateTime(date) { const d = new Date(date); return d.toLocaleString(); }, // Format Date (short) formatDate(date) { const d = new Date(date); const now = new Date(); const diff = now - d; if (diff < 60000) return "Just now"; if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`; if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`; if (diff < 604800000) return `${Math.floor(diff / 86400000)}d ago`; return d.toLocaleDateString(); }, // Count Words countWords(text) { return text.trim() ? text.trim().split(/\s+/).length : 0; }, // Count Characters countChars(text) { return text.length; }, // Count Lines countLines(text) { return text.split("\n").length; }, // Calculate reading time (average 200 words per minute) readingTime(wordCount) { const minutes = Math.ceil(wordCount / 200); if (minutes < 1) return "< 1 min read"; if (minutes === 1) return "1 min read"; return `${minutes} min read`; }, // Download File downloadFile(content, filename, mimeType = "text/plain") { 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); }, // Copy to Clipboard async copyToClipboard(text) { try { await navigator.clipboard.writeText(text); Utils.toast.success("Copied to clipboard!"); return true; } catch (err) { Utils.toast.error("Failed to copy"); return false; } }, // Debounce Function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }, // Generate UUID uuid() { return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => { const r = (Math.random() * 16) | 0; const v = c === "x" ? r : (r & 0x3) | 0x8; return v.toString(16); }); }, // Sanitize Filename sanitizeFilename(name) { return name.replace(/[^a-z0-9_\-\.]/gi, "_"); }, // Truncate Text truncate(text, maxLength) { return text.length > maxLength ? text.substring(0, maxLength) + "..." : text; }, // Escape HTML escapeHtml(text) { const div = document.createElement("div"); div.textContent = text; return div.innerHTML; } }; // Initialize modals on load document.addEventListener("DOMContentLoaded", () => { Utils.modal.init(); }); export default Utils;