# [LIVRE] GUIDE ULTRA-DÉTAILLÉ DES NAVIGATEURS WEB
# ============================================================================
# [OBJECTIF] VOTRE RÉFÉRENCE COMPLÈTE POUR MAÎTRISER LES NAVIGATEURS
# ============================================================================

"""
Bienvenue dans le guide le plus complet sur les navigateurs web !

Ce guide est conçu pour transformer un DÉBUTANT COMPLET en EXPERT des navigateurs,
avec une approche pédagogique unique qui répond à 3 questions essentielles :
- COMMENT ? (étapes pratiques)
- POURQUOI ? (compréhension profonde)  
- QUAND ? (cas d'usage réels)

[TEMPS] TEMPS TOTAL D'ÉTUDE : 25-30 heures
[DOCS] NIVEAU : Débutant -> Expert
[COURS] OBJECTIF : Maîtrise complète des navigateurs web
"""


# ============================================================================
# [DOSSIER] STRUCTURE DU GUIDE
# ============================================================================

"""
Le guide est divisé en 4 PARTIES (fichiers séparés) :

[FICHIER] navigateur_partie1.txt (6-8h)
   ├─ Chapitre 0 : Introduction aux Navigateurs
   ├─ Chapitre 1 : Architecture (processus, event loop, workers)
   ├─ Chapitre 2 : Rendu HTML/CSS (parsing, layout, paint)
   ├─ Chapitre 3 : JavaScript et DOM
   └─ Chapitre 4 : Réseau et HTTP

[FICHIER] navigateur_ultra_detaille_suite.txt (6-8h)
   ├─ Chapitre 5 : DevTools - Console et Debugger
   ├─ Chapitre 6 : DevTools - Inspecteur et Network
   ├─ Chapitre 7 : Performance et Profiling
   └─ Chapitre 8 : Storage et Cookies

[FICHIER] navigateur_partie3_apis.txt (5-7h)
   ├─ Chapitre 9 : APIs Web Modernes
   ├─ Chapitre 10 : Service Workers et PWA
   ├─ Chapitre 11 : WebAssembly
   ├─ Chapitre 12 : Sécurité
   └─ Chapitre 13 : Extensions

[FICHIER] navigateur_partie4_finale.txt (8-10h)
   ├─ Chapitre 14 : Optimisation Performance
   ├─ Chapitre 15 : Accessibilité (A11y)
   ├─ Chapitre 16 : SEO et Crawlers
   ├─ Chapitre 17 : Testing Cross-Browser
   ├─ Chapitre 18 : DevOps et Déploiement
   ├─ Chapitre 19 : Monitoring et Analytics
   └─ Chapitre 20 : Best Practices Finales


ORDRE DE LECTURE RECOMMANDÉ :
1. Partie 1 (fondamentaux) - OBLIGATOIRE en premier
2. Partie 2 (DevTools) - OBLIGATOIRE
3. Partie 3 (APIs avancées) - Selon besoins
4. Partie 4 (Production) - ESSENTIEL pour professionnels
"""


# ============================================================================
# [OBJECTIF] COMMENT UTILISER CE GUIDE
# ============================================================================

"""
MÉTHODE D'APPRENTISSAGE OPTIMALE

1. LECTURE ACTIVE (30%)
   [OK] Lire chapitre par chapitre
   [OK] Prendre des notes manuscrites
   [OK] Poser questions mentalement
   [OK] Ne pas sauter de sections

2. PRATIQUE (60%) <- PLUS IMPORTANT !
   [OK] Ouvrir navigateur en parallèle
   [OK] Tester TOUS les exemples de code
   [OK] Expérimenter dans DevTools
   [OK] Modifier et observer résultats
   [OK] Faire les exercices [COURS]

3. RÉVISION (10%)
   [OK] Relire récapitulatifs [DOCS]
   [OK] Vérifier points clés [CLE]
   [OK] Refaire exercices complexes


SESSIONS D'ÉTUDE IDÉALES

Session type (2-3h) :
├─ 15 min : Révision chapitre précédent
├─ 90 min : Nouveau chapitre (lecture + pratique)
├─ 30 min : Exercices pratiques
├─ 15 min : Récapitulatif et notes
└─ 10 min : Pause / Questions


RYTHME RECOMMANDÉ

Débutant complet :
- 1 chapitre / jour
- 30 jours pour compléter
- 2-3h d'étude / jour

Développeur intermédiaire :
- 2 chapitres / jour  
- 15 jours pour compléter
- 3-4h d'étude / jour

Révision rapide :
- 1 partie / jour
- 4 jours pour réviser
- Focus sur exercices
"""


# ============================================================================
# [OUTILS] PRÉREQUIS ET PRÉPARATION
# ============================================================================

"""
CONNAISSANCES REQUISES

Minimum :
[OK] Bases HTML (balises, attributs)
[OK] Bases CSS (sélecteurs, propriétés)
[OK] Bases JavaScript (variables, fonctions)

Idéal :
[OK] HTML5 sémantique
[OK] CSS Flexbox/Grid
[OK] JavaScript ES6+
[OK] Notion d'async/await


CONFIGURATION REQUISE

Navigateurs à installer :
1. Google Chrome (principal)
   - DevTools les plus complets
   - Utilisé dans les exemples

2. Firefox Developer Edition
   - Excellents DevTools alternatifs
   - Comparaison et tests

3. Edge (optionnel)
   - Basé sur Chromium
   - Tests compatibilité

4. Safari (si Mac)
   - Tests WebKit
   - Spécificités Apple


Éditeur de code :
- VS Code (recommandé)
- Ou votre éditeur préféré


Dossier de travail :
Créer structure :

navigateur_learning/
├── 01_fondamentaux/
│   ├── test1.html
│   ├── test2.html
│   └── notes.txt
├── 02_devtools/
│   └── ...
├── 03_apis/
│   └── ...
└── 04_production/
    └── ...


Extensions Chrome utiles :
- Web Developer
- Lighthouse
- React Developer Tools (si React)
- Vue Developer Tools (si Vue)
"""


# ============================================================================
# [GUIDE] CONVENTION DE LECTURE
# ============================================================================

"""
SYMBOLES UTILISÉS

[IDEE] Information importante / Astuce
[REFLEXION] Question / Réflexion
[OK] Bonne pratique / Recommandation
[X] Mauvaise pratique / À éviter
[ATTENTION] Attention / Avertissement
[CLE] Point clé à retenir absolument
[COURS] Exercice pratique (À FAIRE !)
[DOCS] Récapitulatif de section
[OBJECTIF] Objectif d'apprentissage
[TEMPS] Temps estimé
[RAPIDE] Prêt pour la suite
[ANALYSE] Expérimentation / Test
[OUTILS] Outil pratique


FORMAT CODE

// Commentaires en ligne
/* Commentaires multilignes */

```javascript
// Exemple de code
const example = 'Code JavaScript';
```

```html
<!-- HTML -->
<div>Exemple HTML</div>
```

```css
/* CSS */
.class { color: blue; }
```


ANNOTATIONS CODE

// [X] MAUVAIS - Pratique à éviter
const bad = 'example';

// [OK] BON - Pratique recommandée  
const good = 'example';

// <- Explication importante
const value = 123; // <- Note en ligne
```


# ============================================================================
# [OBJECTIF] OBJECTIFS PAR PARTIE
# ============================================================================

"""
PARTIE 1 : FONDAMENTAUX (Débutant -> Intermédiaire)

Après cette partie, vous saurez :
[OK] Comment fonctionne un navigateur en profondeur
[OK] Processus de rendu complet (HTML -> Pixels)
[OK] Event Loop et asynchronicité JavaScript
[OK] Manipuler le DOM efficacement
[OK] Faire des requêtes réseau (fetch, CORS)
[OK] Gérer cookies et storage

Compétences acquises :
-> Comprendre performance loading
-> Debugger problèmes basiques
-> Écrire code DOM efficace


PARTIE 2 : DEVTOOLS (Intermédiaire -> Avancé)

Après cette partie, vous saurez :
[OK] Maîtriser console et debugger
[OK] Utiliser breakpoints efficacement
[OK] Analyser réseau (Network tab)
[OK] Profiler performance (Performance tab)
[OK] Inspecter storage et cookies

Compétences acquises :
-> Debugger bugs complexes rapidement
-> Identifier bottlenecks performance
-> Optimiser chargement page


PARTIE 3 : APIS AVANCÉES (Avancé)

Après cette partie, vous saurez :
[OK] Utiliser APIs modernes (Geolocation, Notifications, etc.)
[OK] Créer Progressive Web Apps (PWA)
[OK] Implémenter Service Workers
[OK] Comprendre WebAssembly
[OK] Sécuriser applications web

Compétences acquises :
-> Créer apps offline-first
-> Implémenter notifications push
-> Optimiser avec WebAssembly


PARTIE 4 : PRODUCTION (Expert)

Après cette partie, vous saurez :
[OK] Optimiser Core Web Vitals (LCP, FID, CLS)
[OK] Rendre sites accessibles (WCAG 2.1)
[OK] Optimiser SEO
[OK] Tester cross-browser
[OK] Monitorer performance production

Compétences acquises :
-> Déployer sites production-ready
-> Atteindre Lighthouse 95+
-> Conformité accessibilité AA
-> Performance optimale
"""


# ============================================================================
# [COURS] EXERCICES ET PRATIQUE
# ============================================================================

"""
TYPES D'EXERCICES

1. EXERCICES GUIDÉS [COURS]
   - Intégrés dans chaque chapitre
   - Instructions pas à pas
   - Correction fournie
   -> À FAIRE absolument !

2. EXPÉRIMENTATIONS [ANALYSE]
   - Tests dans DevTools
   - Observations et déductions
   - Apprentissage par découverte
   -> Modifier et observer

3. PROJETS PRATIQUES [OUTILS]
   - Mini-projets complets
   - Application concepts multiples
   - Code production-ready
   -> Construire portfolio


EXERCICE RÉCAPITULATIF FINAL

À la fin de Partie 4, vous créerez :
-> Site web complet optimisé
-> Performance : Lighthouse 95+
-> Accessibilité : WCAG AA
-> SEO : Best practices
-> Offline : Service Worker
-> Monitoring : Analytics

Ce projet démontre TOUTES les compétences acquises !
"""


# ============================================================================
# [GRAPHIQUE] ÉVALUATION PROGRESSION
# ============================================================================

"""
CHECKLIST PAR PARTIE

PARTIE 1 - FONDAMENTAUX
[WHITE_SQUARE] Expliquer architecture multi-processus Chrome
[WHITE_SQUARE] Décrire Event Loop en détail
[WHITE_SQUARE] Différencier tasks vs microtasks
[WHITE_SQUARE] Expliquer Critical Rendering Path
[WHITE_SQUARE] Utiliser fetch() avec gestion erreurs
[WHITE_SQUARE] Manipuler DOM efficacement
[WHITE_SQUARE] Comprendre CORS et Same-Origin Policy

PARTIE 2 - DEVTOOLS  
[WHITE_SQUARE] Utiliser console.* methods appropriés
[WHITE_SQUARE] Placer breakpoints stratégiques
[WHITE_SQUARE] Step through code (over, into, out)
[WHITE_SQUARE] Analyser waterfall Network tab
[WHITE_SQUARE] Profiler performance
[WHITE_SQUARE] Identifier memory leaks
[WHITE_SQUARE] Inspecter storage

PARTIE 3 - APIS AVANCÉES
[WHITE_SQUARE] Implémenter Geolocation
[WHITE_SQUARE] Créer notifications browser
[WHITE_SQUARE] Utiliser Clipboard API
[WHITE_SQUARE] Créer Service Worker basique
[WHITE_SQUARE] Implémenter cache strategies
[WHITE_SQUARE] Comprendre WebAssembly basics

PARTIE 4 - PRODUCTION
[WHITE_SQUARE] Optimiser Core Web Vitals
[WHITE_SQUARE] Atteindre LCP < 2.5s
[WHITE_SQUARE] Implémenter lazy loading
[WHITE_SQUARE] Créer site accessible (WCAG AA)
[WHITE_SQUARE] Utiliser ARIA appropriés
[WHITE_SQUARE] Optimiser images (WebP, srcset)
[WHITE_SQUARE] Mettre en place monitoring


AUTO-ÉVALUATION

Après chaque partie, demandez-vous :

1. Puis-je expliquer concepts à quelqu'un ?
2. Puis-je reproduire exemples de mémoire ?
3. Puis-je identifier et fixer problèmes similaires ?
4. Puis-je appliquer sur mes propres projets ?

Si OUI aux 4 -> Partie maîtrisée [OK]
Si NON -> Réviser sections concernées
"""


# ============================================================================
# 🆘 AIDE ET RESSOURCES
# ============================================================================

"""
SI VOUS ÊTES BLOQUÉ

1. RELIRE LA SECTION
   - Souvent, réponse dans texte
   - Lire lentement, mot par mot

2. TESTER DANS DEVTOOLS
   - Expérimenter directement
   - Observer comportement réel

3. CONSULTER MDN
   - Documentation officielle
   - Exemples supplémentaires
   - https://developer.mozilla.org

4. COMMUNAUTÉ
   - Stack Overflow
   - Discord dev communities
   - Reddit r/webdev

5. PRATIQUER PLUS
   - Répétition = maîtrise
   - Essayer variations
   - Créer projets personnels


RESSOURCES COMPLÉMENTAIRES

Documentation :
- MDN Web Docs (THE référence)
- Web.dev (Google)
- Can I Use (compatibilité)
- WCAG Guidelines (accessibilité)

Outils :
- Chrome DevTools
- Lighthouse
- WebPageTest
- PageSpeed Insights
- axe DevTools (a11y)

Blogs et Newsletters :
- CSS-Tricks
- Smashing Magazine
- JavaScript Weekly
- Frontend Focus

Vidéos :
- Google Chrome Developers (YouTube)
- Mozilla Developer (YouTube)
- Traversy Media
- Web Dev Simplified


MISES À JOUR

Navigateurs évoluent constamment !

Pour rester à jour :
- Suivre Chrome Developers Blog
- Lire release notes navigateurs
- Tester nouvelles APIs
- Participer à communautés


APRÈS CE GUIDE

Prochaines étapes :
1. Construire projets portfolio
2. Contribuer open source
3. Approfondir frameworks (React, Vue, etc.)
4. Spécialiser (Performance, A11y, Sécurité)
5. Enseigner aux autres (meilleur apprentissage !)
"""


# ============================================================================
# [BRAVO] MESSAGE FINAL
# ============================================================================

"""
═══════════════════════════════════════════════════════════════════════════
BIENVENUE DANS VOTRE PARCOURS D'APPRENTISSAGE ! [RAPIDE]
═══════════════════════════════════════════════════════════════════════════

Ce guide représente des centaines d'heures de compilation,
d'expérimentation et de pédagogie.

Chaque exemple a été testé.
Chaque explication a été réfléchie.
Chaque exercice a été conçu pour votre progression.


CONSEILS POUR RÉUSSIR

1. [OBJECTIF] ENGAGEMENT
   - Bloquez temps d'étude régulier
   - Éliminez distractions
   - Soyez patient avec vous-même

2. [OUTILS] PRATIQUE
   - Code > Lecture
   - Testez TOUT
   - Cassez et réparez
   - Expérimentez librement

3. [NOTE] NOTES
   - Écrivez concepts dans vos mots
   - Créez vos propres exemples
   - Documentez découvertes

4. [SYNC] RÉVISION
   - Répétition espacée
   - Refaire exercices
   - Appliquer sur projets réels

5. [ACCORD] COMMUNAUTÉ
   - Partagez apprentissage
   - Enseignez aux autres
   - Demandez aide si besoin


VOTRE TRANSFORMATION

AVANT :
[X] Console.log debugging uniquement
[X] Copier-coller sans comprendre
[X] Peur de DevTools
[X] Mystère du fonctionnement navigateur

APRÈS :
[OK] Debugging professionnel
[OK] Compréhension profonde
[OK] Maîtrise DevTools
[OK] Performance optimization
[OK] Accessibility champion
[OK] Production-ready code


Vous avez maintenant entre les mains la ressource
qui transformera votre approche du développement web.

Prenez votre temps.
Pratiquez intensément.
Devenez excellent.

Bon courage dans votre parcours ! [FORCE]

═══════════════════════════════════════════════════════════════════════════
COMMENCEZ MAINTENANT -> Ouvrez navigateur_partie1.txt
═══════════════════════════════════════════════════════════════════════════
"""


# ============================================================================
# [LISTE] CHECKLIST DÉMARRAGE RAPIDE
# ============================================================================

"""
AVANT DE COMMENCER

Installation :
[WHITE_SQUARE] Chrome installé et à jour
[WHITE_SQUARE] Firefox Developer Edition (optionnel)
[WHITE_SQUARE] Éditeur de code (VS Code recommandé)
[WHITE_SQUARE] Dossier navigateur_learning/ créé

Préparation :
[WHITE_SQUARE] Temps bloqué (2-3h sans interruption)
[WHITE_SQUARE] Environnement calme
[WHITE_SQUARE] Cahier de notes prêt
[WHITE_SQUARE] Motivation au maximum ! [FORCE]

Configuration :
[WHITE_SQUARE] DevTools ouvert (F12)
[WHITE_SQUARE] Console visible
[WHITE_SQUARE] Network tab familier
[WHITE_SQUARE] Fichier test.html créé


PREMIER EXERCICE (5 min)

1. Créer test.html :
```html
<!DOCTYPE html>
<html>
<head>
  <title>Mon Premier Test</title>
</head>
<body>
  <h1>Hello Navigateur!</h1>
  <button onclick="console.log('Click!')">Click me</button>
  
  <script>
    console.log('Page chargée');
    console.table({ name: 'Alice', age: 30 });
  </script>
</body>
</html>
```

2. Ouvrir dans Chrome
3. F12 -> Console
4. Observer logs
5. Cliquer bouton
6. Network tab -> Rafraîchir

Si tout fonctionne -> PRÊT ! [RAPIDE]


═══════════════════════════════════════════════════════════════════════════
-> COMMENCEZ PARTIE 1 MAINTENANT !
═══════════════════════════════════════════════════════════════════════════
"""

# ============================================================================
# [LIVRE] NAVIGATEUR WEB - GUIDE ULTRA-DÉTAILLÉ POUR DÉBUTANTS
# ============================================================================
#
# [OBJECTIF] GUIDE COMPLET POUR MAÎTRISER LES NAVIGATEURS DE ZÉRO À EXPERT
#
# Ce guide est organisé en 4 parties progressives :
#
# PARTIE 1 : FONDAMENTAUX DU NAVIGATEUR (navigateur_partie1.txt)
# - Chapitre 0 : Introduction aux Navigateurs Web
# - Chapitre 1 : Architecture du Navigateur
# - Chapitre 2 : Processus de Rendu HTML/CSS
# - Chapitre 3 : JavaScript et le DOM
# - Chapitre 4 : Réseau et Protocoles HTTP
#
# PARTIE 2 : OUTILS DE DÉVELOPPEMENT (navigateur_partie2.txt)
# - Chapitre 5 : DevTools - Console et Debugger
# - Chapitre 6 : DevTools - Inspecteur et Network
# - Chapitre 7 : Performance et Profiling
# - Chapitre 8 : Storage et Cookies
#
# PARTIE 3 : FONCTIONNALITÉS AVANCÉES (navigateur_partie3.txt)
# - Chapitre 9 : APIs Web Modernes
# - Chapitre 10 : Service Workers et PWA
# - Chapitre 11 : WebAssembly et Performance
# - Chapitre 12 : Sécurité du Navigateur
# - Chapitre 13 : Extensions et Personnalisation
#
# PARTIE 4 : OPTIMISATION ET PRODUCTION (navigateur_partie4.txt)
# - Chapitre 14 : Optimisation Performance
# - Chapitre 15 : Accessibilité (A11y)
# - Chapitre 16 : SEO et Crawlers
# - Chapitre 17 : Testing Cross-Browser
# - Chapitre 18 : DevOps et Déploiement
# - Chapitre 19 : Monitoring et Analytics
# - Chapitre 20 : Best Practices Finales
#
# [TEMPS] TEMPS DE LECTURE TOTAL : ~25-30 heures
# [DOCS] PRÉREQUIS : Notions de base HTML/CSS/JavaScript
#
# [IDEE] COMMENT UTILISER CE GUIDE :
# 1. Lisez les parties dans l'ordre
# 2. Testez TOUS les exemples dans votre navigateur
# 3. Faites les exercices pratiques
# 4. Explorez les DevTools en parallèle
#
# ============================================================================

"""
[OBJECTIF] PHILOSOPHIE DE CE GUIDE

COMMENT ? -> Explications pas à pas avec exemples concrets
POURQUOI ? -> Raisons et contexte technique détaillé
QUAND ? -> Cas d'usage et scénarios réels
PRATIQUE -> Manipulations dans le navigateur

Ce guide vise à être VOTRE SEULE RÉFÉRENCE sur les navigateurs !
"""

# ============================================================================
# [NOTE] CONVENTIONS UTILISÉES DANS CE GUIDE
# ============================================================================

"""
[IDEE] Information importante
[REFLEXION] Question / Réflexion
[OK] Bonne pratique
[X] Mauvaise pratique
[ATTENTION] Attention / Avertissement
[CLE] Point clé à retenir
[COURS] Exercice pratique
[DOCS] Résumé
[OBJECTIF] Objectif
[TEMPS] Temps estimé
[RAPIDE] Prêt pour la suite
[ANALYSE] Expérimentation
[OUTILS] Outil pratique
"""

# ============================================================================
# [OUTILS] PRÉPARATION DE L'ENVIRONNEMENT
# ============================================================================

"""
AVANT DE COMMENCER

1. NAVIGATEURS À INSTALLER
   Pour un apprentissage complet, installez :
   
   [OK] CHROME/CHROMIUM (Recommandé pour apprendre)
      - DevTools les plus complets
      - Mises à jour fréquentes
      - Grande communauté
      
   [OK] FIREFOX DEVELOPER EDITION
      - Excellents DevTools
      - Focus sur les standards web
      - Outils d'accessibilité avancés
      
   [OK] SAFARI (si Mac)
      - Tester compatibilité WebKit
      - DevTools iOS
      
   [ATTENTION] EDGE (Chromium)
      - Basé sur Chromium
      - Bon pour tester compatibilité Windows
"""

# Vérifier versions installées
"""
Chrome : chrome://version/
Firefox : about:support
Safari : Safari > À propos de Safari
Edge : edge://version/
"""

"""
2. ACTIVER LES DEVTOOLS

CHROME/EDGE :
- Windows/Linux : F12 ou Ctrl + Shift + I
- Mac : Cmd + Option + I
- Clic droit > Inspecter

FIREFOX :
- Windows/Linux : F12 ou Ctrl + Shift + I  
- Mac : Cmd + Option + I

SAFARI :
- Activer d'abord : Préférences > Avancées > Afficher menu Développement
- Cmd + Option + I
"""

"""
3. CRÉER UN DOSSIER DE TEST

Créez cette structure pour vos expérimentations :
"""

navigateur_learning/
├── 01_fondamentaux/
│   ├── exemple1.html
│   ├── exemple2.html
│   └── style.css
├── 02_devtools/
│   ├── debug.html
│   └── network.html
├── 03_apis/
│   ├── fetch.html
│   └── storage.html
└── 04_performance/
    ├── optimisation.html
    └── profiling.html

"""
[IDEE] POURQUOI CETTE STRUCTURE ?

Organisation claire pour :
[OK] Retrouver facilement vos expérimentations
[OK] Progresser chapitre par chapitre
[OK] Comparer différentes approches
[OK] Construire une base de référence personnelle
"""


# ============================================================================
# [GUIDE] CHAPITRE 0 : INTRODUCTION AUX NAVIGATEURS WEB
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Ce qu'est un navigateur web et son rôle
[OK] Histoire et évolution des navigateurs
[OK] Différences entre navigateurs modernes
[OK] Composants principaux d'un navigateur
[OK] Processus de navigation de bout en bout
"""


# ----------------------------------------------------------------------------
# [REFLEXION] QU'EST-CE QU'UN NAVIGATEUR WEB ?
# ----------------------------------------------------------------------------

"""
DÉFINITION SIMPLE

Un navigateur web est un logiciel qui :
1. Télécharge des ressources depuis Internet (HTML, CSS, JS, images...)
2. Interprète et exécute le code
3. Affiche le résultat à l'utilisateur
4. Permet l'interaction avec les pages web

[IDEE] ANALOGIE SIMPLE [SCENARIO]

Le navigateur = Théâtre complet

┌─────────────────────────────────────────┐
│           NAVIGATEUR WEB                │
├─────────────────────────────────────────┤
│ [DEMARRAGE] Moteur de Rendu                      │
│    -> Décor du théâtre (affichage)      │
│                                         │
│ [SCENARIO] Moteur JavaScript                    │
│    -> Acteurs (exécution code)          │
│                                         │
│ [WEB] Moteur Réseau                        │
│    -> Coursier (récupère ressources)    │
│                                         │
│ [VERROUILLE] Gestionnaire Sécurité                │
│    -> Vigile (protège utilisateur)      │
│                                         │
│ [SAUVEGARDE] Système de Stockage                  │
│    -> Archives (cookies, cache, etc.)   │
└─────────────────────────────────────────┘


RÔLE FONDAMENTAL

Le navigateur est l'INTERFACE entre :
- L'utilisateur humain
- Les serveurs web du monde entier
- Les applications web

Sans navigateur -> Pas d'accès au web moderne !
"""


# ----------------------------------------------------------------------------
# [DOC] HISTOIRE DES NAVIGATEURS
# ----------------------------------------------------------------------------

"""
CHRONOLOGIE ESSENTIELLE

1990 : WorldWideWeb (Tim Berners-Lee)
│      Premier navigateur de l'histoire
│      -> Seulement du texte, navigation basique
│
1993 : Mosaic
│      Premier navigateur populaire avec images
│      -> Interface graphique intuitive
│
1994 : Netscape Navigator
│      Dominant dans les années 90
│      -> Introduction de JavaScript (1995)
│
1995 : Internet Explorer (Microsoft)
│      "Guerre des navigateurs" commence
│      -> Pratiques anti-concurrentielles
│
1998 : Netscape devient open-source
│      -> Naissance du projet Mozilla
│
2003 : Safari (Apple)
│      Basé sur WebKit
│      -> Optimisé pour macOS
│
2004 : Firefox 1.0
│      Révolution : extensions et standards
│      -> Fin du monopole IE
│
2008 : Google Chrome
│      [RAPIDE] GAME CHANGER
│      -> Ultra-rapide (moteur V8)
│      -> Onglets isolés (processus séparés)
│      -> DevTools puissants
│
2015 : Microsoft Edge
│      Remplacement d'Internet Explorer
│      -> Adopte Chromium en 2019
│
AUJOURD'HUI :
- Chrome : ~65% parts de marché
- Safari : ~19%
- Edge : ~5%
- Firefox : ~3%
- Autres : ~8%


[IDEE] POURQUOI CETTE HISTOIRE COMPTE ?

Comprendre l'histoire aide à :
[OK] Saisir les enjeux actuels (monopole Chrome)
[OK] Apprécier les standards web modernes
[OK] Comprendre les différences entre navigateurs
[OK] Anticiper l'évolution future
"""


# ----------------------------------------------------------------------------
# [RECHERCHE] COMPARAISON DES NAVIGATEURS MODERNES
# ----------------------------------------------------------------------------

"""
NAVIGATEURS MAJEURS EN 2024


1. GOOGLE CHROME / CHROMIUM
──────────────────────────────

Moteur Rendu : Blink (fork de WebKit)
Moteur JS     : V8
Langage       : C++, JavaScript

[OK] AVANTAGES :
   - Performance excellente
   - DevTools les plus avancés
   - Synchronisation Google
   - Extensions nombreuses
   - Mises à jour rapides
   - Documentation extensive

[X] INCONVÉNIENTS :
   - Consommation RAM élevée
   - Préoccupations vie privée (Google)
   - Quasi-monopole (mauvais pour le web)

[OBJECTIF] BON POUR :
   - Développement web
   - Tester dernières APIs
   - Debugging avancé


2. MOZILLA FIREFOX
──────────────────

Moteur Rendu : Gecko
Moteur JS     : SpiderMonkey
Langage       : C++, Rust (Servo)

[OK] AVANTAGES :
   - Respect vie privée
   - Open source (vraiment)
   - DevTools excellents
   - Innovations (containers, CSS Grid inspector)
   - Standards web stricts

[X] INCONVÉNIENTS :
   - Performance légèrement inférieure
   - Parts de marché en baisse
   - Certaines APIs en retard

[OBJECTIF] BON POUR :
   - Vie privée
   - Développement respectueux standards
   - Testing CSS avancé


3. SAFARI (WEBKIT)
──────────────────

Moteur Rendu : WebKit
Moteur JS     : JavaScriptCore (Nitro)
Langage       : C++, Objective-C

[OK] AVANTAGES :
   - Optimisé pour macOS/iOS
   - Économie batterie excellente
   - Intégration écosystème Apple
   - Sécurité renforcée

[X] INCONVÉNIENTS :
   - Seulement sur Apple
   - Mises à jour liées à l'OS
   - Parfois en retard sur APIs modernes
   - DevTools moins complets

[OBJECTIF] BON POUR :
   - Développement iOS/macOS
   - Testing compatibilité Apple
   - Utilisateurs Apple


4. MICROSOFT EDGE (CHROMIUM)
────────────────────────────

Moteur Rendu : Blink (Chromium)
Moteur JS     : V8
Langage       : C++

[OK] AVANTAGES :
   - Basé sur Chromium (compatibilité)
   - Intégration Windows excellente
   - Collections et fonctionnalités uniques
   - Performance similaire Chrome

[X] INCONVÉNIENTS :
   - Préinstallé Windows (choix forcé)
   - Certaines fonctionnalités Microsoft uniquement

[OBJECTIF] BON POUR :
   - Utilisateurs Windows
   - Testing compatibilité Microsoft


TABLEAU COMPARATIF DÉTAILLÉ

┌──────────────┬─────────┬─────────┬────────┬──────┐
│              │ CHROME  │ FIREFOX │ SAFARI │ EDGE │
├──────────────┼─────────┼─────────┼────────┼──────┤
│ Performance  │   ***** │   ***** │ *****  │*****│
│ DevTools     │   ***** │   ***** │ *****  │*****│
│ Vie privée   │   ***** │   ***** │ *****  │*****│
│ Extensions   │   ***** │   ***** │ *****  │*****│
│ Standards    │   ***** │   ***** │ *****  │*****│
│ RAM Usage    │   ***** │   ***** │ *****  │*****│
│ Mobile       │   ***** │   ***** │ *****  │*****│
└──────────────┴─────────┴─────────┴────────┴──────┘


[IDEE] QUEL NAVIGATEUR UTILISER ?

DÉVELOPPEMENT :
-> Chrome/Chromium (principal)
-> Firefox (tests standards)
-> Safari (si développement Apple)

QUOTIDIEN :
-> Firefox (vie privée)
-> Safari (si Apple, batterie)
-> Brave (vie privée + Chromium)

TESTING :
-> TOUS ! (compatibilité cross-browser)
"""


# ----------------------------------------------------------------------------
# [CONSTRUCTION] COMPOSANTS PRINCIPAUX D'UN NAVIGATEUR
# ----------------------------------------------------------------------------

"""
ARCHITECTURE SIMPLIFIÉE D'UN NAVIGATEUR

┌──────────────────────────────────────────────────────┐
│                 INTERFACE UTILISATEUR                │
│   Barre d'adresse | Onglets | Boutons | Menus       │
└───────────────────────┬──────────────────────────────┘
                        │
┌───────────────────────[BLACK_DOWN-POINTING_TRIANGLE]──────────────────────────────┐
│              MOTEUR DE NAVIGATION                     │
│   Coordonne tous les composants                      │
└────┬──────────┬──────────┬──────────┬────────────────┘
     │          │          │          │
     [BLACK_DOWN-POINTING_TRIANGLE]          [BLACK_DOWN-POINTING_TRIANGLE]          [BLACK_DOWN-POINTING_TRIANGLE]          [BLACK_DOWN-POINTING_TRIANGLE]
┌─────────┐ ┌────────┐ ┌───────┐ ┌──────────┐
│ RENDU   │ │   JS   │ │RÉSEAU │ │ STOCKAGE │
│ ENGINE  │ │ ENGINE │ │       │ │          │
└─────────┘ └────────┘ └───────┘ └──────────┘
     │          │          │          │
     [BLACK_DOWN-POINTING_TRIANGLE]          [BLACK_DOWN-POINTING_TRIANGLE]          [BLACK_DOWN-POINTING_TRIANGLE]          [BLACK_DOWN-POINTING_TRIANGLE]
   HTML/CSS   Execute    HTTP/S    Cookies
   Parsing    Code JS    Requests  LocalStorage
   Layout                           Cache
   Paint                            IndexedDB


DÉTAIL DES COMPOSANTS


1. INTERFACE UTILISATEUR (UI)
─────────────────────────────

Ce que l'utilisateur voit :
- Barre d'adresse (URL bar)
- Boutons navigation (<- -> (sync))
- Signets/favoris
- Onglets
- Menu hamburger
- Barre de recherche

[IDEE] TOUT SAUF la zone d'affichage de la page


2. MOTEUR DE NAVIGATION (Browser Engine)
────────────────────────────────────────

Rôle : Chef d'orchestre
- Coordonne Rendu <-> Interface UI
- Gère les requêtes utilisateur
- Ordonne les actions

Exemples :
- Chrome : Blink engine coordination
- Firefox : Gecko coordination


3. MOTEUR DE RENDU (Rendering Engine)
─────────────────────────────────────

[IDEE] CŒUR DU NAVIGATEUR !

Responsabilités :
1. Parser HTML -> DOM tree
2. Parser CSS -> CSSOM tree
3. Combiner -> Render tree
4. Layout (calcul positions/tailles)
5. Paint (dessiner à l'écran)

Moteurs principaux :
- Blink (Chrome, Edge, Opera)
- Gecko (Firefox)
- WebKit (Safari)

[ANALYSE] PROCESSUS DÉTAILLÉ (voir Chapitre 2)


4. MOTEUR JAVASCRIPT (JS Engine)
────────────────────────────────

Exécute le code JavaScript

Moteurs principaux :
- V8 (Chrome, Edge, Node.js)
- SpiderMonkey (Firefox)
- JavaScriptCore/Nitro (Safari)

Fonctionnement :
1. Parse JS -> AST (Abstract Syntax Tree)
2. Compile -> Bytecode
3. Optimise -> Machine code (JIT)
4. Execute

Interaction avec DOM :
JS <--> Moteur Rendu (via APIs)

[ANALYSE] DÉTAILS (voir Chapitre 3)


5. SOUS-SYSTÈME RÉSEAU (Networking)
───────────────────────────────────

Gère TOUTES les communications :
- Requêtes HTTP/HTTPS
- WebSockets
- DNS lookups
- Certificats SSL/TLS
- Cache réseau
- Cookies

Fonctionnement :
1. URL demandée
2. DNS lookup (IP)
3. Connexion TCP/TLS
4. Requête HTTP
5. Réception réponse
6. Stockage cache

[ANALYSE] DÉTAILS (voir Chapitre 4)


6. PERSISTANCE DONNÉES (Data Persistence)
─────────────────────────────────────────

Stockage local :
- Cookies
- LocalStorage
- SessionStorage
- IndexedDB
- Cache API
- Web SQL (deprecated)

[ANALYSE] DÉTAILS (voir Chapitre 8)


7. SÉCURITÉ
───────────

Couche transversale :
- Same-Origin Policy
- CORS
- CSP (Content Security Policy)
- Sandbox processus
- HTTPS enforcement

[ANALYSE] DÉTAILS (voir Chapitre 12)


DIAGRAMME FLUX COMPLET

Utilisateur entre URL
        v
┌───────────────────┐
│ Interface UI      │
│ Barre d'adresse   │
└────────┬──────────┘
         │
         [BLACK_DOWN-POINTING_TRIANGLE]
┌───────────────────┐
│ Moteur Navigation │
│ Valide URL        │
└────────┬──────────┘
         │
         [BLACK_DOWN-POINTING_TRIANGLE]
┌───────────────────┐
│ Réseau            │
│ DNS + HTTP        │
└────────┬──────────┘
         │
         [BLACK_DOWN-POINTING_TRIANGLE] HTML reçu
┌───────────────────┐
│ Moteur Rendu      │
│ Parse HTML        │
│ Parse CSS         │
│ Layout + Paint    │
└────────┬──────────┘
         │
         [BLACK_DOWN-POINTING_TRIANGLE] Scripts trouvés
┌───────────────────┐
│ Moteur JS         │
│ Execute code      │
└────────┬──────────┘
         │
         [BLACK_DOWN-POINTING_TRIANGLE] Manipule DOM
┌───────────────────┐
│ Re-render         │
│ Mise à jour UI    │
└───────────────────┘
         │
         [BLACK_DOWN-POINTING_TRIANGLE]
    PAGE AFFICHÉE
"""


# ----------------------------------------------------------------------------
# [WATER_WAVE] PROCESSUS DE NAVIGATION COMPLET
# ----------------------------------------------------------------------------

"""
VOYAGE D'UNE PAGE WEB : DE L'URL À L'AFFICHAGE

[OBJECTIF] SCÉNARIO : Utilisateur tape "https://example.com" et appuie sur Entrée


PHASE 1 : TRAITEMENT URL
─────────────────────────

1. Validation URL
   - Navigateur vérifie format
   - Détecte protocole (https://)
   - Si pas de protocole -> ajoute "https://"

2. Autocomplétion / Suggestions
   - Historique
   - Signets
   - Moteur de recherche

3. Vérification Cache
   - Cache navigateur consulté
   - Si page récente cachée -> affichage direct
   - Sinon -> continue


PHASE 2 : RÉSOLUTION DNS
─────────────────────────

[IDEE] DNS = Annuaire d'Internet
   Convertit example.com -> 93.184.216.34 (IP)

Étapes DNS lookup :

1. Cache DNS navigateur
   ├─ Trouvé ? -> Utilise IP
   └─ Pas trouvé ? -> Continue

2. Cache DNS Système d'exploitation
   ├─ Trouvé ? -> Utilise IP
   └─ Pas trouvé ? -> Continue

3. Routeur / DNS local
   ├─ Trouvé ? -> Utilise IP
   └─ Pas trouvé ? -> Continue

4. Serveur DNS récursif (ISP)
   ├─ Trouvé ? -> Utilise IP
   └─ Pas trouvé ? -> Continue

5. Serveurs DNS autoritaires
   Root -> TLD (.com) -> Authoritative
   -> IP trouvée : 93.184.216.34

Temps typique : 20-120 ms

[ANALYSE] OBSERVER DNS :
   DevTools > Network > Regarder "Queueing" et "DNS Lookup"


PHASE 3 : ÉTABLISSEMENT CONNEXION TCP
──────────────────────────────────────

TCP = Transmission Control Protocol
3-way handshake :

1. SYN (Synchronize)
   Client -> Serveur : "Je veux me connecter"

2. SYN-ACK (Synchronize-Acknowledge)
   Serveur -> Client : "OK, je suis prêt"

3. ACK (Acknowledge)
   Client -> Serveur : "Parfait, c'est parti"

Temps : ~1.5 × RTT (Round Trip Time)
Typique : 30-150 ms


PHASE 4 : NÉGOCIATION TLS/SSL (HTTPS)
─────────────────────────────────────

Si HTTPS (recommandé !) :

1. Client Hello
   - Versions TLS supportées
   - Cipher suites disponibles

2. Server Hello
   - Version TLS choisie
   - Cipher suite choisie
   - Certificat serveur

3. Vérification Certificat
   - Autorité de certification valide ?
   - Domaine correspond ?
   - Date expiration OK ?

4. Échange clés
   - Clé symétrique générée
   - Chiffrement établi

5. Connexion sécurisée [OK]

Temps supplémentaire : 100-300 ms

[VERROUILLE] OBSERVER TLS :
   DevTools > Security > View certificate


PHASE 5 : REQUÊTE HTTP
──────────────────────

Requête HTTP/1.1 ou HTTP/2 envoyée :

GET / HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)...
Accept: text/html,application/xhtml+xml,...
Accept-Language: fr-FR,fr;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: session=abc123...

Temps : ~1 RTT


PHASE 6 : RÉPONSE SERVEUR
──────────────────────────

Serveur traite et répond :

HTTP/1.1 200 OK
Date: Mon, 17 Jan 2026 10:30:00 GMT
Server: Apache/2.4.41
Content-Type: text/html; charset=UTF-8
Content-Length: 15234
Cache-Control: max-age=3600
Set-Cookie: session=xyz789; HttpOnly; Secure

<!DOCTYPE html>
<html>
<head>
    <title>Example</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>Hello World</h1>
    <script src="app.js"></script>
</body>
</html>

Temps : Variable (10 ms - plusieurs secondes)


PHASE 7 : PARSING HTML
──────────────────────

[HOT] LE NAVIGATEUR COMMENCE À TRAVAILLER !

1. Construction DOM (Document Object Model)

HTML -> Tokens -> Nodes -> DOM Tree

<!DOCTYPE html>          document
<html>                      └─ html
  <head>                      ├─ head
    <title>Test</title>       │  └─ title
  </head>                     │     └─ "Test"
  <body>                      └─ body
    <h1>Hello</h1>               ├─ h1
  </body>                        │  └─ "Hello"
</html>                          └─ ...

Processus :
- Tokenization (découpage)
- Lexing (analyse lexicale)
- Tree construction

[RAPIDE] PARSING INCRÉMENTAL :
   Le navigateur ne attend pas tout le HTML !
   Il parse et affiche au fur et à mesure.


2. Détection ressources externes

Pendant parsing HTML, détecte :
- <link rel="stylesheet"> -> CSS
- <script src="..."> -> JavaScript
- <img src="..."> -> Images
- <video>, <audio>, etc.

[ATTENTION] SCRIPTS BLOQUANTS :

<script src="app.js"></script>
-> STOP parsing HTML
-> Télécharge + Execute script
-> REPREND parsing

Solutions :
<script src="app.js" defer></script>    <- Execute après DOM
<script src="app.js" async></script>    <- Execute dès téléchargé


PHASE 8 : PARSING CSS
─────────────────────

Pendant que HTML se parse :

1. Téléchargement CSS
   Requêtes parallèles pour :
   - <link> dans <head>
   - @import dans CSS
   - Inline <style>

2. Construction CSSOM (CSS Object Model)

CSS -> Tokens -> Nodes -> CSSOM Tree

body { margin: 0; }        body
h1 {                         ├─ margin: 0
  color: blue;             h1
  font-size: 2em;            ├─ color: blue
}                            ├─ font-size: 2em
                             └─ ...

3. Cascade et spécificité
   - Résolution conflits
   - Héritage propriétés
   - Calcul valeurs finales

[ATTENTION] CSS BLOQUE LE RENDU !
   Le navigateur attend le CSS critique avant d'afficher.


PHASE 9 : CONSTRUCTION RENDER TREE
───────────────────────────────────

Fusion DOM + CSSOM -> Render Tree

DOM Tree        CSSOM Tree      Render Tree
───────────  +  ────────────  = ────────────
html             body: {...}     html
├─ head                          └─ body
│  └─ title     h1: {...}           └─ h1 [blue, 2em]
└─ body                                └─ "Hello"
   └─ h1
      └─ "Hello"

Exclusions du Render Tree :
- display: none
- <head>, <script>
- Éléments cachés

Inclusions :
- Éléments visibles seulement
- Avec styles appliqués


PHASE 10 : LAYOUT (REFLOW)
──────────────────────────

[IDEE] Calcul positions et dimensions EXACTES

Processus :
1. Box Model calculation
   - width, height
   - padding, border, margin
   - Box-sizing

2. Layout Flow
   - Position normale (static)
   - Float
   - Flexbox
   - Grid
   - Absolute/Fixed

3. Recursive Layout
   - Parent -> Enfants
   - Propagation contraintes

Exemple :

<body>                  ┌────────────────────┐
  <div>                 │ body: 0,0 1920×1080│
    <h1>Hello</h1>      │  div: 0,0 1920×100 │
    <p>Text</p>         │   h1: 0,0  1920×32 │
  </div>                │   p:  0,32 1920×16 │
</body>                 └────────────────────┘

[RAPIDE] PERFORMANCE :
   Layout coûteux en CPU !
   Éviter layout thrashing (voir Chapitre 14)


PHASE 11 : PAINT
────────────────

Dessiner les pixels à l'écran

Étapes :

1. Paint Order (z-index, stacking context)
   - Background
   - Borders
   - Content
   - Outline

2. Layers création
   - GPU acceleration
   - Compositing layers

3. Rasterization
   - Vecteurs -> Pixels
   - Textures GPU

[DESIGN] PAINT OPERATIONS :
   - Fill (remplissage)
   - Stroke (contours)
   - Text (texte)
   - Images

[ANALYSE] OBSERVER :
   DevTools > More tools > Rendering
   -> Paint flashing


PHASE 12 : COMPOSITE
────────────────────

Assemblage final des layers

Si animations/transformations :
- Layer séparées (GPU)
- Composition hardware-accelerated

Propriétés créant layers :
- transform: translate3d(0,0,0)
- will-change
- position: fixed
- video, canvas


PHASE 13 : JAVASCRIPT EXECUTION
────────────────────────────────

Scripts s'exécutent :

1. Scripts synchrones (bloquants)
   - Parse
   - Compile
   - Execute

2. Scripts defer
   - Execute après DOMContentLoaded

3. Scripts async
   - Execute dès disponible

4. DOM Manipulation
   - Modifications DOM
   - Trigger re-layout/re-paint


PHASE 14 : ÉVÉNEMENTS
─────────────────────

Timeline événements :

1. DOMContentLoaded
   - DOM complètement parsé
   - Scripts defer exécutés
   - Images pas encore chargées

2. load
   - TOUTES ressources chargées
   - Images, CSS, scripts, etc.

Code :

document.addEventListener('DOMContentLoaded', () => {
    console.log('DOM ready!');
});

window.addEventListener('load', () => {
    console.log('Everything loaded!');
});


PHASE 15 : INTERACTIVITÉ
────────────────────────

Page devient interactive :

- Clicks fonctionnent
- Forms utilisables
- Animations lancées
- AJAX/Fetch possibles


[GRAPHIQUE] TIMELINE COMPLÈTE TYPIQUE

0ms      : Entrée URL
20ms     : DNS Lookup
50ms     : TCP Handshake
200ms    : TLS Handshake
250ms    : HTTP Request sent
300ms    : First Byte (TTFB - Time To First Byte)
320ms    : HTML parsing starts
350ms    : CSS requested
400ms    : First Paint (FP)
450ms    : First Contentful Paint (FCP)
500ms    : DOMContentLoaded
800ms    : Images loaded
1000ms   : Largest Contentful Paint (LCP)
1200ms   : load event
1500ms   : Time to Interactive (TTI)


[ANALYSE] MESURER DANS LE NAVIGATEUR

// Performance API
performance.timing

{
  navigationStart: 1642410600000,
  domainLookupStart: 1642410600020,
  domainLookupEnd: 1642410600050,
  connectStart: 1642410600050,
  connectEnd: 1642410600200,
  requestStart: 1642410600250,
  responseStart: 1642410600300,
  responseEnd: 1642410600320,
  domLoading: 1642410600320,
  domInteractive: 1642410600500,
  domContentLoadedEventStart: 1642410600500,
  domContentLoadedEventEnd: 1642410600520,
  domComplete: 1642410601200,
  loadEventStart: 1642410601200,
  loadEventEnd: 1642410601220
}

// Calculs utiles
const dns = timing.domainLookupEnd - timing.domainLookupStart;
const tcp = timing.connectEnd - timing.connectStart;
const ttfb = timing.responseStart - timing.requestStart;
const domReady = timing.domContentLoadedEventEnd - timing.navigationStart;
const pageLoad = timing.loadEventEnd - timing.navigationStart;

console.log(`DNS: ${dns}ms`);
console.log(`TCP: ${tcp}ms`);
console.log(`TTFB: ${ttfb}ms`);
console.log(`DOM Ready: ${domReady}ms`);
console.log(`Page Load: ${pageLoad}ms`);


[IDEE] POINTS CLÉS À RETENIR

1. Navigation = processus multi-étapes
2. Réseau = souvent le goulet
3. Parsing incrémental (progressif)
4. CSS bloque rendu
5. JavaScript bloque parsing (sauf async/defer)
6. Layout coûteux
7. Paint/Composite = GPU
8. Performance mesurable précisément
"""


# ============================================================================
# [COURS] EXERCICE PRATIQUE 0 : OBSERVER UNE NAVIGATION
# ============================================================================

"""
OBJECTIF : Voir le processus en action


ÉTAPE 1 : PRÉPARER

1. Ouvrir Chrome
2. F12 (DevTools)
3. Onglet Network
4. Cocher "Disable cache"
5. Cocher "Preserve log"


ÉTAPE 2 : NAVIGUER

1. Entrer URL : https://example.com
2. Observer les requêtes dans Network
3. Noter l'ordre :
   - example.com (HTML)
   - style.css
   - script.js
   - images...


ÉTAPE 3 : ANALYSER TIMING

Cliquer sur première requête (HTML) :
- Timing tab
- Voir :
  -> Queueing
  -> DNS Lookup
  -> Initial connection
  -> SSL
  -> Request sent
  -> Waiting (TTFB)
  -> Content Download


ÉTAPE 4 : OBSERVER RENDU

1. Performance tab
2. Record (Ctrl+E)
3. Rafraîchir page (Ctrl+R)
4. Stop recording
5. Analyser :
   - Parse HTML
   - Evaluate Script
   - Layout
   - Paint
   - Composite Layers


ÉTAPE 5 : MESURER

Console :
```javascript
// Timing navigation
const nav = performance.getEntriesByType('navigation')[0];

console.table({
  'DNS': nav.domainLookupEnd - nav.domainLookupStart,
  'TCP': nav.connectEnd - nav.connectStart,
  'Request': nav.responseStart - nav.requestStart,
  'Response': nav.responseEnd - nav.responseStart,
  'DOM Processing': nav.domComplete - nav.domLoading,
  'Total': nav.loadEventEnd - nav.fetchStart
});
```


QUESTIONS À SE POSER :

[REFLEXION] Quelle étape prend le plus de temps ?
[REFLEXION] Combien de requêtes pour charger la page ?
[REFLEXION] Quelles ressources bloquent le rendu ?
[REFLEXION] Le HTTPS ajoute combien de temps ?
[REFLEXION] Le JavaScript s'exécute quand exactement ?


[OK] VOUS AVEZ RÉUSSI SI :

- Vous voyez les étapes réseau
- Vous comprenez le timing
- Vous identifiez les ressources bloquantes
- Vous mesurez les performances
"""


# ============================================================================
# [DOCS] RÉCAPITULATIF CHAPITRE 0
# ============================================================================

"""
CE QUE VOUS AVEZ APPRIS

[OK] Définition navigateur web
[OK] Histoire et évolution
[OK] Comparaison navigateurs modernes
[OK] Architecture navigateur (composants)
[OK] Processus navigation complet (15 phases)
[OK] Timeline typique chargement page
[OK] Mesure performance


[CLE] POINTS CLÉS

1. Navigateur = orchestration complexe
2. Moteur rendu + JS + Réseau + UI
3. Parsing incrémental (progressif)
4. Plusieurs phases : DNS -> TCP -> TLS -> HTTP -> Parse -> Render
5. CSS bloque, JS peut bloquer
6. Performance mesurable (Performance API)


[OBJECTIF] AVANT DE CONTINUER

Assurez-vous de pouvoir :
[OK] Expliquer rôle du navigateur
[OK] Lister composants principaux
[OK] Décrire processus navigation
[OK] Ouvrir DevTools
[OK] Observer Network tab
[OK] Mesurer timing navigation


-> PROCHAINE ÉTAPE : Chapitre 1 - Architecture du Navigateur !

Vous allez plonger dans :
- Processus et threads
- Sandbox sécurité
- Architecture multi-processus Chrome
- Event Loop JavaScript
- Et bien plus !

Prêt ? C'est parti ! [RAPIDE]
"""

```python
# ============================================================================
# [GUIDE] CHAPITRE 1 : ARCHITECTURE DU NAVIGATEUR
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Architecture processus/threads du navigateur
[OK] Modèle mono-processus vs multi-processus
[OK] Sandbox et isolation de sécurité
[OK] Event Loop JavaScript en détail
[OK] Call Stack, Task Queue, Microtask Queue
[OK] Web Workers et parallélisme
[OK] Memory Management et Garbage Collection
"""


# ----------------------------------------------------------------------------
# [CONSTRUCTION] ARCHITECTURE PROCESSUS : ÉVOLUTION
# ----------------------------------------------------------------------------

"""
PROBLÈME : ARCHITECTURE MONO-PROCESSUS (Anciens navigateurs)

┌─────────────────────────────────────────┐
│     PROCESSUS NAVIGATEUR UNIQUE         │
│                                         │
│  UI │ Rendu │ JS │ Réseau │ Plugins     │
│                                         │
└─────────────────────────────────────────┘

[X] PROBLÈMES CRITIQUES :

1. CRASH COMPLET
   Un onglet plante -> TOUT le navigateur plante
   
2. BLOCAGE TOTAL
   Script infini -> Navigateur freezé
   
3. SÉCURITÉ FAIBLE
   Plugin malveillant -> Accès complet système
   
4. PAS DE PARALLÉLISME
   Un CPU core utilisé max


EXEMPLE CONCRET :

Imaginez 10 onglets ouverts :
- Onglet 1 : YouTube
- Onglet 2 : Gmail
- Onglet 3 : Site avec bug JS <- PLANTE
-> TOUS les onglets disparaissent ! [!]


[OK] SOLUTION : ARCHITECTURE MULTI-PROCESSUS

Introduite par Google Chrome (2008)
Révolution dans le monde des navigateurs !


ARCHITECTURE MODERNE (Chrome/Chromium)

┌─────────────────────────────────────────────────────────┐
│                  PROCESSUS PRINCIPAL                    │
│                    (Browser Process)                    │
│                                                         │
│  UI │ Gestion onglets │ Réseau │ Stockage │ Plugins     │
└────┬──────────────┬──────────────┬──────────────┬───────┘
     │              │              │              │
     [BLACK_DOWN-POINTING_TRIANGLE]              [BLACK_DOWN-POINTING_TRIANGLE]              [BLACK_DOWN-POINTING_TRIANGLE]              [BLACK_DOWN-POINTING_TRIANGLE]
┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐
│ RENDERER │  │ RENDERER │  │   GPU    │  │  PLUGIN  │
│ Onglet 1 │  │ Onglet 2 │  │ PROCESS  │  │ PROCESS  │
│          │  │          │  │          │  │          │
│ Moteur   │  │ Moteur   │  │ Graphics │  │ Flash    │
│ Rendu    │  │ Rendu    │  │ Hardware │  │ PDF      │
│ + JS     │  │ + JS     │  │ Accel.   │  │ etc.     │
└──────────┘  └──────────┘  └──────────┘  └──────────┘

[OK] AVANTAGES :

1. ISOLATION
   Crash onglet -> Autres continuent
   
2. SÉCURITÉ
   Sandboxing par processus
   
3. PARALLÉLISME
   Multi-core CPU utilisé
   
4. STABILITÉ
   Bug isolé, pas de propagation


TYPES DE PROCESSUS EN DÉTAIL


1. BROWSER PROCESS (Processus Principal)
────────────────────────────────────────

Rôle : Cerveau du navigateur

Responsabilités :
[OK] Interface utilisateur (UI)
[OK] Gestion onglets/fenêtres
[OK] Barre d'adresse
[OK] Favoris
[OK] Historique
[OK] Téléchargements
[OK] Communication inter-processus (IPC)
[OK] Réseau (dans Chrome moderne)
[OK] Stockage (cookies, localStorage)

Threads dans Browser Process :

UI Thread
├─ Dessine interface navigateur
├─ Gère input utilisateur
└─ Dispatche vers autres processus

IO Thread
├─ Gère IPC
├─ Communication réseau
└─ File I/O

Nombre : 1 seul (toujours actif)


2. RENDERER PROCESS (Processus de Rendu)
────────────────────────────────────────

[IDEE] LE PLUS IMPORTANT POUR LE DEV WEB !

Rôle : Transformer code -> Pixels

Un Renderer Process PAR :
- Onglet (généralement)
- iframe cross-origin
- Extension content script (parfois)

Responsabilités :
[OK] Parsing HTML -> DOM
[OK] Parsing CSS -> CSSOM
[OK] Layout (calcul positions)
[OK] Paint (dessiner)
[OK] Exécution JavaScript
[OK] Gestion événements
[OK] Web APIs (fetch, localStorage, etc.)

Threads dans Renderer Process :

Main Thread (Fil Principal)
├─ Parse HTML/CSS
├─ Execute JavaScript
├─ Layout calculations
├─ Paint instructions
└─ Handle events

[ATTENTION] CRITIQUE : Un seul thread pour tout !

Compositor Thread
├─ Compositing layers
├─ Smooth scrolling
└─ Animations (transform, opacity)

Raster Thread(s)
├─ Rasterization (vecteur -> pixels)
└─ GPU texture upload

Worker Threads
├─ Web Workers (JS parallèle)
├─ Service Workers
└─ Worklets


EXEMPLE CONCRET :

Site web avec 3 iframes :

example.com
├─ <iframe src="youtube.com">    <- Renderer Process 2
├─ <iframe src="example.com/a">  <- Même process que parent
└─ <iframe src="maps.google.com"> <- Renderer Process 3

Renderer Process 1 : example.com (parent)
Renderer Process 2 : youtube.com (cross-origin)
Renderer Process 3 : maps.google.com (cross-origin)


3. GPU PROCESS (Processus Graphique)
────────────────────────────────────

Rôle : Accélération matérielle

Responsabilités :
[OK] Compositing (assemblage layers)
[OK] 3D (WebGL, Three.js)
[OK] Video decode/encode
[OK] Canvas 2D accéléré
[OK] CSS transforms/animations

Nombre : 1 seul (partagé)

Communication :
Renderer -> GPU Process (commandes)
GPU Process -> Hardware GPU (exécution)


4. PLUGIN PROCESS (Processus Plugins)
─────────────────────────────────────

[ATTENTION] EN DÉCLIN (Flash mort, NPAPI deprecated)

Rôle : Exécuter plugins tiers

Un processus PAR plugin :
- Flash (†)
- Java Applets (†)
- Silverlight (†)
- PDF viewer (intégré maintenant)

Sécurité : Sandbox séparé


5. UTILITY PROCESS (Processus Utilitaires)
──────────────────────────────────────────

Rôles divers :
- Network Service (requêtes réseau)
- Audio Service (son)
- Storage Service (stockage)
- Device Service (périphériques)

Nombre : Variable selon besoins


6. EXTENSION PROCESS (Processus Extensions)
───────────────────────────────────────────

Un processus PAR extension Chrome

Isolation totale entre extensions

Background scripts exécutés ici


SCHÉMA COMPLET NAVIGATION

Utilisateur clique lien
        v
┌─────────────────────────┐
│   Browser Process       │
│   UI Thread             │
│   Détecte clic          │
└────────┬────────────────┘
         │
         │ IPC Message
         [BLACK_DOWN-POINTING_TRIANGLE]
┌─────────────────────────┐
│   Browser Process       │
│   IO Thread             │
│   Prépare navigation    │
└────────┬────────────────┘
         │
         │ Créer/réutiliser
         [BLACK_DOWN-POINTING_TRIANGLE]
┌─────────────────────────┐
│   Renderer Process      │
│   Main Thread           │
│   Reçoit HTML           │
│   Parse -> Render        │
└────────┬────────────────┘
         │
         │ Commandes rendu
         [BLACK_DOWN-POINTING_TRIANGLE]
┌─────────────────────────┐
│   GPU Process           │
│   Composite & Display   │
└─────────────────────────┘


GESTION MÉMOIRE : Process Per Site

Chrome moderne utilise "Process Per Site" :

Site A : example.com
├─ Onglet 1 : example.com         ┐
├─ Onglet 2 : example.com/page2   ├─ Renderer Process 1
└─ Onglet 3 : example.com/page3   ┘

Site B : google.com
├─ Onglet 4 : google.com          ┐
└─ Onglet 5 : google.com/search   ├─ Renderer Process 2
                                  ┘

Avantage : Économie RAM (partage process)
Inconvénient : Isolation moins stricte


LIMITE PROCESSUS

Chrome limite le nombre de Renderer Processes :
- Maximum : ~20-40 (selon RAM)
- Au-delà : Réutilisation processus

Raison : Éviter explosion RAM


[ANALYSE] OBSERVER LES PROCESSUS

DANS CHROME :

1. Menu -> Plus d'outils -> Gestionnaire de tâches
   (ou Shift + Esc)

Affiche :
- Processus navigateur
- Onglets (Renderer Processes)
- Extensions
- GPU Process
- Utilisation mémoire/CPU

2. chrome://process-internals/
   Vue détaillée technique


DANS FIREFOX :

about:processes

Affiche :
- Processus principal
- Processus contenu (onglets)
- Utilisation ressources
"""


# ----------------------------------------------------------------------------
# [VERROUILLE] SANDBOX ET SÉCURITÉ
# ----------------------------------------------------------------------------

"""
PROBLÈME : CODE NON FIABLE

Sites web = code de n'importe qui
-> Potentiellement malveillant
-> DOIT être isolé du système


[IDEE] SANDBOX = Prison de Sécurité

Principe :
Processus Renderer exécute dans environnement restreint
-> Pas d'accès direct système
-> Tout passe par Browser Process


ARCHITECTURE SÉCURITÉ

┌───────────────────────────────────────┐
│         SYSTÈME D'EXPLOITATION        │
│                                       │
│  ┌─────────────────────────────────┐ │
│  │     Browser Process             │ │
│  │     (Privilèges complets)       │ │
│  │                                 │ │
│  │  [OK] Accès disque                │ │
│  │  [OK] Accès réseau                │ │
│  │  [OK] Accès périphériques         │ │
│  └────────┬────────────────────────┘ │
│           │ IPC (contrôlé)           │
│  ┌────────[BLACK_DOWN-POINTING_TRIANGLE]────────────────────────┐ │
│  │   Renderer Process (SANDBOX)   │ │
│  │                                │ │
│  │  [X] Pas accès disque           │ │
│  │  [X] Pas accès réseau direct    │ │
│  │  [X] Pas accès OS APIs          │ │
│  │                                │ │
│  │  [OK] Execute JS                  │ │
│  │  [OK] Render HTML/CSS             │ │
│  │  [OK] Demandes via IPC            │ │
│  └────────────────────────────────┘ │
└───────────────────────────────────────┘


RESTRICTIONS SANDBOX

Renderer Process NE PEUT PAS :

[X] Lire/écrire fichiers disque directement
[X] Ouvrir connexions réseau (sauf via Browser)
[X] Accéder caméra/micro sans permission
[X] Lire clipboard sans action utilisateur
[X] Modifier paramètres système
[X] Accéder à d'autres processus
[X] Échapper le sandbox


Renderer Process PEUT :

[OK] Exécuter JavaScript
[OK] Parser HTML/CSS
[OK] Dessiner à l'écran (via GPU Process)
[OK] DEMANDER accès (via Browser Process)
[OK] Utiliser APIs web autorisées


COMMUNICATION SÉCURISÉE : IPC

IPC = Inter-Process Communication

Exemple : fetch() depuis site web

1. JavaScript : fetch('https://api.example.com/data')

2. Renderer Process -> Browser Process
   IPC Message: {
     type: 'NETWORK_REQUEST',
     url: 'https://api.example.com/data',
     method: 'GET',
     headers: {...}
   }

3. Browser Process vérifie :
   [OK] URL valide ?
   [OK] CORS autorisé ?
   [OK] Pas bloqué par politique ?

4. Browser Process -> Réseau
   Fait requête HTTP réelle

5. Réponse -> Browser Process -> Renderer Process
   IPC Message: {
     type: 'NETWORK_RESPONSE',
     data: {...}
   }

6. JavaScript reçoit réponse


COUCHES SÉCURITÉ

Défense en profondeur :

1. SANDBOX OS
   - Restrictions système d'exploitation
   - Pas d'accès kernel

2. SAME-ORIGIN POLICY
   - Isolation entre domaines
   - Pas d'accès cross-origin

3. PERMISSIONS API
   - Utilisateur doit autoriser
   - Caméra, micro, localisation, etc.

4. CSP (Content Security Policy)
   - Contrôle ressources chargées
   - Bloque inline scripts (optionnel)

5. HTTPS
   - Chiffrement transit
   - Authenticité serveur

6. SITE ISOLATION
   - Process par site (Chrome)
   - Protection Spectre/Meltdown


EXEMPLE ATTAQUE BLOQUÉE

Site malveillant essaie :

```javascript
// [X] ÉCHOUE - Pas d'accès disque
const fs = require('fs'); // undefined dans navigateur
fs.readFile('/etc/passwd', ...); // Erreur

// [X] ÉCHOUE - Same-Origin Policy
fetch('https://bank.com/api/account')
  .then(r => r.json())
  .then(data => {
    // CORS bloqué si bank.com ne l'autorise pas
  });

// [X] ÉCHOUE - Permission requise
navigator.geolocation.getCurrentPosition(pos => {
  // Popup permission apparaît
  // Utilisateur peut refuser
});

// [X] ÉCHOUE - Clipboard nécessite interaction
navigator.clipboard.readText()
  .then(text => {
    // Seulement si action utilisateur récente
  });
```


FAILLES HISTORIQUES

Même avec sandbox, failles possibles :

1. SPECTRE/MELTDOWN (2018)
   Failles CPU hardware
   -> Chrome : Site Isolation ajouté

2. SANDBOX ESCAPE
   Bug dans sandbox lui-même
   -> Rare mais possible
   -> Patches rapides critiques

3. SOCIAL ENGINEERING
   Sandbox ne protège pas contre :
   - Phishing
   - Utilisateur qui télécharge malware
   - Extensions malveillantes


[ANALYSE] VÉRIFIER SANDBOX

Chrome :
chrome://sandbox/

Affiche :
- Sandbox layer 1 : OS-level
- Sandbox layer 2 : Chrome-level
- Statut pour chaque processus


COMPROMIS SÉCURITÉ/FONCTIONNALITÉ

Plus de sécurité = Moins de fonctionnalités

C'est pourquoi :
- Pas d'accès fichiers arbitraires (File API limité)
- Pas de sockets TCP directs (WebSocket seulement)
- Permissions explicites requises
"""


# ----------------------------------------------------------------------------
# [CONFIG] EVENT LOOP : CŒUR DE JAVASCRIPT
# ----------------------------------------------------------------------------

"""
[IDEE] FONDAMENTAL POUR COMPRENDRE JS DANS LE NAVIGATEUR !


PROBLÈME : JAVASCRIPT EST SINGLE-THREADED

JavaScript s'exécute sur UN SEUL thread (Main Thread)

Mais alors... Comment gérer :
- Requêtes réseau (lentes) ?
- Timers (setTimeout) ?
- Événements utilisateur (clicks) ?
- Animations ?

Sans bloquer l'interface ? [REFLEXION]


[OK] SOLUTION : EVENT LOOP

Modèle asynchrone non-bloquant


COMPOSANTS DE L'EVENT LOOP

┌─────────────────────────────────────────────┐
│            HEAP (Mémoire)                   │
│    Stockage objets, variables               │
└─────────────────────────────────────────────┘

┌─────────────────────────────────────────────┐
│         CALL STACK (Pile d'appels)          │
│                                             │
│  function c() { ... }  <- En cours           │
│  function b() { c() }                       │
│  function a() { b() }                       │
│  (global)              <- Base               │
└─────────────────────────────────────────────┘
                    │
                    │ Synchrone
                    [BLACK_DOWN-POINTING_TRIANGLE]
┌─────────────────────────────────────────────┐
│           WEB APIs                          │
│  (Fournies par le navigateur)               │
│                                             │
│  setTimeout | fetch | DOM Events            │
│  setInterval| XMLHttpRequest                │
│  Promise    | localStorage                  │
└────────┬────────────────────────────────────┘
         │ Callbacks ready
         [BLACK_DOWN-POINTING_TRIANGLE]
┌─────────────────────────────────────────────┐
│        TASK QUEUE (Macro-tasks)             │
│                                             │
│  [onclick callback] [setTimeout callback]   │
└────────┬────────────────────────────────────┘
         │
         │ Event Loop check
         [BLACK_DOWN-POINTING_TRIANGLE]
┌─────────────────────────────────────────────┐
│      MICROTASK QUEUE                        │
│                                             │
│  [Promise.then] [MutationObserver]          │
└────────┬────────────────────────────────────┘
         │
         │ Event Loop priorité
         [BLACK_DOWN-POINTING_TRIANGLE]
      RENDER


CALL STACK EN DÉTAIL

Pile LIFO (Last In, First Out)

Exemple simple :

```javascript
function multiply(a, b) {
  return a * b;
}

function square(n) {
  return multiply(n, n);
}

function printSquare(n) {
  const result = square(n);
  console.log(result);
}

printSquare(4);
```

Call Stack progression :

Étape 1 :
┌─────────────────┐
│                 │
│                 │
│                 │
│   (global)      │ <- printSquare(4) appelé
└─────────────────┘

Étape 2 :
┌─────────────────┐
│                 │
│                 │
│ printSquare(4)  │ <- Exécution commence
│   (global)      │
└─────────────────┘

Étape 3 :
┌─────────────────┐
│                 │
│   square(4)     │ <- square appelé
│ printSquare(4)  │
│   (global)      │
└─────────────────┘

Étape 4 :
┌─────────────────┐
│ multiply(4, 4)  │ <- multiply appelé
│   square(4)     │
│ printSquare(4)  │
│   (global)      │
└─────────────────┘

Étape 5 (multiply retourne 16) :
┌─────────────────┐
│                 │
│   square(4)     │ <- multiply sorti
│ printSquare(4)  │
│   (global)      │
└─────────────────┘

Étape 6 (square retourne 16) :
┌─────────────────┐
│                 │
│                 │
│ printSquare(4)  │ <- square sorti
│   (global)      │
└─────────────────┘

Étape 7 (console.log) :
┌─────────────────┐
│                 │
│  console.log    │
│ printSquare(4)  │
│   (global)      │
└─────────────────┘

Étape 8 (printSquare termine) :
┌─────────────────┐
│                 │
│                 │
│                 │
│   (global)      │ <- Stack vide
└─────────────────┘


[ATTENTION] STACK OVERFLOW

Si récursion infinie :

```javascript
function recurse() {
  recurse(); // <- Appel infini
}

recurse();
```

Call Stack :
┌─────────────┐
│ recurse()   │
│ recurse()   │
│ recurse()   │
│ recurse()   │
│    ...      │
│ recurse()   │ <- 10,000+ appels
│  (global)   │
└─────────────┘

-> Error: Maximum call stack size exceeded


WEB APIs : ASYNCHRONE

Code asynchrone = hors Call Stack

Exemple setTimeout :

```javascript
console.log('1');

setTimeout(() => {
  console.log('2');
}, 1000);

console.log('3');
```

Exécution :

1. Call Stack : console.log('1')
   -> Affiche "1"
   -> Sort du stack

2. Call Stack : setTimeout(callback, 1000)
   -> Enregistré dans Web APIs
   -> Timer démarre (1000ms)
   -> Sort du stack immédiatement

3. Call Stack : console.log('3')
   -> Affiche "3"
   -> Sort du stack

4. Call Stack vide

5. 1000ms plus tard -> Timer expire
   -> Callback ajouté à Task Queue

6. Event Loop détecte :
   - Stack vide [OK]
   - Task Queue pas vide
   -> Callback déplacé vers Stack

7. Call Stack : callback
   -> console.log('2')
   -> Affiche "2"

Résultat : 1, 3, 2


TASK QUEUE (Macro-tasks)

File d'attente pour callbacks

Sources de tasks :
- setTimeout / setInterval
- setImmediate (Node.js)
- I/O operations
- UI rendering
- User interactions (click, etc.)

Ordre : FIFO (First In, First Out)


MICROTASK QUEUE

Priorité SUPÉRIEURE aux tasks

Sources de microtasks :
- Promise.then / catch / finally
- queueMicrotask()
- MutationObserver
- process.nextTick (Node.js)

[IDEE] RÈGLE : Microtasks AVANT tasks


EXEMPLE PRIORITÉ

```javascript
console.log('1');

setTimeout(() => console.log('2'), 0);

Promise.resolve().then(() => console.log('3'));

console.log('4');
```

Exécution :

1. Call Stack : console.log('1') -> "1"
2. setTimeout -> Task Queue
3. Promise.then -> Microtask Queue
4. Call Stack : console.log('4') -> "4"
5. Stack vide -> Event Loop check
6. Microtask Queue : Promise callback -> "3"
7. Microtask Queue vide
8. Task Queue : setTimeout callback -> "2"

Résultat : 1, 4, 3, 2

Ordre : Sync -> Microtasks -> Tasks


EXEMPLE COMPLEXE

```javascript
console.log('Script start');

setTimeout(() => {
  console.log('setTimeout');
}, 0);

Promise.resolve()
  .then(() => {
    console.log('Promise 1');
  })
  .then(() => {
    console.log('Promise 2');
  });

console.log('Script end');
```

Exécution détaillée :

État initial :
Call Stack: []
Microtask Queue: []
Task Queue: []

1. console.log('Script start')
   Call Stack: [console.log]
   -> Affiche "Script start"
   Call Stack: []

2. setTimeout(callback, 0)
   Call Stack: [setTimeout]
   -> Enregistre dans Web APIs
   Call Stack: []
   Task Queue: [setTimeout callback]

3. Promise.resolve().then(...)
   Call Stack: [Promise.then]
   -> Enregistre microtask
   Call Stack: []
   Microtask Queue: [Promise 1 callback]

4. console.log('Script end')
   Call Stack: [console.log]
   -> Affiche "Script end"
   Call Stack: []

5. Stack vide -> Event Loop check Microtasks

6. Microtask: Promise 1 callback
   Call Stack: [Promise 1 callback]
   -> Affiche "Promise 1"
   -> .then() enregistre nouvelle microtask
   Call Stack: []
   Microtask Queue: [Promise 2 callback]

7. Microtask: Promise 2 callback
   Call Stack: [Promise 2 callback]
   -> Affiche "Promise 2"
   Call Stack: []
   Microtask Queue: []

8. Microtasks vides -> Event Loop check Tasks

9. Task: setTimeout callback
   Call Stack: [setTimeout callback]
   -> Affiche "setTimeout"
   Call Stack: []

Résultat final :
Script start
Script end
Promise 1
Promise 2
setTimeout


EVENT LOOP ALGORITHM

Pseudo-code :

```
while (true) {
  // 1. Exécuter script initial (sync)
  executeSynchronousCode();
  
  // 2. Exécuter TOUTES les microtasks
  while (microtaskQueue.length > 0) {
    const microtask = microtaskQueue.shift();
    execute(microtask);
  }
  
  // 3. Render si nécessaire (60 FPS = ~16ms)
  if (timeToRender()) {
    requestAnimationFrame callbacks
    render();
  }
  
  // 4. Exécuter UNE task
  if (taskQueue.length > 0) {
    const task = taskQueue.shift();
    execute(task);
  }
  
  // 5. Retour à l'étape 2
}
```


BLOCKING vs NON-BLOCKING

[X] BLOCKING (Mauvais) :

```javascript
// Boucle lourde - BLOQUE le thread
function heavyComputation() {
  let result = 0;
  for (let i = 0; i < 1000000000; i++) {
    result += i;
  }
  return result;
}

console.log('Start');
const result = heavyComputation(); // <- UI freezée !
console.log('End', result);
```

Pendant heavyComputation() :
- UI freezée
- Pas de scroll
- Pas de clicks
- Pas d'animations
-> Mauvaise expérience utilisateur !


[OK] NON-BLOCKING (Bon) :

```javascript
// Découper en chunks
function heavyComputationAsync() {
  let result = 0;
  let i = 0;
  
  function chunk() {
    const end = Math.min(i + 1000000, 1000000000);
    
    for (; i < end; i++) {
      result += i;
    }
    
    if (i < 1000000000) {
      setTimeout(chunk, 0); // <- Yield au navigateur
    } else {
      console.log('Done', result);
    }
  }
  
  chunk();
}

console.log('Start');
heavyComputationAsync(); // <- UI responsive !
console.log('End');
```

Avec setTimeout(chunk, 0) :
- Chaque chunk exécuté
- Yield à l'Event Loop entre chunks
- UI peut render / répondre
-> Bonne expérience utilisateur !


[ANALYSE] VISUALISER L'EVENT LOOP

Outil : http://latentflip.com/loupe/

Excellent pour comprendre visuellement :
- Call Stack
- Web APIs
- Task Queue
- Event Loop

Essayez ce code :

```javascript
console.log('Hi');

setTimeout(function timeout() {
  console.log('There');
}, 5000);

console.log('Friend');
```


REQUESTANIMATIONFRAME

Cas spécial : animations fluides

```javascript
function animate() {
  // Code animation
  element.style.left = position + 'px';
  position += 1;
  
  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);
```

Exécution :
- Appelé AVANT chaque frame render
- ~60 FPS = toutes les ~16ms
- Synchronisé avec refresh écran
- Meilleur que setTimeout pour animations


ORDRE COMPLET EXECUTION

1. Synchronous code (Call Stack)
2. Microtasks (Promise, queueMicrotask)
3. requestAnimationFrame callbacks
4. Render (Layout, Paint, Composite)
5. requestIdleCallback (si temps disponible)
6. Tasks (setTimeout, setInterval, events)
7. Retour à 2


[COURS] EXERCICE PRATIQUE : PRÉDIRE L'ORDRE

```javascript
console.log('1');

setTimeout(() => console.log('2'), 0);

Promise.resolve()
  .then(() => console.log('3'))
  .then(() => console.log('4'));

setTimeout(() => console.log('5'), 0);

Promise.resolve().then(() => console.log('6'));

console.log('7');
```

Pause ici et essayez de prédire l'ordre ! [REFLEXION]

...

...

Réponse : 1, 7, 3, 6, 4, 2, 5

Explication :
- 1, 7 : Synchrone (Call Stack direct)
- 3, 6, 4 : Microtasks (Promises)
  - 3 ajouté, exécuté, ajoute 4
  - 6 ajouté, exécuté
  - 4 exécuté
- 2, 5 : Tasks (setTimeout)
  - FIFO : 2 puis 5
```


# ----------------------------------------------------------------------------
# [CHANTIER] WEB WORKERS : PARALLÉLISME RÉEL
# ----------------------------------------------------------------------------

"""
PROBLÈME : Main Thread surchargé

JavaScript = Single-threaded
-> Calculs lourds bloquent UI


[OK] SOLUTION : WEB WORKERS

Threads JavaScript séparés
-> Vrai parallélisme !


QU'EST-CE QU'UN WEB WORKER ?

Worker = Thread JavaScript dédié
- Exécuté en parallèle du Main Thread
- Pas d'accès au DOM
- Communication via messages


TYPES DE WORKERS

1. DEDICATED WORKER
   Lié à un seul script
   
2. SHARED WORKER
   Partagé entre plusieurs scripts/onglets
   
3. SERVICE WORKER
   Proxy réseau, cache (voir Chapitre 10)


CRÉER UN WORKER

Main script (index.html) :

```javascript
// Créer worker
const worker = new Worker('worker.js');

// Envoyer message au worker
worker.postMessage({ type: 'START', data: [1, 2, 3, 4, 5] });

// Recevoir message du worker
worker.onmessage = function(event) {
  console.log('Résultat du worker:', event.data);
};

// Gérer erreurs
worker.onerror = function(error) {
  console.error('Erreur worker:', error.message);
};

// Terminer worker
// worker.terminate();
```

Worker script (worker.js) :

```javascript
// Écouter messages du main thread
self.onmessage = function(event) {
  console.log('Worker a reçu:', event.data);
  
  // Calcul lourd
  const result = heavyComputation(event.data.data);
  
  // Envoyer résultat
  self.postMessage({ type: 'RESULT', value: result });
};

function heavyComputation(arr) {
  // Simulation calcul lourd
  let sum = 0;
  for (let i = 0; i < 1000000000; i++) {
    sum += i;
  }
  return sum + arr.reduce((a, b) => a + b, 0);
}
```


ARCHITECTURE AVEC WORKER

Main Thread              Worker Thread
────────────            ──────────────
Execute JS UI     [BLACK_LEFT-POINTING_POINTER]───[BLACK_RIGHT-POINTING_POINTER]  Execute JS calculs
Accès DOM               Pas accès DOM
Events                  Isolé
Render

Communication :
├─ postMessage() (asynchrone)
├─ onmessage
└─ MessageChannel


RESTRICTIONS WORKERS

Worker NE PEUT PAS :
[X] Accéder au DOM (window, document)
[X] Accéder certaines APIs (alert, localStorage)
[X] Manipuler interface utilisateur

Worker PEUT :
[OK] XMLHttpRequest / fetch
[OK] WebSockets
[OK] IndexedDB
[OK] Cache API
[OK] Calculs intensifs
[OK] Importer scripts (importScripts())


EXEMPLE PRATIQUE : FILTRAGE IMAGE

Main thread :

```javascript
const worker = new Worker('image-processor.js');

// Envoyer image data
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

worker.postMessage({
  imageData: imageData,
  filter: 'grayscale'
}, [imageData.data.buffer]); // <- Transferable Object (voir ci-dessous)

worker.onmessage = (e) => {
  // Recevoir image traitée
  ctx.putImageData(e.data.imageData, 0, 0);
};
```

Worker thread (image-processor.js) :

```javascript
self.onmessage = (e) => {
  const imageData = e.data.imageData;
  const data = imageData.data;
  
  // Appliquer filtre grayscale
  for (let i = 0; i < data.length; i += 4) {
    const avg = (data[i] + data[i+1] + data[i+2]) / 3;
    data[i] = avg;     // R
    data[i+1] = avg;   // G
    data[i+2] = avg;   // B
    // data[i+3] reste inchangé (Alpha)
  }
  
  self.postMessage({ imageData: imageData }, [data.buffer]);
};
```


TRANSFERABLE OBJECTS

Problème : Copier grosse data = lent

Solution : Transfer ownership (pas de copie !)

```javascript
// [X] Copie (lent pour gros ArrayBuffer)
worker.postMessage({ data: hugeArrayBuffer });

// [OK] Transfer (instantané)
worker.postMessage(
  { data: hugeArrayBuffer },
  [hugeArrayBuffer] // <- Liste Transferables
);

// Après transfer :
// hugeArrayBuffer est neutered (inutilisable) dans main thread
// Maintenant possédé par worker
```

Types Transferables :
- ArrayBuffer
- MessagePort
- ImageBitmap
- OffscreenCanvas


SHARED WORKERS

Partagé entre plusieurs onglets/windows

Main script :

```javascript
const worker = new SharedWorker('shared.js');

worker.port.start();

worker.port.postMessage('Hello from tab 1');

worker.port.onmessage = (e) => {
  console.log('Received:', e.data);
};
```

Shared worker (shared.js) :

```javascript
const connections = [];

self.onconnect = (e) => {
  const port = e.ports[0];
  connections.push(port);
  
  port.onmessage = (event) => {
    // Broadcast à tous les onglets
    connections.forEach(p => {
      p.postMessage(`Broadcast: ${event.data}`);
    });
  };
  
  port.start();
};
```

Use case : Chat multi-onglets, sync data


WORKLETS (MODERNE)

Worklets = Workers légers pour tâches spécifiques

Types :
- PaintWorklet (CSS Paint API)
- AudioWorklet (Web Audio)
- AnimationWorklet (animations)
- LayoutWorklet (CSS Layout API)

Exemple Paint Worklet :

```javascript
// main.js
CSS.paintWorklet.addModule('paint.js');

// CSS
.element {
  background-image: paint(myPainter);
}

// paint.js
registerPaint('myPainter', class {
  paint(ctx, geom, properties) {
    ctx.fillStyle = 'blue';
    ctx.fillRect(0, 0, geom.width, geom.height);
  }
});
```


[ANALYSE] QUAND UTILISER WORKERS ?

[OK] BON POUR :
- Calculs intensifs (crypto, compression)
- Processing data (parsing gros JSON/XML)
- Image/video processing
- Offline sync (Service Worker)
- Background tasks

[X] PAS NÉCESSAIRE :
- Petits calculs rapides
- Manipulation DOM directe
- Overhead communication > bénéfice


DEBUGGING WORKERS

Chrome DevTools :
1. Sources > Threads
2. Voir workers actifs
3. Debugger comme code normal
4. Console séparée par worker
"""


# ----------------------------------------------------------------------------
# [LOGIQUE] MEMORY MANAGEMENT ET GARBAGE COLLECTION
# ----------------------------------------------------------------------------

"""
GESTION MÉMOIRE JAVASCRIPT

JavaScript = Gestion automatique (Garbage Collection)
Développeur n'alloue/libère pas manuellement


HEAP MEMORY

Heap = Zone mémoire pour objets

Allocation :

```javascript
// Primitives (stack)
let x = 42;
let str = "hello";

// Objets (heap)
let obj = { name: "Alice" };
let arr = [1, 2, 3, 4, 5];
let func = function() { console.log("hi"); };
```

Stack vs Heap :

STACK (Pile)              HEAP (Tas)
────────────             ──────────────
Primitives               Objets
Rapide                   Plus lent
Taille fixe              Taille variable
Auto-nettoyé             Garbage Collected


GARBAGE COLLECTION (GC)

Algorithmes :

1. REFERENCE COUNTING (Ancien)
   [X] Problème : Cycles

```javascript
function createCycle() {
  let obj1 = {};
  let obj2 = {};
  
  obj1.ref = obj2;
  obj2.ref = obj1;
  
  // obj1 et obj2 se référencent mutuellement
  // -> Memory leak avec reference counting !
}
```

2. MARK-AND-SWEEP (Moderne)
   [OK] Résout les cycles

Algorithme :
1. MARK : Parcourir depuis roots (global, stack)
   Marquer tous objets atteignables

2. SWEEP : Supprimer objets non marqués


ROOTS :
- Variables globales (window)
- Variables locales (stack)
- Closures
- DOM nodes référencés


Exemple :

```javascript
let obj1 = { data: "A" };  // <- Atteignable (global var)
let obj2 = { data: "B" };  // <- Atteignable

obj1.ref = obj2;           // obj1 -> obj2

obj1 = null;               // obj1 plus atteignable
                           // obj2 toujours atteignable via obj1.ref ? NON
                           // obj2 sera garbage collected
```


MEMORY LEAKS COURANTS

1. GLOBAL VARIABLES ACCIDENTELLES

```javascript
// [X] Oubli 'let/const/var' -> global
function bad() {
  leak = "I'm global!"; // -> window.leak
}
```

2. TIMERS OUBLIÉS

```javascript
// [X] setTimeout jamais cleared
setInterval(() => {
  // Code qui garde références
}, 1000);

// [OK] Bon
const id = setInterval(() => { ... }, 1000);
// Plus tard :
clearInterval(id);
```

3. EVENT LISTENERS OUBLIÉS

```javascript
// [X] Listener jamais removed
element.addEventListener('click', handler);

// Element supprimé du DOM mais listener reste
// -> Memory leak

// [OK] Bon
element.addEventListener('click', handler);
// Plus tard :
element.removeEventListener('click', handler);
```

4. CLOSURES

```javascript
// [X] Closure garde référence
function outer() {
  const bigData = new Array(1000000);
  
  return function inner() {
    console.log('hi');
    // bigData pas utilisé mais gardé en mémoire !
  };
}

const fn = outer();
// bigData reste en mémoire tant que fn existe

// [OK] Bon
function outer() {
  const bigData = new Array(1000000);
  const needed = bigData[0];
  
  return function inner() {
    console.log(needed); // Seulement needed gardé
  };
}
```

5. DÉTACHED DOM NODES

```javascript
// [X] DOM node gardé en mémoire
let detached = document.getElementById('element');
element.parentNode.removeChild(element);
// element supprimé du DOM
// Mais detached garde référence
// -> Memory leak

// [OK] Bon
let element = document.getElementById('element');
element.parentNode.removeChild(element);
element = null; // Libérer référence
```


[ANALYSE] PROFILER MEMORY

Chrome DevTools > Memory :

1. HEAP SNAPSHOT
   - Photo mémoire instant T
   - Voir tous objets
   - Comparer snapshots (avant/après)

2. ALLOCATION TIMELINE
   - Enregistrer allocations dans le temps
   - Voir patterns

3. ALLOCATION SAMPLING
   - Profile allocations
   - Moins overhead

Workflow :
1. Heap Snapshot initial
2. Faire action (ex: charger data)
3. Heap Snapshot final
4. Comparer -> Voir ce qui a augmenté

Identifier leaks :
- Objets qui augmentent constamment
- Détached DOM nodes
- Event listeners orphelins


WEAK REFERENCES

WeakMap / WeakSet : Références faibles

```javascript
// Map normale : Empêche GC
const map = new Map();
let obj = { data: "important" };
map.set(obj, "value");

obj = null; // obj PAS garbage collected (map le garde)

// WeakMap : Permet GC
const weakMap = new WeakMap();
let obj2 = { data: "important" };
weakMap.set(obj2, "value");

obj2 = null; // obj2 SERA garbage collected
             // Entry dans weakMap automatiquement supprimée
```

Use case : Cache, metadata sur objets


OPTIMISATIONS V8

Hidden Classes :

```javascript
// [OK] Bon - Même shape
function Point(x, y) {
  this.x = x; // Toujours dans même ordre
  this.y = y;
}

const p1 = new Point(1, 2);
const p2 = new Point(3, 4);
// V8 optimise : même hidden class

// [X] Mauvais - Shapes différents
const p3 = { x: 1, y: 2 };
const p4 = { y: 4, x: 3 }; // Ordre différent !
// V8 crée hidden classes différentes -> Moins optimisé
```

Inline Caching :

```javascript
function getName(obj) {
  return obj.name; // V8 cache lookup si obj toujours même type
}
```


BEST PRACTICES MEMORY

[OK] Nullifier références inutilisées
[OK] Clear timers/intervals
[OK] Remove event listeners
[OK] Utiliser WeakMap pour caches
[OK] Éviter closures inutiles
[OK] Libérer gros ArrayBuffers
[OK] Profiler régulièrement
```


# ============================================================================
# [DOCS] RÉCAPITULATIF CHAPITRE 1
# ============================================================================

"""
CE QUE VOUS AVEZ APPRIS

[OK] Architecture multi-processus des navigateurs modernes
[OK] Browser, Renderer, GPU, Plugin processes
[OK] Sandbox et sécurité (isolation)
[OK] Event Loop JavaScript (Call Stack, Queues)
[OK] Tasks vs Microtasks (ordre exécution)
[OK] Web Workers (parallélisme réel)
[OK] Memory Management et Garbage Collection


[CLE] POINTS CLÉS

1. Chrome = Multi-processus (onglet = processus)
2. Sandbox = Sécurité (Renderer isolé)
3. JavaScript = Single-threaded (Main Thread)
4. Event Loop = Asynchrone non-bloquant
5. Microtasks > Tasks (priorité)
6. Workers = Threads séparés (pas DOM)
7. GC = Automatique (Mark-and-Sweep)


[OBJECTIF] AVANT DE CONTINUER

Assurez-vous de pouvoir :
[OK] Expliquer architecture multi-processus
[OK] Décrire Event Loop
[OK] Prédire ordre exécution async
[OK] Utiliser Web Workers basique
[OK] Identifier memory leaks simples
[OK] Profiler memory dans DevTools


-> PROCHAINE ÉTAPE : Chapitre 2 - Processus de Rendu HTML/CSS !

Vous allez découvrir :
- Parsing HTML -> DOM
- Parsing CSS -> CSSOM
- Construction Render Tree
- Layout (calcul positions)
- Paint (rasterization)
- Composite (layers)
- Critical Rendering Path
- Optimisations rendu

Prêt à voir comment le navigateur transforme code en pixels ? [DESIGN][RAPIDE]
"""
```

Voulez-vous que je continue avec le Chapitre 2 (Processus de Rendu HTML/CSS) ?

```python
# ============================================================================
# [GUIDE] CHAPITRE 2 : PROCESSUS DE RENDU HTML/CSS
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Comment le navigateur parse le HTML en DOM
[OK] Construction du CSSOM depuis le CSS
[OK] Fusion DOM + CSSOM -> Render Tree
[OK] Processus Layout (calcul positions/tailles)
[OK] Processus Paint (rasterization)
[OK] Compositing et layers GPU
[OK] Critical Rendering Path complet
[OK] Optimiser le rendu pour la performance
"""


# ----------------------------------------------------------------------------
# [ARBRE] PARSING HTML : CONSTRUCTION DU DOM
# ----------------------------------------------------------------------------

"""
VOYAGE DU HTML : DES BYTES AUX PIXELS

HTML reçu -> Bytes -> Characters -> Tokens -> Nodes -> DOM Tree


ÉTAPE 1 : BYTES -> CHARACTERS (Décodage)
────────────────────────────────────────

Réponse HTTP :

Content-Type: text/html; charset=UTF-8
Content-Encoding: gzip

<compressed bytes>

Navigateur :
1. Décompresse (gzip/br/deflate)
2. Détecte encoding (UTF-8)
3. Convertit bytes -> caractères

Bytes (hexadécimal) :
3C 68 74 6D 6C 3E 3C 68 65 61 64 3E ...

Characters (UTF-8) :
< h t m l > < h e a d > ...


ÉTAPE 2 : CHARACTERS -> TOKENS (Tokenization)
─────────────────────────────────────────────

[IDEE] TOKENIZER = Lexical Analysis

HTML :
```html
<!DOCTYPE html>
<html>
  <head>
    <title>Test</title>
  </head>
  <body>
    <h1 class="title">Hello</h1>
    <p id="text">World</p>
  </body>
</html>
```

Tokens générés :

[
  { type: 'DOCTYPE', value: 'html' },
  { type: 'StartTag', name: 'html', attributes: [] },
  { type: 'StartTag', name: 'head', attributes: [] },
  { type: 'StartTag', name: 'title', attributes: [] },
  { type: 'Character', data: 'Test' },
  { type: 'EndTag', name: 'title' },
  { type: 'EndTag', name: 'head' },
  { type: 'StartTag', name: 'body', attributes: [] },
  { type: 'StartTag', name: 'h1', attributes: [
      { name: 'class', value: 'title' }
    ]
  },
  { type: 'Character', data: 'Hello' },
  { type: 'EndTag', name: 'h1' },
  { type: 'StartTag', name: 'p', attributes: [
      { name: 'id', value: 'text' }
    ]
  },
  { type: 'Character', data: 'World' },
  { type: 'EndTag', name: 'p' },
  { type: 'EndTag', name: 'body' },
  { type: 'EndTag', name: 'html' }
]


TYPES DE TOKENS :

1. DOCTYPE
   <!DOCTYPE html>

2. StartTag
   <div class="box">
   
3. EndTag
   </div>

4. Character
   Texte entre balises

5. Comment
   <!-- commentaire -->

6. Self-closing tag
   <img src="photo.jpg" />


ÉTAPE 3 : TOKENS -> NODES (Tree Construction)
─────────────────────────────────────────────

Parser construit arbre (DOM Tree)

Algorithme (simplifié) :

1. Stack de nodes ouvertes
2. Pour chaque token :

   StartTag -> Créer node, push stack
   Character -> Ajouter au parent (top stack)
   EndTag -> Pop stack

Exemple pas à pas :

HTML :
<html>
  <body>
    <p>Hi</p>
  </body>
</html>

Étape 1 : StartTag <html>
Stack: [html]
Tree:
  html

Étape 2 : StartTag <body>
Stack: [html, body]
Tree:
  html
   └─ body

Étape 3 : StartTag <p>
Stack: [html, body, p]
Tree:
  html
   └─ body
       └─ p

Étape 4 : Character "Hi"
Stack: [html, body, p]
Tree:
  html
   └─ body
       └─ p
           └─ "Hi" (text node)

Étape 5 : EndTag </p>
Stack: [html, body]
Tree: (inchangé)

Étape 6 : EndTag </body>
Stack: [html]
Tree: (inchangé)

Étape 7 : EndTag </html>
Stack: []
Tree: Complété !


DOM TREE FINAL :

document
  └─ html (HTMLHtmlElement)
      ├─ head (HTMLHeadElement)
      │   └─ title (HTMLTitleElement)
      │       └─ "Test" (Text)
      └─ body (HTMLBodyElement)
          ├─ h1 (HTMLHeadingElement)
          │   class: "title"
          │   └─ "Hello" (Text)
          └─ p (HTMLParagraphElement)
              id: "text"
              └─ "World" (Text)


TYPES DE NODES :

1. ELEMENT NODE (balise HTML)
   Exemple : <div>, <p>, <h1>
   Interface : HTMLElement

2. TEXT NODE (texte)
   Exemple : "Hello World"
   Interface : Text

3. COMMENT NODE (commentaire)
   Exemple : <!-- commentaire -->
   Interface : Comment

4. DOCUMENT NODE (root)
   Exemple : document
   Interface : Document

5. DOCTYPE NODE
   Exemple : <!DOCTYPE html>
   Interface : DocumentType

6. ATTRIBUTE NODE (obsolète, maintenant propriété)


PROPRIÉTÉS NODES :

Chaque node a :
- nodeType (1=Element, 3=Text, etc.)
- nodeName (tag name ou #text)
- nodeValue (valeur pour text, null pour element)
- parentNode
- childNodes (liste enfants)
- firstChild, lastChild
- nextSibling, previousSibling


[ANALYSE] EXPLORER LE DOM

Console :

```javascript
// Document complet
console.dir(document);

// Body
console.dir(document.body);

// Premier enfant de body
console.log(document.body.firstChild);

// Tous les enfants de body
console.log(document.body.childNodes);
// -> NodeList [text, h1, text, p, text]
// Note : Whitespace = text nodes !

// Seulement éléments (pas text)
console.log(document.body.children);
// -> HTMLCollection [h1, p]

// Naviguer
const h1 = document.querySelector('h1');
console.log(h1.nodeName);        // "H1"
console.log(h1.nodeType);        // 1 (ELEMENT_NODE)
console.log(h1.textContent);     // "Hello"
console.log(h1.parentNode);      // body
console.log(h1.nextSibling);     // #text (whitespace)
console.log(h1.nextElementSibling); // p
```


PARSING INCRÉMENTAL

[IDEE] CLÉS : Le navigateur NE ATTEND PAS tout le HTML !

Il parse et affiche progressivement :

0-1 KB reçu -> Parse -> Affiche
1-2 KB reçu -> Parse -> Affiche
2-3 KB reçu -> Parse -> Affiche
...

Avantage : Utilisateur voit quelque chose rapidement
-> Perception de vitesse améliorée !


SPECULATION PARSING

Pendant que Main Thread parse HTML :
Preload Scanner (thread séparé) scanne en avance

Détecte :
- <link rel="stylesheet">
- <script src="...">
- <img src="...">

Et commence téléchargement EN AVANCE !

Exemple :

<html>
<head>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <img src="hero.jpg">
  <script src="app.js"></script>
</body>

Preload Scanner voit style.css, hero.jpg, app.js
-> Commence téléchargements immédiatement
-> Pas besoin d'attendre parsing complet


SCRIPTS BLOQUANTS

[ATTENTION] PROBLÈME MAJEUR : <script> bloque parsing

HTML :
<html>
<head>
  <title>Page</title>
</head>
<body>
  <h1>Hello</h1>
  
  <script src="big-script.js"></script> <- BLOQUE ICI
  
  <p>World</p> <- Pas parsé tant que script pas fini
</body>

Timeline :
1. Parse <html>, <head>, <title>, <body>, <h1>
2. Rencontre <script src="big-script.js">
3. [DOUBLE_VERTICAL_BAR] PAUSE PARSING
4. Télécharge big-script.js (si pas déjà téléchargé)
5. Execute big-script.js
6. [BLACK_RIGHT-POINTING_TRIANGLE_WITH_DOUBLE_VERTICAL_BAR] REPREND PARSING
7. Parse <p>World</p>

Pendant script :
- Pas de parsing HTML
- Pas de rendu
- UI potentiellement bloquée


SOLUTIONS :

1. ASYNC SCRIPTS

<script src="app.js" async></script>

Comportement :
- Télécharge en parallèle (pas de blocage)
- Execute dès téléchargé (peut bloquer parsing)
- Ordre d'exécution NON garanti

Timeline :
Parse HTML ───────────────────────────────[BLACK_RIGHT-POINTING_POINTER]
Télécharge app.js ─────[BLACK_RIGHT-POINTING_POINTER]
                        Execute ─[BLACK_RIGHT-POINTING_POINTER]
                                  Parse HTML reprend [BLACK_RIGHT-POINTING_POINTER]

Use case : Scripts indépendants (analytics, ads)


2. DEFER SCRIPTS

<script src="app.js" defer></script>

Comportement :
- Télécharge en parallèle (pas de blocage)
- Execute APRÈS parsing HTML complet
- Ordre d'exécution garanti (dans ordre déclaration)

Timeline :
Parse HTML ──────────────────────────────[BLACK_RIGHT-POINTING_POINTER] DOM Ready
Télécharge app.js ─────[BLACK_RIGHT-POINTING_POINTER]                          │
                                                  [BLACK_DOWN-POINTING_TRIANGLE]
                                            Execute app.js

Use case : Scripts manipulant DOM (la plupart)


3. MODULE SCRIPTS

<script type="module" src="app.js"></script>

Comportement :
- Defer par défaut
- Support import/export ES6
- Exécuté en mode strict automatiquement

Use case : Code moderne avec modules


COMPARAISON :

Sans attribut :
HTML ──[BLACK_RIGHT-POINTING_POINTER] [DOUBLE_VERTICAL_BAR] Download + Execute ──[BLACK_RIGHT-POINTING_POINTER] HTML ──[BLACK_RIGHT-POINTING_POINTER]

async :
HTML ─────────────────────────────────────[BLACK_RIGHT-POINTING_POINTER]
     Download ──[BLACK_RIGHT-POINTING_POINTER] [DOUBLE_VERTICAL_BAR] Execute ──[BLACK_RIGHT-POINTING_POINTER] HTML [BLACK_RIGHT-POINTING_POINTER]

defer :
HTML ─────────────────────────────────────[BLACK_RIGHT-POINTING_POINTER] DOMContentLoaded
     Download ──────────────────[BLACK_RIGHT-POINTING_POINTER]              [BLACK_DOWN-POINTING_TRIANGLE]
                                         Execute


INLINE SCRIPTS

<script>
  console.log('Inline script');
</script>

Bloque parsing aussi !
Pas de téléchargement, mais exécution bloque.


CSS BLOQUE SCRIPTS

[ATTENTION] CSS bloque exécution scripts (pas parsing)

HTML :
<head>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <script>
    // Ce script attend que style.css soit chargé !
    console.log(getComputedStyle(document.body).color);
  </script>
</body>

Pourquoi ?
Script pourrait accéder styles (getComputedStyle)
-> Doit attendre CSS pour avoir valeurs correctes


PARSING ERROR HANDLING

HTML est TRÈS tolérant aux erreurs

Erreurs auto-corrigées :

1. Balise non fermée
<p>Hello
<p>World

Navigateur corrige :
<p>Hello</p>
<p>World</p>

2. Balises mal imbriquées
<b><i>Text</b></i>

Navigateur corrige :
<b><i>Text</i></b>

3. Balise inconnue
<custom-element>Content</custom-element>

Navigateur crée HTMLUnknownElement
-> Fonctionne quand même !


QUIRKS MODE vs STANDARDS MODE

DOCTYPE détermine mode :

<!-- Standards Mode -->
<!DOCTYPE html>

<!-- Quirks Mode (ancien) -->
<!-- Pas de DOCTYPE -->

Quirks Mode :
- Émule bugs IE5/IE6
- Layout non-standard
- Éviter absolument !

Toujours utiliser : <!DOCTYPE html>


[COURS] EXERCICE : OBSERVER LE PARSING

Créer test.html :

```html
<!DOCTYPE html>
<html>
<head>
  <title>Test Parsing</title>
  <script>
    console.log('1: Script in head (before body exists)');
    console.log('Body:', document.body); // null !
  </script>
</head>
<body>
  <h1>Hello</h1>
  
  <script>
    console.log('2: Script in body (after h1)');
    console.log('H1:', document.querySelector('h1')); // Existe !
    console.log('P:', document.querySelector('p')); // null (pas encore parsé)
  </script>
  
  <p>World</p>
  
  <script>
    console.log('3: Script at end');
    console.log('P:', document.querySelector('p')); // Existe maintenant !
  </script>
</body>
</html>
```

Ouvrir dans navigateur, observer console :
1: Script in head (before body exists)
   Body: null
2: Script in body (after h1)
   H1: <h1>Hello</h1>
   P: null
3: Script at end
   P: <p>World</p>

-> Prouve que parsing est incrémental !
"""


# ----------------------------------------------------------------------------
# [DESIGN] PARSING CSS : CONSTRUCTION DU CSSOM
# ----------------------------------------------------------------------------

"""
CSS OBJECT MODEL (CSSOM)

Comme DOM pour HTML, CSSOM pour CSS


ÉTAPE 1 : TÉLÉCHARGEMENT CSS
─────────────────────────────

Sources CSS :

1. External stylesheet
<link rel="stylesheet" href="style.css">

2. Inline stylesheet
<style>
  body { margin: 0; }
</style>

3. Inline styles
<div style="color: red;">

4. User agent stylesheet (défaut navigateur)
Exemple : <h1> est bold et grand par défaut


ÉTAPE 2 : PARSING CSS -> CSSOM
──────────────────────────────

CSS :
```css
body {
  margin: 0;
  font-size: 16px;
}

h1 {
  color: blue;
  font-size: 2em;
}

.title {
  text-align: center;
}
```

CSSOM Tree :

document.styleSheets[0]
  └─ cssRules
      ├─ CSSStyleRule (body)
      │   └─ style
      │       ├─ margin: 0
      │       └─ font-size: 16px
      ├─ CSSStyleRule (h1)
      │   └─ style
      │       ├─ color: blue
      │       └─ font-size: 2em
      └─ CSSStyleRule (.title)
          └─ style
              └─ text-align: center


ÉTAPE 3 : CASCADE
─────────────────

[IDEE] CLÉS : Résolution des conflits

Priorité (du plus faible au plus fort) :

1. User agent stylesheet
   (Défauts navigateur)

2. User stylesheet
   (Préférences utilisateur)

3. Author stylesheet
   (Votre CSS)
   
   a. External stylesheets
   b. <style> blocks
   c. Inline styles

4. !important (dans ordre inverse)


Exemple :

```css
/* User agent */
h1 { font-size: 2em; }

/* Votre CSS */
h1 { font-size: 3em; color: blue; }
.title { font-size: 2.5em; }

/* HTML */
<h1 class="title" style="color: red;">Hello</h1>
```

Résolution :
- font-size: 2.5em (.title gagne sur h1 - spécificité)
- color: red (inline gagne)


SPÉCIFICITÉ

Poids des sélecteurs :

Inline style:        1000
ID:                   100
Class/Pseudo-class:    10
Element:                1

Exemples :

#header               -> 100
.nav                  -> 10
div                   -> 1
div.nav               -> 1 + 10 = 11
#header .nav          -> 100 + 10 = 110
div#header .nav li    -> 1 + 100 + 10 + 1 = 112


Calcul spécificité :

```css
/* Spécificité : 1 */
p { color: blue; }

/* Spécificité : 11 */
p.intro { color: green; }

/* Spécificité : 101 */
#content p { color: red; }

/* Inline : 1000 */
<p style="color: yellow;">
```

Pour <p class="intro" id="text"> :
Si dans #content :
  #content p (101) > p.intro (11) > p (1)
  -> color: red


!important :

```css
p { color: blue !important; }
#text { color: red; }
```

<p id="text"> -> color: blue
!important surpasse tout (sauf autre !important plus spécifique)


HÉRITAGE (Inheritance)

Certaines propriétés héritées du parent :

Héritées :
- color
- font-family, font-size, font-weight
- line-height
- text-align
- cursor

Non héritées :
- margin, padding
- border
- width, height
- background
- position

Exemple :

```css
body {
  color: #333;
  font-size: 16px;
}
```

Tous éléments dans <body> héritent color et font-size
(sauf si overridden)


VALEURS CALCULÉES

CSS passe par plusieurs étapes :

1. DECLARED VALUE (déclarée)
   Valeur dans CSS

2. CASCADED VALUE (cascade)
   Après résolution cascade

3. SPECIFIED VALUE (spécifiée)
   Après héritage et défauts

4. COMPUTED VALUE (calculée)
   Valeurs relatives -> absolues
   Exemple : 2em -> 32px

5. USED VALUE (utilisée)
   Valeurs finales pour layout
   Exemple : 50% -> 400px

6. ACTUAL VALUE (réelle)
   Après contraintes device
   Exemple : 1.5px -> 2px (arrondi)


Exemple :

```css
body {
  font-size: 16px;
}

h1 {
  font-size: 2em;
  width: 50%;
}
```

Pour <h1> dans container de 800px :

font-size:
  Declared: 2em
  Computed: 32px (2 * 16px)
  Used: 32px

width:
  Declared: 50%
  Computed: 50%
  Used: 400px (50% de 800px)


[ANALYSE] EXPLORER LE CSSOM

Console :

```javascript
// Toutes les stylesheets
console.log(document.styleSheets);

// Première stylesheet
const sheet = document.styleSheets[0];
console.log(sheet.cssRules);

// Règles CSS
for (let rule of sheet.cssRules) {
  console.log(rule.selectorText, rule.style.cssText);
}

// Styles computés d'un élément
const h1 = document.querySelector('h1');
const styles = getComputedStyle(h1);

console.log(styles.color);        // rgb(0, 0, 255)
console.log(styles.fontSize);     // 32px
console.log(styles.fontFamily);   // "Arial, sans-serif"
console.log(styles.marginTop);    // 0px
```


RENDER-BLOCKING CSS

[ATTENTION] CSS BLOQUE LE RENDU !

Navigateur attend CSS avant d'afficher contenu
(pour éviter FOUC - Flash of Unstyled Content)

Timeline :

HTML parsing ────────────────────────[BLACK_RIGHT-POINTING_POINTER]
CSS download ──────[BLACK_RIGHT-POINTING_POINTER]
                    CSSOM construction ─[BLACK_RIGHT-POINTING_POINTER]
                                         Render [BLACK_RIGHT-POINTING_POINTER]

Pendant téléchargement CSS :
- Page blanche (white screen)
- Utilisateur attend


OPTIMISATIONS CSS :

1. CRITICAL CSS (Inline)

```html
<head>
  <style>
    /* Critical CSS inline (above-the-fold) */
    body { margin: 0; font-family: Arial; }
    .header { background: blue; }
  </style>
  
  <!-- Non-critical CSS async -->
  <link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'">
</head>
```

2. MEDIA QUERIES

```html
<!-- Seulement pour impression -->
<link rel="stylesheet" href="print.css" media="print">

<!-- Seulement pour petits écrans -->
<link rel="stylesheet" href="mobile.css" media="(max-width: 600px)">
```

Si media query ne match pas -> Pas render-blocking !


3. PRELOAD

```html
<link rel="preload" href="styles.css" as="style">
<link rel="stylesheet" href="styles.css">
```

Commence téléchargement plus tôt


CSS PARSING EST RAPIDE

Parsing CSS beaucoup plus rapide que JS
-> CSS complexe OK (dans raison)

Éviter :
- Sélecteurs trop complexes (deep nesting)
- Styles inline partout (mauvais cache)
"""


# ----------------------------------------------------------------------------
# [ARBRE] CONSTRUCTION DU RENDER TREE
# ----------------------------------------------------------------------------

"""
FUSION : DOM + CSSOM -> RENDER TREE

Render Tree = Éléments visibles avec styles appliqués


ALGORITHME :

1. Parcourir DOM tree
2. Pour chaque node visible :
   a. Récupérer styles CSSOM
   b. Calculer styles finaux (cascade + héritage)
   c. Créer render object
3. Exclure éléments invisibles


EXCLUSIONS :

Ne sont PAS dans Render Tree :

1. display: none
2. <head> et contenu
3. <script>
4. <style>
5. <meta>, <link>, etc.
6. Commentaires


EXEMPLE :

DOM Tree :
document
  └─ html
      ├─ head
      │   ├─ title -> "Test"
      │   └─ style -> (CSS rules)
      └─ body
          ├─ h1.title -> "Hello"
          ├─ p#hidden style="display:none" -> "Hidden"
          └─ div
              └─ span -> "World"

CSSOM :
h1 { color: blue; font-size: 2em; }
#hidden { display: none; }
span { color: red; }

Render Tree :
RenderBody
  ├─ RenderBlock (h1)
  │   styles: { color: blue, fontSize: 32px }
  │   └─ RenderText "Hello"
  └─ RenderBlock (div)
      └─ RenderInline (span)
          styles: { color: red }
          └─ RenderText "World"

Notes :
- <head> exclu
- p#hidden exclu (display: none)
- Styles calculés attachés


RENDER OBJECTS

Chaque node visible -> Render Object

Types :
- RenderBlock (block-level: div, p, h1)
- RenderInline (inline: span, a, strong)
- RenderText (text nodes)
- RenderImage (img)
- RenderTable (table)
- RenderFlex (flexbox)
- RenderGrid (grid)


VISUAL FORMATTING MODEL

Box Model appliqué :

┌─────────────────────────────────┐
│         MARGIN                  │
│   ┌─────────────────────────┐   │
│   │       BORDER            │   │
│   │   ┌─────────────────┐   │   │
│   │   │    PADDING      │   │   │
│   │   │   ┌─────────┐   │   │   │
│   │   │   │ CONTENT │   │   │   │
│   │   │   └─────────┘   │   │   │
│   │   └─────────────────┘   │   │
│   └─────────────────────────┘   │
└─────────────────────────────────┘

Chaque render object a :
- Content box
- Padding
- Border
- Margin


STACKING CONTEXT

z-index et ordre d'empilement

Exemples créant stacking context :
- position: absolute/relative/fixed avec z-index
- opacity < 1
- transform
- filter
- will-change


[ANALYSE] OBSERVER RENDER TREE

Chrome DevTools :

1. Layers panel
   More tools > Layers
   -> Voir layers compositing

2. Rendering > Paint flashing
   -> Voir zones repeintes

3. Performance > Frames
   -> Voir render tree construction
"""


# ----------------------------------------------------------------------------
# [MESURE] LAYOUT (REFLOW) : CALCUL POSITIONS ET DIMENSIONS
# ----------------------------------------------------------------------------

"""
LAYOUT = CALCUL GÉOMÉTRIE

Processus : Calculer position et taille EXACTE de chaque élément


ALGORITHME LAYOUT (Simplifié)

1. Calculer width du parent
2. Pour chaque enfant :
   a. Placer enfant
   b. Calculer width enfant
   c. Calculer height enfant (si pas fixe)
   d. Layout enfants de l'enfant (récursif)
3. Calculer height du parent (basé sur enfants)
4. Positionner éléments absolus/fixed


LAYOUT GLOBAL vs INCREMENTAL

GLOBAL LAYOUT (Coûteux) :
Recalcul tout l'arbre
Déclenché par :
- First layout
- Resize window
- Font size change
- DOM addition/removal

INCREMENTAL LAYOUT (Optimisé) :
Recalcul seulement partie modifiée
Déclenché par :
- Style change local
- Content change


FLOW LAYOUT (Normal Flow)

Block-level elements :
- Empilés verticalement
- Prennent toute la largeur disponible

Inline-level elements :
- Côte à côte horizontalement
- Seulement largeur nécessaire
- Wrap si pas assez de place


EXEMPLE LAYOUT :

HTML :
```html
<div class="container" style="width: 800px;">
  <div class="box" style="width: 50%; padding: 20px; border: 1px solid; margin: 10px;">
    <p>Text</p>
  </div>
</div>
```

Layout calcul :

Container :
  width: 800px (explicite)
  height: auto (calculé après enfants)

Box :
  width: 50% -> 400px (50% de 800px)
  padding: 20px (chaque côté)
  border: 1px (chaque côté)
  margin: 10px (chaque côté)
  
  Content width: 400px - 40px (padding) - 2px (border) = 358px
  
  (si box-sizing: border-box)
  Content width: 400px - 40px - 2px = 358px
  Total width: 400px

P (enfant de box) :
  width: 358px (hérite disponible)
  height: auto (basé sur contenu text)
  
Suppose p height = 20px

Box height (recalculé) :
  content height: 20px (p)
  + padding: 40px (20px * 2)
  + border: 2px (1px * 2)
  = 62px

Container height (recalculé) :
  content height: 62px (box)
  + margin box: 20px (10px * 2)
  = 82px


FLEXBOX LAYOUT

Algorithme différent :

```html
<div style="display: flex; width: 800px;">
  <div style="flex: 1;">A</div>
  <div style="flex: 2;">B</div>
  <div style="flex: 1;">C</div>
</div>
```

Layout :
Total flex units: 1 + 2 + 1 = 4
Available space: 800px

A: 800px * (1/4) = 200px
B: 800px * (2/4) = 400px
C: 800px * (1/4) = 200px


GRID LAYOUT

Encore différent :

```html
<div style="display: grid; grid-template-columns: 1fr 2fr 1fr; width: 800px;">
  <div>A</div>
  <div>B</div>
  <div>C</div>
</div>
```

Layout :
Total fr units: 1 + 2 + 1 = 4
Available: 800px

Column 1: 800px * (1/4) = 200px
Column 2: 800px * (2/4) = 400px
Column 3: 800px * (1/4) = 200px


POSITION ABSOLUTE

Hors du flow normal :

```html
<div style="position: relative; width: 800px; height: 600px;">
  <div style="position: absolute; top: 50px; left: 100px; width: 200px;">
    Positioned
  </div>
</div>
```

Layout :
Parent (relative) : 800x600
Enfant (absolute) :
  - Retiré du normal flow
  - Positionné à (100, 50) relatif au parent
  - width: 200px
  - N'affecte PAS height du parent


LAYOUT THRASHING (Performance)

[X] MAUVAIS (Force layout multiple fois) :

```javascript
const elements = document.querySelectorAll('.box');

elements.forEach(el => {
  const height = el.offsetHeight; // <- READ (force layout)
  el.style.height = height + 10 + 'px'; // <- WRITE
  
  // Prochain READ force RE-LAYOUT !
});
```

Timeline :
READ -> Layout -> WRITE -> READ -> Layout -> WRITE -> ...
Chaque READ force layout complet !


[OK] BON (Batch read/write) :

```javascript
const elements = document.querySelectorAll('.box');

// 1. Lire tout
const heights = Array.from(elements).map(el => el.offsetHeight);

// 2. Écrire tout
elements.forEach((el, i) => {
  el.style.height = heights[i] + 10 + 'px';
});
```

Timeline :
READ -> READ -> READ -> Layout UNE FOIS -> WRITE -> WRITE -> WRITE
Beaucoup plus rapide !


PROPRIÉTÉS DÉCLENCHANT LAYOUT

Lecture (force layout si dirty) :
- offsetWidth, offsetHeight
- offsetTop, offsetLeft
- clientWidth, clientHeight
- scrollWidth, scrollHeight
- getBoundingClientRect()
- getComputedStyle()

Écriture (invalide layout) :
- width, height
- margin, padding, border
- top, left, right, bottom
- font-size, font-family
- display
- position


[ANALYSE] MESURER LAYOUT

Performance API :

```javascript
performance.mark('layout-start');

// Code qui modifie layout
element.style.width = '500px';

// Force layout
const height = element.offsetHeight;

performance.mark('layout-end');
performance.measure('layout', 'layout-start', 'layout-end');

const measure = performance.getEntriesByName('layout')[0];
console.log('Layout took:', measure.duration, 'ms');
```


DevTools > Performance :
- Enregistrer
- Voir "Recalculate Style" et "Layout"
- Violet = Layout
- Violet foncé = Forced reflow
```


# ----------------------------------------------------------------------------
# [DESIGN] PAINT : RASTERIZATION
# ----------------------------------------------------------------------------

"""
PAINT = TRANSFORMER VECTEURS EN PIXELS

Après Layout -> On sait OÙ dessiner
Paint -> On dessine QUOI


PAINT RECORDS (Display Lists)

Navigateur crée liste de commandes dessin :

Exemple :

<div style="background: blue; border: 2px solid red; color: white;">
  Hello
</div>

Paint records :
1. FillRect(x, y, width, height, color: blue)
2. StrokeRect(x, y, width, height, color: red, width: 2px)
3. DrawText("Hello", x, y, font, color: white)


PAINT ORDER

Ordre de dessin (de bas en haut) :

1. Background color
2. Background image
3. Border
4. Children (récursif)
5. Outline


Z-INDEX et Stacking

Éléments avec z-index créent stacking layers :

```html
<div style="position: relative;">
  <div style="position: absolute; z-index: 1; background: red;">Behind</div>
  <div style="position: absolute; z-index: 2; background: blue;">Front</div>
</div>
```

Paint order :
1. z-index: 1 (red)
2. z-index: 2 (blue) <- Dessine par-dessus


COMPOSITING LAYERS

Certains éléments créent leur propre layer GPU :

Créent layer :
- position: fixed
- transform (3D)
- will-change
- video, canvas
- overflow: scroll (avec contenu)
- opacity < 1 (avec transition/animation)
- filter


Avantage layers :
- Animations fluides (GPU)
- Pas de repaint du reste de la page
- Compositing hardware-accelerated


Exemple :

```css
.animated {
  transform: translateZ(0); /* Force layer */
  will-change: transform;   /* Hint au navigateur */
}

.animated:hover {
  transform: translateY(10px); /* GPU uniquement ! */
}
```

Pas de repaint, juste compositing [OK]


PAINT INVALIDATION

Quand repeindre ?

1. DOM change
   - Ajouter/supprimer éléments
   
2. Style change
   - color, background, border

3. Geometrie change
   - width, height (trigger layout aussi)

4. Scroll
   - Nouvelle zone visible


Optimisation navigateur :
- Repaint seulement zone affectée (dirty rectangle)
- Pas tout l'écran à chaque fois !


[ANALYSE] OBSERVER PAINT

DevTools > Rendering :

1. Paint flashing
   [OK] Activé -> Zones vertes = repeintes

2. Layer borders
   Voir compositing layers (orange)

3. Scrolling performance issues
   Detect scroll jank


DevTools > Performance :
- Record
- Voir "Paint" events (vert)
- Cliquer -> Voir paint records détaillés


PAINT vs COMPOSITE

PAINT (Lent) :
- Rasterization CPU/GPU
- Génère textures

COMPOSITE (Rapide) :
- Assemblage layers
- GPU uniquement
- Très rapide !


Propriétés CSS :

Only COMPOSITE (ultra rapide [OK]) :
- transform
- opacity

PAINT + COMPOSITE (moyen) :
- color
- background
- border
- box-shadow

LAYOUT + PAINT + COMPOSITE (lent [X]) :
- width, height
- margin, padding
- top, left (absolute)
- font-size


Optimisation animations :

[X] MAUVAIS :
```css
.box {
  transition: margin-left 0.3s;
}
.box:hover {
  margin-left: 100px; /* Layout + Paint + Composite */
}
```

[OK] BON :
```css
.box {
  transition: transform 0.3s;
}
.box:hover {
  transform: translateX(100px); /* Composite seulement ! */
}
```


RASTERIZATION

Processus final : Vecteurs -> Pixels

Techniques :

1. SOFTWARE RASTERIZATION (CPU)
   Ancien, lent

2. HARDWARE RASTERIZATION (GPU)
   Moderne, rapide
   
   Processus :
   - Upload textures à GPU
   - GPU rasterize
   - Composite dans framebuffer
   - Display


GPU ACCELERATION

Critères :
- Layer séparé
- Transform 3D ou will-change
- Video, Canvas, WebGL
- CSS filters

Vérifier :
Chrome DevTools > Layers panel
-> Voir "Compositing Reasons"
```


# ----------------------------------------------------------------------------
# [RAPIDE] CRITICAL RENDERING PATH
# ----------------------------------------------------------------------------

"""
[IDEE] CHEMIN CRITIQUE = Étapes minimum pour premier render

Objectif : Optimiser pour affichage le plus rapide possible


TIMELINE COMPLÈTE :

1. HTML reçu (TTFB - Time To First Byte)
   v
2. Parse HTML -> DOM construction
   v
3. CSS reçu (parallel)
   v
4. Parse CSS -> CSSOM construction
   v
5. Fusion DOM + CSSOM -> Render Tree
   v
6. Layout (calcul géométrie)
   v
7. Paint (rasterization)
   v
8. Composite (assemblage layers)
   v
9. FIRST PAINT (FP)


MÉTRIQUES PERFORMANCE :

FP (First Paint)
  Premier pixel affiché
  
FCP (First Contentful Paint)
  Premier contenu affiché (texte, image)
  
LCP (Largest Contentful Paint)
  Plus gros élément affiché
  Cible : < 2.5s
  
TTI (Time To Interactive)
  Page interactive
  Cible : < 3.8s


OPTIMISER CRITICAL PATH :

1. MINIMISER BYTES
   - Minify HTML/CSS/JS
   - Compress (gzip/brotli)
   - Remove unused CSS

2. MINIMISER REQUESTS
   - Inline critical CSS
   - Combine files
   - HTTP/2 multiplexing

3. PRIORITIZE RESOURCES
   - Preload key resources
   - Defer non-critical JS
   - Async scripts

4. CACHE AGRESSIVELY
   - Service Worker
   - HTTP cache headers
   - CDN


EXEMPLE OPTIMISÉ :

```html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Optimized Page</title>
  
  <!-- Critical CSS inline -->
  <style>
    body{margin:0;font:16px Arial}
    .hero{height:100vh;background:#1e90ff}
  </style>
  
  <!-- Preload key resources -->
  <link rel="preload" href="font.woff2" as="font" crossorigin>
  <link rel="preload" href="hero.jpg" as="image">
  
  <!-- Async non-critical CSS -->
  <link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'">
</head>
<body>
  <div class="hero">
    <h1>Fast Loading!</h1>
  </div>
  
  <!-- Defer JavaScript -->
  <script src="app.js" defer></script>
</body>
</html>
```


RENDER-BLOCKING RESOURCES

[X] Bloquent rendering :
- CSS (all)
- Synchronous JS in <head>

[OK] Ne bloquent PAS :
- Images
- Async/defer scripts
- Media-query CSS (non-matching)


PROGRESSIVE RENDERING

Afficher progressivement :

1. HTML streaming
   Envoyer HTML par chunks
   
2. CSS scoped
   Inline critical CSS par section
   
3. Lazy load images
   Below-the-fold images lazy


[ANALYSE] MESURER CRITICAL PATH

Lighthouse (Chrome DevTools) :
- Audit > Performance
- Voir métriques
- Recommendations


WebPageTest :
- Test vitesse chargement
- Waterfall chart
- Filmstrip view


Performance API :

```javascript
// Navigation timing
const nav = performance.getEntriesByType('navigation')[0];

console.log('DOM Interactive:', nav.domInteractive);
console.log('DOM Complete:', nav.domComplete);
console.log('Load Event:', nav.loadEventEnd);

// Paint timing
const paint = performance.getEntriesByType('paint');

paint.forEach(entry => {
  console.log(entry.name, entry.startTime);
});
// first-paint: 423.4
// first-contentful-paint: 456.2
```


BUDGET PERFORMANCE :

Targets :
- FCP < 1.8s
- LCP < 2.5s
- TTI < 3.8s
- Total size < 500KB (initial)
- < 50 requests
```


# ============================================================================
# [COURS] EXERCICE PRATIQUE : ANALYSER LE RENDERING
# ============================================================================

"""
OBJECTIF : Observer le processus complet de rendu


EXERCICE 1 : CRITICAL PATH
──────────────────────────

Créer page.html :

```html
<!DOCTYPE html>
<html>
<head>
  <title>Render Test</title>
  <link rel="stylesheet" href="slow.css">
  <script src="slow.js"></script>
</head>
<body>
  <h1>Hello World</h1>
</body>
</html>
```

slow.css (simuler CSS lent) :
```css
/* Attendre 2s avant de servir ce fichier */
body { margin: 0; }
```

slow.js (simuler JS lent) :
```javascript
// Attendre 2s avant de servir ce fichier
console.log('JS loaded');
```

DevTools > Network :
- Throttle 3G
- Observer ordre chargement
- Voir temps avant FCP


EXERCICE 2 : LAYOUT THRASHING
─────────────────────────────

```html
<div id="container"></div>

<script>
const container = document.getElementById('container');

// Créer 100 boxes
for (let i = 0; i < 100; i++) {
  const box = document.createElement('div');
  box.className = 'box';
  box.textContent = i;
  container.appendChild(box);
}

// [X] MAUVAIS - Layout thrashing
console.time('thrashing');
document.querySelectorAll('.box').forEach(box => {
  const height = box.offsetHeight; // READ (force layout)
  box.style.height = height + 10 + 'px'; // WRITE
});
console.timeEnd('thrashing');

// [OK] BON - Batch operations
console.time('optimized');
const boxes = document.querySelectorAll('.box');
const heights = Array.from(boxes).map(b => b.offsetHeight); // Batch READ
boxes.forEach((box, i) => {
  box.style.height = heights[i] + 10 + 'px'; // Batch WRITE
});
console.timeEnd('optimized');
</script>
```

Comparer temps !


EXERCICE 3 : PAINT vs COMPOSITE
───────────────────────────────

```html
<style>
.box {
  width: 100px;
  height: 100px;
  background: red;
  position: absolute;
}

.paint-anim {
  transition: background-color 1s;
}

.composite-anim {
  transition: transform 1s;
  will-change: transform;
}
</style>

<div class="box paint-anim" id="box1">Paint</div>
<div class="box composite-anim" id="box2" style="top: 120px;">Composite</div>

<button onclick="animatePaint()">Animate Paint</button>
<button onclick="animateComposite()">Animate Composite</button>

<script>
function animatePaint() {
  document.getElementById('box1').style.backgroundColor = 'blue';
}

function animateComposite() {
  const box = document.getElementById('box2');
  box.style.transform = 'translateX(200px)';
}
</script>

DevTools > Performance :
- Record
- Cliquer boutons
- Comparer :
  - Paint animation : Voir Paint events (vert)
  - Composite animation : Voir seulement Composite events
```


QUESTIONS À EXPLORER :

[REFLEXION] Quelle différence de performance Paint vs Composite ?
[REFLEXION] Combien de fois Layout appelé dans thrashing ?
[REFLEXION] Quel impact CSS bloquant sur FCP ?
[REFLEXION] Comment async script change le timeline ?


[OK] VOUS AVEZ RÉUSSI SI :

- Vous visualisez le Critical Rendering Path
- Vous identifiez layout thrashing
- Vous différenciez Paint et Composite
- Vous mesurez performance avec DevTools
"""


# ============================================================================
# [DOCS] RÉCAPITULATIF CHAPITRE 2
# ============================================================================

"""
CE QUE VOUS AVEZ APPRIS

[OK] Parsing HTML : Bytes -> DOM Tree
[OK] Tokenization et tree construction
[OK] Scripts bloquants (async/defer)
[OK] Parsing CSS : Création CSSOM
[OK] Cascade, spécificité, héritage
[OK] Render Tree = DOM + CSSOM
[OK] Layout : Calcul positions/dimensions
[OK] Paint : Rasterization pixels
[OK] Compositing : Assemblage layers GPU
[OK] Critical Rendering Path complet
[OK] Optimisations performance


[CLE] POINTS CLÉS

1. HTML parsing est incrémental (progressif)
2. CSS bloque rendering (render-blocking)
3. Scripts bloquent parsing (sauf async/defer)
4. Render Tree = Seulement éléments visibles
5. Layout coûteux (éviter thrashing)
6. Paint -> Composite (transform/opacity = rapide)
7. Critical Path : Minimiser bytes, requests, prioritize


[OBJECTIF] CONCEPTS CRITIQUES

PERFORMANCE :
- FCP < 1.8s
- LCP < 2.5s
- Inline critical CSS
- Defer JavaScript
- Optimize images

ANIMATIONS :
- Prefer transform/opacity (composite only)
- Avoid layout properties (width, margin, etc.)
- Use will-change hint

LAYOUT :
- Batch DOM reads/writes
- Avoid forced synchronous layout
- Use requestAnimationFrame


-> PROCHAINE ÉTAPE : Chapitre 3 - JavaScript et le DOM !

Vous allez explorer :
- DOM API complète
- Manipulation éléments
- Event handling
- Event propagation (bubbling/capturing)
- Event delegation
- Custom events
- MutationObserver
- IntersectionObserver

Prêt à manipuler le DOM comme un pro ? [RAPIDE]
"""
```

Voulez-vous que je continue avec le Chapitre 3 (JavaScript et le DOM) ?

```python
# ============================================================================
# [GUIDE] CHAPITRE 3 : JAVASCRIPT ET LE DOM
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Manipuler le DOM avec toutes les APIs
[OK] Sélectionner éléments efficacement
[OK] Créer, modifier, supprimer des éléments
[OK] Gérer les événements (listeners, propagation)
[OK] Event delegation et best practices
[OK] Utiliser observers (Mutation, Intersection, Resize)
[OK] Optimiser manipulations DOM pour performance
[OK] Comprendre le Virtual DOM concept
"""


# ----------------------------------------------------------------------------
# [RECHERCHE] SÉLECTIONNER DES ÉLÉMENTS
# ----------------------------------------------------------------------------

"""
MÉTHODES DE SÉLECTION

Le navigateur offre plusieurs APIs pour sélectionner éléments


1. getElementById()
───────────────────

Sélectionne par ID (le plus rapide)

```javascript
// HTML: <div id="container">Content</div>

const element = document.getElementById('container');
console.log(element); // <div id="container">

// [ATTENTION] Pas de # nécessaire !
// [X] document.getElementById('#container') -> null

// Retourne null si non trouvé
const notFound = document.getElementById('nonexistent');
console.log(notFound); // null
```

Caractéristiques :
- Seulement sur document (pas sur éléments)
- Retourne 1 élément ou null
- Le plus rapide (hash map interne)
- Case-sensitive


2. getElementsByClassName()
───────────────────────────

Sélectionne par classe

```javascript
// HTML: <div class="box">1</div>
//       <div class="box">2</div>

const boxes = document.getElementsByClassName('box');
console.log(boxes); // HTMLCollection [div.box, div.box]
console.log(boxes.length); // 2
console.log(boxes[0]); // Premier div.box

// [ATTENTION] Pas de . nécessaire !
// [X] document.getElementsByClassName('.box') -> []

// HTMLCollection est LIVE (mise à jour auto)
const container = document.getElementById('container');
console.log(boxes.length); // 2

const newBox = document.createElement('div');
newBox.className = 'box';
container.appendChild(newBox);

console.log(boxes.length); // 3 (auto-updated!)
```

Caractéristiques :
- Retourne HTMLCollection (live)
- Peut être appelé sur n'importe quel élément
- Multiple classes : getElementsByClassName('box active')


3. getElementsByTagName()
─────────────────────────

Sélectionne par nom de balise

```javascript
// Tous les <p>
const paragraphs = document.getElementsByTagName('p');

// Tous les éléments
const all = document.getElementsByTagName('*');

// Dans un container spécifique
const container = document.getElementById('main');
const links = container.getElementsByTagName('a');
```

Caractéristiques :
- Retourne HTMLCollection (live)
- Case-insensitive pour HTML
- '*' = tous les éléments


4. querySelector()
──────────────────

[IDEE] MODERNE ET PUISSANT !

Sélectionne avec sélecteur CSS (premier match)

```javascript
// Par ID
const element = document.querySelector('#container');

// Par classe
const box = document.querySelector('.box');

// Par tag
const paragraph = document.querySelector('p');

// Sélecteurs complexes
const firstLink = document.querySelector('nav a');
const activeButton = document.querySelector('button.active');
const checkbox = document.querySelector('input[type="checkbox"]');

// Pseudo-classes
const firstChild = document.querySelector('ul > li:first-child');
const notFirst = document.querySelector('li:not(:first-child)');

// Combinateurs
const directChild = document.querySelector('div > p');
const descendant = document.querySelector('div p');
const adjacent = document.querySelector('h1 + p');
const sibling = document.querySelector('h1 ~ p');

// Attributs
const externalLink = document.querySelector('a[target="_blank"]');
const startsWithHttp = document.querySelector('a[href^="http"]');
const endsWithPdf = document.querySelector('a[href$=".pdf"]');
const containsGoogle = document.querySelector('a[href*="google"]');
```

Caractéristiques :
- Retourne 1 élément ou null
- Sélecteurs CSS complets
- Retourne le PREMIER match
- Statique (non-live)


5. querySelectorAll()
─────────────────────

Comme querySelector mais retourne TOUS les matches

```javascript
// Tous les .box
const boxes = document.querySelectorAll('.box');
console.log(boxes); // NodeList [div.box, div.box, div.box]

// Itérer
boxes.forEach(box => {
  console.log(box.textContent);
});

// Convertir en Array (si besoin méthodes array)
const boxesArray = Array.from(boxes);
const boxesArray2 = [...boxes]; // Spread

// Sélecteurs complexes
const activeButtons = document.querySelectorAll('button.active');
const visibleDivs = document.querySelectorAll('div:not([hidden])');
const checkboxes = document.querySelectorAll('input[type="checkbox"]:checked');
```

Caractéristiques :
- Retourne NodeList (statique, non-live)
- forEach() disponible directement
- Peut être vide (length: 0)


6. Méthodes de Traversée
────────────────────────

Naviguer dans l'arbre DOM

```javascript
const element = document.querySelector('.box');

// Parent
element.parentElement;           // Parent element
element.parentNode;              // Parent node (peut être non-element)
element.closest('.container');   // Premier ancêtre matchant sélecteur

// Enfants
element.children;                // HTMLCollection des enfants éléments
element.childNodes;              // NodeList de tous nodes (incl. text)
element.firstElementChild;       // Premier enfant élément
element.lastElementChild;        // Dernier enfant élément
element.firstChild;              // Premier node (peut être text)
element.lastChild;               // Dernier node

// Siblings (frères/sœurs)
element.nextElementSibling;      // Élément suivant
element.previousElementSibling;  // Élément précédent
element.nextSibling;             // Node suivant
element.previousSibling;         // Node précédent
```


COMPARAISON MÉTHODES

┌──────────────────────┬─────────────┬──────────┬──────────┐
│ Méthode              │ Retourne    │ Live     │ Vitesse  │
├──────────────────────┼─────────────┼──────────┼──────────┤
│ getElementById       │ Element     │ N/A      │ *****    │
│ getElementsByClass   │ Collection  │ Oui      │ *****    │
│ getElementsByTag     │ Collection  │ Oui      │ *****    │
│ querySelector        │ Element     │ Non      │ *****    │
│ querySelectorAll     │ NodeList    │ Non      │ *****    │
└──────────────────────┴─────────────┴──────────┴──────────┘


LIVE vs STATIC

Live (auto-update) :
```javascript
const boxes = document.getElementsByClassName('box');
console.log(boxes.length); // 2

document.body.innerHTML += '<div class="box">New</div>';
console.log(boxes.length); // 3 <- Auto-updated!
```

Static (snapshot) :
```javascript
const boxes = document.querySelectorAll('.box');
console.log(boxes.length); // 2

document.body.innerHTML += '<div class="box">New</div>';
console.log(boxes.length); // 2 <- Pas changé
```


PERFORMANCE

Pour sélection unique :
1. getElementById (le plus rapide)
2. querySelector avec ID
3. querySelector avec classe/tag

Pour sélection multiple :
1. getElementsByClassName/TagName (si critères simples)
2. querySelectorAll (si critères complexes)


BEST PRACTICES

[OK] Utiliser getElementById pour IDs
[OK] Cache sélections si réutilisées
[OK] Limiter scope de recherche
[OK] Utiliser querySelector pour complexité

[X] Ne pas sélectionner dans boucles
[X] Ne pas utiliser querySelectorAll si un seul élément voulu


EXEMPLES PRATIQUES

```javascript
// [X] MAUVAIS - Sélection répétée
for (let i = 0; i < 100; i++) {
  const container = document.getElementById('container'); // <- 100x !
  container.innerHTML += `<div>${i}</div>`;
}

// [OK] BON - Cache sélection
const container = document.getElementById('container');
for (let i = 0; i < 100; i++) {
  container.innerHTML += `<div>${i}</div>`;
}

// [X] MAUVAIS - Scope trop large
const link = document.querySelectorAll('a')[5];

// [OK] BON - Scope limité
const nav = document.querySelector('nav');
const link = nav.querySelectorAll('a')[5];

// [OK] BON - Encore mieux avec sélecteur
const link = document.querySelector('nav a:nth-child(6)');
```


[ANALYSE] EXERCICE : COMPARAISON PERFORMANCE

```javascript
console.time('getElementById');
for (let i = 0; i < 10000; i++) {
  document.getElementById('container');
}
console.timeEnd('getElementById');

console.time('querySelector');
for (let i = 0; i < 10000; i++) {
  document.querySelector('#container');
}
console.timeEnd('querySelector');

console.time('querySelectorAll');
for (let i = 0; i < 10000; i++) {
  document.querySelectorAll('.box');
}
console.timeEnd('querySelectorAll');

console.time('getElementsByClassName');
for (let i = 0; i < 10000; i++) {
  document.getElementsByClassName('box');
}
console.timeEnd('getElementsByClassName');
```

Résultats typiques :
getElementById: ~5ms
querySelector: ~15ms
getElementsByClassName: ~10ms
querySelectorAll: ~25ms
"""


# ----------------------------------------------------------------------------
# [EDIT] CRÉER ET MODIFIER DES ÉLÉMENTS
# ----------------------------------------------------------------------------

"""
CRÉER DES ÉLÉMENTS


1. createElement()
──────────────────

Créer un nouvel élément

```javascript
// Créer élément
const div = document.createElement('div');

// Définir propriétés
div.id = 'myDiv';
div.className = 'box active';
div.textContent = 'Hello';

// Ajouter au DOM
document.body.appendChild(div);
```


2. createTextNode()
───────────────────

Créer node texte

```javascript
const text = document.createTextNode('Hello World');
const p = document.createElement('p');
p.appendChild(text);
```

Note : Rarement nécessaire, textContent plus simple


3. createDocumentFragment()
───────────────────────────

[IDEE] OPTIMISATION : Container temporaire

```javascript
// [X] MAUVAIS - Reflow à chaque ajout
const container = document.getElementById('list');
for (let i = 0; i < 1000; i++) {
  const li = document.createElement('li');
  li.textContent = `Item ${i}`;
  container.appendChild(li); // <- Reflow 1000x !
}

// [OK] BON - Reflow une seule fois
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
  const li = document.createElement('li');
  li.textContent = `Item ${i}`;
  fragment.appendChild(li); // Pas de reflow
}
container.appendChild(fragment); // Reflow 1x
```


4. cloneNode()
──────────────

Dupliquer élément existant

```javascript
const original = document.querySelector('.template');

// Clone superficiel (sans enfants)
const shallow = original.cloneNode(false);

// Clone profond (avec enfants)
const deep = original.cloneNode(true);

// Modifier et ajouter
deep.id = 'copy';
document.body.appendChild(deep);
```


MODIFIER LE CONTENU


1. textContent
──────────────

Texte brut (sans HTML)

```javascript
const p = document.querySelector('p');

// Lire
console.log(p.textContent); // "Hello World"

// Écrire (sécurisé, échappe HTML)
p.textContent = '<strong>Bold</strong>'; 
// Affiche littéralement: <strong>Bold</strong>
```


2. innerHTML
────────────

HTML (peut être dangereux !)

```javascript
const div = document.querySelector('div');

// Lire
console.log(div.innerHTML); // "<p>Hello</p>"

// Écrire
div.innerHTML = '<p><strong>Bold</strong></p>';
// Affiche: Bold (en gras)

// [ATTENTION] DANGER : XSS si données utilisateur
const userInput = '<img src=x onerror="alert(\'XSS\')">';
div.innerHTML = userInput; // <- MAUVAIS ! Script exécuté
```

[ATTENTION] SÉCURITÉ :

```javascript
// [X] DANGEREUX
element.innerHTML = userInput;

// [OK] SÉCURISÉ
element.textContent = userInput;

// OU sanitize
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userInput);
```


3. outerHTML
────────────

Inclut l'élément lui-même

```javascript
const div = document.querySelector('div');

console.log(div.innerHTML);  // "<p>Content</p>"
console.log(div.outerHTML);  // "<div><p>Content</p></div>"

// Remplacer complètement l'élément
div.outerHTML = '<section>New</section>';
// div n'existe plus dans le DOM !
```


4. insertAdjacentHTML()
───────────────────────

Insérer HTML à position spécifique

```javascript
const element = document.querySelector('.target');

// 4 positions possibles
element.insertAdjacentHTML('beforebegin', '<div>Before</div>');
// <div>Before</div>
// <div class="target">Target</div>

element.insertAdjacentHTML('afterbegin', '<div>Start</div>');
// <div class="target">
//   <div>Start</div>
//   ...contenu existant
// </div>

element.insertAdjacentHTML('beforeend', '<div>End</div>');
// <div class="target">
//   ...contenu existant
//   <div>End</div>
// </div>

element.insertAdjacentHTML('afterend', '<div>After</div>');
// <div class="target">Target</div>
// <div>After</div>
```

Positions :
```
<!-- beforebegin -->
<div class="target">
  <!-- afterbegin -->
  contenu
  <!-- beforeend -->
</div>
<!-- afterend -->
```


AJOUTER AU DOM


1. appendChild()
────────────────

Ajouter à la fin

```javascript
const parent = document.getElementById('container');
const child = document.createElement('div');

parent.appendChild(child);

// Déplace si déjà dans le DOM
const existing = document.querySelector('.item');
parent.appendChild(existing); // Déplacé, pas dupliqué
```


2. append()
───────────

Moderne, plus flexible

```javascript
const parent = document.getElementById('container');

// Plusieurs éléments
parent.append(child1, child2, child3);

// Texte directement
parent.append('Hello');

// Mélange
parent.append('Text', element, 'More text');
```

Différences appendChild vs append :
```javascript
// appendChild
- Retourne l'élément ajouté
- Seulement 1 élément à la fois
- Seulement Nodes

// append
- Retourne undefined
- Multiple éléments/texte
- Plus moderne
```


3. prepend()
────────────

Ajouter au début

```javascript
parent.prepend(child); // Avant tous les enfants
```


4. insertBefore()
─────────────────

Insérer avant un élément spécifique

```javascript
const reference = document.querySelector('.reference');
const newElement = document.createElement('div');

parent.insertBefore(newElement, reference);
// newElement inséré AVANT reference
```


5. insertAdjacentElement()
──────────────────────────

Comme insertAdjacentHTML mais pour éléments

```javascript
const target = document.querySelector('.target');
const newEl = document.createElement('div');

target.insertAdjacentElement('beforebegin', newEl);
target.insertAdjacentElement('afterbegin', newEl);
target.insertAdjacentElement('beforeend', newEl);
target.insertAdjacentElement('afterend', newEl);
```


6. replaceChild()
─────────────────

Remplacer un enfant

```javascript
const parent = document.getElementById('container');
const oldChild = document.querySelector('.old');
const newChild = document.createElement('div');

parent.replaceChild(newChild, oldChild);
```


7. replaceWith()
────────────────

Plus moderne

```javascript
const old = document.querySelector('.old');
const newEl = document.createElement('div');

old.replaceWith(newEl);

// Plusieurs éléments
old.replaceWith(el1, el2, 'text');
```


SUPPRIMER DU DOM


1. removeChild()
────────────────

```javascript
const parent = document.getElementById('container');
const child = document.querySelector('.item');

parent.removeChild(child);

// Retourne l'élément supprimé
const removed = parent.removeChild(child);
console.log(removed); // Peut être réutilisé
```


2. remove()
───────────

Plus simple (moderne)

```javascript
const element = document.querySelector('.item');
element.remove(); // Supprimé du DOM
```


3. innerHTML = ''
─────────────────

Vider complètement

```javascript
const container = document.getElementById('container');

// Supprimer tous les enfants
container.innerHTML = '';

// OU (plus performant)
while (container.firstChild) {
  container.removeChild(container.firstChild);
}

// OU (moderne)
container.replaceChildren();
```


MODIFIER ATTRIBUTS


1. getAttribute() / setAttribute()
──────────────────────────────────

```javascript
const img = document.querySelector('img');

// Lire
const src = img.getAttribute('src');
const alt = img.getAttribute('alt');

// Écrire
img.setAttribute('src', 'new-image.jpg');
img.setAttribute('alt', 'Description');

// Attributs personnalisés
img.setAttribute('data-id', '123');

// Vérifier existence
if (img.hasAttribute('alt')) {
  console.log('Alt text exists');
}

// Supprimer
img.removeAttribute('title');
```


2. Propriétés directes
──────────────────────

Pour attributs standards

```javascript
const link = document.querySelector('a');

// [OK] Propriétés (recommandé)
link.href = 'https://example.com';
link.target = '_blank';
link.title = 'Click here';

// Équivalent getAttribute/setAttribute
link.getAttribute('href');
link.setAttribute('href', 'https://example.com');
```

Différences :
```javascript
const link = document.createElement('a');
link.setAttribute('href', '/path');

console.log(link.getAttribute('href')); // "/path"
console.log(link.href); // "http://example.com/path" (absolute URL)
```


3. dataset (data-* attributs)
─────────────────────────────

```javascript
// HTML: <div data-user-id="123" data-role="admin">

const div = document.querySelector('div');

// Lire
console.log(div.dataset.userId);  // "123"
console.log(div.dataset.role);    // "admin"

// Écrire
div.dataset.userId = '456';
div.dataset.newProp = 'value';

// HTML résultant:
// <div data-user-id="456" data-role="admin" data-new-prop="value">

// Supprimer
delete div.dataset.role;
```

Conversion kebab-case <-> camelCase :
```javascript
data-user-id    -> dataset.userId
data-first-name -> dataset.firstName
```


MODIFIER CLASSES


1. className
────────────

String complet

```javascript
const div = document.querySelector('div');

// Lire
console.log(div.className); // "box active red"

// Écrire (remplace tout)
div.className = 'container blue';

// Ajouter (manuel)
div.className += ' new-class'; // Attention espace !
```


2. classList
────────────

[IDEE] API MODERNE (recommandé)

```javascript
const div = document.querySelector('div');

// Ajouter
div.classList.add('active');
div.classList.add('red', 'large'); // Multiple

// Supprimer
div.classList.remove('active');
div.classList.remove('red', 'large');

// Toggle
div.classList.toggle('active'); // Ajoute si absent, enlève si présent

// Vérifier
if (div.classList.contains('active')) {
  console.log('Is active');
}

// Remplacer
div.classList.replace('old-class', 'new-class');

// Nombre de classes
console.log(div.classList.length);

// Itérer
div.classList.forEach(className => {
  console.log(className);
});
```


MODIFIER STYLES


1. style (inline)
─────────────────

Modifie attribut style

```javascript
const div = document.querySelector('div');

// Une propriété
div.style.color = 'red';
div.style.backgroundColor = 'blue'; // camelCase !
div.style.fontSize = '20px';

// Lire (inline seulement)
console.log(div.style.color); // "red"

// cssText (string complète)
div.style.cssText = 'color: red; background: blue; font-size: 20px;';

// setProperty / getPropertyValue
div.style.setProperty('color', 'red');
div.style.setProperty('--custom-var', '10px'); // CSS variables
console.log(div.style.getPropertyValue('color'));

// Supprimer
div.style.removeProperty('color');
div.style.color = ''; // Alternative
```


2. getComputedStyle()
─────────────────────

Styles calculés (après CSS)

```javascript
const div = document.querySelector('div');

// Tous les styles computés
const styles = getComputedStyle(div);

console.log(styles.color);       // Valeur finale
console.log(styles.fontSize);    // En pixels
console.log(styles.display);     // Valeur actuelle

// Pseudo-éléments
const beforeStyles = getComputedStyle(div, '::before');
console.log(beforeStyles.content);
```


PROPRIÉTÉS SPÉCIALES


1. Contenu
──────────

```javascript
const input = document.querySelector('input');

// Value (formulaires)
input.value = 'New value';
console.log(input.value);

// Checked (checkbox/radio)
const checkbox = document.querySelector('input[type="checkbox"]');
checkbox.checked = true;
console.log(checkbox.checked); // true/false
```


2. Dimensions et Position
─────────────────────────

```javascript
const div = document.querySelector('div');

// Dimensions (incluent padding mais pas border/margin)
console.log(div.clientWidth);   // width + padding
console.log(div.clientHeight);  // height + padding

// Dimensions (incluent border mais pas margin)
console.log(div.offsetWidth);   // width + padding + border
console.log(div.offsetHeight);  // height + padding + border

// Scroll
console.log(div.scrollWidth);   // Largeur totale contenu
console.log(div.scrollHeight);  // Hauteur totale contenu
console.log(div.scrollTop);     // Pixels scrollés verticalement
console.log(div.scrollLeft);    // Pixels scrollés horizontalement

// Position
console.log(div.offsetTop);     // Distance du parent positionné
console.log(div.offsetLeft);

// getBoundingClientRect (position viewport)
const rect = div.getBoundingClientRect();
console.log(rect.top);      // Distance du haut viewport
console.log(rect.left);     // Distance de la gauche viewport
console.log(rect.width);
console.log(rect.height);
console.log(rect.bottom);   // top + height
console.log(rect.right);    // left + width
```


PERFORMANCE : BATCH OPERATIONS

[X] MAUVAIS (reflow multiple) :
```javascript
element.style.width = '100px';   // Reflow
element.style.height = '100px';  // Reflow
element.style.border = '1px';    // Reflow
```

[OK] BON (reflow une fois) :
```javascript
// Méthode 1: cssText
element.style.cssText = 'width: 100px; height: 100px; border: 1px;';

// Méthode 2: class
element.classList.add('sized'); // CSS définit tout

// Méthode 3: DocumentFragment
const fragment = document.createDocumentFragment();
// Construire structure dans fragment
container.appendChild(fragment); // Reflow 1x
```


[ANALYSE] EXERCICE PRATIQUE

```javascript
// Créer liste dynamique optimisée
function createList(items) {
  const ul = document.createElement('ul');
  const fragment = document.createDocumentFragment();
  
  items.forEach((item, index) => {
    const li = document.createElement('li');
    li.textContent = item;
    li.dataset.index = index;
    li.classList.add('list-item');
    
    if (index === 0) {
      li.classList.add('first');
    }
    
    fragment.appendChild(li);
  });
  
  ul.appendChild(fragment);
  return ul;
}

// Utilisation
const myList = createList(['Item 1', 'Item 2', 'Item 3']);
document.body.appendChild(myList);
```
"""


# ----------------------------------------------------------------------------
# [OBJECTIF] ÉVÉNEMENTS (EVENTS)
# ----------------------------------------------------------------------------

"""
SYSTÈME D'ÉVÉNEMENTS DU NAVIGATEUR


TYPES D'ÉVÉNEMENTS

1. SOURIS (Mouse)
─────────────────

click        - Clic complet (mousedown + mouseup)
dblclick     - Double-clic
mousedown    - Bouton pressé
mouseup      - Bouton relâché
mousemove    - Souris bouge
mouseenter   - Souris entre (pas de bubble)
mouseleave   - Souris sort (pas de bubble)
mouseover    - Souris entre (avec bubble)
mouseout     - Souris sort (avec bubble)
contextmenu  - Clic droit


2. CLAVIER (Keyboard)
─────────────────────

keydown      - Touche pressée
keyup        - Touche relâchée
keypress     - [ATTENTION] Deprecated


3. FORMULAIRE (Form)
────────────────────

submit       - Formulaire soumis
change       - Valeur changée (après perte focus)
input        - Valeur changée (immédiat)
focus        - Élément reçoit focus
blur         - Élément perd focus
invalid      - Validation échoue


4. DOCUMENT/FENÊTRE
───────────────────

DOMContentLoaded - DOM parsé (sans images)
load             - Tout chargé (avec images)
beforeunload     - Avant décharger page
unload           - Page déchargée
resize           - Fenêtre redimensionnée
scroll           - Scroll


5. DRAG & DROP
──────────────

drag, dragstart, dragend, dragenter, dragleave, dragover, drop


6. MEDIA
────────

play, pause, ended, volumechange, etc.


AJOUTER EVENT LISTENERS


1. addEventListener()
─────────────────────

[IDEE] MÉTHODE RECOMMANDÉE

```javascript
const button = document.querySelector('button');

// Syntaxe
element.addEventListener(eventType, handlerFunction, options);

// Exemple basique
button.addEventListener('click', function(event) {
  console.log('Button clicked!');
  console.log('Event:', event);
});

// Avec arrow function
button.addEventListener('click', (event) => {
  console.log('Clicked');
});

// Fonction nommée (pour pouvoir remove)
function handleClick(event) {
  console.log('Clicked');
}
button.addEventListener('click', handleClick);

// Plusieurs listeners sur même événement
button.addEventListener('click', handler1);
button.addEventListener('click', handler2);
// Les deux s'exécutent !
```


2. OPTIONS (troisième paramètre)
────────────────────────────────

```javascript
// Ancienne syntaxe (booléen)
element.addEventListener('click', handler, true); // Capture phase

// Nouvelle syntaxe (objet)
element.addEventListener('click', handler, {
  capture: false,    // Capture phase (défaut: false = bubbling)
  once: true,        // Exécuter une seule fois puis auto-remove
  passive: true,     // Ne pas appeler preventDefault()
  signal: abortController.signal // AbortController pour remove
});
```

Exemples :

```javascript
// Once (auto-remove)
button.addEventListener('click', () => {
  console.log('Clicked once!');
}, { once: true });

// Passive (optimisation scroll/touch)
document.addEventListener('touchstart', handler, { passive: true });
// Indique au navigateur qu'on n'appellera pas preventDefault()
// -> Peut optimiser scrolling

// AbortController (remove multiple listeners)
const controller = new AbortController();

button.addEventListener('click', handler1, { signal: controller.signal });
button.addEventListener('click', handler2, { signal: controller.signal });

// Plus tard : remove tous les listeners
controller.abort();
```


3. removeEventListener()
────────────────────────

```javascript
function handleClick(event) {
  console.log('Clicked');
}

// Ajouter
button.addEventListener('click', handleClick);

// Retirer (fonction identique requise!)
button.removeEventListener('click', handleClick);

// [X] NE FONCTIONNE PAS (fonction anonyme différente)
button.addEventListener('click', () => console.log('A'));
button.removeEventListener('click', () => console.log('A'));
```


4. INLINE HANDLERS ([X] À ÉVITER)
────────────────────────────────

```javascript
// HTML
<button onclick="handleClick()">Click</button>

// JS
function handleClick() {
  console.log('Clicked');
}

// OU
<button onclick="console.log('Clicked')">Click</button>
```

Pourquoi éviter :
[X] Mélange HTML et JS
[X] Un seul handler possible
[X] Difficile à maintenir
[X] Scope global requis


5. ON-PROPERTY ([X] ANCIEN)
──────────────────────────

```javascript
button.onclick = function(event) {
  console.log('Clicked');
};

// Écrase précédent !
button.onclick = function(event) {
  console.log('New handler');
};
```

Problème : Un seul handler possible


OBJET EVENT


```javascript
button.addEventListener('click', (event) => {
  // Type d'événement
  console.log(event.type); // "click"
  
  // Élément qui a déclenché
  console.log(event.target); // L'élément cliqué
  
  // Élément avec le listener
  console.log(event.currentTarget); // button (peut différer de target)
  
  // Position souris
  console.log(event.clientX, event.clientY); // Relatif viewport
  console.log(event.pageX, event.pageY);     // Relatif document
  console.log(event.screenX, event.screenY); // Relatif écran
  
  // Touches modificatrices
  console.log(event.ctrlKey);  // Ctrl pressé ?
  console.log(event.shiftKey); // Shift pressé ?
  console.log(event.altKey);   // Alt pressé ?
  console.log(event.metaKey);  // Cmd (Mac) / Win pressé ?
  
  // Bouton souris
  console.log(event.button); // 0=gauche, 1=milieu, 2=droit
  
  // Timestamp
  console.log(event.timeStamp);
  
  // Empêcher défaut
  event.preventDefault();
  
  // Arrêter propagation
  event.stopPropagation();
  event.stopImmediatePropagation();
});
```


EVENT PROPAGATION (FONDAMENTAL !)

[IDEE] 3 PHASES : Capture -> Target -> Bubbling


Exemple HTML :
```html
<div id="outer">
  <div id="inner">
    <button id="btn">Click</button>
  </div>
</div>
```

Propagation quand button cliqué :

PHASE 1 : CAPTURE (descente)
document -> html -> body -> outer -> inner -> button

PHASE 2 : TARGET
button (élément cliqué)

PHASE 3 : BUBBLING (remontée)
button -> inner -> outer -> body -> html -> document


Code :

```javascript
const outer = document.getElementById('outer');
const inner = document.getElementById('inner');
const btn = document.getElementById('btn');

// Bubbling (défaut)
outer.addEventListener('click', () => {
  console.log('Outer');
});

inner.addEventListener('click', () => {
  console.log('Inner');
});

btn.addEventListener('click', () => {
  console.log('Button');
});

// Clic sur button -> Ordre :
// "Button" (target)
// "Inner" (bubbling)
// "Outer" (bubbling)


// Capture phase
outer.addEventListener('click', () => {
  console.log('Outer (capture)');
}, true); // ou { capture: true }

btn.addEventListener('click', () => {
  console.log('Button');
});

// Clic sur button -> Ordre :
// "Outer (capture)" (capture)
// "Button" (target)
// "Outer" (bubbling)
```


VISUALISATION :

```
         document <-───────────────┐
            v                     │
           html <-────────────┐    │
            v                │    │
           body <-─────────┐  │    │
            v             │  │    │
          outer <-──────┐  │  │    │
            v          │  │  │    │
          inner <-───┐  │  │  │    │
            v       │  │  │  │    │
          button ───┘──┘──┘──┘────┘
          (click)
          
    CAPTURE  TARGET  BUBBLING
      v        v        ^
```


STOPPER LA PROPAGATION

```javascript
btn.addEventListener('click', (event) => {
  console.log('Button');
  
  event.stopPropagation();
  // Arrête bubbling (outer/inner ne s'exécutent pas)
});

// stopImmediatePropagation
btn.addEventListener('click', (event) => {
  console.log('Handler 1');
  event.stopImmediatePropagation();
  // Arrête TOUS les autres listeners (même sur button)
});

btn.addEventListener('click', () => {
  console.log('Handler 2'); // Ne s'exécute pas
});
```


PREVENT DEFAULT

Empêcher comportement par défaut

```javascript
// Empêcher soumission formulaire
form.addEventListener('submit', (event) => {
  event.preventDefault();
  // Validation personnalisée
});

// Empêcher navigation lien
link.addEventListener('click', (event) => {
  event.preventDefault();
  // Navigation SPA
});

// Empêcher menu contextuel
document.addEventListener('contextmenu', (event) => {
  event.preventDefault();
  // Menu custom
});

// Empêcher sélection texte
element.addEventListener('selectstart', (event) => {
  event.preventDefault();
});
```


EVENT DELEGATION

[IDEE] PATTERN PUISSANT : Un listener pour plusieurs éléments

Problème :
```javascript
// [X] MAUVAIS - Listener par élément
const buttons = document.querySelectorAll('.button');
buttons.forEach(button => {
  button.addEventListener('click', handleClick);
});
// Si 1000 buttons -> 1000 listeners !
```

Solution :
```javascript
// [OK] BON - Un listener sur parent
const container = document.getElementById('container');

container.addEventListener('click', (event) => {
  // Vérifier si click sur button
  if (event.target.matches('.button')) {
    handleClick(event);
  }
});

// Fonctionne même pour éléments ajoutés dynamiquement !
```


Exemple pratique :

```javascript
// Liste dynamique
const list = document.getElementById('list');

// Un listener sur <ul>
list.addEventListener('click', (event) => {
  const target = event.target;
  
  // Si clic sur <li>
  if (target.tagName === 'LI') {
    console.log('Item clicked:', target.textContent);
    target.classList.toggle('active');
  }
  
  // Si clic sur bouton delete
  if (target.classList.contains('delete-btn')) {
    const li = target.closest('li');
    li.remove();
  }
});

// Ajouter items dynamiquement
function addItem(text) {
  const li = document.createElement('li');
  li.innerHTML = `
    ${text}
    <button class="delete-btn">×</button>
  `;
  list.appendChild(li);
}

// Listeners fonctionnent automatiquement !
```


ÉVÉNEMENTS CLAVIER

```javascript
document.addEventListener('keydown', (event) => {
  // Touche pressée
  console.log(event.key);        // "a", "Enter", "ArrowUp"
  console.log(event.code);       // "KeyA", "Enter", "ArrowUp"
  console.log(event.keyCode);    // [ATTENTION] Deprecated
  
  // Combinaisons
  if (event.ctrlKey && event.key === 's') {
    event.preventDefault(); // Empêcher Ctrl+S natif
    console.log('Save triggered');
  }
  
  // Flèches
  switch(event.key) {
    case 'ArrowUp':
      moveUp();
      break;
    case 'ArrowDown':
      moveDown();
      break;
    case 'Enter':
      submit();
      break;
    case 'Escape':
      close();
      break;
  }
});
```


ÉVÉNEMENTS FORMULAIRE

```javascript
const input = document.querySelector('input');
const form = document.querySelector('form');

// Input change (immediate)
input.addEventListener('input', (event) => {
  console.log('Value:', event.target.value);
  // Validation en temps réel
});

// Change (after blur)
input.addEventListener('change', (event) => {
  console.log('Final value:', event.target.value);
});

// Focus
input.addEventListener('focus', () => {
  console.log('Input focused');
});

// Blur
input.addEventListener('blur', () => {
  console.log('Input blurred');
});

// Submit
form.addEventListener('submit', (event) => {
  event.preventDefault();
  
  const formData = new FormData(event.target);
  const data = Object.fromEntries(formData);
  
  console.log('Form data:', data);
});
```


CUSTOM EVENTS

Créer événements personnalisés

```javascript
// Créer événement
const myEvent = new CustomEvent('myevent', {
  detail: { message: 'Hello', timestamp: Date.now() },
  bubbles: true,
  cancelable: true
});

// Dispatcher
element.dispatchEvent(myEvent);

// Écouter
element.addEventListener('myevent', (event) => {
  console.log('Custom event:', event.detail);
});
```

Exemple pratique :

```javascript
// Composant qui émet événement
class Counter {
  constructor(element) {
    this.element = element;
    this.count = 0;
  }
  
  increment() {
    this.count++;
    
    // Émettre événement custom
    const event = new CustomEvent('counterchange', {
      detail: { count: this.count }
    });
    this.element.dispatchEvent(event);
  }
}

// Utilisation
const counter = new Counter(document.getElementById('counter'));

document.getElementById('counter').addEventListener('counterchange', (e) => {
  console.log('Count changed to:', e.detail.count);
});

counter.increment(); // Déclenche événement
```


DEBOUNCE ET THROTTLE

Optimiser événements fréquents (scroll, resize, input)

```javascript
// DEBOUNCE - Attendre que l'utilisateur arrête
function debounce(func, delay) {
  let timeout;
  return function(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(this, args), delay);
  };
}

// Utilisation
const debouncedSearch = debounce((query) => {
  console.log('Searching for:', query);
  // API call
}, 300);

input.addEventListener('input', (e) => {
  debouncedSearch(e.target.value);
});


// THROTTLE - Limiter fréquence
function throttle(func, delay) {
  let lastCall = 0;
  return function(...args) {
    const now = Date.now();
    if (now - lastCall >= delay) {
      lastCall = now;
      func.apply(this, args);
    }
  };
}

// Utilisation
const throttledScroll = throttle(() => {
  console.log('Scroll position:', window.scrollY);
}, 100);

window.addEventListener('scroll', throttledScroll);
```


[ANALYSE] EXERCICE : EVENT DELEGATION

```html
<div id="app">
  <button id="add">Add Item</button>
  <ul id="list"></ul>
</div>

<script>
const addBtn = document.getElementById('add');
const list = document.getElementById('list');
let itemCount = 0;

// Add items
addBtn.addEventListener('click', () => {
  const li = document.createElement('li');
  li.innerHTML = `
    Item ${++itemCount}
    <button class="delete">Delete</button>
  `;
  list.appendChild(li);
});

// Event delegation pour delete
list.addEventListener('click', (event) => {
  if (event.target.classList.contains('delete')) {
    event.target.parentElement.remove();
  }
});

// Fonctionne pour TOUS les items, même futurs !
</script>
```
"""


# ----------------------------------------------------------------------------
# [EYE] OBSERVERS (APIs MODERNES)
# ----------------------------------------------------------------------------

"""
MUTATION OBSERVER

Observer changements DOM


```javascript
// Créer observer
const observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    console.log('Type:', mutation.type);
    
    if (mutation.type === 'childList') {
      console.log('Children changed');
      console.log('Added:', mutation.addedNodes);
      console.log('Removed:', mutation.removedNodes);
    }
    
    if (mutation.type === 'attributes') {
      console.log('Attribute changed:', mutation.attributeName);
      console.log('Old value:', mutation.oldValue);
    }
    
    if (mutation.type === 'characterData') {
      console.log('Text changed');
    }
  });
});

// Observer élément
const target = document.getElementById('target');

observer.observe(target, {
  childList: true,        // Observer ajout/suppression enfants
  attributes: true,       // Observer changements attributs
  attributeOldValue: true,// Capturer ancienne valeur
  characterData: true,    // Observer changements texte
  subtree: true,          // Observer descendants aussi
  attributeFilter: ['class', 'style'] // Seulement ces attributs
});

// Stopper observation
observer.disconnect();

// Récupérer mutations en attente
const pending = observer.takeRecords();
```


Exemple pratique :

```javascript
// Auto-save quand contenu change
const editor = document.getElementById('editor');

const saveObserver = new MutationObserver(() => {
  console.log('Content changed, auto-saving...');
  saveContent(editor.textContent);
});

saveObserver.observe(editor, {
  childList: true,
  characterData: true,
  subtree: true
});
```


INTERSECTION OBSERVER

Observer visibilité éléments (viewport)


```javascript
// Créer observer
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    console.log('Target:', entry.target);
    console.log('Is intersecting:', entry.isIntersecting);
    console.log('Intersection ratio:', entry.intersectionRatio);
    
    if (entry.isIntersecting) {
      // Élément visible
      entry.target.classList.add('visible');
    } else {
      // Élément caché
      entry.target.classList.remove('visible');
    }
  });
}, {
  root: null,           // null = viewport
  rootMargin: '0px',    // Margin autour root
  threshold: 0.5        // 0-1 (0.5 = 50% visible)
});

// Observer éléments
const elements = document.querySelectorAll('.observe');
elements.forEach(el => observer.observe(el));

// Stopper observation
observer.disconnect();

// Unobserve élément spécifique
observer.unobserve(element);
```


Use cases :

```javascript
// 1. LAZY LOADING IMAGES
const imgObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src; // Charger image
      imgObserver.unobserve(img); // Stop observing
    }
  });
});

document.querySelectorAll('img[data-src]').forEach(img => {
  imgObserver.observe(img);
});


// 2. INFINITE SCROLL
const sentinel = document.getElementById('sentinel');

const scrollObserver = new IntersectionObserver((entries) => {
  if (entries[0].isIntersecting) {
    loadMoreContent();
  }
});

scrollObserver.observe(sentinel);


// 3. ANALYTICS (track visibility)
const analyticsObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      trackImpression(entry.target.dataset.id);
    }
  });
}, { threshold: 0.9 }); // 90% visible


// 4. ANIMATIONS ON SCROLL
const animObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('animate-in');
    }
  });
}, { threshold: 0.2 });
```


RESIZE OBSERVER

Observer changements taille


```javascript
const observer = new ResizeObserver((entries) => {
  entries.forEach(entry => {
    const { width, height } = entry.contentRect;
    console.log(`Size: ${width}x${height}`);
    
    // Responsive behavior
    if (width < 600) {
      entry.target.classList.add('small');
    } else {
      entry.target.classList.remove('small');
    }
  });
});

observer.observe(element);
```


Use case :

```javascript
// Textarea auto-resize
const textarea = document.querySelector('textarea');

const resizeObserver = new ResizeObserver(() => {
  textarea.style.height = 'auto';
  textarea.style.height = textarea.scrollHeight + 'px';
});

resizeObserver.observe(textarea);


// Responsive charts
const chart = document.getElementById('chart');

const chartObserver = new ResizeObserver(entries => {
  const { width, height } = entries[0].contentRect;
  updateChart(width, height);
});

chartObserver.observe(chart);
```


PERFORMANCE OBSERVER

Observer métriques performance (avancé)


```javascript
const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach(entry => {
    console.log('Name:', entry.name);
    console.log('Type:', entry.entryType);
    console.log('Duration:', entry.duration);
  });
});

// Observer types
observer.observe({
  entryTypes: ['navigation', 'resource', 'paint', 'measure']
});
```


[ANALYSE] EXERCICE : LAZY LOADING COMPLET

```html
<style>
.lazy-img {
  min-height: 400px;
  background: #f0f0f0;
  opacity: 0;
  transition: opacity 0.5s;
}

.lazy-img.loaded {
  opacity: 1;
}
</style>

<img class="lazy-img" data-src="image1.jpg" alt="Image 1">
<img class="lazy-img" data-src="image2.jpg" alt="Image 2">
<img class="lazy-img" data-src="image3.jpg" alt="Image 3">

<script>
const lazyImages = document.querySelectorAll('.lazy-img');

const imageObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      
      // Charger image
      img.src = img.dataset.src;
      
      // Quand chargée
      img.addEventListener('load', () => {
        img.classList.add('loaded');
      });
      
      // Stop observing
      observer.unobserve(img);
    }
  });
}, {
  rootMargin: '50px' // Charger 50px avant d'être visible
});

lazyImages.forEach(img => imageObserver.observe(img));
</script>
```
"""


# ============================================================================
# [DOCS] RÉCAPITULATIF CHAPITRE 3
# ============================================================================

"""
CE QUE VOUS AVEZ APPRIS

[OK] Sélection éléments (querySelector, getElementById, etc.)
[OK] Création et modification DOM
[OK] Manipulation attributs, classes, styles
[OK] Système d'événements complet
[OK] Event propagation (capture, target, bubbling)
[OK] Event delegation pattern
[OK] Custom events
[OK] Observers modernes (Mutation, Intersection, Resize)
[OK] Performance et optimisations DOM


[CLE] POINTS CLÉS

SÉLECTION :
- getElementById() le plus rapide
- querySelector() pour sélecteurs complexes
- Cache sélections réutilisées

MODIFICATION :
- DocumentFragment pour batch inserts
- classList pour classes (pas className)
- textContent (sécurisé) vs innerHTML (XSS risk)

ÉVÉNEMENTS :
- addEventListener() (pas onclick)
- Event delegation pour listes dynamiques
- stopPropagation() vs preventDefault()
- Debounce/throttle pour événements fréquents

OBSERVERS :
- MutationObserver -> Changements DOM
- IntersectionObserver -> Visibilité (lazy loading)
- ResizeObserver -> Dimensions


[OBJECTIF] BEST PRACTICES

[OK] Event delegation pour listes
[OK] Batch DOM operations (fragment)
[OK] Éviter layout thrashing
[OK] Utiliser observers au lieu de scroll listeners
[OK] Remove event listeners quand plus nécessaires
[OK] Sanitize user input (XSS)


-> PROCHAINE ÉTAPE : Chapitre 4 - Réseau et Protocoles HTTP !

Vous allez découvrir :
- Requêtes HTTP avec fetch()
- XMLHttpRequest (legacy)
- CORS et same-origin policy
- Cookies et headers
- WebSockets temps réel
- Server-Sent Events
- Upload de fichiers
- Gestion erreurs réseau

Prêt à communiquer avec les serveurs ? [WEB][RAPIDE]
"""
```

Voulez-vous que je continue avec le Chapitre 4 (Réseau et Protocoles HTTP) ?

```python
# ============================================================================
# [GUIDE] CHAPITRE 4 : RÉSEAU ET PROTOCOLES HTTP
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Faire des requêtes HTTP avec fetch()
[OK] Gérer les Promises et async/await
[OK] Comprendre CORS et same-origin policy
[OK] Manipuler cookies et headers HTTP
[OK] Utiliser WebSockets pour temps réel
[OK] Server-Sent Events (SSE)
[OK] Upload de fichiers et FormData
[OK] Gérer erreurs réseau et timeouts
[OK] Caching et stratégies réseau
"""


# ----------------------------------------------------------------------------
# [WEB] PROTOCOLE HTTP : FONDAMENTAUX
# ----------------------------------------------------------------------------

"""
HTTP = HyperText Transfer Protocol

[IDEE] PROTOCOLE CLIENT-SERVEUR

Client (Navigateur)  <-────->  Serveur
       │                        │
       │  1. Requête HTTP       │
       │─────────────────────->  │
       │                        │
       │  2. Réponse HTTP       │
       │  <-─────────────────────│


STRUCTURE REQUÊTE HTTP

GET /api/users HTTP/1.1                    <- Request Line
Host: example.com                          ┐
User-Agent: Mozilla/5.0...                 │
Accept: application/json                   ├─ Headers
Content-Type: application/json             │
Cookie: session=abc123                     ┘
                                           <- Ligne vide
{"name": "Alice", "age": 30}               <- Body (optionnel)


STRUCTURE RÉPONSE HTTP

HTTP/1.1 200 OK                            <- Status Line
Date: Mon, 17 Jan 2026 10:00:00 GMT        ┐
Server: nginx/1.18.0                       │
Content-Type: application/json             ├─ Headers
Content-Length: 48                         │
Set-Cookie: session=xyz789                 ┘
                                           <- Ligne vide
{"id": 1, "name": "Alice", "age": 30}      <- Body


MÉTHODES HTTP (VERBES)

GET      - Récupérer ressource (lecture)
POST     - Créer ressource (création)
PUT      - Remplacer ressource (mise à jour complète)
PATCH    - Modifier ressource (mise à jour partielle)
DELETE   - Supprimer ressource
HEAD     - Comme GET mais sans body (seulement headers)
OPTIONS  - Obtenir options disponibles (CORS preflight)


REST PATTERN CONVENTIONNEL

GET     /api/users          -> Liste tous les users
GET     /api/users/123      -> Récupère user 123
POST    /api/users          -> Crée nouveau user
PUT     /api/users/123      -> Remplace user 123
PATCH   /api/users/123      -> Modifie user 123
DELETE  /api/users/123      -> Supprime user 123


STATUS CODES HTTP

1xx - Informationnel
────────────────────
100 Continue            - Continuer la requête
101 Switching Protocols - Changement protocole (WebSocket)

2xx - Succès
────────────
200 OK                  - Succès
201 Created             - Ressource créée
204 No Content          - Succès sans contenu
206 Partial Content     - Contenu partiel (streaming)

3xx - Redirection
─────────────────
301 Moved Permanently   - Redirection permanente
302 Found               - Redirection temporaire
304 Not Modified        - Pas modifié (cache valide)
307 Temporary Redirect  - Redirection temp (méthode préservée)

4xx - Erreur Client
───────────────────
400 Bad Request         - Requête invalide
401 Unauthorized        - Non authentifié
403 Forbidden           - Non autorisé (authentifié mais refusé)
404 Not Found           - Ressource introuvable
405 Method Not Allowed  - Méthode non permise
409 Conflict            - Conflit (ex: doublon)
422 Unprocessable       - Données invalides
429 Too Many Requests   - Rate limit dépassé

5xx - Erreur Serveur
────────────────────
500 Internal Error      - Erreur serveur
502 Bad Gateway         - Erreur proxy
503 Service Unavailable - Service indisponible
504 Gateway Timeout     - Timeout proxy


HEADERS IMPORTANTS

Requête :
─────────
Host: example.com              - Domaine cible (requis)
User-Agent: Mozilla/5.0...     - Navigateur/client
Accept: application/json       - Type contenu accepté
Accept-Language: fr-FR,en      - Langues acceptées
Accept-Encoding: gzip,deflate  - Compression acceptée
Content-Type: application/json - Type contenu envoyé
Content-Length: 123            - Taille body
Authorization: Bearer token    - Authentification
Cookie: session=abc            - Cookies
Referer: https://previous.com  - URL précédente
Origin: https://example.com    - Origine (CORS)

Réponse :
─────────
Content-Type: application/json - Type contenu retourné
Content-Length: 456            - Taille body
Set-Cookie: session=xyz        - Définir cookie
Cache-Control: max-age=3600    - Instructions cache
ETag: "abc123"                 - Version ressource
Last-Modified: Mon, 17 Jan...  - Date modification
Location: /new-url             - URL redirection
Access-Control-Allow-Origin: * - CORS permission
```


# ----------------------------------------------------------------------------
# [RAPIDE] FETCH API : REQUÊTES HTTP MODERNES
# ----------------------------------------------------------------------------

"""
FETCH API = Méthode moderne pour requêtes HTTP

Remplace XMLHttpRequest (ancien)


SYNTAXE DE BASE

```javascript
// GET simple
fetch('https://api.example.com/users')
  .then(response => response.json())
  .then(data => {
    console.log('Users:', data);
  })
  .catch(error => {
    console.error('Error:', error);
  });
```


FETCH RETOURNE UNE PROMISE

```javascript
const promise = fetch(url);

promise
  .then(response => {
    // Response object reçu
    console.log('Status:', response.status);
    return response.json(); // Retourne nouvelle Promise
  })
  .then(data => {
    // Data parsée
    console.log('Data:', data);
  })
  .catch(error => {
    // Erreur réseau ou parsing
    console.error('Error:', error);
  });
```


ASYNC/AWAIT (Plus lisible !)

```javascript
async function getUsers() {
  try {
    const response = await fetch('https://api.example.com/users');
    const data = await response.json();
    console.log('Users:', data);
    return data;
  } catch (error) {
    console.error('Error:', error);
    throw error;
  }
}

// Utilisation
getUsers();

// OU avec IIFE
(async () => {
  const users = await getUsers();
  console.log(users);
})();
```


OBJET RESPONSE

```javascript
const response = await fetch(url);

// Status
console.log(response.status);     // 200, 404, 500...
console.log(response.statusText); // "OK", "Not Found"...
console.log(response.ok);         // true si 200-299

// Headers
console.log(response.headers.get('Content-Type'));
console.log(response.headers.get('Content-Length'));

// Headers complètes
for (let [key, value] of response.headers) {
  console.log(`${key}: ${value}`);
}

// Type
console.log(response.type);       // "basic", "cors", "opaque"
console.log(response.url);        // URL finale (après redirects)
console.log(response.redirected); // true si redirections

// Body methods (consomment le stream, une seule fois !)
const json = await response.json();        // Parse JSON
const text = await response.text();        // Texte brut
const blob = await response.blob();        // Binary (images, files)
const buffer = await response.arrayBuffer(); // ArrayBuffer
const formData = await response.formData(); // FormData

// Clone si besoin utiliser plusieurs fois
const clone = response.clone();
const json1 = await response.json();
const json2 = await clone.json(); // OK
```


REQUÊTE GET AVEC PARAMÈTRES

```javascript
// Méthode 1 : Construction manuelle
const url = 'https://api.example.com/search?q=javascript&limit=10';

// Méthode 2 : URLSearchParams
const params = new URLSearchParams({
  q: 'javascript',
  limit: 10,
  sort: 'date'
});

const url = `https://api.example.com/search?${params}`;
// -> https://api.example.com/search?q=javascript&limit=10&sort=date

// Méthode 3 : URL object
const url = new URL('https://api.example.com/search');
url.searchParams.set('q', 'javascript');
url.searchParams.set('limit', 10);

const response = await fetch(url);
```


REQUÊTE POST (Envoyer données)

```javascript
// POST JSON
const response = await fetch('https://api.example.com/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Alice',
    email: 'alice@example.com'
  })
});

const data = await response.json();
console.log('Created:', data);
```


AUTRES MÉTHODES HTTP

```javascript
// PUT (remplacer)
await fetch(`https://api.example.com/users/${id}`, {
  method: 'PUT',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'Alice Updated' })
});

// PATCH (modifier partiellement)
await fetch(`https://api.example.com/users/${id}`, {
  method: 'PATCH',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ email: 'newemail@example.com' })
});

// DELETE
await fetch(`https://api.example.com/users/${id}`, {
  method: 'DELETE'
});
```


HEADERS PERSONNALISÉS

```javascript
const response = await fetch(url, {
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer eyJhbGci...',
    'X-Custom-Header': 'value',
    'Accept-Language': 'fr-FR'
  }
});

// Headers object
const headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Authorization', 'Bearer token');

const response = await fetch(url, { headers });
```


CREDENTIALS (Cookies)

```javascript
// Same-origin (défaut)
fetch(url, {
  credentials: 'same-origin' // Envoie cookies si même domaine
});

// Include (toujours)
fetch(url, {
  credentials: 'include' // Envoie cookies même cross-origin
});

// Omit (jamais)
fetch(url, {
  credentials: 'omit' // Ne jamais envoyer cookies
});
```


MODE CORS

```javascript
fetch(url, {
  mode: 'cors',        // Cross-origin (défaut)
  // mode: 'no-cors',  // Pas de CORS (response opaque)
  // mode: 'same-origin' // Seulement same-origin
});
```


CACHE

```javascript
fetch(url, {
  cache: 'default',      // Défaut navigateur
  // cache: 'no-store',  // Ne jamais cacher
  // cache: 'reload',    // Bypass cache, update cache
  // cache: 'no-cache',  // Valider avec serveur
  // cache: 'force-cache' // Utiliser cache même expiré
});
```


ABORT REQUESTS (Annulation)

```javascript
// Créer AbortController
const controller = new AbortController();

// Passer signal
const fetchPromise = fetch(url, {
  signal: controller.signal
});

// Annuler après 5s
setTimeout(() => {
  controller.abort();
}, 5000);

// Gérer annulation
try {
  const response = await fetchPromise;
  const data = await response.json();
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('Fetch aborted');
  } else {
    console.error('Error:', error);
  }
}
```

Use case : Cancel previous request

```javascript
let controller = null;

async function search(query) {
  // Cancel previous request
  if (controller) {
    controller.abort();
  }
  
  controller = new AbortController();
  
  try {
    const response = await fetch(`/search?q=${query}`, {
      signal: controller.signal
    });
    const data = await response.json();
    displayResults(data);
  } catch (error) {
    if (error.name !== 'AbortError') {
      console.error('Search failed:', error);
    }
  }
}

// User types in search box
searchInput.addEventListener('input', (e) => {
  search(e.target.value);
});
```


TIMEOUT CUSTOM

```javascript
// Fetch avec timeout
async function fetchWithTimeout(url, options = {}, timeout = 5000) {
  const controller = new AbortController();
  const signal = controller.signal;
  
  const timeoutId = setTimeout(() => controller.abort(), timeout);
  
  try {
    const response = await fetch(url, { ...options, signal });
    clearTimeout(timeoutId);
    return response;
  } catch (error) {
    clearTimeout(timeoutId);
    if (error.name === 'AbortError') {
      throw new Error('Request timeout');
    }
    throw error;
  }
}

// Utilisation
try {
  const response = await fetchWithTimeout('/api/data', {}, 3000);
  const data = await response.json();
} catch (error) {
  console.error('Error:', error.message);
}
```


RETRY LOGIC

```javascript
async function fetchWithRetry(url, options = {}, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      const response = await fetch(url, options);
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }
      
      return response;
    } catch (error) {
      const isLastAttempt = i === retries - 1;
      
      if (isLastAttempt) {
        throw error;
      }
      
      // Exponential backoff
      const delay = Math.pow(2, i) * 1000;
      console.log(`Retry ${i + 1} after ${delay}ms`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
}

// Utilisation
try {
  const response = await fetchWithRetry('/api/data');
  const data = await response.json();
} catch (error) {
  console.error('All retries failed:', error);
}
```


VÉRIFIER STATUS

```javascript
async function fetchData(url) {
  const response = await fetch(url);
  
  // [ATTENTION] fetch ne rejette QUE pour erreurs réseau !
  // Status 404, 500 etc. = response.ok = false
  
  if (!response.ok) {
    throw new Error(`HTTP error! Status: ${response.status}`);
  }
  
  return response.json();
}

// OU helper
async function checkResponse(response) {
  if (!response.ok) {
    const error = await response.text();
    throw new Error(`${response.status}: ${error}`);
  }
  return response;
}

// Utilisation
const response = await fetch(url);
await checkResponse(response);
const data = await response.json();
```


GESTION ERREURS COMPLÈTE

```javascript
async function robustFetch(url, options = {}) {
  try {
    const response = await fetch(url, options);
    
    // Vérifier status
    if (!response.ok) {
      // Tenter parser erreur JSON
      let errorMessage;
      try {
        const errorData = await response.json();
        errorMessage = errorData.message || errorData.error;
      } catch {
        errorMessage = await response.text();
      }
      
      throw new Error(`HTTP ${response.status}: ${errorMessage}`);
    }
    
    // Parse response
    const contentType = response.headers.get('Content-Type');
    
    if (contentType?.includes('application/json')) {
      return await response.json();
    } else if (contentType?.includes('text/')) {
      return await response.text();
    } else {
      return await response.blob();
    }
    
  } catch (error) {
    // Erreur réseau
    if (error instanceof TypeError) {
      throw new Error('Network error: Check your connection');
    }
    
    // Abort
    if (error.name === 'AbortError') {
      throw new Error('Request was cancelled');
    }
    
    // Autre erreur
    throw error;
  }
}
```


[ANALYSE] EXERCICE : API WRAPPER

```javascript
class API {
  constructor(baseURL, defaultHeaders = {}) {
    this.baseURL = baseURL;
    this.defaultHeaders = defaultHeaders;
  }
  
  async request(endpoint, options = {}) {
    const url = `${this.baseURL}${endpoint}`;
    
    const config = {
      ...options,
      headers: {
        'Content-Type': 'application/json',
        ...this.defaultHeaders,
        ...options.headers
      }
    };
    
    const response = await fetch(url, config);
    
    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.message || `HTTP ${response.status}`);
    }
    
    return response.json();
  }
  
  get(endpoint, params = {}) {
    const url = new URL(`${this.baseURL}${endpoint}`);
    Object.keys(params).forEach(key => 
      url.searchParams.append(key, params[key])
    );
    
    return this.request(url.pathname + url.search);
  }
  
  post(endpoint, data) {
    return this.request(endpoint, {
      method: 'POST',
      body: JSON.stringify(data)
    });
  }
  
  put(endpoint, data) {
    return this.request(endpoint, {
      method: 'PUT',
      body: JSON.stringify(data)
    });
  }
  
  delete(endpoint) {
    return this.request(endpoint, {
      method: 'DELETE'
    });
  }
}

// Utilisation
const api = new API('https://api.example.com', {
  'Authorization': 'Bearer token'
});

// GET
const users = await api.get('/users', { limit: 10 });

// POST
const newUser = await api.post('/users', {
  name: 'Alice',
  email: 'alice@example.com'
});

// PUT
const updated = await api.put('/users/123', {
  name: 'Alice Updated'
});

// DELETE
await api.delete('/users/123');
```
"""


# ----------------------------------------------------------------------------
# [VERROUILLE] CORS ET SAME-ORIGIN POLICY
# ----------------------------------------------------------------------------

"""
SAME-ORIGIN POLICY (Sécurité fondamentale)

[IDEE] RÈGLE : Scripts d'une origine ne peuvent accéder ressources autre origine

ORIGINE = Protocole + Domaine + Port


EXEMPLES SAME-ORIGIN

Page : https://example.com/page

Same-origin :
[OK] https://example.com/other
[OK] https://example.com/folder/page
[OK] https://example.com:443/page (443 = défaut HTTPS)

Different-origin :
[X] http://example.com/page       (protocole différent)
[X] https://example.org/page       (domaine différent)
[X] https://www.example.com/page   (sous-domaine différent)
[X] https://example.com:8080/page  (port différent)


RESTRICTIONS SAME-ORIGIN

Sans permission CORS, impossible :
[X] fetch/XHR vers autre origine
[X] Lire contenu iframe autre origine
[X] Accéder cookies/localStorage autre origine
[X] Manipuler DOM iframe autre origine


POURQUOI ?

Protéger contre attaques :

Scénario sans Same-Origin Policy :
1. User connecté à bank.com
2. Visite malicious.com
3. Script malicious.com fait :
   fetch('https://bank.com/api/account')
     .then(r => r.json())
     .then(data => {
       sendToAttacker(data); // <- Vol de données !
     });
4. Sans Same-Origin -> Données volées !

Avec Same-Origin Policy :
-> Requête bloquée par navigateur [OK]


CORS = Cross-Origin Resource Sharing

Mécanisme permettant au SERVEUR d'autoriser cross-origin


HEADERS CORS

Serveur envoie :

Access-Control-Allow-Origin: https://trusted-site.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 86400


REQUÊTE CORS SIMPLE

Simple request = Pas de preflight

Conditions :
- Méthode : GET, HEAD, POST
- Headers : Accept, Accept-Language, Content-Language, Content-Type
- Content-Type : application/x-www-form-urlencoded, multipart/form-data, text/plain

Exemple :
```javascript
// Depuis https://app.example.com

fetch('https://api.example.com/data')
  .then(r => r.json());
```

Requête :
GET /data HTTP/1.1
Host: api.example.com
Origin: https://app.example.com

Réponse serveur :
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://app.example.com

-> Succès [OK]

Si serveur répond :
Access-Control-Allow-Origin: https://other-site.com

-> Bloqué par navigateur [X]


PREFLIGHT REQUEST

Pour requêtes "non-simples" (ex: PUT, DELETE, custom headers)

Navigateur envoie OPTIONS d'abord

Exemple :
```javascript
fetch('https://api.example.com/users/123', {
  method: 'DELETE',
  headers: {
    'Authorization': 'Bearer token'
  }
});
```

1. Preflight (automatique) :
OPTIONS /users/123 HTTP/1.1
Host: api.example.com
Origin: https://app.example.com
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: Authorization

2. Réponse preflight :
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: DELETE, GET, POST
Access-Control-Allow-Headers: Authorization
Access-Control-Max-Age: 86400

3. Si OK -> Vraie requête :
DELETE /users/123 HTTP/1.1
Authorization: Bearer token


WILDCARD (*)

Access-Control-Allow-Origin: *

Autorise TOUTES les origines
[ATTENTION] Mais pas avec credentials !

Avec credentials :
Access-Control-Allow-Origin: https://specific-site.com
Access-Control-Allow-Credentials: true


CREDENTIALS (Cookies)

```javascript
// Envoyer cookies cross-origin
fetch('https://api.example.com/data', {
  credentials: 'include'
});
```

Serveur DOIT répondre :
Access-Control-Allow-Origin: https://app.example.com (pas *)
Access-Control-Allow-Credentials: true


BYPASS CORS (DÉVELOPPEMENT SEULEMENT)

1. PROXY
```javascript
// Au lieu de : https://api.example.com/data
// Utiliser : /api/data

// Server dev (Express, Webpack, Vite) proxie vers API
```

2. BROWSER EXTENSION
Chrome extension "Allow CORS"
[ATTENTION] Seulement pour dev ! Désactiver après.

3. SERVEUR DEV SANS CORS
```javascript
// Express
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  next();
});
```


JSONP (ANCIEN, éviter)

Bypass CORS avec script tag

```javascript
// [X] NE PAS UTILISER (obsolète, insécure)
function handleData(data) {
  console.log(data);
}

const script = document.createElement('script');
script.src = 'https://api.example.com/data?callback=handleData';
document.body.appendChild(script);

// Serveur répond :
// handleData({"name": "Alice"});
```

Problèmes :
[X] Seulement GET
[X] Pas sécurisé (XSS risk)
[X] Pas d'error handling


[ANALYSE] DEBUG CORS

Erreur typique :
```
Access to fetch at 'https://api.example.com/data' from origin 
'https://app.example.com' has been blocked by CORS policy: 
No 'Access-Control-Allow-Origin' header is present on the 
requested resource.
```

Vérifications :
1. Serveur envoie Access-Control-Allow-Origin ?
2. Valeur correspond à votre origine ?
3. Credentials configurés correctement ?
4. Preflight réussit ?

DevTools > Network :
- Voir request headers (Origin)
- Voir response headers (Access-Control-*)
- OPTIONS request si preflight
```


# ----------------------------------------------------------------------------
# [COOKIE] COOKIES
# ----------------------------------------------------------------------------

"""
COOKIES = Petits morceaux de données stockés par le navigateur


CRÉER COOKIE (JavaScript)

```javascript
// Simple
document.cookie = "username=Alice";

// Avec options
document.cookie = "session=abc123; max-age=3600; path=/; secure; samesite=strict";
```


OPTIONS COOKIE

; expires=DATE     - Date expiration
; max-age=SECONDS  - Durée de vie en secondes
; path=/           - Chemin valide (défaut: path actuel)
; domain=.example.com - Domaine (défaut: domaine actuel)
; secure           - HTTPS seulement
; httponly         - Pas accessible en JS (serveur only)
; samesite=STRICT/LAX/NONE - Protection CSRF


EXEMPLES

```javascript
// Session cookie (supprimé à fermeture navigateur)
document.cookie = "temp=value";

// Cookie persistant (30 jours)
const d = new Date();
d.setTime(d.getTime() + (30 * 24 * 60 * 60 * 1000));
document.cookie = `user=Alice; expires=${d.toUTCString()}`;

// Ou avec max-age (plus simple)
document.cookie = "user=Alice; max-age=" + (30 * 24 * 60 * 60);

// Secure + SameSite
document.cookie = "session=xyz; secure; samesite=strict; max-age=3600";

// Sous-domaines
document.cookie = "shared=value; domain=.example.com";
// Accessible sur example.com, www.example.com, api.example.com
```


LIRE COOKIES

```javascript
console.log(document.cookie);
// "username=Alice; session=abc123; theme=dark"

// Parser cookies
function getCookie(name) {
  const value = `; ${document.cookie}`;
  const parts = value.split(`; ${name}=`);
  if (parts.length === 2) {
    return parts.pop().split(';').shift();
  }
  return null;
}

const username = getCookie('username');
console.log(username); // "Alice"

// Ou regex
function getCookieRegex(name) {
  const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
  return match ? match[2] : null;
}
```


SUPPRIMER COOKIE

```javascript
// Mettre max-age=0 ou expires passée
document.cookie = "username=; max-age=0";

// Ou
document.cookie = "username=; expires=Thu, 01 Jan 1970 00:00:00 GMT";

// Fonction helper
function deleteCookie(name) {
  document.cookie = name + "=; max-age=0; path=/";
}

deleteCookie('username');
```


HELPER COMPLET

```javascript
const Cookies = {
  set(name, value, options = {}) {
    let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
    
    if (options.maxAge) {
      cookie += `; max-age=${options.maxAge}`;
    }
    
    if (options.expires) {
      cookie += `; expires=${options.expires.toUTCString()}`;
    }
    
    if (options.path) {
      cookie += `; path=${options.path}`;
    }
    
    if (options.domain) {
      cookie += `; domain=${options.domain}`;
    }
    
    if (options.secure) {
      cookie += '; secure';
    }
    
    if (options.sameSite) {
      cookie += `; samesite=${options.sameSite}`;
    }
    
    document.cookie = cookie;
  },
  
  get(name) {
    const value = `; ${document.cookie}`;
    const parts = value.split(`; ${name}=`);
    if (parts.length === 2) {
      return decodeURIComponent(parts.pop().split(';').shift());
    }
    return null;
  },
  
  delete(name, options = {}) {
    this.set(name, '', { ...options, maxAge: 0 });
  },
  
  getAll() {
    const cookies = {};
    document.cookie.split(';').forEach(cookie => {
      const [name, value] = cookie.trim().split('=');
      cookies[decodeURIComponent(name)] = decodeURIComponent(value);
    });
    return cookies;
  }
};

// Utilisation
Cookies.set('user', 'Alice', { maxAge: 3600, path: '/', secure: true });
const user = Cookies.get('user');
Cookies.delete('user');
const all = Cookies.getAll();
```


HTTPONLY COOKIES

[ATTENTION] Cookies avec flag httpOnly ne sont PAS accessibles en JavaScript

```javascript
// Serveur définit :
Set-Cookie: session=abc123; httponly; secure

// JavaScript ne peut pas lire :
console.log(document.cookie); // session n'apparaît pas !

// Envoyé automatiquement dans requêtes
fetch('/api/data'); // Cookie session=abc123 envoyé automatiquement
```

Utilité : Sécurité (protection XSS)


SAMESITE ATTRIBUTE

Protection CSRF

```javascript
// Strict : Cookie JAMAIS envoyé cross-site
document.cookie = "session=abc; samesite=strict";
// Lien depuis autre site -> Pas de cookie
// Formulaire POST depuis autre site -> Pas de cookie

// Lax (défaut) : Cookie envoyé pour navigation GET
document.cookie = "session=abc; samesite=lax";
// Lien depuis autre site -> Cookie envoyé [OK]
// Formulaire POST depuis autre site -> Pas de cookie [X]

// None : Toujours envoyé (require Secure)
document.cookie = "session=abc; samesite=none; secure";
// Tout cross-site -> Cookie envoyé
```


TAILLE LIMITE

- Max 4096 bytes par cookie
- Max ~50 cookies par domaine
- Max ~3000 cookies total

Si dépassé -> Ancien cookie supprimé


[ANALYSE] VOIR COOKIES

DevTools > Application/Storage > Cookies

Affiche :
- Name
- Value
- Domain
- Path
- Expires
- Size
- HttpOnly
- Secure
- SameSite
```


# ----------------------------------------------------------------------------
# [SORTIE] UPLOAD DE FICHIERS
# ----------------------------------------------------------------------------

"""
FORMDATA API

Interface pour envoyer fichiers et données formulaire


CRÉER FORMDATA

```javascript
// 1. Depuis formulaire HTML
const form = document.querySelector('form');
const formData = new FormData(form);

// 2. Manuellement
const formData = new FormData();

// Ajouter champs
formData.append('name', 'Alice');
formData.append('email', 'alice@example.com');

// Ajouter fichier
const fileInput = document.querySelector('input[type="file"]');
formData.append('avatar', fileInput.files[0]);

// Ajouter Blob
const blob = new Blob(['Hello World'], { type: 'text/plain' });
formData.append('file', blob, 'hello.txt');
```


MÉTHODES FORMDATA

```javascript
const formData = new FormData();

// append - Ajouter valeur (permet doublons)
formData.append('name', 'Alice');
formData.append('name', 'Bob'); // Deux valeurs pour 'name'

// set - Définir valeur (remplace si existe)
formData.set('name', 'Charlie'); // Remplace les deux précédentes

// get - Récupérer première valeur
formData.get('name'); // "Charlie"

// getAll - Récupérer toutes valeurs
formData.getAll('name'); // ["Charlie"]

// has - Vérifier existence
formData.has('name'); // true

// delete - Supprimer
formData.delete('name');

// Itérer
for (let [key, value] of formData) {
  console.log(key, value);
}

// Ou
formData.forEach((value, key) => {
  console.log(key, value);
});
```


UPLOAD FICHIER UNIQUE

```html
<input type="file" id="fileInput">
<button onclick="uploadFile()">Upload</button>

<script>
async function uploadFile() {
  const input = document.getElementById('fileInput');
  const file = input.files[0];
  
  if (!file) {
    alert('Please select a file');
    return;
  }
  
  const formData = new FormData();
  formData.append('file', file);
  formData.append('description', 'My file');
  
  try {
    const response = await fetch('/upload', {
      method: 'POST',
      body: formData
      // [ATTENTION] Ne PAS définir Content-Type !
      // Navigateur le fait automatiquement avec boundary
    });
    
    if (!response.ok) {
      throw new Error(`Upload failed: ${response.status}`);
    }
    
    const data = await response.json();
    console.log('Upload success:', data);
  } catch (error) {
    console.error('Upload error:', error);
  }
}
</script>
```


UPLOAD FICHIERS MULTIPLES

```html
<input type="file" id="filesInput" multiple>
<button onclick="uploadFiles()">Upload All</button>

<script>
async function uploadFiles() {
  const input = document.getElementById('filesInput');
  const files = input.files;
  
  if (files.length === 0) {
    alert('Please select files');
    return;
  }
  
  const formData = new FormData();
  
  // Ajouter tous les fichiers
  for (let i = 0; i < files.length; i++) {
    formData.append('files', files[i]);
  }
  
  const response = await fetch('/upload-multiple', {
    method: 'POST',
    body: formData
  });
  
  const data = await response.json();
  console.log('Uploaded:', data);
}
</script>
```


PROGRESS (Progression upload)

[ATTENTION] Fetch API ne supporte pas progress nativement
Utiliser XMLHttpRequest

```javascript
function uploadWithProgress(file) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    const formData = new FormData();
    formData.append('file', file);
    
    // Progress upload
    xhr.upload.addEventListener('progress', (e) => {
      if (e.lengthComputable) {
        const percent = (e.loaded / e.total) * 100;
        console.log(`Upload: ${percent.toFixed(2)}%`);
        
        // Mettre à jour UI
        updateProgressBar(percent);
      }
    });
    
    // Load complete
    xhr.addEventListener('load', () => {
      if (xhr.status === 200) {
        resolve(JSON.parse(xhr.responseText));
      } else {
        reject(new Error(`Upload failed: ${xhr.status}`));
      }
    });
    
    // Error
    xhr.addEventListener('error', () => {
      reject(new Error('Upload failed'));
    });
    
    // Abort
    xhr.addEventListener('abort', () => {
      reject(new Error('Upload cancelled'));
    });
    
    xhr.open('POST', '/upload');
    xhr.send(formData);
  });
}

// Utilisation
const file = fileInput.files[0];
try {
  const result = await uploadWithProgress(file);
  console.log('Upload complete:', result);
} catch (error) {
  console.error('Upload error:', error);
}
```


PREVIEW IMAGE AVANT UPLOAD

```javascript
function previewImage(file) {
  const reader = new FileReader();
  
  reader.onload = (e) => {
    const img = document.createElement('img');
    img.src = e.target.result; // Data URL
    document.body.appendChild(img);
  };
  
  reader.readAsDataURL(file);
}

// Utilisation
fileInput.addEventListener('change', (e) => {
  const file = e.target.files[0];
  
  if (file && file.type.startsWith('image/')) {
    previewImage(file);
  }
});
```


VALIDATION FICHIER

```javascript
function validateFile(file) {
  // Type
  const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
  if (!allowedTypes.includes(file.type)) {
    throw new Error('Invalid file type. Only JPEG, PNG, GIF allowed.');
  }
  
  // Taille (5 MB max)
  const maxSize = 5 * 1024 * 1024;
  if (file.size > maxSize) {
    throw new Error('File too large. Max 5 MB.');
  }
  
  // Extension
  const ext = file.name.split('.').pop().toLowerCase();
  const allowedExts = ['jpg', 'jpeg', 'png', 'gif'];
  if (!allowedExts.includes(ext)) {
    throw new Error('Invalid file extension.');
  }
  
  return true;
}

// Utilisation
fileInput.addEventListener('change', (e) => {
  const file = e.target.files[0];
  
  try {
    validateFile(file);
    // Upload
    uploadFile(file);
  } catch (error) {
    alert(error.message);
    fileInput.value = ''; // Reset input
  }
});
```


DRAG & DROP UPLOAD

```html
<div id="dropZone" style="border: 2px dashed #ccc; padding: 50px;">
  Drop files here
</div>

<script>
const dropZone = document.getElementById('dropZone');

// Empêcher comportement par défaut
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
  dropZone.addEventListener(eventName, (e) => {
    e.preventDefault();
    e.stopPropagation();
  });
});

// Highlight zone
['dragenter', 'dragover'].forEach(eventName => {
  dropZone.addEventListener(eventName, () => {
    dropZone.classList.add('highlight');
  });
});

['dragleave', 'drop'].forEach(eventName => {
  dropZone.addEventListener(eventName, () => {
    dropZone.classList.remove('highlight');
  });
});

// Handle drop
dropZone.addEventListener('drop', (e) => {
  const files = e.dataTransfer.files;
  
  Array.from(files).forEach(file => {
    uploadFile(file);
  });
});
</script>
```


CHUNKED UPLOAD (Gros fichiers)

```javascript
async function uploadLargeFile(file, chunkSize = 1024 * 1024) { // 1 MB chunks
  const totalChunks = Math.ceil(file.size / chunkSize);
  
  for (let i = 0; i < totalChunks; i++) {
    const start = i * chunkSize;
    const end = Math.min(start + chunkSize, file.size);
    const chunk = file.slice(start, end);
    
    const formData = new FormData();
    formData.append('chunk', chunk);
    formData.append('chunkIndex', i);
    formData.append('totalChunks', totalChunks);
    formData.append('filename', file.name);
    
    const response = await fetch('/upload-chunk', {
      method: 'POST',
      body: formData
    });
    
    if (!response.ok) {
      throw new Error(`Chunk ${i} upload failed`);
    }
    
    const progress = ((i + 1) / totalChunks) * 100;
    console.log(`Progress: ${progress.toFixed(2)}%`);
  }
  
  console.log('Upload complete!');
}
```
"""


# ============================================================================
# [DOCS] RÉCAPITULATIF CHAPITRE 4
# ============================================================================

"""
CE QUE VOUS AVEZ APPRIS

[OK] Protocole HTTP (méthodes, status, headers)
[OK] Fetch API moderne (GET, POST, PUT, DELETE)
[OK] Promises et async/await
[OK] Gestion erreurs réseau
[OK] Abort et timeout requêtes
[OK] CORS et Same-Origin Policy
[OK] Cookies (création, lecture, suppression)
[OK] Upload fichiers avec FormData
[OK] Progress et validation uploads
[OK] Drag & Drop


[CLE] POINTS CLÉS

FETCH :
- Retourne Promise
- async/await pour lisibilité
- Vérifier response.ok (fetch ne rejette que réseau errors)
- AbortController pour annulation
- Clone response si utilisation multiple

CORS :
- Same-Origin Policy = sécurité fondamentale
- CORS = serveur autorise cross-origin
- Preflight pour requêtes complexes
- credentials: 'include' pour cookies

COOKIES :
- max-age/expires pour persistance
- secure pour HTTPS only
- httpOnly pour protection XSS
- sameSite pour protection CSRF

UPLOAD :
- FormData pour fichiers
- Ne pas set Content-Type (auto boundary)
- XMLHttpRequest pour progress
- Validation côté client + serveur


[OBJECTIF] BEST PRACTICES

[OK] Toujours vérifier response.ok
[OK] Gérer erreurs proprement (try/catch)
[OK] Timeout pour éviter hang
[OK] Retry avec backoff exponentiel
[OK] Valider fichiers avant upload
[OK] Chunked upload pour gros fichiers
[OK] Utiliser AbortController pour cancel


-> PROCHAINE ÉTAPE : Partie 2 - Outils de Développement !

Vous allez explorer :
- DevTools Console et Debugger
- DevTools Network et Performance
- Profiling et optimisation
- Storage et Cookies inspection
- Et bien plus !

Prêt à devenir expert des DevTools ? [OUTILS][RAPIDE]


═══════════════════════════════════════════════════════════════════════════
FIN DE LA PARTIE 1 : FONDAMENTAUX DU NAVIGATEUR
═══════════════════════════════════════════════════════════════════════════

Vous maîtrisez maintenant :
[OK] Architecture navigateur (processus, threads, event loop)
[OK] Processus rendu complet (parsing, layout, paint, composite)
[OK] DOM et manipulation JavaScript
[OK] Réseau et protocoles HTTP

CONTINUEZ AVEC :
[LIVRE] navigateur_partie2.txt - Outils de Développement (DevTools)
"""
```

Parfait ! J'ai complété la Partie 1 du guide ultra-détaillé sur les navigateurs. Cette partie couvre tous les fondamentaux essentiels :

**Chapitre 0** : Introduction complète aux navigateurs
**Chapitre 1** : Architecture (processus, Event Loop, Workers, GC)
**Chapitre 2** : Rendu HTML/CSS (parsing, layout, paint, Critical Rendering Path)
**Chapitre 3** : JavaScript et DOM (sélection, manipulation, événements, Observers)
**Chapitre 4** : Réseau et HTTP (fetch, CORS, cookies, upload)

Voulez-vous que je crée la **Partie 2** qui couvrira les DevTools (Console, Debugger, Network, Performance, etc.) ?

# ============================================================================
# [LIVRE] NAVIGATEUR WEB - PARTIE 2 : OUTILS DE DÉVELOPPEMENT
# ============================================================================
#
# [OBJECTIF] GUIDE COMPLET DES DEVTOOLS POUR MAÎTRISER LE DEBUGGING
#
# Cette partie couvre :
# - Chapitre 5 : DevTools - Console et Debugger
# - Chapitre 6 : DevTools - Inspecteur et Network
# - Chapitre 7 : Performance et Profiling
# - Chapitre 8 : Storage et Cookies
#
# [TEMPS] TEMPS DE LECTURE : ~6-8 heures
# [DOCS] PRÉREQUIS : Avoir lu Partie 1 (Fondamentaux)
#
# ============================================================================

"""
[OBJECTIF] PHILOSOPHIE DE CETTE PARTIE

Les DevTools sont votre MEILLEUR AMI en développement web !

Objectifs :
[OK] Maîtriser TOUS les outils de debugging
[OK] Profiler et optimiser les performances
[OK] Comprendre le réseau en profondeur
[OK] Inspecter et manipuler le DOM live
[OK] Devenir 10x plus productif

[IDEE] Cette partie transforme un débutant en expert DevTools !
"""


# ============================================================================
# [GUIDE] CHAPITRE 5 : DEVTOOLS - CONSOLE ET DEBUGGER
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Maîtriser la Console (logs, erreurs, warnings)
[OK] Utiliser toutes les méthodes console.*
[OK] Debugger avec breakpoints
[OK] Step through code (step over, into, out)
[OK] Watch expressions et call stack
[OK] Conditional breakpoints
[OK] Logpoints et DOM breakpoints
[OK] Error handling et stack traces
"""


# ----------------------------------------------------------------------------
# [ECRAN] LA CONSOLE : VOTRE MEILLEUR OUTIL
# ----------------------------------------------------------------------------

"""
OUVRIR LA CONSOLE

Chrome/Edge :
- F12 ou Ctrl + Shift + J (Windows/Linux)
- Cmd + Option + J (Mac)
- Clic droit > Inspecter > Console

Firefox :
- F12 ou Ctrl + Shift + K
- Cmd + Option + K (Mac)

Safari :
- Cmd + Option + C (après activation dans Préférences)


[IDEE] POURQUOI LA CONSOLE EST FONDAMENTALE ?

1. Debugging rapide (console.log)
2. Tester du code JavaScript live
3. Inspecter objets/variables
4. Voir erreurs et warnings
5. Mesurer performance
6. Manipuler DOM interactivement


CONSOLE.LOG() ET VARIANTES

1. console.log() - Logging basique
───────────────────────────────────

```javascript
// Simple message
console.log('Hello World');

// Multiple arguments
console.log('User:', 'Alice', 'Age:', 30);

// Variables
const user = 'Bob';
console.log('User:', user);

// Objets
const obj = { name: 'Alice', age: 30 };
console.log(obj);
// -> {name: "Alice", age: 30}

// Expand object
console.log('User:', obj);
// Cliquer sur [BLACK_RIGHT-POINTING_TRIANGLE] pour voir détails

// Arrays
const arr = [1, 2, 3, 4, 5];
console.log(arr);
console.log('Array:', arr);
```


2. console.error() - Erreurs
────────────────────────────

```javascript
console.error('Something went wrong!');
// Affiche en ROUGE avec icône [X]

console.error('Error:', error);
// Avec stack trace si Error object

try {
  throw new Error('Custom error');
} catch (error) {
  console.error('Caught error:', error);
  // -> Affiche stack trace complète
}
```


3. console.warn() - Avertissements
──────────────────────────────────

```javascript
console.warn('This is deprecated!');
// Affiche en JAUNE avec icône [ATTENTION]

if (oldAPI) {
  console.warn('Using old API. Please migrate to new API.');
}
```


4. console.info() - Information
───────────────────────────────

```javascript
console.info('App started successfully');
// Affiche avec icône ℹ

console.info('Version:', '1.2.3');
```


5. console.debug() - Debug
──────────────────────────

```javascript
console.debug('Debug info:', debugData);
// Niveau de log le plus bas
// Peut être filtré dans console
```


FORMATAGE DES LOGS

Styles CSS dans console :

```javascript
// %c = Style CSS
console.log('%cStyled Text', 'color: blue; font-size: 20px; font-weight: bold');

// Multiple styles
console.log(
  '%cError: %cSomething failed',
  'color: red; font-weight: bold',
  'color: black'
);

// Combinaisons
console.log(
  '%c[RAPIDE] %cApp Started %c[OK]',
  'font-size: 30px',
  'color: green; font-weight: bold; font-size: 16px',
  'font-size: 20px'
);
```


String substitution :

```javascript
// %s = String
console.log('Hello %s', 'World');

// %d ou %i = Integer
console.log('Count: %d', 42);

// %f = Float
console.log('Price: %f', 19.99);

// %o = Object
console.log('User: %o', { name: 'Alice' });

// %O = Object (optimized)
console.log('User: %O', { name: 'Alice' });
```


MÉTHODES AVANCÉES

1. console.table() - Tableaux formatés
──────────────────────────────────────

[IDEE] SUPER UTILE POUR VISUALISER DATA !

```javascript
// Array d'objets
const users = [
  { name: 'Alice', age: 30, city: 'Paris' },
  { name: 'Bob', age: 25, city: 'London' },
  { name: 'Charlie', age: 35, city: 'Berlin' }
];

console.table(users);
// Affiche tableau formaté :
// ┌─────────┬───────────┬─────┬──────────┐
// │ (index) │   name    │ age │   city   │
// ├─────────┼───────────┼─────┼──────────┤
// │    0    │  'Alice'  │ 30  │ 'Paris'  │
// │    1    │   'Bob'   │ 25  │ 'London' │
// │    2    │ 'Charlie' │ 35  │ 'Berlin' │
// └─────────┴───────────┴─────┴──────────┘

// Colonnes spécifiques
console.table(users, ['name', 'age']);

// Objet
const obj = {
  name: 'Alice',
  age: 30,
  hobbies: ['reading', 'coding']
};
console.table(obj);
```


2. console.group() - Groupes collapsibles
──────────────────────────────────────────

```javascript
// Groupe normal
console.group('User Details');
console.log('Name:', 'Alice');
console.log('Age:', 30);
console.log('Email:', 'alice@example.com');
console.groupEnd();

// Groupe collapsed par défaut
console.groupCollapsed('Debug Info');
console.log('Timestamp:', Date.now());
console.log('User Agent:', navigator.userAgent);
console.groupEnd();

// Groupes imbriqués
console.group('Application');
  console.group('User');
    console.log('Name:', 'Alice');
  console.groupEnd();
  
  console.group('Settings');
    console.log('Theme:', 'dark');
  console.groupEnd();
console.groupEnd();
```


3. console.time() - Mesurer temps
──────────────────────────────────

```javascript
// Démarrer timer
console.time('myOperation');

// Code à mesurer
for (let i = 0; i < 1000000; i++) {
  // Operations
}

// Arrêter et afficher
console.timeEnd('myOperation');
// -> myOperation: 45.234ms

// Checkpoint intermédiaire
console.time('longOperation');

doSomething();
console.timeLog('longOperation', 'Checkpoint 1');
// -> longOperation: 123ms Checkpoint 1

doSomethingElse();
console.timeLog('longOperation', 'Checkpoint 2');
// -> longOperation: 456ms Checkpoint 2

console.timeEnd('longOperation');
// -> longOperation: 789ms
```


4. console.count() - Compteur
──────────────────────────────

```javascript
function processUser(user) {
  console.count('processUser called');
  // Process...
}

processUser({ name: 'Alice' });
// -> processUser called: 1

processUser({ name: 'Bob' });
// -> processUser called: 2

processUser({ name: 'Charlie' });
// -> processUser called: 3

// Reset counter
console.countReset('processUser called');

processUser({ name: 'Dave' });
// -> processUser called: 1


// Compteurs différents
console.count('userAction');    // -> userAction: 1
console.count('apiCall');       // -> apiCall: 1
console.count('userAction');    // -> userAction: 2
console.count('apiCall');       // -> apiCall: 2
```


5. console.assert() - Assertions
─────────────────────────────────

```javascript
// Assert vérifie condition
const x = 5;
console.assert(x > 10, 'x should be greater than 10');
// -> Assertion failed: x should be greater than 10

console.assert(x < 10, 'x should be less than 10');
// -> Rien (assertion passe)

// Avec objets
const user = { name: 'Alice' };
console.assert(user.age !== undefined, 'User should have age', user);
// -> Assertion failed: User should have age {name: "Alice"}
```


6. console.trace() - Stack trace
─────────────────────────────────

```javascript
function a() {
  b();
}

function b() {
  c();
}

function c() {
  console.trace('Where am I?');
}

a();

// Affiche :
// Where am I?
// c @ script.js:11
// b @ script.js:7
// a @ script.js:3
// (anonymous) @ script.js:14
```


7. console.dir() - Propriétés objet
────────────────────────────────────

```javascript
// console.log affiche DOM element comme HTML
const element = document.body;
console.log(element);
// -> <body>...</body>

// console.dir affiche comme object JavaScript
console.dir(element);
// -> body {...}
//   [BLACK_RIGHT-POINTING_TRIANGLE] accessKey: ""
//   [BLACK_RIGHT-POINTING_TRIANGLE] attributes: NamedNodeMap
//   [BLACK_RIGHT-POINTING_TRIANGLE] childNodes: NodeList
//   ... toutes les propriétés

// Utile pour voir propriétés cachées
console.dir(document);
console.dir(window);
```


8. console.clear() - Effacer console
─────────────────────────────────────

```javascript
console.clear();
// Efface tous les logs
```


FILTRER LES LOGS

Dans la console :

1. Niveaux :
   [[OK]] Verbose
   [[OK]] Info
   [[OK]] Warnings
   [[OK]] Errors

2. Filtres texte :
   Taper dans barre de recherche
   Exemple : "user" -> Affiche seulement logs contenant "user"

3. Regex :
   /pattern/
   Exemple : /error|warning/i


COPIER/SAUVEGARDER LOGS

```javascript
// Copier dans clipboard
copy(objectToCopy);
// Object maintenant dans clipboard

// Sauvegarder dans variable temporaire
const myVar = { data: 'important' };
console.log(myVar);
// Clic droit sur output > Store as global variable
// -> temp1 créé automatiquement
temp1.data; // "important"
```


EXÉCUTER CODE DANS CONSOLE

```javascript
// Variable globale
var x = 10;
console.log(x); // 10

// Fonction
function greet(name) {
  return `Hello ${name}`;
}
greet('Alice'); // "Hello Alice"

// Manipuler DOM
document.body.style.backgroundColor = 'red';

// Sélectionner éléments
$('selector'); // Équivalent document.querySelector
$$('selector'); // Équivalent document.querySelectorAll

$('#myId');
$$('.myClass');

// Récent élément inspecté
$0 // Dernier élément sélectionné
$1 // Avant-dernier
$2 // etc.

// Exemple :
// 1. Inspecter un <div> dans Elements tab
// 2. Console :
$0.style.color = 'blue';
// Le div devient bleu !
```


SHORTCUTS CONSOLE

Ctrl + L        - Clear console
Ctrl + U        - Clear input
Esc             - Ouvrir/fermer console drawer
^ / v           - Historique commandes
Tab             - Autocomplete
Shift + Enter   - Multi-ligne
Enter           - Exécuter


CONSOLE DANS CODE

Live expressions :

```javascript
// Click on [EYE] "Create live expression"
// Taper : Date.now()
// -> Se met à jour en temps réel !

// Utile pour :
window.scrollY        // Position scroll
document.activeElement // Element focus
performance.memory.usedJSHeapSize // Mémoire utilisée
```


[ANALYSE] EXERCICE PRATIQUE : LOGGER EFFICACEMENT

```javascript
// [X] MAUVAIS - Logs basiques peu informatifs
function processData(data) {
  console.log(data);
  // Process...
  console.log('done');
}

// [OK] BON - Logs structurés et informatifs
function processData(data) {
  console.group('Processing Data');
  console.time('processData');
  
  console.log('Input:', data);
  console.assert(data !== null, 'Data should not be null');
  
  // Process...
  const result = data.map(x => x * 2);
  
  console.log('Result:', result);
  console.timeEnd('processData');
  console.groupEnd();
  
  return result;
}

// Utilisation
const input = [1, 2, 3, 4, 5];
const output = processData(input);

// Console affiche :
// [BLACK_DOWN-POINTING_TRIANGLE] Processing Data
//   Input: [1, 2, 3, 4, 5]
//   Result: [2, 4, 6, 8, 10]
//   processData: 0.234ms
```


CONSOLE API PERSONNALISÉE

```javascript
// Créer logger custom
const logger = {
  info: (msg, ...args) => {
    console.log('%c[BLEU] INFO', 'color: blue; font-weight: bold', msg, ...args);
  },
  
  success: (msg, ...args) => {
    console.log('%c[OK] SUCCESS', 'color: green; font-weight: bold', msg, ...args);
  },
  
  warning: (msg, ...args) => {
    console.warn('%c[ATTENTION]  WARNING', 'font-weight: bold', msg, ...args);
  },
  
  error: (msg, ...args) => {
    console.error('%c[X] ERROR', 'font-weight: bold', msg, ...args);
  },
  
  debug: (msg, ...args) => {
    if (DEBUG_MODE) {
      console.debug('%c[BUG] DEBUG', 'color: purple', msg, ...args);
    }
  }
};

// Utilisation
logger.info('App started');
logger.success('Data loaded', { count: 100 });
logger.warning('API response slow');
logger.error('Failed to save', error);
logger.debug('Variable state', { x, y, z });
```
"""


# ----------------------------------------------------------------------------
# [BUG] DEBUGGER : DEBUGGING AVANCÉ
# ----------------------------------------------------------------------------

"""
DEBUGGER TAB (Sources dans Chrome)

Sections :
1. File Navigator (gauche) - Arbre fichiers
2. Code Editor (centre) - Code source
3. Debugger Sidebar (droite) - Watch, Call Stack, Scope, Breakpoints


BREAKPOINTS (Points d'arrêt)

[IDEE] FONDAMENTAL : Arrêter exécution à ligne spécifique


1. LINE BREAKPOINT
──────────────────

Cliquer sur numéro de ligne dans Sources tab

```javascript
function calculate(x, y) {
  const sum = x + y;        // <- Cliquer ici (ligne 2)
  const product = x * y;
  return { sum, product };
}

calculate(5, 3);
```

Code s'arrête à ligne 2
-> Inspecter variables
-> Continuer step by step


2. CONDITIONAL BREAKPOINT
──────────────────────────

Clic droit sur numéro ligne > Add conditional breakpoint

```javascript
for (let i = 0; i < 100; i++) {
  processItem(i);  // Breakpoint : i === 50
}
// S'arrête SEULEMENT quand i === 50
```

Exemples conditions :
- `i === 50`
- `user.name === 'Alice'`
- `response.status !== 200`
- `arr.length > 10`


3. LOGPOINT
───────────

Clic droit > Add logpoint

```javascript
function processUser(user) {
  // Logpoint : console.log('Processing:', user.name)
  // Code...
}
```

Avantage : Log sans modifier code source !


CONTRÔLES DEBUGGER

Boutons (ou raccourcis) :

F8 (ou [BLACK_RIGHT-POINTING_TRIANGLE])  - Resume (continuer jusqu'au prochain breakpoint)
F10 (ou ⤵)  - Step Over (ligne suivante, sans entrer dans fonctions)
F11 (ou v)  - Step Into (entrer dans fonction)
Shift+F11 (^) - Step Out (sortir de fonction actuelle)
Ctrl+Shift+F10 - Continue to here


EXEMPLE STEP BY STEP

```javascript
function a(x) {
  console.log('a:', x);
  return b(x * 2);
}

function b(x) {
  console.log('b:', x);
  return c(x + 1);
}

function c(x) {
  console.log('c:', x);
  return x * 2;
}

const result = a(5);  // <- Breakpoint ici
console.log('Result:', result);
```

Exécution avec Step Into (F11) :

1. [DOUBLE_VERTICAL_BAR] Arrêt ligne `const result = a(5);`
2. F11 -> Entre dans fonction `a`
   [DOUBLE_VERTICAL_BAR] Arrêt ligne `console.log('a:', x);`
   Variables : x = 5
   
3. F11 -> Ligne suivante
   [DOUBLE_VERTICAL_BAR] Arrêt ligne `return b(x * 2);`
   
4. F11 -> Entre dans fonction `b`
   [DOUBLE_VERTICAL_BAR] Arrêt ligne `console.log('b:', x);`
   Variables : x = 10
   
5. F11 -> ...continue dans `c`
   
6. Shift+F11 -> Sort de `c` directement
   [DOUBLE_VERTICAL_BAR] Retour dans `b`
   
7. F8 -> Resume jusqu'au bout


Exécution avec Step Over (F10) :

1. [DOUBLE_VERTICAL_BAR] Arrêt ligne `const result = a(5);`
2. F10 -> Exécute `a(5)` SANS entrer dedans
   [DOUBLE_VERTICAL_BAR] Arrêt ligne `console.log('Result:', result);`
   Variables : result = 22


CALL STACK (Pile d'appels)

Pendant pause, voir pile :

```javascript
function first() {
  second();
}

function second() {
  third();
}

function third() {
  debugger;  // <- Pause ici
}

first();
```

Call Stack affiche :
```
third     @ script.js:10  <- Current frame
second    @ script.js:6
first     @ script.js:2
(anonymous) @ script.js:13
```

Cliquer sur frame -> Voir variables à ce moment


SCOPE (Portée variables)

Sidebar Scope affiche :

```
[BLACK_DOWN-POINTING_TRIANGLE] Local
  x: 10
  y: 20
  sum: 30
  
[BLACK_DOWN-POINTING_TRIANGLE] Closure
  outerVar: "hello"
  
[BLACK_DOWN-POINTING_TRIANGLE] Global
  window: Window
  document: Document
  ...
```

Permet d'inspecter toutes variables accessibles


WATCH EXPRESSIONS

Ajouter expressions à surveiller :

Click "+" dans Watch section

Exemples :
- `x + y`
- `user.name`
- `arr.length`
- `this.state`

Se mettent à jour à chaque pause


DEBUGGER STATEMENT

Dans code source :

```javascript
function problematicFunction(data) {
  // Code...
  
  debugger;  // <- Pause ICI automatiquement si DevTools ouvert
  
  // Code suspect...
}
```

Équivalent d'un breakpoint dans code
[ATTENTION] À retirer avant production !


DOM BREAKPOINTS

Clic droit sur élément dans Elements tab > Break on :

1. **Subtree modifications**
   S'arrête si enfants ajoutés/supprimés
   
2. **Attribute modifications**
   S'arrête si attribut change
   
3. **Node removal**
   S'arrête si élément supprimé

Exemple :

```javascript
// HTML : <div id="container"></div>

// DOM Breakpoint : Subtree modifications sur #container

// Ce code déclenchera pause :
const container = document.getElementById('container');
container.appendChild(document.createElement('div'));  // [DOUBLE_VERTICAL_BAR] Pause !
```

Très utile pour trouver QUI modifie le DOM !


EVENT LISTENER BREAKPOINTS

Dans Sources > Event Listener Breakpoints

Cocher événements :
- Mouse > click
- Keyboard > keydown
- Timer > setInterval fired

Exemple :

```javascript
// Cocher "Mouse > click"

document.addEventListener('click', (e) => {
  console.log('Clicked');  // [DOUBLE_VERTICAL_BAR] Pause automatique !
});
```


EXCEPTION BREAKPOINTS

Toggle "Pause on exceptions" ([DOUBLE_VERTICAL_BAR][INTERDIT] icon)

```javascript
function riskyFunction() {
  throw new Error('Something went wrong');
}

try {
  riskyFunction();  // [DOUBLE_VERTICAL_BAR] Pause à l'erreur !
} catch (error) {
  console.error(error);
}
```

Options :
- [ ] Pause on exceptions
- [ ] Pause on caught exceptions


BLACKBOXING (Ignorer fichiers)

Clic droit sur fichier > Blackbox script

Utile pour ignorer :
- Libraries (jQuery, React, etc.)
- Vendor code
- Minified files

Debugger ne s'arrêtera pas dans fichiers blackboxés


ASYNC DEBUGGING

Cocher "Async" dans Call Stack

```javascript
async function fetchData() {
  const response = await fetch('/api/data');  // <- Breakpoint
  const data = await response.json();
  return data;
}

fetchData();
```

Call Stack avec Async montre :
```
fetchData (async)  <- Current
(anonymous) (async) <- Caller
```


SOURCE MAPS

Pour code transcompilé (TypeScript, minified) :

```javascript
// Code original : app.ts
function greet(name: string): void {
  console.log(`Hello ${name}`);
}

// Code compilé : app.js
function greet(name) {
  console.log("Hello " + name);
}
//# sourceMappingURL=app.js.map
```

Avec source map :
-> Debugger montre code TypeScript original !
-> Breakpoints dans .ts fonctionnent


SNIPPETS (Code réutilisable)

Sources > Snippets (onglet)

Créer snippets pour code fréquent :

```javascript
// Snippet : "clear-storage"
localStorage.clear();
sessionStorage.clear();
console.log('Storage cleared');

// Snippet : "show-performance"
console.table({
  'DNS': performance.timing.domainLookupEnd - performance.timing.domainLookupStart,
  'TCP': performance.timing.connectEnd - performance.timing.connectStart,
  'DOM': performance.timing.domComplete - performance.timing.domLoading
});
```

Exécuter : Clic droit > Run
Ou Ctrl+Enter


OVERRIDES (Modifications locales)

Sources > Overrides

Permettent de modifier fichiers et persister changements

1. Enable local overrides
2. Sélectionner dossier
3. Modifier fichier dans Sources
4. Rechargements ultérieurs utilisent version modifiée !

Utile pour :
- Tester fixes rapidement
- Débugger production sans redéployer


[ANALYSE] EXERCICE : DEBUGGING COMPLET

```javascript
// Bug à trouver
function calculateDiscount(price, discountPercent) {
  const discount = price * discountPercent;
  const finalPrice = price - discount;
  return finalPrice;
}

// Test
const result = calculateDiscount(100, 20);
console.log('Final price:', result);
// Attendu : 80
// Obtenu : -1900 [REFLEXION]

// SOLUTION AVEC DEBUGGER :
// 1. Breakpoint sur ligne `const discount = ...`
// 2. F10 step over
// 3. Inspecter variables :
//    price = 100 [OK]
//    discountPercent = 20 <- Devrait être 0.20 !
//    discount = 2000 [X]
// 4. Bug trouvé : discountPercent doit être divisé par 100

// Fix :
function calculateDiscount(price, discountPercent) {
  const discount = price * (discountPercent / 100);
  const finalPrice = price - discount;
  return finalPrice;
}
```


DEBUGGING BEST PRACTICES

[OK] Utiliser breakpoints au lieu de console.log partout
[OK] Conditional breakpoints pour boucles
[OK] Watch expressions pour variables importantes
[OK] Step Over pour code connu, Step Into pour code suspect
[OK] Blackbox libraries pour focus sur votre code
[OK] DOM breakpoints pour mystérieux changements DOM
[OK] Exception breakpoints pour erreurs silencieuses


RACCOURCIS DEBUGGER (Chrome)

Ctrl+P          - Ouvrir fichier rapidement
Ctrl+Shift+P    - Command palette
Ctrl+Shift+F    - Rechercher dans tous fichiers
Ctrl+Shift+O    - Aller à symbole (fonction, classe)
Ctrl+G          - Aller à ligne
F8              - Resume/Pause
F10             - Step over
F11             - Step into
Shift+F11       - Step out
"""


# ============================================================================
# [DOCS] RÉCAPITULATIF CHAPITRE 5
# ============================================================================

"""
CE QUE VOUS AVEZ APPRIS

[OK] Console complète (log, error, warn, info, debug)
[OK] console.table(), group(), time(), count(), assert()
[OK] Formatage logs (styles CSS, substitution)
[OK] Breakpoints (line, conditional, logpoint)
[OK] Contrôles debugger (step over, into, out)
[OK] Call Stack et Scope inspection
[OK] Watch expressions
[OK] DOM breakpoints
[OK] Event listener breakpoints
[OK] Exception breakpoints
[OK] Async debugging
[OK] Snippets et Overrides


[CLE] POINTS CLÉS

CONSOLE :
- console.table() pour visualiser data
- console.group() pour structurer logs
- console.time() pour mesurer performance
- Styles CSS avec %c pour logs lisibles

DEBUGGER :
- Breakpoints > console.log (plus efficace)
- Step Over : ligne par ligne
- Step Into : entrer dans fonctions
- Watch expressions : surveiller variables
- DOM/Event breakpoints : trouver qui modifie quoi


[OBJECTIF] WORKFLOW DEBUGGING EFFICACE

1. Reproduire le bug
2. Hypothèse sur la cause
3. Placer breakpoint stratégique
4. Inspecter variables et call stack
5. Step through suspect code
6. Confirmer cause
7. Fixer et vérifier


-> PROCHAINE ÉTAPE : Chapitre 6 - Inspecteur et Network !

Vous allez découvrir :
- Elements tab (inspecteur DOM)
- Modification CSS live
- Network tab complet
- Analyse requêtes HTTP
- Throttling et offline mode
- WebSocket debugging

Prêt à maîtriser l'inspection visuelle ? [DESIGN][WEB]
"""


# ============================================================================
# [GUIDE] CHAPITRE 6 : DEVTOOLS - INSPECTEUR ET NETWORK
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Inspecter et modifier DOM en temps réel
[OK] Éditer CSS live avec aperçu instantané
[OK] Analyser box model et layout
[OK] Utiliser Network tab pour debugging réseau
[OK] Analyser requêtes HTTP en détail
[OK] Throttling et simulation conditions réseau
[OK] Inspecter WebSockets et EventSource
[OK] Bloquer requêtes et override responses
"""


# ----------------------------------------------------------------------------
# [DESIGN] ELEMENTS TAB : INSPECTEUR DOM
# ----------------------------------------------------------------------------

"""
OUVRIR ELEMENTS TAB

1. F12 > Elements tab
2. Clic droit sur élément page > Inspecter
3. Ctrl+Shift+C puis cliquer élément


SECTIONS

┌─────────────────────────────────────────────┐
│         Elements (DOM Tree)                 │
│  <html>                                     │
│    <body>                                   │
│      <div class="container">                │
│        <h1>Title</h1>          <- Sélectionné│
│      </div>                                 │
├─────────────────────────────────────────────┤
│  Styles | Computed | Layout | Event Listen │
│                                             │
│  element.style { }                          │
│  .container h1 {                            │
│    color: blue;                             │
│    font-size: 24px;                         │
│  }                                          │
└─────────────────────────────────────────────┘


NAVIGUER DANS LE DOM

Clavier :
^ v     - Élément précédent/suivant
<- ->     - Collapse/expand node
Enter   - Éditer attribut
F2      - Éditer comme HTML
Delete  - Supprimer élément

Souris :
Simple clic  - Sélectionner
Double clic  - Éditer texte/attribut
Clic droit   - Menu contextuel


INSPECTER ÉLÉMENT RAPIDEMENT

1. **Inspect element tool** (Ctrl+Shift+C)
   Cliquer sur page -> Sélectionne dans DOM tree

2. **$0 dans Console**
   Élément sélectionné accessible via $0
   
   ```javascript
   // Sélectionner <h1> dans Elements
   // Puis Console :
   $0.textContent = 'New Title';
   $0.style.color = 'red';
   $0.classList.add('active');
   ```

3. **Breadcrumbs** (fil d'Ariane en bas)
   html > body > div.container > h1
   Cliquer pour naviguer


MODIFIER DOM EN LIVE

1. **Éditer contenu texte**
   Double-clic sur texte -> Modifier

2. **Éditer attributs**
   Double-clic sur attribut -> Modifier
   
   ```html
   <!-- Avant -->
   <img src="old.jpg" alt="Old">
   
   <!-- Après modification -->
   <img src="new.jpg" alt="New">
   ```

3. **Ajouter attribut**
   Clic droit > Add attribute
   
4. **Éditer HTML complet**
   Clic droit > Edit as HTML
   
   Permet modification complexe :
   ```html
   <div class="box">
     Content
   </div>
   
   <!-- Modifier en : -->
   <section class="box active" data-id="123">
     <h2>New Title</h2>
     <p>Content</p>
   </section>
   ```

5. **Copier HTML**
   Clic droit > Copy > Copy outerHTML

6. **Supprimer élément**
   Sélectionner > Delete
   Ou Clic droit > Delete element

7. **Déplacer élément**
   Drag & drop dans tree


ÉTATS PSEUDO-CLASSES

Tester :hover, :active, :focus sans interaction

1. Sélectionner élément
2. Styles panel > :hov
3. Cocher états :
   [ ] :hover
   [ ] :active
   [ ] :focus
   [ ] :visited
   [ ] :focus-within

```css
/* CSS */
.button:hover {
  background: blue;
}

/* Cocher :hover dans DevTools */
/* -> Background devient bleu sans survoler ! */
```


BREAKPOINTS DOM

Clic droit sur élément > Break on :

1. **Subtree modifications**
   ```javascript
   element.appendChild(child);  // [DOUBLE_VERTICAL_BAR] Pause
   element.innerHTML = 'new';   // [DOUBLE_VERTICAL_BAR] Pause
   ```

2. **Attribute modifications**
   ```javascript
   element.className = 'active'; // [DOUBLE_VERTICAL_BAR] Pause
   element.setAttribute('data-id', '123'); // [DOUBLE_VERTICAL_BAR] Pause
   ```

3. **Node removal**
   ```javascript
   element.remove(); // [DOUBLE_VERTICAL_BAR] Pause
   parent.removeChild(element); // [DOUBLE_VERTICAL_BAR] Pause
   ```


STYLES PANEL

Voir et modifier CSS

```
element.style {
  /* Inline styles ici */
}

.container h1 {               <- Selector
  color: rgb(0, 0, 255);      <- Property (cliquer pour modifier)
  font-size: 24px;
  margin: 20px 0;
}
  -> styles.css:45            <- Source file:line

.container {
  padding: 10px;
}
```


MODIFIER STYLES

1. **Cliquer sur valeur** -> Éditer
   
   ```css
   color: blue;  <- Cliquer sur "blue"
   /* Modifier en */
   color: red;
   /* Voir changement en temps réel ! */
   ```

2. **Color picker**
   Cliquer sur carré couleur -> Sélecteur visuel
   - Pipette pour capturer couleur page
   - HEX, RGB, HSL
   - Palette couleurs

3. **Numeric values**
   Hover valeur numérique :
   - ^ v : +1 / -1
   - Shift + ^ v : +10 / -10
   - Alt + ^ v : +0.1 / -0.1
   - Page Up/Down : +10 / -10

4. **Ajouter propriété**
   Cliquer dans bloc vide -> Taper propriété
   Autocomplete disponible !

5. **Toggle propriété**
   [x] property: value; <- Cliquer checkbox pour désactiver

6. **Ajouter nouvelle règle**
   + icon -> Nouvelle règle CSS
   
   ```css
   element.style {
   }
   
   /* Click + */
   
   .my-class {  <- Nouveau selector
     /* Propriétés ici */
   }
   ```


COMPUTED STYLES

Valeurs finales calculées

```
[BLACK_DOWN-POINTING_TRIANGLE] Styles Computed         <- Tab
  
  [x] Show all              <- Afficher tout

  color: rgb(0, 0, 255)
  [BLACK_RIGHT-POINTING_TRIANGLE] font-size: 24px       <- Expand pour voir cascade
      24px
      .container h1 - styles.css:45
      Inherited from body - styles.css:10
  
  [x] margin-top: 20px
```

Utile pour :
- Voir valeur exacte (après cascade)
- Comprendre d'où vient la valeur
- Trouver propriétés héritées


BOX MODEL

Visualiser dimensions

```
      Margin
    ┌─────────────────┐
    │    Border       │
    │ ┌─────────────┐ │
    │ │   Padding   │ │
    │ │ ┌─────────┐ │ │
    │ │ │ Content │ │ │
    │ │ │ 200x100 │ │ │
    │ │ └─────────┘ │ │
    │ │   10  10    │ │
    │ └─────────────┘ │
    │    2   2        │
    └─────────────────┘
       20   20

Position: static
Display: block
```

Hover sur valeur -> Éditer directement

```
Margin-top: 20  <- Changer en 40
-> Voir changement immédiat !
```


LAYOUT PANEL (Flexbox/Grid)

Pour éléments flex/grid :

**Flexbox**
```
Flex Container: .container
  Direction: row
  Wrap: wrap
  Align: center
  Justify: space-between

Flex Items:
  Item 1: flex-grow: 1
  Item 2: flex-grow: 2
```

Overlay visuel sur page montre :
- Axes flex
- Gaps
- Alignment

**Grid**
```
Grid Container: .grid
  Columns: 1fr 1fr 1fr
  Rows: auto auto
  Gap: 10px

Grid Lines numbered
Areas highlighted
```


EVENT LISTENERS

Voir tous les listeners sur élément

```
[BLACK_DOWN-POINTING_TRIANGLE] click (2)
  handler - script.js:10
  handler - script.js:25
  
[BLACK_DOWN-POINTING_TRIANGLE] mouseenter (1)
  handler - script.js:45
  
[BLACK_DOWN-POINTING_TRIANGLE] keydown (1) 
  handler - script.js:67
```

Cliquer sur handler -> Voir code dans Sources


ACCESSIBILITY (a11y)

Tab Accessibility tree

```
button "Submit"
  Role: button
  Name: "Submit"
  Keyboard focusable: Yes
  
[BLACK_DOWN-POINTING_TRIANGLE] ARIA attributes
  aria-label: "Submit form"
  aria-disabled: false
```

Vérifier accessibilité :
- Roles ARIA
- Labels
- Keyboard navigation
- Contrast ratios


SCREENSHOTS

Capture élément ou page

Clic droit sur élément > Capture node screenshot

Ou Command Menu (Ctrl+Shift+P) :
- Screenshot Capture full size screenshot
- Screenshot Capture node screenshot
- Screenshot Capture area screenshot


[ANALYSE] EXERCICE : REDESIGN LIVE

```html
<div class="card">
  <h2>Original Design</h2>
  <p>Some content here</p>
  <button>Action</button>
</div>

<style>
.card {
  background: white;
  padding: 20px;
  border: 1px solid #ccc;
}

.card h2 {
  color: black;
  font-size: 24px;
}

.card button {
  background: blue;
  color: white;
}
</style>
```

MISSION : Redesign dans DevTools

1. Sélectionner .card
   Modifier :
   - background -> linear-gradient(135deg, #667eea 0%, #764ba2 100%)
   - border-radius -> 10px
   - box-shadow -> 0 10px 30px rgba(0,0,0,0.2)

2. Sélectionner h2
   - color -> white
   - text-shadow -> 2px 2px 4px rgba(0,0,0,0.3)

3. Sélectionner button
   - background -> white
   - color -> #667eea
   - border -> 2px solid white
   - border-radius -> 25px
   - padding -> 10px 30px
   - Ajouter :hover état
     background -> transparent
     color -> white

4. Copier CSS modifié
   Clic droit > Copy > Copy all declarations

Résultat : Design moderne créé en quelques minutes !
"""


# ----------------------------------------------------------------------------
# [WEB] NETWORK TAB : DEBUGGING RÉSEAU
# ----------------------------------------------------------------------------

"""
NETWORK TAB OVERVIEW

```
┌─────────────────────────────────────────────────────────────┐
│ [ROUGE] ⊗ [RECHERCHE] [Filter] [ENTREE] [CONFIG]                  [Preserve log] [ ]   │
├───┬────────┬──────┬────┬──────┬──────┬──────────┬──────────┤
│   │ Name   │ Type │Size│ Time │Status│ Waterfall│ Timeline │
├───┼────────┼──────┼────┼──────┼──────┼──────────┼──────────┤
│200│index.html│html│24k │120ms │200   │████████  │          │
│200│style.css│css │12k │45ms  │200   │  ████    │          │
│200│app.js  │js   │89k │234ms │200   │    ████  │          │
│404│api/data│xhr  │0   │567ms │404   │      ████│          │
└───┴────────┴──────┴────┴──────┴──────┴──────────┴──────────┘
```


DÉMARRER CAPTURE

1. Ouvrir Network tab
2. [ROUGE] Rouge = Recording (défaut)
3. Rafraîchir page (Ctrl+R)
4. Voir requêtes s'afficher


CONTRÔLES

[ROUGE] Record     - Démarrer/arrêter capture
⊗ Clear       - Effacer requêtes
[RECHERCHE] Filter     - Filtrer requêtes
[ENTREE] Export     - Exporter HAR file
[CONFIG] Settings   - Options

[x] Preserve log - Garder logs entre pages
[x] Disable cache - Désactiver cache (important dev !)


FILTRER REQUÊTES

**Par type :**
- All
- Fetch/XHR (requêtes AJAX)
- JS
- CSS
- Img
- Media
- Font
- Doc
- WS (WebSocket)
- Manifest
- Other

**Par texte :**
Taper dans barre filtre :
- `api` -> Toutes requêtes contenant "api"
- `status-code:404` -> Seulement 404
- `method:POST` -> Seulement POST
- `domain:example.com` -> Domaine spécifique
- `larger-than:1M` -> Plus de 1 MB
- `-css` -> Exclure CSS

**Expressions avancées :**
```
is:running        -> Requêtes en cours
mime-type:image/  -> Images seulement
has-response-header:Content-Encoding -> Avec header spécifique
resource-type:Document -> Type ressource
```


COLONNES

**Name** : URL de la ressource
**Status** : Code HTTP (200, 404, 500...)
**Type** : MIME type (html, css, js, xhr...)
**Initiator** : Qui a déclenché (script, parser, autre)
**Size** : Taille (transfert / ressource)
**Time** : Durée totale
**Waterfall** : Timeline visuelle
**Priority** : Priorité (High, Low, Lowest)


DÉTAILS REQUÊTE

Cliquer sur requête -> Tabs en bas :

**Headers**
```
General
  Request URL: https://api.example.com/users
  Request Method: POST
  Status Code: 201 Created
  Remote Address: 93.184.216.34:443
  
Response Headers
  Content-Type: application/json
  Content-Length: 234
  Cache-Control: no-cache
  Set-Cookie: session=abc123
  
Request Headers
  Accept: application/json
  Authorization: Bearer token...
  Content-Type: application/json
  Origin: https://app.example.com
```

**Preview** : Aperçu formaté
- JSON -> Tree view
- Images -> Preview image
- HTML -> Rendered preview

**Response** : Contenu brut
- Raw response body
- Copier, rechercher

**Timing** : Breakdown détaillé
```
Queued at: 0 ms
Started at: 1.2 ms

Resource Scheduling
  Queueing:          1.2 ms
  
Connection Start
  DNS Lookup:        15.3 ms
  Initial connection: 28.7 ms
  SSL:               45.2 ms
  
Request/Response
  Request sent:       0.5 ms
  Waiting (TTFB):    67.8 ms
  Content Download:  12.4 ms
  
Total: 171.1 ms
```

**Cookies** : Cookies envoyés/reçus

**Initiator** : Call stack de déclenchement


TIMING DÉTAILLÉ

```
Queueing          ▓
Stalled           ▓▓
DNS Lookup        ▓▓▓
Initial connection▓▓▓▓
SSL               ▓▓▓▓▓
Request sent      ▓
Waiting (TTFB)    ▓▓▓▓▓▓▓▓▓
Content Download  ▓▓

Total: 234 ms
```

**Phases :**

1. **Queueing**
   - Requête en attente
   - Raisons : limite connexions, priorité basse

2. **Stalled**
   - Requête bloquée
   - Raisons : proxy, limite domaine

3. **DNS Lookup**
   - Résolution nom domaine -> IP
   - Première requête domaine

4. **Initial connection**
   - TCP handshake (SYN, SYN-ACK, ACK)

5. **SSL**
   - TLS handshake si HTTPS

6. **Request sent**
   - Envoi requête au serveur

7. **Waiting (TTFB)**
   - Time To First Byte
   - Attente réponse serveur
   - Temps traitement serveur

8. **Content Download**
   - Téléchargement réponse


WATERFALL CHART

Timeline visuelle toutes requêtes

```
0ms   100ms   200ms   300ms   400ms   500ms
│      │       │       │       │       │
index.html  ████████
style.css     ████
app.js          ████████
logo.png            ████
api/data                  ████████████
```

Couleurs :
- Vert clair  : Queueing
- Orange      : Stalled
- Vert foncé  : DNS
- Orange      : TCP
- Violet      : SSL
- Vert        : Request
- Gris        : Waiting
- Bleu        : Download


THROTTLING (Simulation réseau)

Dropdown "No throttling" :

Presets :
- Fast 3G
- Slow 3G
- Offline

Custom :
- Download : 1.5 Mb/s
- Upload : 750 kb/s
- Latency : 40 ms

Utile pour :
- Tester sur connexions lentes
- Optimiser chargement
- Comprendre expérience mobile


OFFLINE MODE

[x] Offline

Test :
```javascript
fetch('/api/data')
  .catch(error => {
    console.error('Failed:', error);
    // -> TypeError: Failed to fetch
  });
```


REPLAY REQUÊTE

Clic droit > Replay XHR

Renvoie requête identique
Utile pour :
- Tester API
- Reproduire erreur


COPIER REQUÊTE

Clic droit > Copy :

- Copy link address
- Copy as fetch
  ```javascript
  fetch("https://api.example.com/users", {
    "headers": {
      "accept": "application/json",
      "authorization": "Bearer token"
    },
    "method": "GET"
  });
  ```
  
- Copy as cURL
  ```bash
  curl 'https://api.example.com/users' \
    -H 'accept: application/json' \
    -H 'authorization: Bearer token'
  ```

- Copy all as HAR


BLOQUER REQUÊTES

Clic droit > Block request URL

Test échec requête :
```javascript
// Requête vers /api/blocked
fetch('/api/blocked')
  .catch(error => {
    // Gestion erreur
  });
```

Network Request Blocking panel :
- Enable
- Add pattern : `*.jpg` -> Bloque toutes images


OVERRIDE RESPONSES

Sources > Overrides

1. Enable overrides
2. Select folder
3. Network > Clic droit > Override content
4. Modifier réponse
5. Sauver

Rechargements utilisent override !


WEBSOCKET DEBUGGING

Filter : WS

```
Name: ws://example.com/socket
Status: 101 Switching Protocols

[BLACK_DOWN-POINTING_TRIANGLE] Messages
  ^ {"type":"ping"}           12:34:56.123
  v {"type":"pong"}           12:34:56.234
  ^ {"type":"message","data":"hello"}
  v {"type":"ack"}
```

^ = Sent (client -> server)
v = Received (server -> client)


HAR EXPORT

Export > Save as HAR with content

HAR = HTTP Archive
Format standard pour partager captures réseau

Utile pour :
- Partager avec équipe
- Analyser avec outils externes
- Bug reports


[ANALYSE] EXERCICE : ANALYSER PERFORMANCE RÉSEAU

```html
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="style1.css">
  <link rel="stylesheet" href="style2.css">
  <link rel="stylesheet" href="style3.css">
  <script src="lib1.js"></script>
  <script src="lib2.js"></script>
  <script src="app.js"></script>
</head>
<body>
  <img src="hero.jpg">
  <img src="logo.png">
  <script>
    fetch('/api/data').then(r => r.json());
  </script>
</body>
</html>
```

MISSION : Identifier problèmes

1. Ouvrir Network tab
2. [x] Disable cache
3. Rafraîchir page
4. Observer :

   **Problème 1 : CSS bloquent render**
   - style1.css, style2.css, style3.css chargés en série
   - FCP retardé

   **Solution :**
   - Inline critical CSS
   - Async non-critical CSS
   - Combiner en 1 fichier

   **Problème 2 : JS bloquent parsing**
   - lib1.js, lib2.js bloquent
   
   **Solution :**
   - defer ou async
   - Mettre avant </body>

   **Problème 3 : Images lourdes**
   - hero.jpg : 2 MB !
   
   **Solution :**
   - Optimiser images
   - Lazy loading
   - WebP format

5. Timing analysis :
   - TTFB : 200 ms -> OK
   - Content Download : 3 s -> Trop lent !
   - Total : 4.5 s -> À optimiser !

6. Waterfall montre :
   - Requêtes en cascade
   - Pas de parallélisation

   **Solution :**
   - Preload key resources
   - HTTP/2 multiplexing
   - CDN
```


NETWORK BEST PRACTICES

[OK] Disable cache pendant dev
[OK] Throttle pour tester mobile
[OK] Analyser waterfall pour optimiser
[OK] Vérifier status codes (404, 500)
[OK] Minimiser requêtes (bundle)
[OK] Utiliser cache headers
[OK] Compresser (gzip/brotli)
[OK] CDN pour static assets
[OK] Lazy load images below fold
[OK] Preload critical resources
"""


# ============================================================================
# [DOCS] RÉCAPITULATIF CHAPITRE 6
# ============================================================================

"""
CE QUE VOUS AVEZ APPRIS

[OK] Elements tab complet (DOM inspection)
[OK] Modifier DOM et CSS en live
[OK] Box model et layout visualization
[OK] Pseudo-classes et états
[OK] DOM breakpoints
[OK] Network tab détaillé
[OK] Analyser requêtes HTTP
[OK] Timing breakdown
[OK] Waterfall charts
[OK] Throttling et offline
[OK] WebSocket debugging
[OK] Block/override requests


[CLE] POINTS CLÉS

ELEMENTS :
- Modifier DOM live pour tester rapidement
- Styles panel pour tweaker CSS
- Box model pour comprendre dimensions
- DOM breakpoints pour trouver modifications

NETWORK :
- Disable cache essentiel en dev
- Waterfall révèle bottlenecks
- TTFB = performance serveur
- Content Download = taille/bande passante
- Throttling simule mobile


[OBJECTIF] WORKFLOW DEBUGGING RÉSEAU

1. Ouvrir Network tab
2. Disable cache
3. Throttle si mobile
4. Reproduire problème
5. Filtrer requêtes pertinentes
6. Analyser timing et waterfall
7. Identifier bottlenecks
8. Vérifier status/headers
9. Test fix
10. Comparer avant/après


-> PROCHAINE ÉTAPE : Chapitre 7 - Performance et Profiling !

Vous allez découvrir :
- Performance tab complet
- CPU profiling
- Memory profiling
- Rendering performance
- Frame rate (60 FPS)
- Layout thrashing detection
- Performance budgets
- Optimization techniques

Prêt à optimiser comme un pro ? [RAPIDE][HOT]
"""

# ============================================================================
# [LIVRE] NAVIGATEUR WEB - PARTIE 3 : FONCTIONNALITÉS AVANCÉES
# ============================================================================
#
# [OBJECTIF] GUIDE COMPLET DES APIS WEB MODERNES ET FONCTIONNALITÉS AVANCÉES
#
# Cette partie couvre :
# - Chapitre 9 : APIs Web Modernes
# - Chapitre 10 : Service Workers et PWA
# - Chapitre 11 : WebAssembly et Performance
# - Chapitre 12 : Sécurité du Navigateur
# - Chapitre 13 : Extensions et Personnalisation
#
# [TEMPS] TEMPS DE LECTURE : ~10-12 heures
# [DOCS] PRÉREQUIS : Parties 1 et 2 complétées
#
# ============================================================================


# ============================================================================
# [GUIDE] CHAPITRE 9 : APIs WEB MODERNES
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Utiliser Geolocation API
[OK] Notifications API
[OK] Clipboard API moderne
[OK] File System Access API
[OK] Web Storage (localStorage, sessionStorage, IndexedDB)
[OK] WebRTC pour vidéo/audio
[OK] Web Audio API
[OK] Canvas et animation
[OK] WebGL pour 3D
[OK] Drag and Drop API
"""


# ----------------------------------------------------------------------------
# [IMPORTANT] GEOLOCATION API
# ----------------------------------------------------------------------------

"""
OBTENIR LA POSITION DE L'UTILISATEUR

[IDEE] Permission utilisateur requise !


POSITION ACTUELLE

```javascript
// Vérifier support
if ('geolocation' in navigator) {
  console.log('Geolocation supportée [OK]');
} else {
  console.log('Geolocation non supportée [X]');
}

// Obtenir position
navigator.geolocation.getCurrentPosition(
  // Success callback
  (position) => {
    console.log('Position obtenue :');
    console.log('Latitude:', position.coords.latitude);
    console.log('Longitude:', position.coords.longitude);
    console.log('Précision:', position.coords.accuracy, 'mètres');
    console.log('Altitude:', position.coords.altitude);
    console.log('Vitesse:', position.coords.speed, 'm/s');
    console.log('Direction:', position.coords.heading, '°');
    console.log('Timestamp:', position.timestamp);
  },
  
  // Error callback
  (error) => {
    switch(error.code) {
      case error.PERMISSION_DENIED:
        console.error('Permission refusée');
        break;
      case error.POSITION_UNAVAILABLE:
        console.error('Position indisponible');
        break;
      case error.TIMEOUT:
        console.error('Timeout');
        break;
    }
  },
  
  // Options
  {
    enableHighAccuracy: true,  // Précision max (GPS)
    timeout: 10000,            // Timeout 10s
    maximumAge: 0              // Pas de cache
  }
);
```


SUIVI POSITION (TEMPS RÉEL)

```javascript
let watchId;

// Démarrer suivi
watchId = navigator.geolocation.watchPosition(
  (position) => {
    console.log('Nouvelle position:', position.coords);
    updateMap(position.coords);
  },
  (error) => {
    console.error('Erreur suivi:', error);
  },
  {
    enableHighAccuracy: true,
    maximumAge: 1000  // Update si > 1s
  }
);

// Arrêter suivi
function stopTracking() {
  navigator.geolocation.clearWatch(watchId);
  console.log('Suivi arrêté');
}
```


EXEMPLE COMPLET : CARTE

```javascript
async function showUserLocation() {
  if (!('geolocation' in navigator)) {
    alert('Geolocation non supportée');
    return;
  }
  
  try {
    const position = await new Promise((resolve, reject) => {
      navigator.geolocation.getCurrentPosition(resolve, reject, {
        enableHighAccuracy: true,
        timeout: 5000
      });
    });
    
    const { latitude, longitude } = position.coords;
    
    // Afficher sur carte (exemple Google Maps)
    const mapUrl = `https://www.google.com/maps?q=${latitude},${longitude}`;
    window.open(mapUrl, '_blank');
    
    // Ou afficher adresse (Reverse Geocoding)
    const address = await getAddress(latitude, longitude);
    console.log('Adresse:', address);
    
  } catch (error) {
    console.error('Erreur géolocalisation:', error.message);
  }
}

async function getAddress(lat, lon) {
  const response = await fetch(
    `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lon}&format=json`
  );
  const data = await response.json();
  return data.display_name;
}
```


DISTANCE ENTRE DEUX POINTS

```javascript
function calculateDistance(lat1, lon1, lat2, lon2) {
  // Formule Haversine
  const R = 6371; // Rayon Terre en km
  
  const dLat = toRad(lat2 - lat1);
  const dLon = toRad(lon2 - lon1);
  
  const a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
    Math.sin(dLon/2) * Math.sin(dLon/2);
  
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
  const distance = R * c;
  
  return distance; // km
}

function toRad(degrees) {
  return degrees * (Math.PI / 180);
}

// Usage
const paris = { lat: 48.8566, lon: 2.3522 };
const london = { lat: 51.5074, lon: -0.1278 };

const distance = calculateDistance(
  paris.lat, paris.lon,
  london.lat, london.lon
);

console.log(`Distance Paris-Londres: ${distance.toFixed(2)} km`);
// -> Distance Paris-Londres: 343.56 km
```
"""


# ----------------------------------------------------------------------------
# [NOTIF] NOTIFICATIONS API
# ----------------------------------------------------------------------------

"""
AFFICHER NOTIFICATIONS DESKTOP

Permission requise !


DEMANDER PERMISSION

```javascript
// Vérifier support
if (!('Notification' in window)) {
  console.log('Notifications non supportées');
}

// Vérifier permission actuelle
console.log(Notification.permission);
// -> "default" (pas demandé)
// -> "granted" (accordé)
// -> "denied" (refusé)

// Demander permission
async function requestNotificationPermission() {
  if (Notification.permission === 'granted') {
    console.log('Permission déjà accordée');
    return true;
  }
  
  if (Notification.permission === 'denied') {
    console.log('Permission refusée');
    return false;
  }
  
  const permission = await Notification.requestPermission();
  return permission === 'granted';
}

// Usage
const granted = await requestNotificationPermission();
if (granted) {
  console.log('Permission accordée [OK]');
} else {
  console.log('Permission refusée [X]');
}
```


CRÉER NOTIFICATION SIMPLE

```javascript
if (Notification.permission === 'granted') {
  new Notification('Hello!', {
    body: 'Ceci est une notification test',
    icon: '/icon.png'
  });
}
```


OPTIONS NOTIFICATION

```javascript
const options = {
  body: 'Message de la notification',
  icon: '/icon.png',              // Icône
  badge: '/badge.png',            // Badge (Android)
  image: '/image.jpg',            // Grande image
  vibrate: [200, 100, 200],       // Pattern vibration (mobile)
  timestamp: Date.now(),          // Timestamp
  tag: 'unique-id',               // ID unique (remplace si existe)
  requireInteraction: true,       // Ne disparaît pas auto
  silent: false,                  // Pas de son
  data: { userId: 123 },          // Données custom
  
  // Actions (boutons)
  actions: [
    {
      action: 'reply',
      title: 'Répondre',
      icon: '/reply.png'
    },
    {
      action: 'close',
      title: 'Fermer',
      icon: '/close.png'
    }
  ]
};

const notification = new Notification('Titre', options);
```


ÉVÉNEMENTS NOTIFICATION

```javascript
const notification = new Notification('Message reçu', {
  body: 'Nouveau message de Alice',
  data: { messageId: 456 }
});

// Click
notification.onclick = (event) => {
  console.log('Notification cliquée');
  event.preventDefault(); // Empêche focus fenêtre
  window.focus();
  window.location.href = '/messages/456';
  notification.close();
};

// Close
notification.onclose = () => {
  console.log('Notification fermée');
};

// Error
notification.onerror = (error) => {
  console.error('Erreur notification:', error);
};

// Show (affichée)
notification.onshow = () => {
  console.log('Notification affichée');
};
```


NOTIFICATION AVEC TAG (Remplacement)

```javascript
// Première notification
new Notification('Message 1', {
  body: 'Premier message',
  tag: 'message-notification'
});

// Après 2 secondes
setTimeout(() => {
  // Remplace la précédente (même tag)
  new Notification('Message 2', {
    body: 'Deuxième message',
    tag: 'message-notification'  // <- Même tag
  });
}, 2000);

// Résultat : Une seule notification visible (remplacée)
```


EXEMPLE COMPLET : SYSTÈME NOTIFICATION

```javascript
class NotificationSystem {
  constructor() {
    this.permission = Notification.permission;
  }
  
  async requestPermission() {
    if (this.permission === 'granted') return true;
    
    this.permission = await Notification.requestPermission();
    return this.permission === 'granted';
  }
  
  async notify(title, options = {}) {
    const granted = await this.requestPermission();
    
    if (!granted) {
      console.warn('Permission notification refusée');
      return null;
    }
    
    const notification = new Notification(title, {
      icon: '/icon.png',
      ...options
    });
    
    // Auto-close après 5s si pas requireInteraction
    if (!options.requireInteraction) {
      setTimeout(() => notification.close(), 5000);
    }
    
    return notification;
  }
  
  notifyMessage(from, message) {
    return this.notify(`Message de ${from}`, {
      body: message,
      tag: 'message',
      data: { from, message },
      actions: [
        { action: 'reply', title: 'Répondre' },
        { action: 'dismiss', title: 'Ignorer' }
      ]
    });
  }
  
  notifyError(message) {
    return this.notify('Erreur', {
      body: message,
      tag: 'error',
      requireInteraction: true
    });
  }
}

// Usage
const notifications = new NotificationSystem();

await notifications.notifyMessage('Alice', 'Salut ! Comment vas-tu ?');
await notifications.notifyError('Échec de la sauvegarde');
```
"""


# ----------------------------------------------------------------------------
# [LISTE] CLIPBOARD API
# ----------------------------------------------------------------------------

"""
COPIER/COLLER MODERNE

API asynchrone sécurisée


COPIER TEXTE

```javascript
// API moderne
async function copyText(text) {
  try {
    await navigator.clipboard.writeText(text);
    console.log('Texte copié !');
  } catch (error) {
    console.error('Échec copie:', error);
  }
}

// Usage
await copyText('Hello World');

// Avec bouton
button.addEventListener('click', async () => {
  await copyText('Texte à copier');
  button.textContent = 'Copié [OK]';
});
```


LIRE CLIPBOARD

```javascript
async function pasteText() {
  try {
    const text = await navigator.clipboard.readText();
    console.log('Clipboard:', text);
    return text;
  } catch (error) {
    console.error('Échec lecture:', error);
  }
}

// Usage
input.addEventListener('click', async () => {
  const text = await pasteText();
  input.value = text;
});
```


COPIER HTML/IMAGES

```javascript
// Copier HTML riche
async function copyRichText(html, plainText) {
  const blob = new Blob([html], { type: 'text/html' });
  const textBlob = new Blob([plainText], { type: 'text/plain' });
  
  const clipboardItem = new ClipboardItem({
    'text/html': blob,
    'text/plain': textBlob
  });
  
  await navigator.clipboard.write([clipboardItem]);
}

// Usage
await copyRichText(
  '<strong>Bold text</strong>',
  'Bold text'
);


// Copier image
async function copyImage(imageUrl) {
  const response = await fetch(imageUrl);
  const blob = await response.blob();
  
  const clipboardItem = new ClipboardItem({
    [blob.type]: blob
  });
  
  await navigator.clipboard.write([clipboardItem]);
}

// Usage
await copyImage('/logo.png');
```


LIRE CLIPBOARD (Images/HTML)

```javascript
async function readClipboard() {
  const items = await navigator.clipboard.read();
  
  for (const item of items) {
    console.log('Types disponibles:', item.types);
    
    for (const type of item.types) {
      const blob = await item.getType(type);
      
      if (type.startsWith('image/')) {
        // Image
        const url = URL.createObjectURL(blob);
        const img = document.createElement('img');
        img.src = url;
        document.body.appendChild(img);
        
      } else if (type === 'text/html') {
        // HTML
        const html = await blob.text();
        console.log('HTML:', html);
        
      } else if (type === 'text/plain') {
        // Texte
        const text = await blob.text();
        console.log('Text:', text);
      }
    }
  }
}
```


ÉVÉNEMENT PASTE

```javascript
// Écouter événement paste
document.addEventListener('paste', async (event) => {
  event.preventDefault();
  
  const items = event.clipboardData.items;
  
  for (const item of items) {
    if (item.type.startsWith('image/')) {
      // Image collée
      const file = item.getAsFile();
      const url = URL.createObjectURL(file);
      console.log('Image:', url);
      
    } else if (item.type === 'text/plain') {
      // Texte
      const text = await new Promise(resolve => {
        item.getAsString(resolve);
      });
      console.log('Text:', text);
    }
  }
});
```


COPY EVENT

```javascript
// Modifier contenu copié
document.addEventListener('copy', (event) => {
  const selection = window.getSelection().toString();
  
  // Ajouter attribution
  const modified = selection + '\n\nSource: ' + window.location.href;
  
  event.clipboardData.setData('text/plain', modified);
  event.preventDefault();
});
```


EXEMPLE : COPIER CODE AVEC BOUTON

```html
<pre><code id="code">function hello() {
  console.log('Hello World');
}</code></pre>
<button onclick="copyCode()">Copy Code</button>

<script>
async function copyCode() {
  const code = document.getElementById('code').textContent;
  
  try {
    await navigator.clipboard.writeText(code);
    
    // Feedback visuel
    event.target.textContent = 'Copied [OK]';
    event.target.style.background = 'green';
    
    setTimeout(() => {
      event.target.textContent = 'Copy Code';
      event.target.style.background = '';
    }, 2000);
    
  } catch (error) {
    alert('Échec copie');
  }
}
</script>
```
"""


# ----------------------------------------------------------------------------
# [SAUVEGARDE] WEB STORAGE
# ----------------------------------------------------------------------------

"""
3 TYPES DE STOCKAGE

1. LocalStorage - Persistant, synchrone
2. SessionStorage - Session uniquement
3. IndexedDB - Base de données, asynchrone


1. LOCALSTORAGE
───────────────

Stockage simple clé-valeur (strings)

```javascript
// Sauvegarder
localStorage.setItem('username', 'Alice');
localStorage.setItem('theme', 'dark');

// Lire
const username = localStorage.getItem('username');
console.log(username); // "Alice"

// Supprimer
localStorage.removeItem('username');

// Tout effacer
localStorage.clear();

// Nombre d'items
console.log(localStorage.length);

// Itérer
for (let i = 0; i < localStorage.length; i++) {
  const key = localStorage.key(i);
  const value = localStorage.getItem(key);
  console.log(`${key}: ${value}`);
}

// Ou
for (let key in localStorage) {
  if (localStorage.hasOwnProperty(key)) {
    console.log(`${key}: ${localStorage[key]}`);
  }
}
```


STOCKER OBJETS (JSON)

```javascript
// Objet
const user = {
  name: 'Alice',
  age: 30,
  preferences: {
    theme: 'dark',
    language: 'fr'
  }
};

// Sauvegarder (stringify)
localStorage.setItem('user', JSON.stringify(user));

// Lire (parse)
const stored = localStorage.getItem('user');
const parsedUser = JSON.parse(stored);
console.log(parsedUser.name); // "Alice"

// Helper
function saveObject(key, object) {
  localStorage.setItem(key, JSON.stringify(object));
}

function loadObject(key) {
  const item = localStorage.getItem(key);
  return item ? JSON.parse(item) : null;
}

// Usage
saveObject('user', user);
const loadedUser = loadObject('user');
```


2. SESSIONSTORAGE
─────────────────

API identique, mais données supprimées à fermeture onglet

```javascript
// Même API que localStorage
sessionStorage.setItem('tempData', 'value');
const data = sessionStorage.getItem('tempData');
sessionStorage.removeItem('tempData');
sessionStorage.clear();
```

Différence :
- localStorage : Persiste indéfiniment
- sessionStorage : Supprimé à fermeture onglet


ÉVÉNEMENT STORAGE

Écouter changements (autres onglets)

```javascript
window.addEventListener('storage', (event) => {
  console.log('Storage changed:');
  console.log('Key:', event.key);
  console.log('Old value:', event.oldValue);
  console.log('New value:', event.newValue);
  console.log('URL:', event.url);
  console.log('Storage:', event.storageArea);
});

// Note : Event pas déclenché dans onglet qui modifie
// Seulement dans AUTRES onglets du même domaine !
```

Use case : Sync entre onglets

```javascript
// Onglet 1
localStorage.setItem('theme', 'dark');

// Onglet 2 (automatiquement)
window.addEventListener('storage', (e) => {
  if (e.key === 'theme') {
    applyTheme(e.newValue);
  }
});
```


3. INDEXEDDB
────────────

Base de données NoSQL dans navigateur


OUVRIR DATABASE

```javascript
// Ouvrir (créer si n'existe pas)
const request = indexedDB.open('MyDatabase', 1);

request.onerror = () => {
  console.error('Erreur ouverture DB');
};

request.onsuccess = (event) => {
  const db = event.target.result;
  console.log('DB ouverte:', db);
};

// Upgrade (création/modification schema)
request.onupgradeneeded = (event) => {
  const db = event.target.result;
  
  // Créer object store (table)
  const objectStore = db.createObjectStore('users', {
    keyPath: 'id',
    autoIncrement: true
  });
  
  // Créer index
  objectStore.createIndex('email', 'email', { unique: true });
  objectStore.createIndex('name', 'name', { unique: false });
};
```


AJOUTER DONNÉES

```javascript
function addUser(db, user) {
  const transaction = db.transaction(['users'], 'readwrite');
  const objectStore = transaction.objectStore('users');
  
  const request = objectStore.add(user);
  
  request.onsuccess = () => {
    console.log('User ajouté');
  };
  
  request.onerror = () => {
    console.error('Erreur ajout');
  };
}

// Usage
const user = {
  name: 'Alice',
  email: 'alice@example.com',
  age: 30
};

addUser(db, user);
```


LIRE DONNÉES

```javascript
// Par clé
function getUser(db, id) {
  const transaction = db.transaction(['users'], 'readonly');
  const objectStore = transaction.objectStore('users');
  
  const request = objectStore.get(id);
  
  request.onsuccess = (event) => {
    const user = event.target.result;
    console.log('User:', user);
  };
}

// Par index
function getUserByEmail(db, email) {
  const transaction = db.transaction(['users'], 'readonly');
  const objectStore = transaction.objectStore('users');
  const index = objectStore.index('email');
  
  const request = index.get(email);
  
  request.onsuccess = (event) => {
    const user = event.target.result;
    console.log('User:', user);
  };
}

// Tous
function getAllUsers(db) {
  const transaction = db.transaction(['users'], 'readonly');
  const objectStore = transaction.objectStore('users');
  
  const request = objectStore.getAll();
  
  request.onsuccess = (event) => {
    const users = event.target.result;
    console.log('All users:', users);
  };
}
```


METTRE À JOUR

```javascript
function updateUser(db, user) {
  const transaction = db.transaction(['users'], 'readwrite');
  const objectStore = transaction.objectStore('users');
  
  const request = objectStore.put(user);
  
  request.onsuccess = () => {
    console.log('User mis à jour');
  };
}
```


SUPPRIMER

```javascript
function deleteUser(db, id) {
  const transaction = db.transaction(['users'], 'readwrite');
  const objectStore = transaction.objectStore('users');
  
  const request = objectStore.delete(id);
  
  request.onsuccess = () => {
    console.log('User supprimé');
  };
}

// Supprimer tout
function clearUsers(db) {
  const transaction = db.transaction(['users'], 'readwrite');
  const objectStore = transaction.objectStore('users');
  
  const request = objectStore.clear();
  
  request.onsuccess = () => {
    console.log('Tous users supprimés');
  };
}
```


CURSORS (Itération)

```javascript
function iterateUsers(db) {
  const transaction = db.transaction(['users'], 'readonly');
  const objectStore = transaction.objectStore('users');
  
  const request = objectStore.openCursor();
  
  request.onsuccess = (event) => {
    const cursor = event.target.result;
    
    if (cursor) {
      console.log('User:', cursor.value);
      cursor.continue(); // Suivant
    } else {
      console.log('Fin');
    }
  };
}
```


WRAPPER PROMISIFIÉ

```javascript
class IndexedDBWrapper {
  constructor(dbName, version) {
    this.dbName = dbName;
    this.version = version;
    this.db = null;
  }
  
  async open(upgrade) {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open(this.dbName, this.version);
      
      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        this.db = request.result;
        resolve(this.db);
      };
      request.onupgradeneeded = (event) => {
        upgrade(event.target.result);
      };
    });
  }
  
  async add(storeName, item) {
    const tx = this.db.transaction([storeName], 'readwrite');
    const store = tx.objectStore(storeName);
    
    return new Promise((resolve, reject) => {
      const request = store.add(item);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }
  
  async get(storeName, key) {
    const tx = this.db.transaction([storeName], 'readonly');
    const store = tx.objectStore(storeName);
    
    return new Promise((resolve, reject) => {
      const request = store.get(key);
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }
  
  async getAll(storeName) {
    const tx = this.db.transaction([storeName], 'readonly');
    const store = tx.objectStore(storeName);
    
    return new Promise((resolve, reject) => {
      const request = store.getAll();
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }
  
  async delete(storeName, key) {
    const tx = this.db.transaction([storeName], 'readwrite');
    const store = tx.objectStore(storeName);
    
    return new Promise((resolve, reject) => {
      const request = store.delete(key);
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }
}

// Usage
const db = new IndexedDBWrapper('MyDB', 1);

await db.open((db) => {
  const store = db.createObjectStore('users', { keyPath: 'id', autoIncrement: true });
  store.createIndex('email', 'email', { unique: true });
});

await db.add('users', { name: 'Alice', email: 'alice@example.com' });
const users = await db.getAll('users');
console.log(users);
```


LIMITES STORAGE

localStorage/sessionStorage :
- ~5-10 MB par domaine
- Synchrone (peut bloquer UI)
- Strings seulement

IndexedDB :
- ~50 MB - illimité (selon navigateur)
- Asynchrone
- Tout type de données
- Transactions

Vérifier espace :

```javascript
if (navigator.storage && navigator.storage.estimate) {
  const estimate = await navigator.storage.estimate();
  console.log('Quota:', estimate.quota);
  console.log('Usage:', estimate.usage);
  console.log('Disponible:', estimate.quota - estimate.usage);
  console.log('Pourcentage:', (estimate.usage / estimate.quota * 100).toFixed(2) + '%');
}
```
"""


# ============================================================================
# [DOCS] RÉCAPITULATIF CHAPITRE 9
# ============================================================================

"""
CE QUE VOUS AVEZ APPRIS

[OK] Geolocation API (position, suivi, distance)
[OK] Notifications API (permission, création, événements)
[OK] Clipboard API (copier/coller moderne)
[OK] LocalStorage et SessionStorage
[OK] IndexedDB (base de données complète)
[OK] Storage events et limites


[CLE] POINTS CLÉS

GEOLOCATION :
- Permission requise
- enableHighAccuracy pour GPS précis
- watchPosition pour suivi temps réel

NOTIFICATIONS :
- Demander permission d'abord
- Tag pour remplacer notifications
- Actions pour boutons interactifs

CLIPBOARD :
- API async moderne (writeText/readText)
- Support images et HTML riche
- Événements copy/paste

STORAGE :
- localStorage : Persistant, synchrone, 5-10 MB
- sessionStorage : Session, synchrone
- IndexedDB : Async, illimité, transactions


[OBJECTIF] CHOIX STORAGE

Simple data (settings) -> localStorage
Temporary data -> sessionStorage
Complex/large data -> IndexedDB
Très gros fichiers -> File System Access API


-> PROCHAINE ÉTAPE : Chapitre 10 - Service Workers et PWA !

Vous allez découvrir :
- Service Workers complets
- Cache strategies
- Offline support
- Background sync
- Push notifications
- Progressive Web Apps (PWA)

Prêt pour les apps hors-ligne ? [MOBILE]*
"""

# ============================================================================
# [LIVRE] NAVIGATEUR WEB - PARTIE 4 : OPTIMISATION ET PRODUCTION
# ============================================================================
#
# [OBJECTIF] GUIDE COMPLET DE L'OPTIMISATION ET DES BEST PRACTICES
#
# Cette partie couvre :
# - Chapitre 14 : Optimisation Performance
# - Chapitre 15 : Accessibilité (A11y)
# - Chapitre 16 : SEO et Crawlers
# - Chapitre 17 : Testing Cross-Browser
# - Chapitre 18 : DevOps et Déploiement
# - Chapitre 19 : Monitoring et Analytics
# - Chapitre 20 : Best Practices Finales
#
# [TEMPS] TEMPS DE LECTURE : ~8-10 heures
# [DOCS] PRÉREQUIS : Parties 1, 2 et 3 complétées
#
# ============================================================================


# ============================================================================
# [GUIDE] CHAPITRE 14 : OPTIMISATION PERFORMANCE
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Mesurer performance avec précision
[OK] Core Web Vitals (LCP, FID, CLS)
[OK] Optimiser images et assets
[OK] Code splitting et lazy loading
[OK] Caching strategies
[OK] Minimiser JavaScript et CSS
[OK] Optimiser fonts
[OK] Preload, prefetch, preconnect
[OK] Resource hints
[OK] Performance budgets
"""


# ----------------------------------------------------------------------------
# [GRAPHIQUE] CORE WEB VITALS
# ----------------------------------------------------------------------------

"""
3 MÉTRIQUES ESSENTIELLES

Google utilise pour ranking :
1. LCP - Largest Contentful Paint
2. FID - First Input Delay
3. CLS - Cumulative Layout Shift


1. LCP - LARGEST CONTENTFUL PAINT
──────────────────────────────────

[IDEE] Temps pour afficher le plus gros élément visible

Cible : < 2.5s

Mesurer :

```javascript
new PerformanceObserver((list) => {
  const entries = list.getEntries();
  const lastEntry = entries[entries.length - 1];
  
  console.log('LCP:', lastEntry.renderTime || lastEntry.loadTime);
  console.log('Element:', lastEntry.element);
  console.log('Size:', lastEntry.size);
}).observe({ entryTypes: ['largest-contentful-paint'] });
```

Optimiser LCP :

[OK] Optimiser images (WebP, compression)
[OK] Preload ressources critiques
[OK] Minimiser CSS bloquant
[OK] Utiliser CDN
[OK] Server-side rendering (SSR)
[OK] Éviter gros JavaScript bloquant


2. FID - FIRST INPUT DELAY
──────────────────────────

[IDEE] Temps entre première interaction et réponse navigateur

Cible : < 100ms

Mesurer :

```javascript
new PerformanceObserver((list) => {
  const firstInput = list.getEntries()[0];
  
  console.log('FID:', firstInput.processingStart - firstInput.startTime);
  console.log('Event type:', firstInput.name);
  console.log('Target:', firstInput.target);
}).observe({ entryTypes: ['first-input'] });
```

Optimiser FID :

[OK] Réduire JavaScript execution time
[OK] Code splitting
[OK] Defer non-critical JS
[OK] Web Workers pour calculs lourds
[OK] Éviter long tasks (> 50ms)


3. CLS - CUMULATIVE LAYOUT SHIFT
─────────────────────────────────

[IDEE] Stabilité visuelle (éléments qui bougent)

Cible : < 0.1

Mesurer :

```javascript
let clsScore = 0;

new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    if (!entry.hadRecentInput) {
      clsScore += entry.value;
      console.log('CLS:', clsScore);
      console.log('Shift:', entry.value);
      entry.sources.forEach((source) => {
        console.log('Element:', source.node);
      });
    }
  });
}).observe({ entryTypes: ['layout-shift'] });
```

Optimiser CLS :

[OK] Définir width/height sur images
[OK] Définir dimensions sur ads/embeds
[OK] Éviter insérer contenu au-dessus du contenu existant
[OK] Utiliser transform pour animations (pas top/left)
[OK] Fonts : font-display: swap


MESURER TOUTES WEB VITALS

```javascript
import { getCLS, getFID, getLCP } from 'web-vitals';

getCLS(console.log);
getFID(console.log);
getLCP(console.log);

// Ou manuellement
function measureWebVitals() {
  const metrics = {
    lcp: null,
    fid: null,
    cls: 0
  };
  
  // LCP
  new PerformanceObserver((list) => {
    const entries = list.getEntries();
    metrics.lcp = entries[entries.length - 1].renderTime;
  }).observe({ entryTypes: ['largest-contentful-paint'] });
  
  // FID
  new PerformanceObserver((list) => {
    const firstInput = list.getEntries()[0];
    metrics.fid = firstInput.processingStart - firstInput.startTime;
  }).observe({ entryTypes: ['first-input'] });
  
  // CLS
  new PerformanceObserver((list) => {
    list.getEntries().forEach((entry) => {
      if (!entry.hadRecentInput) {
        metrics.cls += entry.value;
      }
    });
  }).observe({ entryTypes: ['layout-shift'] });
  
  return metrics;
}
```
"""


# ----------------------------------------------------------------------------
# [FRAME_WITH_PICTURE] OPTIMISATION IMAGES
# ----------------------------------------------------------------------------

"""
FORMATS MODERNES

WebP : -25-35% taille vs JPEG
AVIF : -50% taille vs JPEG (nouveau)
SVG : Vectoriel (logos, icônes)


RESPONSIVE IMAGES

```html
<!-- srcset pour différentes tailles -->
<img 
  src="image-800.jpg"
  srcset="
    image-400.jpg 400w,
    image-800.jpg 800w,
    image-1200.jpg 1200w
  "
  sizes="
    (max-width: 600px) 400px,
    (max-width: 1200px) 800px,
    1200px
  "
  alt="Description"
  loading="lazy"
  decoding="async"
>

<!-- picture pour formats différents -->
<picture>
  <source type="image/avif" srcset="image.avif">
  <source type="image/webp" srcset="image.webp">
  <img src="image.jpg" alt="Fallback">
</picture>
```


LAZY LOADING

```html
<!-- Native lazy loading -->
<img src="image.jpg" loading="lazy" alt="Description">

<!-- OU Intersection Observer -->
<script>
const images = document.querySelectorAll('img[data-src]');

const imageObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.removeAttribute('data-src');
      observer.unobserve(img);
    }
  });
}, {
  rootMargin: '50px' // Charger 50px avant visible
});

images.forEach(img => imageObserver.observe(img));
</script>
```


OPTIMISER TAILLE

```javascript
// Compression
// Utiliser outils : 
// - ImageOptim
// - TinyPNG
// - Squoosh (web)

// Dimensions appropriées
// Ne jamais charger 2000x2000 si affiché 400x400

// Exemple : générer plusieurs tailles
// image-400.jpg   (mobile)
// image-800.jpg   (tablet)
// image-1200.jpg  (desktop)
// image-2400.jpg  (retina)
```


PROGRESSIVE JPEG

```html
<!-- Affichage progressif (blur -> net) -->
<!-- Créer avec ImageMagick ou Photoshop -->
```


SPRITES CSS (Icônes)

```css
/* Combiner petites images en une seule */
.icon {
  background-image: url('sprite.png');
  width: 32px;
  height: 32px;
}

.icon-home {
  background-position: 0 0;
}

.icon-user {
  background-position: -32px 0;
}

/* Mieux : Utiliser SVG sprites ou icon fonts */
```
"""


# ----------------------------------------------------------------------------
# [PACKAGE] CODE SPLITTING ET LAZY LOADING
# ----------------------------------------------------------------------------

"""
DYNAMIC IMPORT

```javascript
// Au lieu de :
import HeavyComponent from './HeavyComponent.js';

// Utiliser :
const loadHeavyComponent = async () => {
  const { default: HeavyComponent } = await import('./HeavyComponent.js');
  return HeavyComponent;
};

// Usage
button.addEventListener('click', async () => {
  const HeavyComponent = await loadHeavyComponent();
  const instance = new HeavyComponent();
});
```


ROUTE-BASED SPLITTING

```javascript
// Router avec lazy loading
const router = {
  '/': () => import('./pages/Home.js'),
  '/about': () => import('./pages/About.js'),
  '/contact': () => import('./pages/Contact.js')
};

async function navigate(path) {
  const pageModule = await router[path]();
  const Page = pageModule.default;
  renderPage(new Page());
}
```


LAZY LOAD BIBLIOTHÈQUES

```javascript
// Charger uniquement si nécessaire
let chart = null;

async function showChart(data) {
  if (!chart) {
    const ChartJS = await import('chart.js');
    chart = ChartJS.default;
  }
  
  new chart(ctx, { data });
}
```


PRELOAD COMPONENT

```javascript
// Preload au hover (avant click)
button.addEventListener('mouseenter', () => {
  import('./ExpensiveModal.js'); // Précharge
}, { once: true });

button.addEventListener('click', async () => {
  const { default: Modal } = await import('./ExpensiveModal.js');
  new Modal().show(); // Déjà chargé !
});
```
"""


# ----------------------------------------------------------------------------
# [SAUVEGARDE] CACHING STRATEGIES
# ----------------------------------------------------------------------------

"""
HTTP CACHE HEADERS

```
# Immutable assets (hash dans nom)
Cache-Control: public, max-age=31536000, immutable

# HTML (toujours revalider)
Cache-Control: no-cache

# API data (cache court)
Cache-Control: public, max-age=300

# Private data
Cache-Control: private, max-age=0, must-revalidate

# Pas de cache
Cache-Control: no-store
```


ETAG ET LAST-MODIFIED

```
# Serveur envoie
ETag: "abc123"
Last-Modified: Mon, 17 Jan 2026 10:00:00 GMT

# Client renvoie
If-None-Match: "abc123"
If-Modified-Since: Mon, 17 Jan 2026 10:00:00 GMT

# Si pas modifié
HTTP/1.1 304 Not Modified
```


SERVICE WORKER CACHE

```javascript
// Cache-first strategy
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((cached) => {
        return cached || fetch(event.request);
      })
  );
});

// Network-first strategy
self.addEventListener('fetch', (event) => {
  event.respondWith(
    fetch(event.request)
      .catch(() => caches.match(event.request))
  );
});

// Stale-while-revalidate
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((cached) => {
        const fetchPromise = fetch(event.request)
          .then((response) => {
            caches.open('v1').then((cache) => {
              cache.put(event.request, response.clone());
            });
            return response;
          });
        
        return cached || fetchPromise;
      })
  );
});
```
"""


# ----------------------------------------------------------------------------
# [RAPIDE] RESOURCE HINTS
# ----------------------------------------------------------------------------

"""
PRELOAD - Charger ressource critique

```html
<!-- CSS critique -->
<link rel="preload" href="critical.css" as="style">

<!-- Font -->
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>

<!-- Image hero -->
<link rel="preload" href="hero.jpg" as="image">

<!-- Script important -->
<link rel="preload" href="app.js" as="script">
```


PREFETCH - Charger ressource future

```html
<!-- Page suivante probable -->
<link rel="prefetch" href="/next-page.html">

<!-- Image utilisée plus tard -->
<link rel="prefetch" href="lazy-image.jpg">
```


PRECONNECT - Établir connexion

```html
<!-- API externe -->
<link rel="preconnect" href="https://api.example.com">

<!-- CDN -->
<link rel="preconnect" href="https://cdn.example.com">

<!-- Fonts (avec crossorigin) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
```


DNS-PREFETCH - Résoudre DNS

```html
<!-- Plus léger que preconnect -->
<link rel="dns-prefetch" href="https://analytics.example.com">
```


PRERENDER - Rendre page en background

```html
<!-- [ATTENTION] Lourd ! Utiliser avec précaution -->
<link rel="prerender" href="/next-page.html">
```


STRATÉGIE COMPLÈTE

```html
<!DOCTYPE html>
<html>
<head>
  <!-- 1. Preconnect domaines tiers -->
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://cdn.example.com">
  
  <!-- 2. Preload ressources critiques -->
  <link rel="preload" href="/critical.css" as="style">
  <link rel="preload" href="/hero.jpg" as="image">
  <link rel="preload" href="/font.woff2" as="font" crossorigin>
  
  <!-- 3. CSS -->
  <link rel="stylesheet" href="/critical.css">
  
  <!-- 4. Prefetch ressources futures -->
  <link rel="prefetch" href="/next-page.html">
  <link rel="prefetch" href="/secondary.js">
</head>
<body>
  <!-- Contenu -->
  
  <!-- 5. Scripts en fin -->
  <script src="/app.js" defer></script>
</body>
</html>
```
"""


# ----------------------------------------------------------------------------
# [DESIGN] OPTIMISATION CSS
# ----------------------------------------------------------------------------

"""
CRITICAL CSS

```html
<head>
  <!-- Inline critical CSS (above-fold) -->
  <style>
    /* Seulement styles pour contenu visible initialement */
    body { margin: 0; font-family: Arial; }
    .header { background: blue; height: 60px; }
    .hero { min-height: 400px; }
  </style>
  
  <!-- Async non-critical CSS -->
  <link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="styles.css"></noscript>
</head>
```


REMOVE UNUSED CSS

```javascript
// Outils :
// - PurgeCSS
// - UnCSS
// - Chrome Coverage tab

// Exemple PurgeCSS config
module.exports = {
  content: ['./src/**/*.html', './src/**/*.js'],
  css: ['./src/**/*.css'],
  safelist: ['active', 'open'] // Classes dynamiques
};
```


MINIFY CSS

```css
/* Avant (15 KB) */
.button {
  background-color: #3498db;
  border-radius: 4px;
  padding: 10px 20px;
  color: white;
  font-size: 16px;
}

/* Après minify (8 KB) */
.button{background-color:#3498db;border-radius:4px;padding:10px 20px;color:#fff;font-size:16px}
```


CSS CONTAINMENT

```css
/* Isoler sous-arbres pour layout/paint */
.article {
  contain: layout style paint;
}

.widget {
  contain: strict; /* layout + style + paint + size */
}

/* Performance : navigateur peut skip re-layout/paint */
```
"""


# ----------------------------------------------------------------------------
# [DOC] OPTIMISATION JAVASCRIPT
# ----------------------------------------------------------------------------

"""
MINIFY ET UGLIFY

```javascript
// Avant (10 KB)
function calculateTotal(items) {
  let total = 0;
  for (let i = 0; i < items.length; i++) {
    total += items[i].price * items[i].quantity;
  }
  return total;
}

// Après minify (2 KB)
function calculateTotal(i){let t=0;for(let e=0;e<i.length;e++)t+=i[e].price*i[e].quantity;return t}
```


TREE SHAKING

```javascript
// Importer seulement ce qui est utilisé
// [X] Mauvais
import _ from 'lodash'; // Tout lodash (70 KB)

// [OK] Bon
import debounce from 'lodash/debounce'; // Seulement debounce (5 KB)

// OU avec ES modules
import { debounce } from 'lodash-es';
```


DEFER ET ASYNC

```html
<!-- Defer : Execute après DOM ready, dans l'ordre -->
<script src="app.js" defer></script>
<script src="utils.js" defer></script>

<!-- Async : Execute dès téléchargé, ordre non garanti -->
<script src="analytics.js" async></script>
<script src="ads.js" async></script>

<!-- Module (defer par défaut) -->
<script type="module" src="app.js"></script>
```


DEBOUNCE EXPENSIVE OPERATIONS

```javascript
// Éviter appels excessifs
function debounce(func, delay) {
  let timeout;
  return function(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(this, args), delay);
  };
}

// Usage
const expensiveSearch = debounce((query) => {
  fetch(`/search?q=${query}`);
}, 300);

input.addEventListener('input', (e) => {
  expensiveSearch(e.target.value);
});
```


REQUESTANIMATIONFRAME

```javascript
// [X] Mauvais (peut causer jank)
setInterval(() => {
  element.style.left = position + 'px';
  position += 1;
}, 16);

// [OK] Bon (sync avec refresh)
function animate() {
  element.style.left = position + 'px';
  position += 1;
  requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
```
"""


# ----------------------------------------------------------------------------
# [TEXTE] OPTIMISATION FONTS
# ----------------------------------------------------------------------------

"""
FONT-DISPLAY

```css
@font-face {
  font-family: 'MyFont';
  src: url('font.woff2') format('woff2');
  font-display: swap; /* Affiche fallback puis swap */
}

/* Options :
   auto     - Navigateur décide (défaut)
   block    - Texte invisible pendant chargement (FOIT)
   swap     - Fallback puis swap (FOUT) [OK] Recommandé
   fallback - Court délai puis fallback si pas chargé
   optional - Utilise seulement si déjà en cache
*/
```


PRELOAD FONTS

```html
<link rel="preload" 
      href="/fonts/MyFont.woff2" 
      as="font" 
      type="font/woff2" 
      crossorigin>
```


SUBSET FONTS

```css
/* Charger seulement caractères utilisés */
@font-face {
  font-family: 'MyFont';
  src: url('font-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF; /* Latin basique */
}
```


SYSTEM FONTS

```css
/* Utiliser fonts système (0 KB download) */
body {
  font-family: 
    -apple-system,           /* macOS/iOS */
    BlinkMacSystemFont,      /* macOS Chrome */
    "Segoe UI",              /* Windows */
    Roboto,                  /* Android */
    "Helvetica Neue",        /* macOS legacy */
    Arial,                   /* Fallback */
    sans-serif;
}
```
"""


# ============================================================================
# [DOCS] RÉCAPITULATIF CHAPITRE 14
# ============================================================================

"""
CE QUE VOUS AVEZ APPRIS

[OK] Core Web Vitals (LCP, FID, CLS)
[OK] Optimisation images (formats, lazy loading, responsive)
[OK] Code splitting et dynamic imports
[OK] Caching strategies (HTTP, Service Worker)
[OK] Resource hints (preload, prefetch, preconnect)
[OK] CSS optimization (critical, minify, containment)
[OK] JavaScript optimization (minify, tree shaking, defer)
[OK] Font optimization (font-display, preload, subset)


[CLE] POINTS CLÉS

CORE WEB VITALS :
- LCP < 2.5s (optimiser images, précharger)
- FID < 100ms (réduire JS, code splitting)
- CLS < 0.1 (dimensions fixes, pas d'insertion dynamique)

IMAGES :
- WebP/AVIF formats
- srcset pour responsive
- loading="lazy" native
- Dimensions appropriées

JAVASCRIPT :
- defer/async scripts
- Code splitting par route
- Tree shaking unused code
- Minify en production

FONTS :
- font-display: swap
- Preload critical fonts
- Subset pour réduire taille


[OBJECTIF] CHECKLIST PERFORMANCE

[OK] Minify HTML, CSS, JS
[OK] Compress (gzip/brotli)
[OK] Optimize images
[OK] Lazy load below fold
[OK] Defer non-critical JS
[OK] Inline critical CSS
[OK] Preload key resources
[OK] Use CDN
[OK] Enable HTTP/2
[OK] Cache agressively
[OK] Monitor Core Web Vitals


-> PROCHAINE ÉTAPE : Chapitre 15 - Accessibilité (A11y) !

Vous allez découvrir :
- ARIA attributes
- Keyboard navigation
- Screen readers
- Semantic HTML
- Color contrast
- Focus management
- Accessibility testing

Prêt à rendre vos sites accessibles à tous ? [WHEELCHAIR_SYMBOL]*
"""


# ============================================================================
# [GUIDE] CHAPITRE 15 : ACCESSIBILITÉ (A11y)
# ============================================================================

"""
[OBJECTIF] OBJECTIFS D'APPRENTISSAGE

À la fin de ce chapitre, vous saurez :
[OK] Principes WCAG (Web Content Accessibility Guidelines)
[OK] Semantic HTML pour accessibilité
[OK] ARIA roles, states, properties
[OK] Keyboard navigation complète
[OK] Screen reader compatibility
[OK] Color contrast et visual accessibility
[OK] Focus management
[OK] Accessibility testing tools
[OK] Forms accessibles
[OK] Dynamic content accessibility
"""


# ----------------------------------------------------------------------------
# [OBJECTIF] PRINCIPES WCAG
# ----------------------------------------------------------------------------

"""
4 PRINCIPES FONDAMENTAUX (POUR)

1. **Perceptible** - L'information doit être présentable
2. **Opérable** - L'interface doit être utilisable
3. **Compréhensible** - L'information doit être claire
4. **Robuste** - Compatible technologies assistives


NIVEAUX CONFORMITÉ

A - Basique (minimum)
AA - Standard (recommandé) [OK]
AAA - Avancé (idéal)


GUIDELINES ESSENTIELS

[OK] Alternatives textuelles (images, vidéos)
[OK] Navigation clavier complète
[OK] Contraste couleurs suffisant
[OK] Texte redimensionnable
[OK] Éviter clignotements
[OK] Labels formulaires
[OK] Ordre focus logique
[OK] Messages erreur clairs
"""


# ----------------------------------------------------------------------------
# [CONSTRUCTION] SEMANTIC HTML
# ----------------------------------------------------------------------------

"""
UTILISER ÉLÉMENTS APPROPRIÉS

```html
<!-- [X] Mauvais - div pour tout -->
<div class="header">
  <div class="nav">
    <div class="link">Home</div>
    <div class="link">About</div>
  </div>
</div>
<div class="content">
  <div class="article-title">Title</div>
  <div class="article-content">Content</div>
</div>

<!-- [OK] Bon - éléments sémantiques -->
<header>
  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
  </nav>
</header>
<main>
  <article>
    <h1>Title</h1>
    <p>Content</p>
  </article>
</main>
```


STRUCTURE PAGE

```html
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <title>Page Title</title>
</head>
<body>
  <!-- Skip link pour clavier -->
  <a href="#main" class="skip-link">Skip to main content</a>
  
  <header>
    <nav aria-label="Main navigation">
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="/about">About</a></li>
      </ul>
    </nav>
  </header>
  
  <main id="main">
    <h1>Page Heading</h1>
    
    <article>
      <h2>Article Title</h2>
      <p>Content...</p>
    </article>
    
    <aside aria-label="Related links">
      <h2>Related</h2>
      <ul>...</ul>
    </aside>
  </main>
  
  <footer>
    <p>&copy; 2026</p>
  </footer>
</body>
</html>
```


HEADINGS HIÉRARCHIE

```html
<!-- [OK] Bon - hiérarchie logique -->
<h1>Main Title</h1>
  <h2>Section 1</h2>
    <h3>Subsection 1.1</h3>
    <h3>Subsection 1.2</h3>
  <h2>Section 2</h2>
    <h3>Subsection 2.1</h3>

<!-- [X] Mauvais - saute niveaux -->
<h1>Main Title</h1>
  <h3>Section</h3> <!-- Skip h2 -->
  <h5>Subsection</h5> <!-- Skip h4 -->
```


LIENS DESCRIPTIFS

```html
<!-- [X] Mauvais -->
<a href="/article">Click here</a>
<a href="/download">Read more</a>

<!-- [OK] Bon -->
<a href="/article">Read the full article about accessibility</a>
<a href="/download">Download the accessibility checklist (PDF, 2MB)</a>
```


IMAGES

```html
<!-- Image décorative -->
<img src="decoration.jpg" alt="" role="presentation">

<!-- Image informative -->
<img src="chart.jpg" alt="Bar chart showing 50% increase in sales">

<!-- Image complexe -->
<img src="infographic.jpg" alt="Infographic about web accessibility">
<a href="infographic-text.html">View text alternative</a>

<!-- Logo lien -->
<a href="/">
  <img src="logo.png" alt="Company Name - Home">
</a>
```
"""


# ----------------------------------------------------------------------------
# [WHEELCHAIR_SYMBOL] ARIA ATTRIBUTES
# ----------------------------------------------------------------------------

"""
ARIA = Accessible Rich Internet Applications

[IDEE] Utiliser SEULEMENT si semantic HTML insuffisant !


ARIA ROLES

```html
<!-- Navigation -->
<nav role="navigation">...</nav>
<div role="navigation">...</div> <!-- Si pas <nav> -->

<!-- Landmarks -->
<div role="banner">Header</div>       <!-- <header> -->
<div role="main">Main content</div>    <!-- <main> -->
<div role="contentinfo">Footer</div>   <!-- <footer> -->
<div role="complementary">Aside</div>  <!-- <aside> -->
<div role="search">Search form</div>

<!-- Widgets -->
<div role="button">Click me</div>
<div role="tab">Tab 1</div>
<div role="tabpanel">Panel content</div>
<div role="dialog">Modal dialog</div>
<div role="alert">Error message</div>
```


ARIA STATES

```html
<!-- Expanded/Collapsed -->
<button aria-expanded="false" aria-controls="menu">
  Menu
</button>
<div id="menu" hidden>...</div>

<script>
button.addEventListener('click', () => {
  const expanded = button.getAttribute('aria-expanded') === 'true';
  button.setAttribute('aria-expanded', !expanded);
  menu.hidden = expanded;
});
</script>

<!-- Pressed state (toggle button) -->
<button aria-pressed="false">Bold</button>

<!-- Selected (tabs, options) -->
<div role="tab" aria-selected="true">Tab 1</div>
<div role="tab" aria-selected="false">Tab 2</div>

<!-- Disabled -->
<button aria-disabled="true">Submit</button>

<!-- Hidden (from screen readers) -->
<div aria-hidden="true">Decorative</div>
```


ARIA PROPERTIES

```html
<!-- Label -->
<button aria-label="Close dialog">×</button>

<div id="dialog-title">Confirm</div>
<div role="dialog" aria-labelledby="dialog-title">...</div>

<!-- Description -->
<button aria-describedby="help-text">Submit</button>
<div id="help-text">This will save your changes</div>

<!-- Controls -->
<button aria-controls="panel-1">Toggle Panel</button>
<div id="panel-1">Panel content</div>

<!-- Live regions -->
<div aria-live="polite">Status updates here</div>
<div aria-live="assertive">Critical alerts here</div>

<!-- Required -->
<input type="text" aria-required="true">

<!-- Invalid -->
<input type="email" aria-invalid="true" aria-describedby="email-error">
<div id="email-error">Please enter a valid email</div>
```


EXEMPLE COMPLET : ACCORDION

```html
<div class="accordion">
  <h2>
    <button
      id="accordion-header-1"
      aria-expanded="false"
      aria-controls="accordion-panel-1"
    >
      Section 1
    </button>
  </h2>
  <div
    id="accordion-panel-1"
    role="region"
    aria-labelledby="accordion-header-1"
    hidden
  >
    <p>Content 1</p>
  </div>
  
  <!-- Repeat for other sections -->
</div>

<script>
const buttons = document.querySelectorAll('[aria-controls]');

buttons.forEach(button => {
  button.addEventListener('click', () => {
    const expanded = button.getAttribute('aria-expanded') === 'true';
    const panelId = button.getAttribute('aria-controls');
    const panel = document.getElementById(panelId);
    
    button.setAttribute('aria-expanded', !expanded);
    panel.hidden = expanded;
  });
});
</script>
```


ARIA LIVE REGIONS

```html
<!-- Polite : annonce quand utilisateur inactif -->
<div aria-live="polite" aria-atomic="true">
  <span id="status">Ready</span>
</div>

<!-- Assertive : annonce immédiatement -->
<div aria-live="assertive" role="alert">
  <span id="error">Form contains errors</span>
</div>

<script>
// Mettre à jour status
function updateStatus(message) {
  document.getElementById('status').textContent = message;
  // Screen reader annonce automatiquement
}

updateStatus('Saving...'); // Annoncé
updateStatus('Saved!');    // Annoncé
</script>
```
"""


# ----------------------------------------------------------------------------
# [KEYBOARD] KEYBOARD NAVIGATION
# ----------------------------------------------------------------------------

"""
ELEMENTS FOCUSABLES

Par défaut focusable :
- <a href="...">
- <button>
- <input>, <select>, <textarea>
- <summary>

Rendre focusable :

```html
<!-- tabindex="0" : dans ordre tab naturel -->
<div tabindex="0" role="button">Custom button</div>

<!-- tabindex="-1" : focusable par JS seulement -->
<div tabindex="-1" id="focus-target">Target</div>

<!-- [X] tabindex > 0 : Éviter ! Casse ordre naturel -->
<div tabindex="1">Don't do this</div>
```


FOCUS VISIBLE

```css
/* Style focus clair */
:focus {
  outline: 2px solid blue;
  outline-offset: 2px;
}

/* Ou custom */
button:focus {
  box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.5);
  outline: none; /* Seulement si style custom ! */
}

/* :focus-visible (moderne) : seulement clavier */
button:focus-visible {
  outline: 2px solid blue;
}

button:focus:not(:focus-visible) {
  outline: none; /* Pas de outline au click souris */
}
```


SKIP LINKS

```html
<a href="#main" class="skip-link">Skip to main content</a>

<style>
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: #000;
  color: #fff;
  padding: 8px;
  text-decoration: none;
  z-index: 100;
}

.skip-link:focus {
  top: 0;
}
</style>
```


KEYBOARD SHORTCUTS

```javascript
document.addEventListener('keydown', (e) => {
  // Home : focus premier élément
  if (e.key === 'Home') {
    e.preventDefault();
    document.querySelector('a, button').focus();
  }
  
  // End : focus dernier
  if (e.key === 'End') {
    e.preventDefault();
    const focusable = document.querySelectorAll('a, button');
    focusable[focusable.length - 1].focus();
  }
  
  // Escape : fermer modal
  if (e.key === 'Escape') {
    closeModal();
  }
  
  // Ctrl+K : focus search
  if (e.ctrlKey && e.key === 'k') {
    e.preventDefault();
    document.getElementById('search').focus();
  }
});
```


ROVING TABINDEX

```html
<!-- Groupe éléments : 1 seul dans tab order -->
<div role="toolbar">
  <button tabindex="0">Bold</button>    <!-- Premier : 0 -->
  <button tabindex="-1">Italic</button>  <!-- Autres : -1 -->
  <button tabindex="-1">Underline</button>
</div>

<script>
const buttons = document.querySelectorAll('[role="toolbar"] button');
let currentIndex = 0;

buttons.forEach((button, index) => {
  button.addEventListener('keydown', (e) => {
    if (e.key === 'ArrowRight') {
      e.preventDefault();
      currentIndex = (index + 1) % buttons.length;
      updateTabindex();
      buttons[currentIndex].focus();
    }
    
    if (e.key === 'ArrowLeft') {
      e.preventDefault();
      currentIndex = (index - 1 + buttons.length) % buttons.length;
      updateTabindex();
      buttons[currentIndex].focus();
    }
  });
});

function updateTabindex() {
  buttons.forEach((btn, i) => {
    btn.tabIndex = i === currentIndex ? 0 : -1;
  });
}
</script>
```


FOCUS TRAP (Modal)

```javascript
function trapFocus(element) {
  const focusableElements = element.querySelectorAll(
    'a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])'
  );
  
  const firstFocusable = focusableElements[0];
  const lastFocusable = focusableElements[focusableElements.length - 1];
  
  element.addEventListener('keydown', (e) => {
    if (e.key !== 'Tab') return;
    
    if (e.shiftKey) {
      // Shift + Tab
      if (document.activeElement === firstFocusable) {
        e.preventDefault();
        lastFocusable.focus();
      }
    } else {
      // Tab
      if (document.activeElement === lastFocusable) {
        e.preventDefault();
        firstFocusable.focus();
      }
    }
  });
  
  firstFocusable.focus();
}

// Usage
const modal = document.getElementById('modal');
trapFocus(modal);
```
"""


# ============================================================================
# [COURS] EXERCICE FINAL : SITE WEB COMPLET OPTIMISÉ
# ============================================================================

"""
CRÉER SITE WEB PROFESSIONNEL

Objectif : Appliquer TOUS les concepts appris


STRUCTURE PROJET

my-optimized-site/
├── index.html
├── css/
│   ├── critical.css    (inline dans <head>)
│   └── styles.css      (chargé async)
├── js/
│   ├── app.js          (defer)
│   └── utils.js
├── images/
│   ├── hero.webp
│   └── logo.svg
├── fonts/
│   └── custom.woff2
└── sw.js               (Service Worker)


INDEX.HTML (Optimisé)

```html
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="description" content="Site web optimisé démontrant toutes les best practices">
  
  <title>Site Web Optimisé - Performance & Accessibilité</title>
  
  <!-- Preconnect -->
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://cdn.example.com">
  
  <!-- Preload critical resources -->
  <link rel="preload" href="/fonts/custom.woff2" as="font" type="font/woff2" crossorigin>
  <link rel="preload" href="/images/hero.webp" as="image">
  
  <!-- Critical CSS inline -->
  <style>
    /* Critical above-fold styles */
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
    .header { background: #333; color: white; padding: 1rem; }
    .hero { min-height: 60vh; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
    .skip-link { position: absolute; top: -40px; left: 0; background: #000; color: #fff; padding: 8px; }
    .skip-link:focus { top: 0; }
  </style>
  
  <!-- Async non-critical CSS -->
  <link rel="preload" href="/css/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="/css/styles.css"></noscript>
</head>
<body>
  <!-- Skip link -->
  <a href="#main" class="skip-link">Skip to main content</a>
  
  <!-- Header -->
  <header class="header" role="banner">
    <nav aria-label="Main navigation">
      <ul role="list">
        <li><a href="/">Home</a></li>
        <li><a href="/about">About</a></li>
        <li><a href="/contact">Contact</a></li>
      </ul>
    </nav>
  </header>
  
  <!-- Main -->
  <main id="main" role="main">
    <!-- Hero -->
    <section class="hero" aria-labelledby="hero-title">
      <h1 id="hero-title">Welcome to Our Optimized Site</h1>
      <p>Performance, Accessibility, and Best Practices</p>
      
      <!-- Optimized image -->
      <picture>
        <source type="image/webp" srcset="/images/hero.webp">
        <img 
          src="/images/hero.jpg" 
          alt="Hero image showing modern web development"
          width="1200"
          height="600"
          loading="eager"
        >
      </picture>
    </section>
    
    <!-- Content -->
    <article>
      <h2>Our Features</h2>
      
      <section aria-labelledby="performance-title">
        <h3 id="performance-title">Performance</h3>
        <p>Lightning fast load times with Core Web Vitals optimization</p>
      </section>
      
      <section aria-labelledby="accessibility-title">
        <h3 id="accessibility-title">Accessibility</h3>
        <p>WCAG 2.1 AA compliant, keyboard navigable, screen reader friendly</p>
      </section>
      
      <!-- Lazy loaded images -->
      <img 
        src="placeholder.jpg"
        data-src="/images/feature-1.webp"
        alt="Feature showcase"
        loading="lazy"
        width="800"
        height="400"
      >
    </article>
    
    <!-- Accessible form -->
    <section aria-labelledby="contact-title">
      <h2 id="contact-title">Contact Us</h2>
      <form action="/submit" method="POST" novalidate>
        <div>
          <label for="name">Name <span aria-label="required">*</span></label>
          <input 
            type="text" 
            id="name" 
            name="name" 
            required 
            aria-required="true"
            aria-describedby="name-hint"
          >
          <div id="name-hint" class="hint">Please enter your full name</div>
        </div>
        
        <div>
          <label for="email">Email <span aria-label="required">*</span></label>
          <input 
            type="email" 
            id="email" 
            name="email" 
            required 
            aria-required="true"
          >
          <div id="email-error" class="error" role="alert" hidden>
            Please enter a valid email address
          </div>
        </div>
        
        <div>
          <label for="message">Message</label>
          <textarea id="message" name="message" rows="5"></textarea>
        </div>
        
        <button type="submit">Send Message</button>
      </form>
    </section>
  </main>
  
  <!-- Footer -->
  <footer role="contentinfo">
    <p>&copy; 2026 Optimized Site. All rights reserved.</p>
  </footer>
  
  <!-- Scripts deferred -->
  <script src="/js/app.js" defer></script>
  
  <!-- Service Worker registration -->
  <script>
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/sw.js')
        .then(() => console.log('SW registered'))
        .catch(err => console.error('SW failed', err));
    }
  </script>
</body>
</html>
```


APP.JS (JavaScript optimisé)

```javascript
// Lazy load images
const lazyImages = document.querySelectorAll('img[data-src]');

const imageObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.removeAttribute('data-src');
      observer.unobserve(img);
    }
  });
}, {
  rootMargin: '50px'
});

lazyImages.forEach(img => imageObserver.observe(img));


// Form validation
const form = document.querySelector('form');

form.addEventListener('submit', (e) => {
  e.preventDefault();
  
  const email = document.getElementById('email');
  const error = document.getElementById('email-error');
  
  if (!email.validity.valid) {
    email.setAttribute('aria-invalid', 'true');
    error.hidden = false;
    email.focus();
    return;
  }
  
  email.setAttribute('aria-invalid', 'false');
  error.hidden = true;
  
  // Submit form
  submitForm(new FormData(form));
});


// Measure performance
window.addEventListener('load', () => {
  if (window.performance) {
    const perfData = window.performance.timing;
    const pageLoadTime = perfData.loadEventEnd - perfData.navigationStart;
    
    console.log('Page load time:', pageLoadTime + 'ms');
    
    // Send to analytics
    if (navigator.sendBeacon) {
      navigator.sendBeacon('/analytics', JSON.stringify({
        loadTime: pageLoadTime,
        url: window.location.href
      }));
    }
  }
});


// Core Web Vitals
new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    console.log('LCP:', entry.renderTime || entry.loadTime);
  });
}).observe({ entryTypes: ['largest-contentful-paint'] });

new PerformanceObserver((list) => {
  const firstInput = list.getEntries()[0];
  console.log('FID:', firstInput.processingStart - firstInput.startTime);
}).observe({ entryTypes: ['first-input'] });

let cls = 0;
new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    if (!entry.hadRecentInput) {
      cls += entry.value;
      console.log('CLS:', cls);
    }
  });
}).observe({ entryTypes: ['layout-shift'] });
```


SW.JS (Service Worker)

```javascript
const CACHE_NAME = 'v1';
const CACHE_ASSETS = [
  '/',
  '/css/styles.css',
  '/js/app.js',
  '/images/logo.svg'
];

// Install
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then((cache) => cache.addAll(CACHE_ASSETS))
      .then(() => self.skipWaiting())
  );
});

// Activate
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((cacheNames) => {
      return Promise.all(
        cacheNames
          .filter((name) => name !== CACHE_NAME)
          .map((name) => caches.delete(name))
      );
    })
  );
});

// Fetch - Stale-while-revalidate
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then((cached) => {
        const fetchPromise = fetch(event.request)
          .then((response) => {
            return caches.open(CACHE_NAME).then((cache) => {
              cache.put(event.request, response.clone());
              return response;
            });
          });
        
        return cached || fetchPromise;
      })
  );
});
```


RÉSULTAT :

[OK] Performance :
   - LCP : < 1.5s
   - FID : < 50ms
   - CLS : < 0.05
   - Lighthouse : 95+

[OK] Accessibilité :
   - WCAG 2.1 AA compliant
   - Keyboard navigable
   - Screen reader friendly
   - Semantic HTML
   - ARIA où nécessaire

[OK] SEO :
   - Meta tags
   - Semantic structure
   - Fast load time
   - Mobile responsive

[OK] Best Practices :
   - HTTPS
   - No console errors
   - Modern APIs
   - Service Worker
   - Optimized assets
"""


# ============================================================================
# [BRAVO] CONCLUSION FINALE
# ============================================================================

"""
═══════════════════════════════════════════════════════════════════════════
FÉLICITATIONS ! [COURS]
═══════════════════════════════════════════════════════════════════════════

Vous avez complété le guide ULTRA-DÉTAILLÉ des navigateurs web !


[DOCS] CE QUE VOUS MAÎTRISEZ MAINTENANT

PARTIE 1 - FONDAMENTAUX :
[OK] Architecture navigateur (processus, threads, event loop)
[OK] Rendu HTML/CSS (parsing, layout, paint, composite)
[OK] DOM et manipulation JavaScript
[OK] Réseau et HTTP (fetch, CORS, cookies)

PARTIE 2 - DEVTOOLS :
[OK] Console et Debugger (breakpoints, profiling)
[OK] Elements et Network tabs
[OK] Performance profiling
[OK] Storage inspection

PARTIE 3 - FONCTIONNALITÉS AVANCÉES :
[OK] APIs Web modernes (Geolocation, Notifications, etc.)
[OK] Service Workers et PWA
[OK] WebAssembly
[OK] Sécurité navigateur
[OK] Extensions

PARTIE 4 - OPTIMISATION ET PRODUCTION :
[OK] Performance optimization (Core Web Vitals)
[OK] Accessibilité (WCAG, ARIA)
[OK] SEO et crawlers
[OK] Cross-browser testing
[OK] Monitoring et analytics


[OBJECTIF] VOTRE NIVEAU MAINTENANT

AVANT ce guide :
[X] Débutant complet
[X] Utilisation basique navigateur
[X] console.log() debugging

APRÈS ce guide :
[OK] Expert navigateur web
[OK] Performance optimization pro
[OK] Accessibility champion
[OK] DevTools master
[OK] Production-ready developer


[RAPIDE] PROCHAINES ÉTAPES

1. **Pratiquer** - Appliquer sur projets réels
2. **Approfondir** - Spécialiser dans domaines spécifiques
3. **Partager** - Enseigner aux autres
4. **Veille** - Suivre évolutions navigateurs


[GUIDE] RESSOURCES ADDITIONNELLES

Sites officiels :
- MDN Web Docs (https://developer.mozilla.org)
- Web.dev (https://web.dev)
- Can I Use (https://caniuse.com)
- WCAG Guidelines (https://www.w3.org/WAI/WCAG21/quickref/)

Outils :
- Chrome DevTools
- Lighthouse
- WebPageTest
- BrowserStack

Communautés :
- Stack Overflow
- Dev.to
- Twitter #WebPerf #A11y


[IDEE] CONSEIL FINAL

La maîtrise vient avec la PRATIQUE !

Ce guide est votre référence.
Revenez-y souvent.
Expérimentez dans DevTools.
Construisez des projets.
Mesurez, optimisez, répétez.


═══════════════════════════════════════════════════════════════════════════
MERCI D'AVOIR LU CE GUIDE !
═══════════════════════════════════════════════════════════════════════════

Vous êtes maintenant équipé pour :
[OBJECTIF] Construire des sites web RAPIDES
[OBJECTIF] Créer des expériences ACCESSIBLES
[OBJECTIF] Debugger comme un PRO
[OBJECTIF] Optimiser pour la PRODUCTION

Bonne chance dans votre parcours de développeur web ! [RAPIDE]

═══════════════════════════════════════════════════════════════════════════
"""