// === UTILITIES GÉNÉRALES === const Utils = { // Gestion des paramètres URL getUrlParams() { const params = new URLSearchParams(window.location.search); return { page: params.get('page') || 'home', game: params.get('game') || null, content: params.get('content') || null }; }, setUrlParams(params) { const url = new URL(window.location); Object.keys(params).forEach(key => { if (params[key]) { url.searchParams.set(key, params[key]); } else { url.searchParams.delete(key); } }); window.history.pushState({}, '', url); }, // Affichage/masquage du loading showLoading() { document.getElementById('loading').classList.add('show'); }, hideLoading() { document.getElementById('loading').classList.remove('show'); }, // Notifications toast showToast(message, type = 'info') { const toast = document.createElement('div'); toast.className = `toast toast-${type}`; toast.textContent = message; toast.style.cssText = ` position: fixed; top: 20px; right: 20px; background: ${type === 'success' ? 'var(--secondary-color)' : type === 'error' ? 'var(--error-color)' : 'var(--primary-color)'}; color: white; padding: 15px 20px; border-radius: var(--border-radius); box-shadow: var(--shadow); z-index: 1001; animation: slideIn 0.3s ease-out; `; document.body.appendChild(toast); setTimeout(() => { toast.style.animation = 'slideOut 0.3s ease-in'; setTimeout(() => document.body.removeChild(toast), 300); }, 3000); }, // Animation d'éléments animateElement(element, animation, duration = 300) { return new Promise(resolve => { element.style.animation = `${animation} ${duration}ms ease-out`; setTimeout(() => { element.style.animation = ''; resolve(); }, duration); }); }, // Génération d'ID unique generateId() { return Math.random().toString(36).substr(2, 9); }, // Mélange d'array (Fisher-Yates) shuffleArray(array) { const shuffled = [...array]; for (let i = shuffled.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; } return shuffled; }, // Sélection aléatoire d'éléments getRandomItems(array, count) { const shuffled = this.shuffleArray(array); return shuffled.slice(0, count); }, // Formatage du temps formatTime(seconds) { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins}:${secs.toString().padStart(2, '0')}`; }, // Debounce pour événements debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; }, // Vérification de support audio canPlayAudio() { return 'Audio' in window; }, // Chargement d'image avec promise loadImage(src) { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => resolve(img); img.onerror = reject; img.src = src; }); }, // Stockage local sécurisé storage: { set(key, value) { try { localStorage.setItem(key, JSON.stringify(value)); } catch (e) { logSh('LocalStorage not available:', e, 'WARN'); } }, get(key, defaultValue = null) { try { const item = localStorage.getItem(key); return item ? JSON.parse(item) : defaultValue; } catch (e) { logSh('LocalStorage read error:', e, 'WARN'); return defaultValue; } }, remove(key) { try { localStorage.removeItem(key); } catch (e) { logSh('LocalStorage remove error:', e, 'WARN'); } } } }; // Ajout de styles CSS dynamiques pour les animations const styleSheet = document.createElement('style'); styleSheet.textContent = ` @keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes slideOut { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } } `; document.head.appendChild(styleSheet); // Export global window.Utils = Utils;