452 lines
16 KiB
JavaScript
452 lines
16 KiB
JavaScript
// sw-register.js - Version 2.0
|
|
// Enregistrement et gestion du Service Worker
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
// ============================================
|
|
// CONFIGURATION
|
|
// ============================================
|
|
|
|
const SW_URL = '/service-worker.js?v=2.1';
|
|
const CHECK_INTERVAL = 60 * 60 * 1000; // 1 heure
|
|
const DEBUG = true;
|
|
|
|
// ============================================
|
|
// VÉRIFICATIONS PRÉALABLES
|
|
// ============================================
|
|
|
|
// 1. Vérifier si le Service Worker est supporté
|
|
if (!('serviceWorker' in navigator)) {
|
|
log('Service Worker non supporté par ce navigateur');
|
|
return;
|
|
}
|
|
|
|
// 2. Vérifier si on est en HTTPS ou localhost
|
|
if (window.location.protocol !== 'https:' && window.location.hostname !== 'localhost') {
|
|
log('Service Worker nécessite HTTPS en production');
|
|
return;
|
|
}
|
|
|
|
// ============================================
|
|
// FONCTION PRINCIPALE D'ENREGISTREMENT
|
|
// ============================================
|
|
|
|
function registerServiceWorker() {
|
|
log('Tentative d\'enregistrement du Service Worker...');
|
|
|
|
navigator.serviceWorker.register(SW_URL)
|
|
.then(handleRegistrationSuccess)
|
|
.catch(handleRegistrationError);
|
|
}
|
|
|
|
// ============================================
|
|
// GESTION DE LA RÉUSSITE
|
|
// ============================================
|
|
|
|
function handleRegistrationSuccess(registration) {
|
|
log('Service Worker enregistré avec succès:', registration.scope);
|
|
|
|
// Configurer les écouteurs d'événements
|
|
setupEventListeners(registration);
|
|
|
|
// Configurer la vérification périodique des mises à jour
|
|
setupUpdateChecking(registration);
|
|
|
|
// Initialiser la gestion hors ligne
|
|
setupOfflineManagement();
|
|
|
|
// Exposer l'API publique
|
|
exposePublicAPI(registration);
|
|
}
|
|
|
|
// ============================================
|
|
// GESTION DES ERREURS
|
|
// ============================================
|
|
|
|
function handleRegistrationError(error) {
|
|
console.error('Erreur d\'enregistrement du Service Worker:', error);
|
|
|
|
// Suggestions de dépannage basées sur l'erreur
|
|
if (error.name === 'SecurityError') {
|
|
console.warn('⚠️ Vérifiez que vous êtes en HTTPS');
|
|
} else if (error.name === 'TypeError') {
|
|
console.warn('⚠️ Vérifiez le chemin du Service Worker');
|
|
} else if (error.message.includes('MIME type')) {
|
|
console.warn('⚠️ Vérifiez l\'en-tête Content-Type du Service Worker');
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// CONFIGURATION DES ÉCOUTEURS
|
|
// ============================================
|
|
|
|
function setupEventListeners(registration) {
|
|
// Écouter les mises à jour du Service Worker
|
|
registration.addEventListener('updatefound', function() {
|
|
const newWorker = registration.installing;
|
|
log('Nouvelle version du Service Worker trouvée:', newWorker.state);
|
|
|
|
newWorker.addEventListener('statechange', function() {
|
|
log('État du nouveau Service Worker:', this.state);
|
|
|
|
if (this.state === 'installed' && navigator.serviceWorker.controller) {
|
|
showUpdateNotification();
|
|
}
|
|
|
|
if (this.state === 'activated') {
|
|
log('Nouveau Service Worker activé');
|
|
notifyClientsOfUpdate();
|
|
}
|
|
});
|
|
});
|
|
|
|
// Écouter les messages du Service Worker
|
|
navigator.serviceWorker.addEventListener('message', handleServiceWorkerMessage);
|
|
}
|
|
|
|
// ============================================
|
|
// GESTION DES MESSAGES DU SERVICE WORKER
|
|
// ============================================
|
|
|
|
function handleServiceWorkerMessage(event) {
|
|
log('Message reçu du Service Worker:', event.data);
|
|
|
|
switch (event.data.type) {
|
|
case 'SW_ACTIVATED':
|
|
log('Service Worker activé, version:', event.data.version);
|
|
break;
|
|
|
|
case 'CACHE_UPDATED':
|
|
log('Cache mis à jour:', event.data.resources);
|
|
break;
|
|
|
|
case 'OFFLINE_MODE':
|
|
showOfflineNotification();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// VÉRIFICATION PÉRIODIQUE DES MISES À JOUR
|
|
// ============================================
|
|
|
|
function setupUpdateChecking(registration) {
|
|
// Vérifier les mises à jour toutes les heures
|
|
setInterval(() => {
|
|
registration.update().catch(err => {
|
|
log('Pas de mise à jour disponible:', err.message);
|
|
});
|
|
}, CHECK_INTERVAL);
|
|
|
|
// Vérifier aussi quand la page redevient visible
|
|
document.addEventListener('visibilitychange', function() {
|
|
if (!document.hidden) {
|
|
registration.update().catch(() => {});
|
|
}
|
|
});
|
|
}
|
|
|
|
// ============================================
|
|
// GESTION HORS LIGNE
|
|
// ============================================
|
|
|
|
function setupOfflineManagement() {
|
|
window.addEventListener('online', function() {
|
|
log('Connexion rétablie');
|
|
document.documentElement.classList.remove('offline');
|
|
showOnlineNotification();
|
|
syncData();
|
|
});
|
|
|
|
window.addEventListener('offline', function() {
|
|
log('Mode hors ligne');
|
|
document.documentElement.classList.add('offline');
|
|
showOfflineNotification();
|
|
});
|
|
|
|
// Vérifier l'état initial
|
|
if (!navigator.onLine) {
|
|
document.documentElement.classList.add('offline');
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// NOTIFICATIONS
|
|
// ============================================
|
|
|
|
function showUpdateNotification() {
|
|
const isAnglophone = window.appConfig?.isAnglophone || false;
|
|
const message = isAnglophone
|
|
? 'A new version is available. Reload to update?'
|
|
: 'Une nouvelle version est disponible. Recharger pour mettre à jour ?';
|
|
|
|
// Utiliser SweetAlert2 si disponible
|
|
if (typeof Swal !== 'undefined') {
|
|
Swal.fire({
|
|
title: isAnglophone ? 'Update Available' : 'Mise à jour disponible',
|
|
text: message,
|
|
icon: 'info',
|
|
showCancelButton: true,
|
|
confirmButtonText: isAnglophone ? 'Reload' : 'Recharger',
|
|
cancelButtonText: isAnglophone ? 'Later' : 'Plus tard',
|
|
allowOutsideClick: false
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
window.location.reload();
|
|
}
|
|
});
|
|
} else {
|
|
// Fallback simple
|
|
if (confirm(message)) {
|
|
window.location.reload();
|
|
}
|
|
}
|
|
}
|
|
|
|
function showOnlineNotification() {
|
|
const isAnglophone = window.appConfig?.isAnglophone || false;
|
|
|
|
showToast(
|
|
isAnglophone ? 'Online' : 'En ligne',
|
|
isAnglophone ? 'Connection restored. Data will be synchronized.' : 'Connexion rétablie. Les données seront synchronisées.',
|
|
'success',
|
|
3000
|
|
);
|
|
|
|
// Notifier le Service Worker
|
|
if (navigator.serviceWorker.controller) {
|
|
navigator.serviceWorker.controller.postMessage({
|
|
type: 'NETWORK_RESTORED',
|
|
timestamp: Date.now()
|
|
});
|
|
}
|
|
}
|
|
|
|
function showOfflineNotification() {
|
|
const isAnglophone = window.appConfig?.isAnglophone || false;
|
|
|
|
showToast(
|
|
isAnglophone ? 'Offline' : 'Hors ligne',
|
|
isAnglophone ? 'No internet connection. Working in offline mode.' : 'Pas de connexion Internet. Mode hors ligne actif.',
|
|
'warning',
|
|
5000
|
|
);
|
|
}
|
|
|
|
function showToast(title, message, type, duration) {
|
|
const toast = document.createElement('div');
|
|
toast.className = `sw-toast sw-toast-${type}`;
|
|
toast.style.cssText = `
|
|
position: fixed;
|
|
top: 20px;
|
|
right: 20px;
|
|
background: ${type === 'success' ? '#27ae60' : '#e74c3c'};
|
|
color: white;
|
|
padding: 15px 20px;
|
|
border-radius: 8px;
|
|
z-index: 9999;
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
|
max-width: 300px;
|
|
animation: slideIn 0.3s ease;
|
|
`;
|
|
|
|
toast.innerHTML = `
|
|
<div style="font-weight: bold; margin-bottom: 5px;">${title}</div>
|
|
<div style="font-size: 14px;">${message}</div>
|
|
`;
|
|
|
|
document.body.appendChild(toast);
|
|
|
|
setTimeout(() => {
|
|
if (toast.parentNode) {
|
|
toast.style.animation = 'slideOut 0.3s ease';
|
|
setTimeout(() => toast.remove(), 300);
|
|
}
|
|
}, duration);
|
|
|
|
// Ajouter les styles d'animation si nécessaire
|
|
addToastStyles();
|
|
}
|
|
|
|
function addToastStyles() {
|
|
if (!document.getElementById('toast-styles')) {
|
|
const styles = document.createElement('style');
|
|
styles.id = 'toast-styles';
|
|
styles.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(styles);
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// SYNCHRONISATION DES DONNÉES
|
|
// ============================================
|
|
|
|
function syncData() {
|
|
log('Synchronisation des données...');
|
|
|
|
// Synchroniser avec le Service Worker
|
|
if (navigator.serviceWorker.controller) {
|
|
navigator.serviceWorker.controller.postMessage({
|
|
type: 'SYNC_DATA',
|
|
timestamp: Date.now()
|
|
});
|
|
}
|
|
|
|
// Synchroniser les données en attente
|
|
syncPendingData();
|
|
}
|
|
|
|
function syncPendingData() {
|
|
// À implémenter selon vos besoins
|
|
// Ex: synchroniser les formulaires en attente
|
|
}
|
|
|
|
// ============================================
|
|
// API PUBLIQUE
|
|
// ============================================
|
|
|
|
function exposePublicAPI(registration) {
|
|
window.serviceWorkerManager = {
|
|
// Forcer une mise à jour
|
|
update: function() {
|
|
return registration.update();
|
|
},
|
|
|
|
// Nettoyer le cache
|
|
clearCache: function() {
|
|
return caches.keys().then(cacheNames => {
|
|
return Promise.all(
|
|
cacheNames.map(cacheName => caches.delete(cacheName))
|
|
);
|
|
});
|
|
},
|
|
|
|
// Obtenir l'état
|
|
getStatus: function() {
|
|
return {
|
|
controller: !!navigator.serviceWorker.controller,
|
|
scope: registration.scope,
|
|
state: registration.installing ? registration.installing.state : 'active',
|
|
supports: {
|
|
push: 'pushManager' in registration,
|
|
sync: 'sync' in registration
|
|
}
|
|
};
|
|
},
|
|
|
|
// Forcer l'activation
|
|
skipWaiting: function() {
|
|
if (navigator.serviceWorker.controller) {
|
|
navigator.serviceWorker.controller.postMessage({ type: 'SKIP_WAITING' });
|
|
}
|
|
},
|
|
|
|
// Recharger pour appliquer les mises à jour
|
|
reload: function() {
|
|
window.location.reload();
|
|
},
|
|
|
|
// Vérifier si supporté
|
|
isSupported: 'serviceWorker' in navigator
|
|
};
|
|
}
|
|
|
|
// ============================================
|
|
// NOTIFICATION AUX CLIENTS
|
|
// ============================================
|
|
|
|
function notifyClientsOfUpdate() {
|
|
// Notifier toutes les pages ouvertes
|
|
if (navigator.serviceWorker.controller) {
|
|
navigator.serviceWorker.controller.postMessage({
|
|
type: 'NEW_VERSION_ACTIVATED',
|
|
version: '2.1',
|
|
timestamp: Date.now()
|
|
});
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// FONCTIONS UTILITAIRES
|
|
// ============================================
|
|
|
|
function log(message, ...args) {
|
|
if (DEBUG) {
|
|
console.log('[SW Register]', message, ...args);
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// INITIALISATION
|
|
// ============================================
|
|
|
|
// Attendre que la page soit complètement chargée
|
|
window.addEventListener('load', function() {
|
|
// Délai pour éviter la concurrence avec d'autres scripts
|
|
setTimeout(registerServiceWorker, 100);
|
|
});
|
|
|
|
// Nettoyer les anciens Service Workers au chargement
|
|
window.addEventListener('load', function() {
|
|
if ('serviceWorker' in navigator) {
|
|
navigator.serviceWorker.getRegistrations().then(function(registrations) {
|
|
registrations.forEach(function(registration) {
|
|
// Supprimer les SW des anciens domaines
|
|
if (!registration.scope.startsWith(window.location.origin)) {
|
|
log('Nettoyage ancien SW:', registration.scope);
|
|
registration.unregister();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
// ============================================
|
|
// GESTION DES ERREURS GLOBALES
|
|
// ============================================
|
|
|
|
window.addEventListener('error', function(event) {
|
|
if (event.message && event.message.includes('ServiceWorker')) {
|
|
console.error('Erreur Service Worker:', event.error);
|
|
}
|
|
});
|
|
|
|
// ============================================
|
|
// EXPORT POUR LES TESTS
|
|
// ============================================
|
|
|
|
// Pour le débogage depuis la console
|
|
window.debugSW = {
|
|
unregisterAll: function() {
|
|
return navigator.serviceWorker.getRegistrations()
|
|
.then(registrations => Promise.all(
|
|
registrations.map(r => r.unregister())
|
|
));
|
|
},
|
|
clearAllCaches: function() {
|
|
return caches.keys()
|
|
.then(cacheNames => Promise.all(
|
|
cacheNames.map(name => caches.delete(name))
|
|
));
|
|
},
|
|
forceUpdate: function() {
|
|
if (navigator.serviceWorker.controller) {
|
|
navigator.serviceWorker.controller.postMessage({ type: 'SKIP_WAITING' });
|
|
setTimeout(() => window.location.reload(), 1000);
|
|
}
|
|
}
|
|
};
|
|
|
|
log('Service Worker Manager initialisé');
|
|
|
|
})(); |