PageSourceSearch

https://cameroun24.net/js/main.js

js cameroun24.net collected 2026-10-02 11:47:06 UTC 23,041 bytes, 619 lines download raw bytes

1// Fonctions utilitaires
2const utils = {
3    debounce(func, wait) {
4        let timeout;
5        return function executedFunction(...args) {
6            const later = () => {
7                clearTimeout(timeout);
8                func(...args);
9            };
10            clearTimeout(timeout);
11            timeout = setTimeout(later, wait);
12        };
13    }
14};
15
16// Gestionnaire du site
17class SiteManager {
18    constructor() {
19        // Attendre que le DOM soit complètement chargé
20        if (document.readyState === 'loading') {
21            document.addEventListener('DOMContentLoaded', () => this.init());
22        } else {
23            this.init();
24        }
25    }
26
27    init() {
28        console.log('🚀 Initialisation du SiteManager...');
29        this.setupNavigation();
30        this.setupHeroSlider();
31        this.setupSearch();
32        this.setupVisitorsCounter();
33        this.setupNewsletter();
34    }
35
36    // Navigation mobile CORRIGÉE
37    setupNavigation() {
38        console.log('🔧 Configuration de la navigation...');
39        
40        // Utiliser setTimeout pour s'assurer que le DOM est prêt
41        setTimeout(() => {
42            const menuToggle = document.querySelector('.menu-toggle');
43            const navMenu = document.querySelector('.nav-menu');
44            const searchToggle = document.querySelector('.search-toggle');
45            const searchBar = document.querySelector('.search-bar');
46            const searchClose = document.querySelector('.search-close');
47            
48            console.log('Menu toggle trouvé:', menuToggle);
49            console.log('Nav menu trouvé:', navMenu);
50            
51            if (!menuToggle || !navMenu) {
52                console.error('❌ Éléments de navigation non trouvés!');
53                this.createEmergencyMenu();
54                return;
55            }
56
57            // Créer l'overlay
58            let navOverlay = document.querySelector('.nav-overlay');
59            if (!navOverlay) {
60                navOverlay = document.createElement('div');
61                navOverlay.className = 'nav-overlay';
62                document.body.appendChild(navOverlay);
63            }
64
65            // Fonction pour fermer le menu mobile
66            const closeMobileMenu = () => {
67                navMenu.classList.remove('active');
68                menuToggle.classList.remove('active');
69                navOverlay.classList.remove('active');
70                document.body.style.overflow = '';
71                
72                // Fermer tous les sous-menus
73                document.querySelectorAll('.nav-menu li.active').forEach(item => {
74                    item.classList.remove('active');
75                });
76            };
77
78            // Toggle menu mobile
79            menuToggle.addEventListener('click', (e) => {
80                e.stopPropagation();
81                const isActive = navMenu.classList.contains('active');
82                
83                if (!isActive) {
84                    navMenu.classList.add('active');
85                    menuToggle.classList.add('active');
86                    navOverlay.classList.add('active');
87                    document.body.style.overflow = 'hidden';
88                } else {
89                    closeMobileMenu();
90                }
91            });
92
93            // Fermer le menu en cliquant sur l'overlay
94            navOverlay.addEventListener('click', closeMobileMenu);
95
96            // Gestion des sous-menus mobiles
97            const navItems = navMenu.querySelectorAll('li');
98            navItems.forEach(item => {
99                const submenu = item.querySelector('.submenu');
100                if (submenu) {
101                    const link = item.querySelector('a');
102                    link.addEventListener('click', (e) => {
103                        if (window.innerWidth <= 768) {
104                            e.preventDefault();
105                            e.stopPropagation();
106                            item.classList.toggle('active');
107                        }
108                    });
109                }
110            });
111
112            // Fermer le menu en cliquant sur un lien
113            navMenu.addEventListener('click', (e) => {
114                if (e.target.tagName === 'A' && window.innerWidth <= 768) {
115                    const parentLi = e.target.closest('li');
116                    if (!parentLi || !parentLi.querySelector('.submenu')) {
117                        setTimeout(closeMobileMenu, 100);
118                    }
119                }
120            });
121
122            // Recherche
123            if (searchToggle) {
124                searchToggle.addEventListener('click', () => {
125                    searchBar.classList.add('active');
126                    const input = searchBar.querySelector('input');
127                    if (input) input.focus();
128                    closeMobileMenu();
129                });
130            }
131
132            if (searchClose) {
133                searchClose.addEventListener('click', () => {
134                    searchBar.classList.remove('active');
135                });
136            }
137
138            // Fermer avec Échap
139            document.addEventListener('keydown', (e) => {
140                if (e.key === 'Escape') {
141                    closeMobileMenu();
142                    if (searchBar) searchBar.classList.remove('active');
143                }
144            });
145
146            // Redimensionnement
147            window.addEventListener('resize', () => {
148                if (window.innerWidth > 768) {
149                    closeMobileMenu();
150                }
151            });
152
153            console.log('✅ Navigation configurée avec succès!');
154
155        }, 100); // Délai pour s'assurer que le DOM est prêt
156    }
157
158    // Créer un menu d'urgence si le menu principal est cassé
159    createEmergencyMenu() {
160        console.log('🆘 Création du menu d\'urgence...');
161        
162        const existingEmergencyMenu = document.querySelector('.emergency-mobile-menu');
163        if (existingEmergencyMenu) return;
164
165        const emergencyHTML = `
166            <div class="emergency-mobile-menu" style="display: none;">
167                <button class="emergency-menu-toggle" style="
168                    position: fixed;
169                    top: 15px;
170                    right: 15px;
171                    z-index: 10001;
172                    background: #007a3d;
173                    border: 2px solid white;
174                    border-radius: 5px;
175                    padding: 10px;
176                    cursor: pointer;
177                    display: flex;
178                    flex-direction: column;
179                    gap: 4px;
180                ">
181                    <span style="display: block; width: 25px; height: 3px; background: white;"></span>
182                    <span style="display: block; width: 25px; height: 3px; background: white;"></span>
183                    <span style="display: block; width: 25px; height: 3px; background: white;"></span>
184                </button>
185                
186                <div class="emergency-nav-menu" style="
187                    position: fixed;
188                    top: 0;
189                    right: -300px;
190                    width: 280px;
191                    height: 100vh;
192                    background: white;
193                    box-shadow: -2px 0 10px rgba(0,0,0,0.3);
194                    padding: 80px 20px 20px;
195                    transition: 0.3s;
196                    z-index: 10000;
197                    overflow-y: auto;
198                ">
199                    <a href="/" style="display: block; padding: 15px; color: #333; border-bottom: 1px solid #eee;">Accueil</a>
200                    <a href="/societe.html" style="display: block; padding: 15px; color: #333; border-bottom: 1px solid #eee;">Actualités</a>
201                    <a href="/le-cameroun.html" style="display: block; padding: 15px; color: #333; border-bottom: 1px solid #eee;">Le Cameroun</a>
202                    <a href="/photos.html" style="display: block; padding: 15px; color: #333; border-bottom: 1px solid #eee;">Photos</a>
203                    <a href="/medias/videos.html" style="display: block; padding: 15px; color: #333; border-bottom: 1px solid #eee;">Médias</a>
204                    <a href="/selection-tv.html" style="display: block; padding: 15px; color: #333; border-bottom: 1px solid #eee;">TV en Direct</a>
205                </div>
206                
207                <div class="emergency-overlay" style="
208                    display: none;
209                    position: fixed;
210                    top: 0;
211                    left: 0;
212                    width: 100%;
213                    height: 100%;
214                    background: rgba(0,0,0,0.5);
215                    z-index: 9999;
216                "></div>
217            </div>
218        `;
219
220        document.body.insertAdjacentHTML('beforeend', emergencyHTML);
221        
222        const emergencyMenu = document.querySelector('.emergency-mobile-menu');
223        const emergencyToggle = document.querySelector('.emergency-menu-toggle');
224        const emergencyNav = document.querySelector('.emergency-nav-menu');
225        const emergencyOverlay = document.querySelector('.emergency-overlay');
226
227        // Afficher seulement sur mobile
228        if (window.innerWidth <= 768) {
229            emergencyMenu.style.display = 'block';
230        }
231
232        // Gestion du toggle
233        emergencyToggle.addEventListener('click', () => {
234            const isActive = emergencyNav.style.right === '0px';
235            
236            if (!isActive) {
237                emergencyNav.style.right = '0px';
238                emergencyOverlay.style.display = 'block';
239                document.body.style.overflow = 'hidden';
240            } else {
241                emergencyNav.style.right = '-300px';
242                emergencyOverlay.style.display = 'none';
243                document.body.style.overflow = '';
244            }
245        });
246
247        // Fermer avec l'overlay
248        emergencyOverlay.addEventListener('click', () => {
249            emergencyNav.style.right = '-300px';
250            emergencyOverlay.style.display = 'none';
251            document.body.style.overflow = '';
252        });
253
254        // Fermer en cliquant sur les liens
255        emergencyNav.addEventListener('click', (e) => {
256            if (e.target.tagName === 'A') {
257                setTimeout(() => {
258                    emergencyNav.style.right = '-300px';
259                    emergencyOverlay.style.display = 'none';
260                    document.body.style.overflow = '';
261                }, 100);
262            }
263        });
264
265        // Adaptation au redimensionnement
266        window.addEventListener('resize', () => {
267            if (window.innerWidth > 768) {
268                emergencyMenu.style.display = 'none';
269                emergencyNav.style.right = '-300px';
270                emergencyOverlay.style.display = 'none';
271            } else {
272                emergencyMenu.style.display = 'block';
273            }
274        });
275
276        console.log('✅ Menu d\'urgence créé!');
277    }
278
279    // Slider hero
280    setupHeroSlider() {
281        const heroSlides = document.querySelectorAll('.hero-slide');
282        const prevButton = document.querySelector('.hero-prev');
283        const nextButton = document.querySelector('.hero-next');
284        const indicators = document.querySelectorAll('.hero-indicator');
285        
286        if (!heroSlides || heroSlides.length <= 1) return;
287        
288        let currentSlide = 0;
289        const totalSlides = heroSlides.length;
290        let autoSlideInterval;
291        
292        const showSlide = (index) => {
293            heroSlides.forEach(slide => slide.classList.remove('active'));
294            indicators.forEach(indicator => indicator.classList.remove('active'));
295            
296            heroSlides[index].classList.add('active');
297            if (indicators[index]) {
298                indicators[index].classList.add('active');
299            }
300            
301            currentSlide = index;
302        };
303        
304        const nextSlide = () => {
305            let nextIndex = currentSlide + 1;
306            if (nextIndex >= totalSlides) nextIndex = 0;
307            showSlide(nextIndex);
308        };
309        
310        const prevSlide = () => {
311            let prevIndex = currentSlide - 1;
312            if (prevIndex < 0) prevIndex = totalSlides - 1;
313            showSlide(prevIndex);
314        };
315        
316        if (nextButton) {
317            nextButton.addEventListener('click', (e) => {
318                e.preventDefault();
319                nextSlide();
320                this.resetAutoSlide();
321            });
322        }
323        
324        if (prevButton) {
325            prevButton.addEventListener('click', (e) => {
326                e.preventDefault();
327                prevSlide();
328                this.resetAutoSlide();
329            });
330        }
331        
332        indicators.forEach((indicator, index) => {
333            indicator.addEventListener('click', (e) => {
334                e.preventDefault();
335                showSlide(index);
336                this.resetAutoSlide();
337            });
338        });
339        
340        this.startAutoSlide = () => {
341            autoSlideInterval = setInterval(nextSlide, 5000);
342        };
343        
344        this.stopAutoSlide = () => {
345            if (autoSlideInterval) {
346                clearInterval(autoSlideInterval);
347            }
348        };
349        
350        this.resetAutoSlide = () => {
351            this.stopAutoSlide();
352            this.startAutoSlide();
353        };
354        
355        this.startAutoSlide();
356        
357        const heroSlider = document.querySelector('.hero-slider');
358        if (heroSlider) {
359            heroSlider.addEventListener('mouseenter', this.stopAutoSlide);
360            heroSlider.addEventListener('mouseleave', this.startAutoSlide);
361        }
362        
363        document.addEventListener('keydown', (e) => {
364            if (e.key === 'ArrowLeft') {
365                prevSlide();
366                this.resetAutoSlide();
367            } else if (e.key === 'ArrowRight') {
368                nextSlide();
369                this.resetAutoSlide();
370            }
371        });
372        
373        document.addEventListener('visibilitychange', () => {
374            if (document.hidden) {
375                this.stopAutoSlide();
376            } else {
377                this.startAutoSlide();
378            }
379        });
380        
381        showSlide(0);
382    }
383
384    // Recherche
385    setupSearch() {
386        const searchForm = document.querySelector('.search-form');
387        if (!searchForm) return;
388
389        searchForm.addEventListener('submit', (e) => {
390            e.preventDefault();
391            const query = searchForm.querySelector('input').value.trim();
392            if (query) {
393                window.location.href = `/recherche.html?q=${encodeURIComponent(query)}`;
394            }
395        });
396    }
397
398    // Compteur de visiteurs
399    setupVisitorsCounter() {
400        this.updateVisitorsCount();
401        setInterval(() => this.updateVisitorsCount(), 30000);
402    }
403
404    async updateVisitorsCount() {
405        try {
406            const response = await fetch('/api/visitors.php');
407            const data = await response.json();
408            
409            const counters = document.querySelectorAll('#visitors-count');
410            counters.forEach(counter => {
411                counter.textContent = data.count;
412            });
413        } catch (error) {
414            console.error('Erreur mise à jour visiteurs:', error);
415        }
416    }
417
418    // NEWSLETTER - Fonction corrigée
419    setupNewsletter() {
420        console.log('📧 Initialisation de la newsletter...');
421        
422        const newsletterForms = document.querySelectorAll('.newsletter-form, .sidebar-newsletter');
423        
424        newsletterForms.forEach(form => {
425            form.addEventListener('submit', async (e) => {
426                e.preventDefault();
427                console.log('🎯 Formulaire newsletter soumis');
428                
429                const emailInput = form.querySelector('input[type="email"]');
430                const submitButton = form.querySelector('button[type="submit"]');
431                const messageContainer = document.getElementById('newsletter-message');
432                
433                if (!emailInput || !submitButton) return;
434                
435                const email = emailInput.value.trim();
436                
437                // Validation email
438                if (!this.isValidEmail(email)) {
439                    this.showNewsletterMessage('❌ Veuillez entrer une adresse email valide.', 'error', messageContainer);
440                    return;
441                }
442                
443                // État de chargement
444                this.setButtonLoading(submitButton, true);
445                
446                try {
447                    const response = await fetch('/newsletter.php', {
448                        method: 'POST',
449                        headers: {
450                            'Content-Type': 'application/x-www-form-urlencoded',
451                        },
452                        body: `email=${encodeURIComponent(email)}&action=subscribe`
453                    });
454                    
455                    const data = await response.json();
456                    console.log('📥 Réponse newsletter:', data);
457                    
458                    // CORRECTION : Décoder les caractères Unicode
459                    let displayMessage = data.message;
460                    
461                    // Remplacer les séquences Unicode
462                    displayMessage = displayMessage
463                        .replace(/\\u00e9/g, 'é')
464                        .replace(/\\u00e0/g, 'à')
465                        .replace(/\\u00e8/g, 'è')
466                        .replace(/\\u00ea/g, 'ê')
467                        .replace(/\\u00eb/g, 'ë')
468                        .replace(/\\u00ee/g, 'î')
469                        .replace(/\\u00ef/g, 'ï')
470                        .replace(/\\u00f4/g, 'ô')
471                        .replace(/\\u00f6/g, 'ö')
472                        .replace(/\\u00f9/g, 'ù')
473                        .replace(/\\u00fb/g, 'û')
474                        .replace(/\\u00fc/g, 'ü')
475                        .replace(/\\u00e7/g, 'ç')
476                        .replace(/\\\\/g, '');
477                    
478                    if (data.success) {
479                        this.showNewsletterMessage('✅ ' + displayMessage, 'success', messageContainer);
480                        emailInput.value = ''; // Vider le champ
481                    } else {
482                        this.showNewsletterMessage('❌ ' + displayMessage, 'error', messageContainer);
483                    }
484                    
485                } catch (error) {
486                    console.error('💥 Erreur newsletter:', error);
487                    this.showNewsletterMessage('❌ Une erreur est survenue. Veuillez réessayer.', 'error', messageContainer);
488                } finally {
489                    this.setButtonLoading(submitButton, false);
490                }
491            });
492        });
493    }
494
495    // Validation email
496    isValidEmail(email) {
497        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
498        return emailRegex.test(email);
499    }
500
501    // Affichage des messages newsletter
502    showNewsletterMessage(message, type, container) {
503        if (!container) {
504            console.error('❌ Container message newsletter non trouvé');
505            // Créer un container si il n'existe pas
506            const forms = document.querySelectorAll('.newsletter-form, .sidebar-newsletter');
507            forms.forEach(form => {
508                if (!form.nextElementSibling || !form.nextElementSibling.id.includes('newsletter-message')) {
509                    const newContainer = document.createElement('div');
510                    newContainer.id = 'newsletter-message';
511                    form.parentNode.insertBefore(newContainer, form.nextSibling);
512                    container = newContainer;
513                }
514            });
515        }
516        
517        if (!container) return;
518        
519        container.innerHTML = `
520            <div class="newsletter-message newsletter-${type}">
521                ${message}
522            </div>
523        `;
524        
525        console.log('📝 Message newsletter affiché:', message);
526        
527        // Auto-suppression après 8 secondes
528        setTimeout(() => {
529            if (container && container.innerHTML.includes('newsletter-message')) {
530                container.innerHTML = '';
531                console.log('🧹 Message newsletter nettoyé');
532            }
533        }, 8000);
534    }
535
536    // Gestion de l'état du bouton newsletter
537    setButtonLoading(button, isLoading) {
538        const originalHTML = button.innerHTML;
539        
540        if (isLoading) {
541            button.disabled = true;
542            button.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Envoi...';
543            button.style.opacity = '0.7';
544        } else {
545            button.disabled = false;
546            button.innerHTML = originalHTML;
547            button.style.opacity = '1';
548        }
549    }
550}
551
552// Initialisation
553console.log('📄 Script main.js chargé, initialisation...');
554window.siteManager = new SiteManager();
555
556// Animation pour les brèves
557document.addEventListener('DOMContentLoaded', function() {
558    setTimeout(() => {
559        const breveItems = document.querySelectorAll('.breve-item');
560        if (!breveItems || breveItems.length <= 1) return;
561        
562        let currentBreve = 0;
563        const totalBreves = breveItems.length;
564        
565        function showBreve(index) {
566            breveItems.forEach(item => item.classList.remove('active'));
567            breveItems[index].classList.add('active');
568            currentBreve = index;
569        }
570        
571        function nextBreve() {
572            let nextIndex = currentBreve + 1;
573            if (nextIndex >= totalBreves) nextIndex = 0;
574            showBreve(nextIndex);
575        }
576        
577        setInterval(nextBreve, 8000);
578    }, 500);
579});
580
581// Micro-interactions
582class MicroInteractions {
583    static init() {
584        // Hover sur les cartes
585        document.querySelectorAll('.news-card, .media-card').forEach(card => {
586            card.addEventListener('mouseenter', () => {
587                card.style.transform = 'translateY(-8px) scale(1.02)';
588            });
589            card.addEventListener('mouseleave', () => {
590                card.style.transform = 'translateY(0) scale(1)';
591            });
592        });
593    }
594}
595
596// Analytics utilisateur
597class UserAnalytics {
598    static trackEvent(category, action, label) {
599        if (typeof gtag !== 'undefined') {
600            gtag('event', action, {
601                'event_category': category,
602                'event_label': label
603            });
604        }
605    }
606
607    static trackReadTime(articleId) {
608        const startTime = Date.now();
609        window.addEventListener('beforeunload', () => {
610            const readTime = Math.round((Date.now() - startTime) / 1000);
611            this.trackEvent('Engagement', 'article_read_time', articleId);
612        });
613    }
614}
615
616// Initialiser les micro-interactions
617document.addEventListener('DOMContentLoaded', () => {
618    MicroInteractions.init();
619});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.