2025-04-08 06:37:14 +04:00
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
2025-07-17 20:10:31 +04:00
|
|
|
// Détection de l'état de connexion
|
|
|
|
|
function updateOnlineStatus() {
|
|
|
|
|
const offlineIndicator = document.querySelector('.offline-indicator');
|
|
|
|
|
|
|
|
|
|
if (!navigator.onLine) {
|
|
|
|
|
if (!offlineIndicator) {
|
|
|
|
|
const indicator = document.createElement('div');
|
|
|
|
|
indicator.className = 'offline-indicator show';
|
|
|
|
|
indicator.textContent = 'Mode hors ligne - Fonctionnalités limitées';
|
|
|
|
|
document.body.insertBefore(indicator, document.body.firstChild);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (offlineIndicator) {
|
|
|
|
|
offlineIndicator.remove();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Écouter les changements de connexion
|
|
|
|
|
window.addEventListener('online', updateOnlineStatus);
|
|
|
|
|
window.addEventListener('offline', updateOnlineStatus);
|
|
|
|
|
|
|
|
|
|
// Vérifier l'état initial
|
|
|
|
|
updateOnlineStatus();
|
2025-04-08 06:37:14 +04:00
|
|
|
// Gestion du menu mobile
|
|
|
|
|
const mobileMenuToggle = document.querySelector('.mobile-menu-toggle');
|
2025-04-08 06:51:33 +04:00
|
|
|
const mobileMenu = document.querySelector('.mobile-menu');
|
2025-04-08 06:37:14 +04:00
|
|
|
const mobileMenuClose = document.querySelector('.mobile-menu-close');
|
|
|
|
|
|
|
|
|
|
if (mobileMenuToggle) {
|
|
|
|
|
mobileMenuToggle.addEventListener('click', function() {
|
2025-04-08 06:51:33 +04:00
|
|
|
mobileMenu.classList.add('active');
|
2025-04-08 06:37:14 +04:00
|
|
|
document.body.style.overflow = 'hidden'; // Empêche le défilement de la page
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mobileMenuClose) {
|
|
|
|
|
mobileMenuClose.addEventListener('click', function() {
|
2025-04-08 06:51:33 +04:00
|
|
|
mobileMenu.classList.remove('active');
|
2025-04-08 06:37:14 +04:00
|
|
|
document.body.style.overflow = ''; // Réactive le défilement
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-22 12:57:49 +04:00
|
|
|
// Gestion du theme (mode clair/sombre)
|
|
|
|
|
const themeToggle = document.querySelector('#theme-toggle');
|
|
|
|
|
const html = document.documentElement;
|
2025-04-08 06:51:33 +04:00
|
|
|
|
2025-07-22 12:57:49 +04:00
|
|
|
// Charger le thème sauvegardé ou détecter la préférence système
|
|
|
|
|
function initTheme() {
|
|
|
|
|
const savedTheme = localStorage.getItem('theme');
|
|
|
|
|
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
|
|
|
|
|
|
|
|
let currentTheme = savedTheme;
|
|
|
|
|
if (!savedTheme) {
|
|
|
|
|
currentTheme = systemPrefersDark ? 'dark' : 'light';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
applyTheme(currentTheme);
|
|
|
|
|
updateThemeIcon(currentTheme);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Appliquer le thème
|
|
|
|
|
function applyTheme(theme) {
|
|
|
|
|
if (theme === 'dark') {
|
|
|
|
|
html.setAttribute('data-theme', 'dark');
|
|
|
|
|
} else {
|
|
|
|
|
html.removeAttribute('data-theme');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Mettre à jour l'icône du bouton
|
|
|
|
|
function updateThemeIcon(theme) {
|
|
|
|
|
if (themeToggle) {
|
|
|
|
|
const icon = themeToggle.querySelector('i');
|
|
|
|
|
if (theme === 'dark') {
|
2025-04-08 06:51:33 +04:00
|
|
|
icon.classList.remove('fa-sun');
|
|
|
|
|
icon.classList.add('fa-moon');
|
2025-07-22 12:57:49 +04:00
|
|
|
themeToggle.setAttribute('aria-label', 'Basculer vers le mode clair');
|
|
|
|
|
themeToggle.setAttribute('title', 'Mode clair');
|
|
|
|
|
} else {
|
|
|
|
|
icon.classList.remove('fa-moon');
|
|
|
|
|
icon.classList.add('fa-sun');
|
|
|
|
|
themeToggle.setAttribute('aria-label', 'Basculer vers le mode sombre');
|
|
|
|
|
themeToggle.setAttribute('title', 'Mode sombre');
|
2025-04-08 06:37:14 +04:00
|
|
|
}
|
2025-07-22 12:57:49 +04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Basculer le thème
|
|
|
|
|
function toggleTheme() {
|
|
|
|
|
const currentTheme = html.getAttribute('data-theme');
|
|
|
|
|
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
|
|
|
|
|
|
|
|
|
applyTheme(newTheme);
|
|
|
|
|
updateThemeIcon(newTheme);
|
|
|
|
|
localStorage.setItem('theme', newTheme);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Event listener pour le bouton
|
|
|
|
|
if (themeToggle) {
|
|
|
|
|
themeToggle.addEventListener('click', toggleTheme);
|
2025-04-08 06:37:14 +04:00
|
|
|
}
|
|
|
|
|
|
2025-07-22 12:57:49 +04:00
|
|
|
// Écouter les changements de préférence système
|
|
|
|
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) {
|
|
|
|
|
if (!localStorage.getItem('theme')) {
|
|
|
|
|
const newTheme = e.matches ? 'dark' : 'light';
|
|
|
|
|
applyTheme(newTheme);
|
|
|
|
|
updateThemeIcon(newTheme);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Initialiser le thème
|
|
|
|
|
initTheme();
|
|
|
|
|
|
2025-04-08 06:51:33 +04:00
|
|
|
// Gestion des carousels
|
|
|
|
|
const carousels = document.querySelectorAll('.carousel');
|
2025-10-08 20:50:52 +04:00
|
|
|
|
2025-04-08 06:51:33 +04:00
|
|
|
carousels.forEach(carousel => {
|
|
|
|
|
const container = carousel.querySelector('.carousel-container');
|
|
|
|
|
const dots = carousel.querySelectorAll('.carousel-dot');
|
|
|
|
|
const items = carousel.querySelectorAll('.carousel-item');
|
2025-10-08 20:50:52 +04:00
|
|
|
|
2025-04-08 06:51:33 +04:00
|
|
|
if (dots.length > 0 && items.length > 0) {
|
2025-10-08 20:50:52 +04:00
|
|
|
let currentIndex = 0;
|
|
|
|
|
let startX = 0;
|
|
|
|
|
let currentX = 0;
|
|
|
|
|
let isDragging = false;
|
|
|
|
|
let startTransform = 0;
|
|
|
|
|
|
|
|
|
|
function getItemWidth() {
|
|
|
|
|
return items[0].offsetWidth + parseInt(getComputedStyle(items[0]).marginRight);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateCarousel(index) {
|
|
|
|
|
const itemWidth = getItemWidth();
|
|
|
|
|
const newTransform = -index * itemWidth;
|
|
|
|
|
container.style.transform = `translateX(${newTransform}px)`;
|
|
|
|
|
|
|
|
|
|
// Mettre à jour les dots
|
|
|
|
|
dots.forEach(d => d.classList.remove('active'));
|
|
|
|
|
if (dots[index]) {
|
|
|
|
|
dots[index].classList.add('active');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentIndex = index;
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-08 06:51:33 +04:00
|
|
|
dots.forEach((dot, index) => {
|
|
|
|
|
dot.addEventListener('click', function() {
|
2025-10-08 20:50:52 +04:00
|
|
|
updateCarousel(index);
|
2025-04-08 06:51:33 +04:00
|
|
|
});
|
|
|
|
|
});
|
2025-10-08 20:50:52 +04:00
|
|
|
|
|
|
|
|
container.addEventListener('touchstart', function(e) {
|
|
|
|
|
isDragging = true;
|
|
|
|
|
startX = e.touches[0].clientX;
|
|
|
|
|
const transform = window.getComputedStyle(container).transform;
|
|
|
|
|
|
|
|
|
|
if (transform !== 'none') {
|
|
|
|
|
const matrix = new DOMMatrix(transform);
|
|
|
|
|
startTransform = matrix.m41;
|
|
|
|
|
} else {
|
|
|
|
|
startTransform = 0;
|
|
|
|
|
}
|
|
|
|
|
container.style.transition = 'none';
|
|
|
|
|
}, { passive: true });
|
|
|
|
|
|
|
|
|
|
container.addEventListener('touchmove', function(e) {
|
|
|
|
|
if (!isDragging) return;
|
|
|
|
|
|
|
|
|
|
currentX = e.touches[0].clientX;
|
|
|
|
|
const diff = currentX - startX;
|
|
|
|
|
const newTransform = startTransform + diff;
|
|
|
|
|
|
|
|
|
|
container.style.transform = `translateX(${newTransform}px)`;
|
|
|
|
|
}, { passive: true });
|
|
|
|
|
|
|
|
|
|
container.addEventListener('touchend', function(e) {
|
|
|
|
|
if (!isDragging) return;
|
|
|
|
|
isDragging = false;
|
|
|
|
|
|
|
|
|
|
container.style.transition = 'transform 0.5s ease';
|
|
|
|
|
|
|
|
|
|
const diff = currentX - startX;
|
|
|
|
|
const threshold = 50; // Distance minimale pour considérer un swipe
|
|
|
|
|
|
|
|
|
|
// Déterminer la direction du swipe
|
|
|
|
|
if (Math.abs(diff) > threshold) {
|
|
|
|
|
if (diff > 0 && currentIndex > 0) {
|
|
|
|
|
// Swipe vers la droite - item précédent
|
|
|
|
|
updateCarousel(currentIndex - 1);
|
|
|
|
|
} else if (diff < 0 && currentIndex < items.length - 1) {
|
|
|
|
|
// Swipe vers la gauche - item suivant
|
|
|
|
|
updateCarousel(currentIndex + 1);
|
|
|
|
|
} else {
|
|
|
|
|
// Retour à la position actuelle
|
|
|
|
|
updateCarousel(currentIndex);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Mouvement trop petit, retour à la position actuelle
|
|
|
|
|
updateCarousel(currentIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentX = 0;
|
|
|
|
|
}, { passive: true });
|
|
|
|
|
|
|
|
|
|
// Support du drag sur desktop (optionnel)
|
|
|
|
|
container.addEventListener('mousedown', function(e) {
|
|
|
|
|
isDragging = true;
|
|
|
|
|
startX = e.clientX;
|
|
|
|
|
const transform = window.getComputedStyle(container).transform;
|
|
|
|
|
if (transform !== 'none') {
|
|
|
|
|
const matrix = new DOMMatrix(transform);
|
|
|
|
|
startTransform = matrix.m41;
|
|
|
|
|
} else {
|
|
|
|
|
startTransform = 0;
|
|
|
|
|
}
|
|
|
|
|
container.style.transition = 'none';
|
|
|
|
|
container.style.cursor = 'grabbing';
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.addEventListener('mousemove', function(e) {
|
|
|
|
|
if (!isDragging) return;
|
|
|
|
|
|
|
|
|
|
currentX = e.clientX;
|
|
|
|
|
const diff = currentX - startX;
|
|
|
|
|
const newTransform = startTransform + diff;
|
|
|
|
|
|
|
|
|
|
container.style.transform = `translateX(${newTransform}px)`;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.addEventListener('mouseup', function(e) {
|
|
|
|
|
if (!isDragging) return;
|
|
|
|
|
isDragging = false;
|
|
|
|
|
|
|
|
|
|
container.style.transition = 'transform 0.5s ease';
|
|
|
|
|
container.style.cursor = 'grab';
|
|
|
|
|
|
|
|
|
|
const diff = currentX - startX;
|
|
|
|
|
const threshold = 50;
|
|
|
|
|
|
|
|
|
|
if (Math.abs(diff) > threshold) {
|
|
|
|
|
if (diff > 0 && currentIndex > 0) {
|
|
|
|
|
updateCarousel(currentIndex - 1);
|
|
|
|
|
} else if (diff < 0 && currentIndex < items.length - 1) {
|
|
|
|
|
updateCarousel(currentIndex + 1);
|
|
|
|
|
} else {
|
|
|
|
|
updateCarousel(currentIndex);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
updateCarousel(currentIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentX = 0;
|
|
|
|
|
});
|
2025-04-08 06:51:33 +04:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Gestion des clics sur les vidéos
|
2025-04-08 10:49:46 +04:00
|
|
|
function initVideoCardClicks() {
|
|
|
|
|
const videoCards = document.querySelectorAll('.video-card');
|
|
|
|
|
|
|
|
|
|
videoCards.forEach(card => {
|
|
|
|
|
if (!card.hasAttribute('data-click-initialized')) {
|
|
|
|
|
card.setAttribute('data-click-initialized', 'true');
|
|
|
|
|
card.addEventListener('click', function() {
|
|
|
|
|
const videoId = this.dataset.videoId;
|
|
|
|
|
if (videoId) {
|
|
|
|
|
window.location.href = 'video.php?id=' + videoId;
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-04-08 06:37:14 +04:00
|
|
|
}
|
|
|
|
|
});
|
2025-04-08 10:49:46 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialiser les clics sur les vidéos existantes
|
|
|
|
|
initVideoCardClicks();
|
2025-04-08 06:37:14 +04:00
|
|
|
|
|
|
|
|
// Lazy loading des images
|
2025-04-08 10:49:46 +04:00
|
|
|
function initLazyLoading() {
|
|
|
|
|
if ('IntersectionObserver' in window) {
|
|
|
|
|
const imageObserver = new IntersectionObserver((entries, observer) => {
|
|
|
|
|
entries.forEach(entry => {
|
|
|
|
|
if (entry.isIntersecting) {
|
|
|
|
|
const img = entry.target;
|
|
|
|
|
const src = img.getAttribute('data-src');
|
|
|
|
|
if (src) {
|
|
|
|
|
img.src = src;
|
|
|
|
|
img.removeAttribute('data-src');
|
|
|
|
|
}
|
|
|
|
|
observer.unobserve(img);
|
2025-04-08 06:37:14 +04:00
|
|
|
}
|
2025-04-08 10:49:46 +04:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.querySelectorAll('img[data-src]').forEach(img => {
|
|
|
|
|
imageObserver.observe(img);
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// Fallback pour les navigateurs qui ne supportent pas IntersectionObserver
|
|
|
|
|
document.querySelectorAll('img[data-src]').forEach(img => {
|
|
|
|
|
const src = img.getAttribute('data-src');
|
|
|
|
|
if (src) {
|
|
|
|
|
img.src = src;
|
|
|
|
|
img.removeAttribute('data-src');
|
2025-04-08 06:37:14 +04:00
|
|
|
}
|
|
|
|
|
});
|
2025-04-08 10:49:46 +04:00
|
|
|
}
|
2025-04-08 06:37:14 +04:00
|
|
|
}
|
|
|
|
|
|
2025-04-08 10:49:46 +04:00
|
|
|
// Initialiser le lazy loading
|
|
|
|
|
initLazyLoading();
|
|
|
|
|
|
2025-04-08 06:51:33 +04:00
|
|
|
// Gestion des boutons "Voir plus"
|
|
|
|
|
const viewMoreButtons = document.querySelectorAll('.view-more');
|
2025-04-08 06:37:14 +04:00
|
|
|
|
2025-04-08 06:51:33 +04:00
|
|
|
viewMoreButtons.forEach(button => {
|
2025-04-08 10:49:46 +04:00
|
|
|
// Déterminer le type de vidéos à charger
|
|
|
|
|
const section = button.closest('.video-section');
|
|
|
|
|
const videoGrid = section.querySelector('.video-grid');
|
|
|
|
|
let videoType = '';
|
2025-04-08 16:33:56 +04:00
|
|
|
let categoryId = null;
|
2026-01-15 19:38:55 +04:00
|
|
|
|
|
|
|
|
// Utiliser l'attribut data-video-type si présent (méthode prioritaire)
|
|
|
|
|
if (section.hasAttribute('data-video-type')) {
|
|
|
|
|
videoType = section.dataset.videoType;
|
|
|
|
|
} else if (section.hasAttribute('data-category-id')) {
|
|
|
|
|
// Section de catégorie
|
|
|
|
|
videoType = 'category';
|
|
|
|
|
categoryId = section.dataset.categoryId;
|
2025-04-08 10:49:46 +04:00
|
|
|
}
|
2026-01-15 19:38:55 +04:00
|
|
|
|
2025-04-08 16:33:56 +04:00
|
|
|
// Si aucun type reconnu, ne pas configurer l'événement
|
|
|
|
|
if (!videoType) return;
|
2026-01-15 19:38:55 +04:00
|
|
|
|
2025-04-08 06:51:33 +04:00
|
|
|
button.addEventListener('click', function() {
|
2026-01-15 19:38:55 +04:00
|
|
|
// Compter les vidéos déjà affichées pour calculer l'offset
|
|
|
|
|
const currentCount = videoGrid.querySelectorAll('.video-card').length;
|
|
|
|
|
|
2025-04-08 10:49:46 +04:00
|
|
|
// Changer le texte du bouton pendant le chargement
|
|
|
|
|
button.textContent = 'Chargement...';
|
|
|
|
|
button.disabled = true;
|
2026-01-15 19:38:55 +04:00
|
|
|
|
|
|
|
|
// Préparer l'URL avec les paramètres (utiliser offset au lieu de page)
|
|
|
|
|
let url = `ajax/load-more-videos?type=${videoType}&offset=${currentCount}`;
|
2025-04-08 16:33:56 +04:00
|
|
|
if (videoType === 'category' && categoryId) {
|
|
|
|
|
url += `&category=${categoryId}`;
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-17 09:58:55 +04:00
|
|
|
// Préparer les données avec token CSRF
|
|
|
|
|
const formData = new FormData();
|
2026-01-15 19:38:55 +04:00
|
|
|
const csrfMeta = document.querySelector('meta[name="csrf-token"]');
|
|
|
|
|
if (csrfMeta) {
|
|
|
|
|
formData.append('csrf_token', csrfMeta.getAttribute('content'));
|
|
|
|
|
}
|
2025-07-17 09:58:55 +04:00
|
|
|
|
2025-04-08 10:49:46 +04:00
|
|
|
// Faire la requête AJAX
|
2025-04-08 16:33:56 +04:00
|
|
|
fetch(url, {
|
2025-07-17 09:58:55 +04:00
|
|
|
method: 'POST',
|
2025-04-08 10:49:46 +04:00
|
|
|
headers: {
|
|
|
|
|
'X-Requested-With': 'XMLHttpRequest'
|
2025-07-17 09:58:55 +04:00
|
|
|
},
|
2026-01-15 19:38:55 +04:00
|
|
|
body: formData,
|
|
|
|
|
credentials: 'same-origin'
|
2025-04-08 10:49:46 +04:00
|
|
|
})
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
.then(data => {
|
|
|
|
|
if (data.success) {
|
|
|
|
|
// Ajouter les nouvelles vidéos à la grille
|
|
|
|
|
const tempDiv = document.createElement('div');
|
|
|
|
|
tempDiv.innerHTML = data.html;
|
|
|
|
|
|
|
|
|
|
// Ajouter chaque vidéo à la grille
|
|
|
|
|
while (tempDiv.firstChild) {
|
|
|
|
|
videoGrid.appendChild(tempDiv.firstChild);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Réinitialiser le texte du bouton
|
|
|
|
|
button.textContent = 'Voir plus';
|
|
|
|
|
button.disabled = false;
|
|
|
|
|
|
|
|
|
|
// Si plus de vidéos à charger, masquer le bouton
|
|
|
|
|
if (!data.hasMore) {
|
|
|
|
|
button.style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initialiser les clics sur les nouvelles vidéos
|
|
|
|
|
initVideoCardClicks();
|
|
|
|
|
// Initialiser le lazy loading pour les nouvelles images
|
|
|
|
|
initLazyLoading();
|
|
|
|
|
} else {
|
|
|
|
|
// En cas d'erreur, afficher un message et réactiver le bouton
|
|
|
|
|
console.error('Erreur lors du chargement des vidéos:', data.error);
|
|
|
|
|
button.textContent = 'Voir plus';
|
|
|
|
|
button.disabled = false;
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.catch(error => {
|
|
|
|
|
console.error('Erreur lors de la requête AJAX:', error);
|
|
|
|
|
button.textContent = 'Voir plus';
|
|
|
|
|
button.disabled = false;
|
|
|
|
|
});
|
2025-04-08 06:51:33 +04:00
|
|
|
});
|
|
|
|
|
});
|
2025-04-10 06:30:59 +04:00
|
|
|
|
|
|
|
|
// Scroll vers le haut avec effet moderne quand on clique sur le logo du footer
|
|
|
|
|
const footerLogo = document.querySelector('.footer-logo');
|
|
|
|
|
if (footerLogo) {
|
|
|
|
|
footerLogo.addEventListener('click', function() {
|
|
|
|
|
window.scrollTo({
|
|
|
|
|
top: 0,
|
|
|
|
|
behavior: 'smooth'
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-04-08 06:37:14 +04:00
|
|
|
});
|