[
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf\n*.svg       binary\n"
  },
  {
    "path": ".gitignore",
    "content": "*.diff\n*.err\n*.orig\n*.log\n*.rej\n*.swo\n*.swp\n*.vi\n*~\n*.sass-cache\n\n# OS or Editor folders\n.DS_Store\n.idea\n.cache\n.project\n.settings\n.tmproj\n.nvmrc\nsftp-config.json\nThumbs.db\ntags\n\n/svgs"
  },
  {
    "path": "1-js/01-getting-started/1-intro/article.md",
    "content": "# Une Introduction à JavaScript\n\nVoyons ce qui est spécial à propos de JavaScript, ce qu'il nous permet de faire et avec quelles autres technologies il s'accorde bien.\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/-eEx3yRZ-sE\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n\n## Qu'est-ce que JavaScript ?\n\n_JavaScript_ a été initialement créé pour \"rendre les pages web vivantes\".\n\nLes programmes dans ce langage sont appelés _scripts_. Ils peuvent être écrits directement dans une page HTML et exécutés automatiquement au chargement des pages.\n\nLes scripts sont fournis et exécutés en texte brut. Ils n'ont pas besoin d'une préparation spéciale ou d'une compilation pour fonctionner.\n\nDe par cet aspect, JavaScript est très différent d'un autre langage appelé [Java](<https://fr.wikipedia.org/wiki/Java_(langage)>).\n\n```smart header=\"Pourquoi est-il appelé <u>Java</u>Script ?\"\nQuand JavaScript a été créé, il portait initialement un autre nom : \"LiveScript\". Mais à cette époque le langage Java était très populaire, il a donc été décidé que positionner un nouveau langage en tant que \"petit frère\" de Java pourrait aider.\n\nMais au fur et à mesure de son évolution, JavaScript est devenu un langage totalement indépendant, avec ses propres spécifications appelées [ECMAScript](https://fr.wikipedia.org/wiki/ECMAScript), aujourd'hui il n'a aucun rapport avec Java.\n```\n\nAujourd'hui, JavaScript peut s'exécuter non seulement dans le navigateur, mais également sur un serveur, ou encore sur n'importe quel appareil dans lequel existe un programme appelé [le moteur JavaScript](https://fr.wikipedia.org/wiki/Moteur_JavaScript).\n\nLe navigateur a un moteur intégré, parfois il peut être également appelé \"la machine virtuelle JavaScript\".\n\nDifférents moteurs ont différents \"nom de code\", par exemple :\n\n- [V8](<https://fr.wikipedia.org/wiki/V8_(moteur_JavaScript)>) -- dans Chrome et Opera.\n- [SpiderMonkey](https://fr.wikipedia.org/wiki/SpiderMonkey) -- dans Firefox.\n- … Il existe d'autres noms de code comme \"Chakra\" pour IE, \"JavaScriptCore\", \"Nitro\" et \"SquirrelFish\" pour Safari etc.\n\nLes termes ci-dessus sont bons à retenir, car ils sont utilisés dans les articles destinés aux développeurs sur Internet. Nous les utiliserons aussi. Par exemple, si \"une fonctionnalité X est prise en charge par V8\", cela fonctionne probablement dans Chrome, Edge et Opera.\n\n```smart header=\"Comment fonctionnent les moteurs ?\"\n\nLes moteurs sont compliqués. Mais le fonctionnement de base est facile à comprendre.\n\n1. Le moteur (intégré si c’est un navigateur) lit (\"analyse\") le script.\n2. Ensuite, il convertit (\"compile\") le script en langage machine.\n3. Enfin le code machine s'exécute, très rapidement.\n\nLe moteur applique des optimisations à chaque étape du processus. Il surveille même le script compilé en cours d'exécution, analyse les données qui le traversent et applique des optimisations au code machine en fonction de ces informations.\n```\n\n## Que peut faire JavaScript dans le navigateur ?\n\nLe JavaScript moderne est un langage de programmation \"sûr\". Il ne fournit pas d'accès de bas niveau à la mémoire ou au processeur, parce qu'il a été initialement conçu pour les navigateurs qui n'en ont pas besoin.\n\nLes fonctionnalités dépendent grandement de l'environnement qui exécute JavaScript. Par exemple, [Node.js](https://fr.wikipedia.org/wiki/Node.js) prend en charge les fonctions qui permettent à JavaScript de lire / écrire des fichiers arbitrairement, d'exécuter des requêtes réseau, etc.\n\nJavaScript intégré au navigateur peut faire tout ce qui concerne la manipulation des pages Web, l'interaction avec l'utilisateur et le serveur Web.\n\nPar exemple, JavaScript dans le navigateur est capable de :\n\n- Ajouter un nouveau code HTML à la page, modifier le contenu existant, modifier les styles.\n- Réagir aux actions de l'utilisateur, s'exécuter sur des clics de souris, des mouvements de pointeur, des appuis sur des touches.\n- Envoyer des requêtes sur le réseau à des serveurs distants, télécharger et envoyer des fichiers (technologies [AJAX](<https://fr.wikipedia.org/wiki/Ajax_(informatique)>) et [COMET](<https://fr.wikipedia.org/wiki/Comet_(informatique)>)).\n- Obtenir et définir des cookies, poser des questions au visiteur, afficher des messages.\n- Se souvenir des données du côté client (\"stockage local\").\n\n## Qu'est-ce que JavaScript ne peut pas faire dans le navigateur ?\n\nLes capacités de JavaScript dans le navigateur sont limitées pour la sécurité de l'utilisateur. L'objectif est d'empêcher une page Web malveillante d'accéder à des informations privées ou de nuire aux données de l'utilisateur.\n\nLes exemples de telles restrictions sont :\n\n- JavaScript sur une page Web ne peut pas lire/écrire des fichiers arbitrairement sur le disque dur, les copier ou exécuter des programmes. Il n'a pas d'accès direct aux fonctions du système d'exploitation.\n\n  Les navigateurs modernes lui permettent de fonctionner avec des fichiers, mais l'accès est limité et n'est fourni que si l'utilisateur effectue certaines actions, comme «déposer» un fichier dans une fenêtre de navigateur ou le sélectionner via une balise `<input>`.\n\n  Il existe des moyens d’interagir avec une webcam/microphone et d’autres périphériques, mais ils nécessitent une autorisation explicite de l’utilisateur. Ainsi, une page contenant du JavaScript ne permet pas d'activer une caméra Web, d'observer l'environnement et d'envoyer les informations à la [NSA](https://fr.wikipedia.org/wiki/National_Security_Agency).\n\n- Différents onglets / fenêtres ne se connaissent généralement pas. Parfois, ils se croisent, par exemple lorsqu'une fenêtre utilise JavaScript pour ouvrir l'autre. Mais même dans ce cas, le JavaScript d'une page ne peut pas accéder à l'autre si elle provient de sites différents (provenant d'un autre domaine, protocole ou port).\n\n  C'est ce qu'on appelle la \"politique de même origine\" (“Same Origin Policy”). Pour contourner cette sécurité, _les deux pages_ doivent se mettre d'accord et contenir un code JavaScript spécial qui gère l'échange de données. Nous verrons cela plus loin dans ce tutoriel.\n\n  Cette limitation concerne également la sécurité de l'utilisateur. Une page de `http://autresite.com` qu'un utilisateur a ouvert ne doit pas pouvoir accéder à un autre onglet du navigateur avec l'URL `http://gmail.com` et y voler des informations.\n\n- JavaScript peut facilement communiquer sur le net avec le serveur d'où provient la page. Mais sa capacité à recevoir des données d'autres sites / domaines est paralysée. Bien que possible, il nécessite un accord explicite (exprimé dans les en-têtes HTTP) du côté distant. Encore une fois, ce sont des limites de sécurité.\n\n![Schéma des limitations du JavaScript dans un navigateur](limitations.svg)\n\nDe telles limites n'existent pas si JavaScript est utilisé en dehors du navigateur, par exemple sur un serveur. Les navigateurs modernes permettent également l’installation de plug-ins / extensions susceptibles d’obtenir des autorisations étendues.\n\n## Qu'est-ce qui rend JavaScript unique ?\n\nIl y a au moins trois bonnes choses à propos de JavaScript :\n\n```compare\n+ Intégration complète avec HTML / CSS.\n+ Les choses simples sont faites simplement.\n+ Pris en charge par tous les principaux navigateurs et activé par défaut.\n```\n\nJavaScript est la seule technologie de navigateur qui combine ces trois éléments.\n\nC’est ce qui rend JavaScript unique. C’est pourquoi il l’outil le plus répandu pour créer des interfaces de navigateur.\n\nCela dit, JavaScript permet également de créer des serveurs, des applications mobiles, etc.\n\n## Les langages \"par dessus\" JavaScript\n\nLa syntaxe de JavaScript ne convient pas aux besoins de tous. Différentes personnes veulent des fonctionnalités différentes.\n\nIl faut s’y attendre, parce que besoins sont différents pour tous.\n\nDonc, récemment, une pléthore de nouveaux langages sont apparus, qui sont _transpilés_ (convertis) en JavaScript avant leur exécution dans le navigateur.\n\nLes outils modernes rendent la [transpilation](https://fr.wiktionary.org/wiki/transpilation) très rapide et transparente, permettant aux développeurs de coder dans un autre langage et de le convertir automatiquement \"sous le capot\".\n\nLes exemples de ce genre de langages :\n\n- [CoffeeScript](http://coffeescript.org/) est un \"sucre syntaxique\" pour JavaScript, il introduit une syntaxe plus courte, permettant d’écrire du code plus précis et plus clair. Habituellement, les développeurs Ruby l'aiment bien.\n- [TypeScript](http://www.typescriptlang.org/) se concentre sur l'ajout de \"typage strict des données\" pour simplifier le développement et la prise en charge de systèmes complexes. Il est développé par Microsoft.\n- [Flow](http://flow.org/) ajoute également la saisie de données, mais de manière différente. Développé par Facebook.\n- [Dart](https://www.dartlang.org/) est un langage autonome doté de son propre moteur qui s'exécute dans des environnements autres que les navigateurs (comme les applications mobiles), mais peut aussi être transpilé en JavaScript. Développé par Google.\n- [Brython](https://brython.info/) est un transpiler (ou transpileur en bon français) Python vers JavaScript qui permet d'écrire des applications en Python pur sans JavaScript.\n- [Kotlin](https://kotlinlang.org/docs/reference/js-overview.html) est un langage de programmation moderne, concis et sûr qui peut cibler le navigateur ou Node.js\n\nIl en existe évidemment bien plus, cela dit, même si nous utilisons un de ces langages transpilés, nous devrions également connaître le langage JavaScript, pour bien comprendre ce que nous faisons.\n\n## Résumé\n\n- JavaScript a été initialement créé en tant que langage de navigateur uniquement, mais il est désormais également utilisé dans de nombreux autres environnements.\n- En ce moment, JavaScript occupe une position unique en tant que langage de navigateur le plus largement adopté avec une intégration complète avec HTML/CSS.\n- De nombreux langages sont _transpilés_ en JavaScript et fournissent certaines fonctionnalités. Il est recommandé d'y jeter un coup d'œil, au moins brièvement, après avoir maîtrisé JavaScript.\n"
  },
  {
    "path": "1-js/01-getting-started/2-manuals-specifications/article.md",
    "content": "\n# Manuels et spécifications\n\nCe livre est un *tutoriel*. Il vise à vous aider à apprendre progressivement le langage. Mais une fois que vous maîtriserez les bases, vous aurez besoin d’autres ressources.\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/TqQs_MKMqwU\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n\n## Spécification\n\n[The ECMA-262 specification](https://www.ecma-international.org/publications/standards/Ecma-262.htm) contient les informations les plus détaillées et formalisées sur JavaScript. C'est elle qui définit le langage.\n\nUne nouvelle version de la spécification est publiée chaque année. La dernière version en cours est disponible à cette adresse : <https://tc39.es/ecma262/>.\n\nPour en savoir plus sur les fonctionnalités à venir, y compris celles qui sont \"presque standards\" (appelées aussi \"stage 3\"), vous pouvez consulter les propositions à cette adresse : <https://github.com/tc39/proposals>.\n\nDe plus, si vous développez pour le navigateur, d'autres spécifications sont couvertes dans la [seconde partie](info:browser-environment) du tutoriel.\n\n## Manuels\n\n- **La référence MDN (Mozilla) JavaScript** est le principal manuel avec des exemples et d’autres informations. C’est une excellente source pour obtenir des informations détaillées sur les fonctions linguistiques, les méthodes, etc.\n\n    On peut la trouver à cette adresse : <https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference>.\n\n  Cependant, il est souvent préférable d’utiliser une recherche sur Internet. Utilisez simplement \"MDN [terme]\" dans la requête, par exemple <https://google.com/search?q=MDN+parseInt> pour rechercher la fonction `parseInt`.\n\n\n## Tableaux de compatibilité\n\nJavaScript est un langage en développement, de nouvelles fonctionnalités sont ajoutées régulièrement.\n\nPour voir si elles sont supportées dans les moteurs, au sein des navigateurs et autres, voir :\n\n- <http://caniuse.com> - tables de prise en charge par fonctionnalité, par exemple pour voir quels moteurs supportent les fonctions de cryptographie modernes : <http://caniuse.com/#feat=cryptography>.\n- <https://kangax.github.io/compat-table> - un tableau avec les fonctionnalités linguistiques et les moteurs qui les prennent en charge ou non.\n\nToutes ces ressources sont utiles dans le quotidien des développeurs, parce qu'elles contiennent des informations précieuses sur les fonctionnalités du langage, leur support, etc.\n\nVeuillez vous en souvenir (ou de cette page) pour les cas où vous avez besoin d'informations détaillées sur une fonctionnalité particulière.\n"
  },
  {
    "path": "1-js/01-getting-started/3-code-editors/article.md",
    "content": "# Les éditeurs de code \n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/KNs-GfVmt3A\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n\nUn éditeur de code est l'endroit où les programmeurs passent la plus grande partie de leur temps.\n\nIl existe deux archétypes: IDE et les éditeurs légers. Beaucoup de personnes se sentent à l'aise pour choisir un outil de chaque type.\n\n## IDE\n\nLe terme [IDE](https://fr.wikipedia.org/wiki/Environnement_de_développement) (Integrated Development Environment) signifie un éditeur puissant avec de nombreuses fonctionnalités qui fonctionne généralement sur un \"projet entier\". Comme son nom l’indique, ce n’est pas seulement un éditeur, mais un environnement de développement complet.\n\nUn IDE charge le projet (peut contenir de nombreux fichiers), permet la navigation entre les fichiers, fournit une auto-complétion basée sur l'ensemble du projet (pas seulement le fichier ouvert), s'intègre à un système de gestion de version (comme [git](https://git-scm.com/)), un environnement de test et d’autres éléments au niveau du projet.\n\nSi vous n'avez pas encore pensé à sélectionner un IDE, examinez les variantes suivantes :\n\n- [Visual Studio Code](https://code.visualstudio.com/) (cross-platform, free).\n- [WebStorm](https://www.jetbrains.com/webstorm/) (cross-platform, payant).\n\nPour Windows, il existe également \"Visual Studio\", à ne pas confondre avec \"Visual Studio Code \". \"Visual Studio\" est un IDE payant et puissant, disponible sur Windows et Mac, bien adapté à la plate-forme .NET. C’est aussi bon en JavaScript. Il y a aussi une version gratuite [Visual Studio Community](https://www.visualstudio.com/vs/community/).\n\nLa plupart des IDE sont payants, mais ont une période d'essai. Leur coût est généralement négligeable par rapport au salaire d’un développeur qualifié, alors choisissez le meilleur pour vous.\n\n## Les éditeurs légers\n\n\"Les éditeurs légers\" ne sont pas aussi puissants que les IDE, mais ils sont rapides, élégants et simples.\n\nIls sont principalement utilisés pour ouvrir et éditer instantanément un fichier.\n\nLa principale différence entre un \"éditeur léger\" et un \"IDE\" réside dans le fait qu’un environnement de développement intégré fonctionne au niveau du projet. Il charge donc beaucoup plus de données au démarrage, analyse la structure du projet si nécessaire, etc. Un éditeur léger est beaucoup plus rapide si nous avons besoin d'un seul fichier.\n\nEn pratique, les éditeurs légers peuvent avoir beaucoup de plug-ins, y compris des analyseurs de syntaxe au niveau des répertoires et des autocompléteurs. Il n’y a donc pas de frontière stricte entre un éditeur léger et un IDE.\n\nIl existe de nombreuses options, par exemple :\n\n- [Sublime Text](http://www.sublimetext.com) (multiplateforme, payant).\n- [Notepad++](https://notepad-plus-plus.org/) (Windows, gratuit).\n- [Vim](https://www.vim.org/) et [Emacs](https://www.gnu.org/software/emacs/) sont également cool, si vous savez comment les utiliser.\n\n\n## Ne discutons pas\n\nLes éditeurs des listes ci-dessus sont ceux que moi-même ou mes amis, que je considère comme de bons développeurs, utilisent depuis longtemps et en sont satisfaits.\n\nIl y a d'autres grands éditeurs dans notre vaste monde. Veuillez choisir celui que vous aimez le plus.\n\nLe choix d'un éditeur, comme tout autre outil, est individuel et dépend de vos projets, de vos habitudes, de vos préférences personnelles.\n\nL'avis personnel de l'auteur :\n\n- J'utilise [Visual Studio Code](https://code.visualstudio.com/) si je développe principalement du frontend.\n- Sinon, s'il s'agit principalement d'un autre langage/plate-forme et partiellement d'un frontend, envisagez d'autres éditeurs, tels que XCode (Mac), Visual Studio (Windows) ou la famille Jetbrains (Webstorm, PHPStorm, RubyMine, etc., selon le langage).\n"
  },
  {
    "path": "1-js/01-getting-started/4-devtools/article.md",
    "content": "# La console de développement\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/OFj8hFVS1KY\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n\nLe code est sujet aux erreurs. Vous êtes susceptible de faire des erreurs … Oh, de quoi je parle ? Vous allez absolument faire des erreurs, du moins si vous êtes un humain, pas un [robot](https://fr.wikipedia.org/wiki/Bender_Tordeur_Rodr%C3%ADguez).\n\nMais dans le navigateur, un utilisateur ne voit pas les erreurs par défaut. Ainsi, si quelque chose ne va pas dans le script, nous ne verrons pas ce qui ne va pas et nous ne pourrons pas le réparer.\n\nPour voir les erreurs et obtenir beaucoup d’informations utiles sur les scripts, les navigateurs intègrent des \"outils de développement\".\n\nLe plus souvent, les développeurs se tournent vers Chrome ou Firefox pour le développement, car ces navigateurs disposent des meilleurs outils de développement. D'autres navigateurs fournissent également des outils de développement, parfois dotés de fonctions spéciales, mais jouent généralement le rôle de \"rattrapage\" pour Chrome ou Firefox. Donc, la plupart des gens ont un navigateur \"favori\" et passent à d'autres si un problème est spécifique au navigateur.\n\nLes outils de développement sont très puissants ; ils possèdent de nombreuses fonctionnalités. Pour commencer, nous allons apprendre à les ouvrir, à examiner les erreurs et à exécuter des commandes JavaScript.\n\n## Google Chrome\n\nOuvrez la page [bug.html](bug.html).\n\nIl y a une erreur dans le code JavaScript. Elle est invisible à un visiteur habituel, alors ouvrons les outils de développement pour la voir.\n\nAppuyez sur `key:F12` ou, si vous êtes sur Mac, utilisez la combinaison `key:Cmd+Opt+J`.\n\nLes outils de développement s'ouvriront sous l'onglet Console par défaut.\n\nCela ressemble un peu à ceci :\n\n![chrome](chrome.png)\n\nL'aspect exact des outils de développement dépend de votre version de Chrome. Cela change de temps en temps, mais devrait être similaire.\n\n- Ici, nous pouvons voir le message d'erreur de couleur rouge. Dans ce cas, le script contient une commande \"lalala\" inconnue.\n- Sur la droite, il y a un lien cliquable vers le code source bug.html:12 avec le numéro de ligne où l'erreur s'est produite.\n\nSous le message d'erreur, il y a un symbole bleu `>`. Il marque une \"ligne de commande\" où l'on peut taper des commandes JavaScript et appuyer sur `key:Enter` pour les exécuter.\n\nNous pouvons maintenant voir les erreurs et c’est suffisant pour le début. Nous reviendrons plus tard sur les outils de développement et approfondirons le débogage plus loin dans le chapitre <info:debugging-chrome>.\n\n```smart header=\"Multi-line input\"\nHabituellement, quand on met une ligne de code dans la console, puis qu'on appuie sur `key:Enter`, elle s'exécute.\n\nPour insérer plusieurs lignes, appuyez sur `key:Shift+Enter`. De cette façon, on peut saisir de longs fragments de code JavaScript.\n```\n\n## Firefox, Edge et autres\n\nLa plupart des autres navigateurs utilisent `key:F12` pour ouvrir les outils de développement.\n\nLeur apparence est assez similaire. Une fois que vous savez comment utiliser l'un d'entre eux (vous pouvez commencer avec Chrome), vous pouvez facilement passer à un autre.\n\n## Safari\n\nSafari (navigateur Mac, non pris en charge par Windows / Linux) est un peu spécial ici. Nous devons d'abord activer le menu \"Développement\".\n\nOuvrez les préférences et accédez au volet \"Avancé\". Il y a une case à cocher en bas :\n\n![safari](safari.png)\n\nMaintenant `key:Cmd+Opt+C` peut activer la console. Notez également que le nouvel élément de menu supérieur nommé \"Développement\" est apparu. Il a beaucoup de commandes et d'options.\n\n\n\n## Résumé\n\n- Les outils de développement nous permettent de voir les erreurs, d'exécuter des commandes, d'examiner des variables et bien plus encore.\n- Ils peuvent être ouverts avec `key:F12` pour la plupart des navigateurs sous Windows. Chrome pour Mac nécessite la combinaison `key:Cmd+Opt+J`, Safari: `key:Cmd+Opt+C` (doit être activé avant).\n\n\nNous avons maintenant notre environnement prêt. Dans la section suivante, nous passerons à JavaScript.\n"
  },
  {
    "path": "1-js/01-getting-started/4-devtools/bug.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  There is an error in the script on this page.\n  <script>\n    lalala\n  </script>\n\n</body>\n\n</html>"
  },
  {
    "path": "1-js/01-getting-started/index.md",
    "content": "# Une introduction\n\nA propos du langage JavaScript et de l'environnement pour le développeur.\n"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/1-hello-alert/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<body>\n\n  <script>\n    alert( \"I'm JavaScript!\" );\n  </script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/1-hello-alert/solution.md",
    "content": "\r\n[html src=\"index.html\"]\r\n"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/1-hello-alert/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<body>\n\n  <script>\n    alert( \"I'm JavaScript!\" );\n  </script>\n\n</body>\n\n</html>"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/1-hello-alert/task.md",
    "content": "importance: 5\n\n---\n\n# Afficher une alerte\n\nCréez une page qui affiche un message \"Je suis JavaScript!\".\n\nFaites-le dans un bac à sable ou sur votre disque dur, peu importe, assurez-vous simplement que cela fonctionne.\n\n[demo src=\"solution\"]\n\n"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/2-hello-alert-ext/alert.js",
    "content": "alert(\"I'm JavaScript!\");"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/2-hello-alert-ext/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<body>\n\n  <script src=\"alert.js\"></script>\n\n</body>\n\n</html>"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/2-hello-alert-ext/solution.md",
    "content": "The HTML code:\n\n[html src=\"index.html\"]\n\npour le fichier `alert.js` dans le même dossier :\n\n[js src=\"alert.js\"]\n\n"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/2-hello-alert-ext/task.md",
    "content": "importance: 5\n\n---\n\n# Afficher une alerte avec un script externe\n\nPrendre la solution de l'exercice précédent <info:task/hello-alert>. Modifiez-le en extrayant le contenu du script dans un fichier externe `alert.js`, résidant dans le même dossier.\n\nOuvrez la page, assurez-vous que l'alerte fonctionne.\n"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/article.md",
    "content": "# Hello, world!\n\n<iframe width=\"560\" height=\"315\" src=\"https://www.youtube.com/embed/0WS0zqhT5fM\" title=\"YouTube video player\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen></iframe>\n\nCette partie du tutoriel est à propos du coeur de JavaScript, le langage lui même.\n\nMais nous avons besoin d'un environnement de travail pour exécuter nos scripts et, étant donné que ce guide est en ligne, le navigateur est un bon choix. Nous allons nous efforcer d'utiliser les commandes spécifiques au navigateur (comme `alert`) au minimum afin de ne pas y consacrer du temps si vous prévoyez de vous concentrer sur un autre environnement tel que Node.JS. Par ailleurs, les détails du navigateur sont expliqués dans [la partie suivante](/ui) du didacticiel.\n\nAlors, voyons d'abord comment intégrer un script à une page Web. Pour les environnements côté serveur, vous pouvez simplement l'exécuter avec une commande comme `\"node mon.js\"` pour Node.JS.\n\n\n## La balise \"script\"\n\nLes programmes JavaScript peuvent être insérés dans n'importe quelle partie d'un document HTML à l'aide de la balise `<script>`.\n\nPar exemple :\n\n```html run height=100\n<!DOCTYPE HTML>\n<html>\n\n<body>\n\n  <p>Before the script...</p>\n\n*!*\n  <script>\n    alert( 'Hello, world!' );\n  </script>\n*/!*\n\n  <p>...After the script.</p>\n\n</body>\n\n</html>\n```\n\n```online\nVous pouvez exécuter l'exemple en cliquant sur le bouton \"Play\" dans son coin supérieur droit.\n```\n\nLa balise `<script>` contient du code JavaScript qui est automatiquement exécuté lorsque le navigateur rencontre la balise.\n\n\n## Le balisage moderne\n\nLa balise `<script>` a quelques attributs qui sont rarement utilisés de nos jours, mais nous pouvons les trouver dans l'ancien code :\n\n L'attribut `type` : <code>&lt;script <u>type</u>=...&gt;</code>\n: L’ancien standard HTML4, nécessitait pour chaque script d'avoir un `type`. Habituellement c'était `type=\"text/javascript\"`. Dorénavant ce n’est plus nécessaire. De plus, le standard HTML moderne a totalement changé la signification de cet attribut. Maintenant, il peut être utilisé pour les modules JavaScript. Mais c’est un sujet avancé, nous parlerons de modules dans une autre partie du tutoriel.\n\n L'attribut `language` : <code>&lt;script <u>language</u>=...&gt;</code>\n: Cet attribut était destiné à afficher le langage du script. Pour l'instant, cet attribut n'a aucun sens, le langage est le JavaScript par défaut. Pas besoin de l'utiliser.\n\nCommentaires avant et après les scripts.\n: Dans des livres et des guides vraiment anciens, on peut trouver des commentaires dans `<script>`, comme ceci :\n\n    ```html no-beautify\n    <script type=\"text/javascript\"><!--\n        ...\n    //--></script>\n    ```\n\n    Cette astuce n’est pas utilisée dans le JavaScript moderne. Ces commentaires ont été utilisés pour masquer le code JavaScript des anciens navigateurs qui ne savaient pas comment traiter une balise `<script>`. Comme les navigateurs nés au cours des 15 dernières années n’ont pas ce problème, ce type de commentaire peut vous aider à identifier un code très ancien.\n\n\n## Scripts externes\n\nSi nous avons beaucoup de code JavaScript, nous pouvons le placer dans un fichier séparé.\n\nLe fichier de script est attaché à HTML avec l'attribut `src` :\n\n```html\n<script src=\"/chemin/vers/script.js\"></script>\n```\n\nIci, `/chemin/vers/script.js` est un chemin absolu du script depuis la racine du site. On peut également fournir un chemin relatif à partir de la page en cours. Par exemple `src=\"script.js\"` signifierait un fichier `\"script.js\"` dans le dossier courant.\n\nNous pouvons également donner une URL complète, par exemple :\n\n```html\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js\"></script>\n```\n\nPour joindre plusieurs scripts, utilisez plusieurs tags :\n\n```html\n<script src=\"/js/script1.js\"></script>\n<script src=\"/js/script2.js\"></script>\n…\n```\n\n```smart\nEn règle générale, seuls les scripts les plus simples sont mis en HTML. Les plus complexes résident dans des fichiers séparés.\n\nL’avantage d’un fichier séparé est que le navigateur le télécharge puis le stocke dans son [cache](https://fr.wikipedia.org/wiki/Cache_web).\n\nAprès cela, les autres pages qui veulent le même script le récupéreront à partir du cache au lieu de le télécharger à nouveau. Le fichier n'est donc téléchargé qu'une seule fois.\n\nCela économise du trafic et rend les pages plus rapides.\n```\n\n````warn header=\"Si `src` est défini, le contenu de la balise script est ignoré.\"\nUne seule balise `<script>` ne peut pas avoir à la fois l'attribut `src` et le code à l'intérieur.\n\nCeci ne fonctionnera pas :\n\n```html\n<script *!*src*/!*=\"file.js\">\n  alert(1); // le contenu est ignoré, parce que src est défini\n</script>\n```\n\nNous devons choisir : soit un script `<script src = \"…\">` externe, soit un `<script>` régulier avec du code.\n\nL'exemple ci-dessus peut être divisé en deux scripts pour fonctionner :\n\n```html\n<script src=\"file.js\"></script>\n<script>\n  alert(1);\n</script>\n```\n````\n\n## Résumé\n\n- Nous pouvons utiliser une balise `<script>` pour ajouter du code JavaScript à la page.\n- Les attributs de `type` et de `langue` ne sont pas requis.\n- Un script dans un fichier externe peut être inséré avec `<script src = \"chemin / vers / script.js\"> </ script>`.\n\n\nIl y a beaucoup plus à apprendre sur les scripts de navigateur et leur interaction avec la page Web. Mais gardons à l’esprit que cette partie du tutoriel est consacrée au langage JavaScript, nous ne devons donc pas nous en distraire. Nous utiliserons un navigateur comme moyen pour exécuter JavaScript, ce qui est très pratique pour la lecture en ligne, mais il en existe beaucoup d'autres.\n"
  },
  {
    "path": "1-js/02-first-steps/02-structure/article.md",
    "content": "# Structure du code\n\nLa première chose à apprendre est de construire des blocs de code.\n\n## Instructions\n\nLes instructions sont des constructions de syntaxe et des commandes qui effectuent des actions.\n\nNous avons déjà vu une instruction `alert(\"Hello World!\")` qui affiche le message.\n\nNous pouvons avoir autant d'instructions dans le code que nous le souhaitons. Une autre instruction peut être séparée par un point-virgule.\n\nPar exemple, ici nous divisons le message en deux :\n\n```js run no-beautify\nalert(\"Hello\"); alert(\"World\");\n```\n\nChaque instruction est généralement écrite sur une ligne distincte - le code devient donc plus lisible :\n\n```js run no-beautify\nalert(\"Hello\");\nalert(\"World\");\n```\n\n## Les points-virgules [#semicolon]\n\nUn point-virgule peut être omis dans la plupart des cas lorsqu'une rupture de ligne existe.\n\nCela fonctionnerait aussi :\n\n```js run no-beautify\nalert(\"Hello\")\nalert(\"World\")\n```\n\nIci, JavaScript interprète le saut de ligne comme un point-virgule \"implicite\". Cela s'appelle également [une insertion automatique de point-virgule](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion).\n\n**Dans la plupart des cas, une nouvelle ligne implique un point-virgule. Mais \"dans la plupart des cas\" ne signifie pas \"toujours\"!**\n\nIl y a des cas où une nouvelle ligne ne signifie pas un point-virgule, par exemple :\n\n```js run no-beautify\nalert(3 +\n1\n+ 2);\n```\n\nLe code génère `6`, car JavaScript n'insère pas de point-virgule ici. Il est intuitivement évident que si la ligne se termine par un plus `\"+\"`, alors c'est une \"expression incomplète\", donc un point-virgule serait incorrect. Et dans ce cas, cela fonctionne comme prévu.\n\n**Mais il existe des situations où JavaScript \"échoue\" à prendre un point-virgule là où il est vraiment nécessaire.**\n\nLes erreurs qui surviennent dans de tels cas sont assez difficiles à trouver et à corriger.\n\n````smart header=\"Un exemple d'erreur\"\nSi vous êtes curieux de voir un exemple concret d’une telle erreur, vérifiez ce code :\n\n```js run\nalert(\"Hello\");\n\n[1, 2].forEach(alert);\n```\n\nPas besoin de penser à la signification des crochets `[]` et `forEach` pour le moment. Nous les étudierons plus tard, pour l'instant, retenons simplement le résultat de l’exécution du code : il affiche `Hello`, puis `1`, puis `2`.\n\nSupprimons maintenant le point-virgule après l’`alert` :\n\n```js run no-beautify\nalert(\"Hello\")\n\n[1, 2].forEach(alert);\n```\n\nLa différence par rapport au code ci-dessus n'est qu'un caractère : le point-virgule à la fin de la première ligne a disparu.\n\nSi nous exécutons ce code, seul le premier `Hello` s'affiche (et il y a une erreur, vous devrez peut-être ouvrir la console pour le voir). Il n'y a plus de chiffres.\n\nC'est parce que JavaScript ne suppose pas de point-virgule avant les crochets `[...]`. Ainsi, le code du dernier exemple est traité comme une seule instruction.\n\nVoici comment le moteur le voit :\n\n```js run no-beautify\nalert(\"Hello\")[1, 2].forEach(alert);\n```\n\nÇa a l'air bizarre, non ? Une telle fusion dans ce cas est tout simplement une erreur. Nous devons mettre un point-virgule après l'`alert` pour que le code fonctionne correctement.\n\nCela peut arriver dans d'autres situations aussi.\n````\n\nIl est recommandé de mettre les points-virgules entre les instructions, même si elles sont séparées par des nouvelles lignes. Cette règle est largement adoptée par la communauté. Notons encore une fois - _il est possible_ de laisser de côté les points-virgules la plupart du temps. Mais il est plus sûr -- surtout pour un débutant -- de les utiliser.\n\n## Les Commentaires [#code-comments]\n\nAu fil du temps, le programme devient de plus en plus complexe. Il devient nécessaire d'ajouter des _commentaires_ qui décrivent ce qui se passe et pourquoi.\n\nLes commentaires peuvent être placés à n'importe quel endroit du script. Ils n'affectent pas l'exécution, car le moteur les ignore simplement.\n\n**Les commentaires sur une ligne commencent par deux barres obliques `//`.**\n\nLe reste de la ligne est un commentaire. Il peut occuper une ligne complète ou suivre une déclaration.\n\nComme ici :\n\n```js run\n// Ce commentaire occupe une ligne à part\nalert(\"Hello\");\n\nalert(\"World\"); // Ce commentaire suit l'instruction\n```\n\n**Les commentaires multilignes commencent par une barre oblique et un astérisque <code>/\\*</code> et se termine par un astérisque et une barre oblique <code>\\*/</code>.**\n\nComme ceci :\n\n```js run\n/* Un exemple avec deux messages.\nC'est un commentaire multiligne.\n*/\nalert(\"Hello\");\nalert(\"World\");\n```\n\nLe contenu des commentaires est ignoré, donc si nous mettons du code à l'intérieur <code>/\\* ... \\*/</code> il ne s'exécutera pas.\n\nParfois, il est utile de désactiver temporairement une partie du code :\n\n```js run\n/* Commenter le code\nalert('Hello');\n*/\nalert(\"World\");\n```\n\n```smart header=\"Utiliser les raccourcis !\"\nDans la plupart des éditeurs, une ligne de code peut être commentée par le raccourci `key:Ctrl+/` pour un commentaire sur une seule ligne et quelque chose comme `key:Ctrl+Shift+/` -- pour les commentaires multilignes (sélectionnez un morceau de code et appuyez sur la combinaison de touches). Pour Mac essayez `key:Cmd` au lieu de `key:Ctrl` et `key:Option` au lieu de `key:Shift`.\n```\n\n````warn header=\"Les commentaires imbriqués ne sont pas supportés !\"\nIl peut ne pas y avoir `/*...*/` à l'intérieur d'un autre `/*...*/`.\n\nUn tel code se terminera avec une erreur :\n\n```js run no-beautify\n/*\n  /* commentaire imbriqué ?!? */\n*/\nalert( 'World' );\n```\n````\n\nN'hésitez pas à commenter votre code.\n\nLes commentaires augmentent la taille globale du code, mais ce n'est pas un problème du tout. De nombreux outils permettent de réduire le code avant de le publier sur le serveur de production. Ils suppriment les commentaires, ils n'apparaissent donc pas dans les scripts de travail. Les commentaires n'ont donc aucun effet négatif sur la production.\n\nPlus loin dans le tutoriel, il y aura un chapitre <info:coding-style> qui explique également comment écrire de meilleurs commentaires.\n"
  },
  {
    "path": "1-js/02-first-steps/03-strict-mode/article.md",
    "content": "# Le mode moderne, \"use strict\"\n\nJavaScript a longtemps évolué sans problèmes de compatibilité. De nouvelles fonctionnalités ont été ajoutées au langage, mais les anciennes fonctionnalités n’ont pas été modifiées.\n\nCela a l'avantage de ne jamais casser le code existant. Mais l'inconvénient était que toute erreur ou décision imparfaite prise par les créateurs de JavaScript restait bloquée dans le langage pour toujours.\n\nIl en avait été ainsi jusqu'en 2009 lorsque ECMAScript 5 (ES5) est apparu. Il a ajouté de nouvelles fonctionnalités au langage et modifié certaines des fonctionnalités existantes. Pour conserver l'ancien code, la plupart des modifications sont désactivées par défaut. Vous devez les activer explicitement avec une directive spéciale : `\"use strict\"`.\n\n## \"use strict\"\n\nLa directive ressemble à une chaîne de caractères : `\"use strict\"` ou `'use strict'`. Lorsqu'il se trouve en haut du script, l'ensemble du script fonctionne de manière \"moderne\".\n\nPar exemple\n\n```js\n\"use strict\";\n\n// ce code fonctionne de manière moderne\n...\n```\n\nNous allons bientôt apprendre les fonctions (un moyen de regrouper les commandes). À l'avenir, notons que `\"use strict\"` peut être placé au début du corps de la fonction. Faire cela active le mode strict dans cette fonction uniquement. Mais d'habitude, les gens l'utilisent pour tout le script.\n\n\n````warn header=\"Assurez-vous que \\\"use strict\\\" est tout en haut\"\nAssurez-vous que `\"use strict\"` est en haut du script, sinon le mode strict peut ne pas être activé.\n\nIl n'y a pas de mode strict ici :\n\n```js no-strict\nalert(\"some code\");\n// \"use strict\" ci-dessous est ignoré, il doit être en haut\n\n\"use strict\";\n\n// le mode strict n'est pas activé\n```\n\nSeuls les commentaires peuvent apparaître avant `\"use strict\"`.\n````\n\n```warn header=\"Il n'y a aucun moyen d'annuler `use strict`\"\nIl n'y a pas de directive `\"no use strict\"` ou similaire, qui réactiverait l'ancien comportement.\n\nUne fois que nous entrons dans le mode strict, il n’y a plus de retour possible.\n```\n## Console du Navigateur\n\nPour l’avenir, lorsque vous utilisez une console de navigation pour tester des fonctionnalités, veuillez noter qu’elle n’utilise pas `use strict` par défaut.\n\nLorsque vous utilisez une [console de développement](info:devtools) pour exécuter du code, veuillez noter qu'elle n'utilise pas `use strict` par défaut.\n\nParfois, lorsque `use strict` fait une différence, vous obtenez des résultats incorrects.\n\nAlors, comment utiliser `use strict` dans la console ?\n\nD'abord, vous pouvez essayer d'appuyer sur `key:Shift+Enter` pour saisir plusieurs lignes et mettre `use strict` en haut comme cela :\n\n```js\n'use strict'; <Shift+Enter for a newline>\n//  ...votre code\n<Enter to run>\n```\n\nCela fonctionne dans la plupart des navigateurs, à savoir Firefox et Chrome.\n\nSi ce n’est pas le cas, comme par exemple dans un ancien navigateur, le moyen le plus fiable d’assurer `use strict` serait d'encapsuler le code dans la console comme ceci :\n\n```js\n(function() {\n  'use strict';\n\n  // ... votre code ici ...\n})()\n```\n\n## Devrions-nous activer \"use strict\" ?\n\nLa question peut sembler évidente, mais ce n'est pas le cas.\n\nOn pourrait recommander de démarrer les scripts avec `\"use strict\"` ... Mais vous savez ce qui est cool ?\n\nLe JavaScript moderne prend en charge les \"classes\" et les \"modules\" -- des structures de langage avancées (nous y arriverons sûrement), qui activent automatiquement `use strict`. Nous n'avons donc pas besoin d'ajouter la directive `\"use strict\"` si nous les utilisons.\n\n**Donc, pour l'instant `\"use strict\";` est un invité bienvenu en haut de vos scripts. Plus tard, lorsque votre code est entièrement dans des classes et des modules, vous pouvez l'omettre.**\n\nA partir de maintenant, nous devons connaître `use strict` en général.\n\nDans les chapitres suivants, au fur et à mesure que nous apprendrons les fonctionnalités du langage, nous verrons les différences entre les modes strict et anciens modes. Heureusement, il n'y en a pas beaucoup et ils améliorent en fait nos vies.\n\nTous les exemples de ce tutoriel supposent le mode strict, sauf indication contraire (très rarement).\n"
  },
  {
    "path": "1-js/02-first-steps/04-variables/1-hello-variables/solution.md",
    "content": "Dans le code ci-dessous, chaque ligne correspond à l'élément de la liste des tâches.\n\n```js run\nlet admin, name; // on peut déclarer deux variables à la fois\n\nname = \"John\";\n\nadmin = name;\n\nalert( admin ); // \"John\"\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/04-variables/1-hello-variables/task.md",
    "content": "importance: 2\n\n---\n\n# Travailler avec des variables\n\n1. Déclarez deux variables : `admin` and `name`.\n2. Assignez la valeur `\"John\"` à `name`.\n3. Copiez la valeur de `name` à `admin`.\n4. Afficher la valeur de `admin` en utilisant `alert` (devrait afficher \"John\").\n"
  },
  {
    "path": "1-js/02-first-steps/04-variables/2-declare-variables/solution.md",
    "content": "## La variable pour notre planète\n\nC’est simple :\n\n```js\nlet notrePlanete = \"Terre\";\n```\n\nNotez que nous pourrions utiliser un nom `planète` plus court, mais la planète à laquelle il fait référence pourrait ne pas être évidente. C’est bien d’être plus verbeux. Au moins jusqu'à ce que la variable n'estPasTropLongue.\n\n## le nom du visiteur actuel\n\n```js\nlet currentUserName = \"John\";\n```\n\nEncore une fois, nous pourrions réduire cela à `userName` si nous savons avec certitude que c'est bien l'utilisateur actuel.\n\nLes éditeurs modernes et la saisie semi-automatique facilitent l'écriture de noms de variables longs. Ne vous en privez pas. Un nom avec 3 mots est bien.\n\nEt si votre éditeur ne dispose pas de l'autocomplétion correcte, [obtenez-en un](/editors).\n"
  },
  {
    "path": "1-js/02-first-steps/04-variables/2-declare-variables/task.md",
    "content": "importance: 3\n\n---\n\n# Assigner le bon nom\n\n1. Créez la variable avec le nom de notre planète. Comment nommeriez-vous une telle variable ?\n2. Créez la variable pour stocker le nom du visiteur actuel. Comment nommeriez-vous cette variable ?"
  },
  {
    "path": "1-js/02-first-steps/04-variables/3-uppercast-constant/solution.md",
    "content": "Nous utilisons généralement des majuscules pour les constantes \"codées en dur\". Ou, en d'autres termes, lorsque la valeur est connue avant exécution et directement écrite dans le code.\n\nDans ce code, `birthday` correspond exactement à cela. Nous pourrions donc utiliser les majuscules pour cela.\n\nEn revanche, `age` est évalué en exécution. Aujourd'hui, nous avons un âge, un an après, nous en aurons un autre. C'est constant dans le sens où cela ne change pas avec l'exécution du code. Mais c’est un peu \"moins constant\" que `birthday` : il est calculé, nous devrions donc garder les minuscules pour cela.\n"
  },
  {
    "path": "1-js/02-first-steps/04-variables/3-uppercast-constant/task.md",
    "content": "importance: 4\n\n---\n\n# Constante en majuscule ?\n\nExaminez le code suivant :\n\n```js\nconst birthday = '18.04.1982';\n\nconst age = someCode(birthday);\n```\n\nIci, nous avons une constante `birthday` pour la date de naissance, ainsi que la constante `age`.\n\nL'`age` est calculé à partir de `birthday` en utilisant `someCode()`, ce qui signifie un appel de fonction que nous n'avons pas encore expliqué (nous le ferons bientôt !), mais les détails n'ont pas d'importance ici, le fait est que l'`age` est calculé d'une manière ou d'une autre en fonction de la date de naissance `birthday`.\n\nSerait-il juste d'utiliser des majuscules pour `birthday`? Pour `age`? Ou même pour les deux ?\n\n```js\nconst BIRTHDAY = '18.04.1982'; // mettre l'anniversaire en majuscule ?\n\nconst AGE = someCode(BIRTHDAY); // mettre l'âge en majuscule ?\n```\n"
  },
  {
    "path": "1-js/02-first-steps/04-variables/article.md",
    "content": "# Les variables\n\nLa plupart du temps, une application JavaScript doit utiliser des informations. Voici 2 exemples :\n1. Une boutique en ligne - les informations peuvent inclure des articles vendus et un panier d'achat. \n2. Une application de chat - les informations peuvent inclure des utilisateurs, des messages et bien plus encore.\n\nLes variables sont utilisées pour stocker ces informations.\n\n## Une variable\n\nUne [variable](https://fr.wikipedia.org/wiki/Variable_(informatique)) est un \"stockage nommé\" pour les données. Nous pouvons utiliser des variables pour stocker des goodies, des visiteurs et d'autres données.\n\nPour créer une variable en JavaScript, nous devons utiliser le mot-clé `let`.\n\nL'instruction ci-dessous crée (autrement dit: *déclare*) une variable avec le nom \"message\" :\n\n```js\nlet message;\n```\n\nMaintenant, nous pouvons y mettre des données en utilisant l'opérateur d'affectation `=` :\n\n```js\nlet message;\n\n*!*\nmessage = 'Hello'; // stocke la chaîne de caractères 'Hello' dans la variable nommée message\n*/!*\n```\n\nLa chaîne de caractères est maintenant enregistrée dans la zone de mémoire associée à la variable. Nous pouvons y accéder en utilisant le nom de la variable :\n\n```js run\nlet message;\nmessage = 'Hello!';\n\n*!*\nalert(message); // affiche le contenu de la variable\n*/!*\n```\n\nPour être concis, nous pouvons fusionner la déclaration et l'affectation de variables en une seule ligne :\n\n```js run\nlet message = 'Hello!'; // définir la variable et assigner la valeur\n\nalert(message); // Hello!\n```\n\nNous pouvons également déclarer plusieurs variables sur une seule ligne :\n\n```js no-beautify\nlet user = 'John', age = 25, message = 'Hello';\n```\n\nCela peut sembler plus court, mais ce n'est pas recommandé. Pour une meilleure lisibilité, veuillez utiliser une seule ligne par variable.\n\nLa variante multiligne est un peu plus longue, mais plus facile à lire :\n\n```js\nlet user = 'John';\nlet age = 25;\nlet message = 'Hello';\n```\n\nCertaines personnes définissent également plusieurs variables dans ce style multiligne :\n\n```js no-beautify\nlet user = 'John',\n  age = 25,\n  message = 'Hello';\n```\n\n... Ou même dans le style \"virgule première\" :\n\n```js no-beautify\nlet user = 'John'\n  , age = 25\n  , message = 'Hello';\n```\n\nTechniquement, toutes ces variantes font la même chose. C'est donc une question de goût personnel et d'esthétique.\n\n````smart header=\"`var` au lieu de `let`\"\nDans les anciens scripts, vous pouvez également trouver un autre mot-clé : `var` au lieu de `let` :\n\n```js\n*!*var*/!* message = 'Hello';\n```\n\nLe mot-clé `var` est *presque* identique à `let`. Il déclare également une variable, mais d'une manière légèrement différente, à la mode \"old school\".\n\nIl y a des différences subtiles entre `let` et `var`, mais elles n'ont pas encore d'importance pour nous. Nous les couvrirons en détails plus tard, dans le chapitre <info:var>.\n````\n\n## Une analogie avec la vie réelle\n\nNous pouvons facilement saisir le concept d'une \"variable\" si nous l'imaginons comme une \"boîte\" pour les données, avec un autocollant portant un nom unique.\n\nPar exemple, la variable message peut être imaginée comme une boîte étiquetée \"message\" avec la valeur \"Hello!\" à l'intérieur :\n\n![](variable.svg)\n\nNous pouvons mettre n'importe quelle valeur dans la boîte.\n\nOn peut aussi le changer autant de fois qu'on veut :\n\n```js run\nlet message;\n\nmessage = 'Hello!';\n\nmessage = 'World!'; // valeur changée\n\nalert(message);\n```\n\nLorsque la valeur est modifiée, les anciennes données sont supprimées de la variable :\n\n![](variable-change.svg)\n\nNous pouvons également déclarer deux variables et copier des données de l'une à l'autre.\n\n```js run\nlet hello = 'Hello world!';\n\nlet message;\n\n*!*\n// copier 'Hello world' de hello vers message\nmessage = hello;\n*/!*\n\n// maintenant les deux variables contiennent les mêmes données\nalert(hello); // Hello world!\nalert(message); // Hello world!\n```\n\n````warn header=\"Déclarer deux fois déclenche une erreur\"\nUne variable ne doit être déclarée qu'une seule fois.\n\nUne déclaration répétée de la même variable est une erreur :\n\n```js run\nlet message = \"This\";\n\n// répéter 'let' conduit à une erreur\nlet message = \"That\"; // SyntaxError: 'message' has already been declared\n```\nDonc, nous devrions déclarer une variable une fois et y faire référence sans `let`.\n````\n\n```smart header=\"Langages fonctionnels\"\nIl peut être intéressant de savoir qu'il existe aussi des langages de [programmation purement fonctionelle](https://fr.wikipedia.org/wiki/Programmation_fonctionnelle), comme [Haskell](https://www.scala-lang.org/) qui interdisent de modifier une valeur de variable.\n\nDans ce genre de langage, une fois la valeur stockée dans la boîte, elle est là pour toujours. Si nous devons stocker autre chose, le langage nous oblige à créer une nouvelle boîte (déclarer une nouvelle variable). Nous ne pouvons pas réutiliser l’ancienne.\n\nBien que cela puisse paraître un peu étrange à première vue, ces langages sont tout à fait capables de développements sérieux. Plus que cela, il existe des domaines tels que les calculs parallèles où cette limitation confère certains avantages.\n```\n\n## Nom de variable [#variable-naming]\n\nIl existe deux limitations pour un nom de variable en JavaScript :\n\n1. Le nom ne doit contenir que des lettres, des chiffres, des symboles `$` et `_`.\n2. Le premier caractère ne doit pas être un chiffre.\n\nNoms valides, par exemple :\n\n```js\nlet userName;\nlet test123;\n```\n\nLorsque le nom contient plusieurs mots, le [camelCase](https://fr.wikipedia.org/wiki/Camel_case) est couramment utilisé. C'est-à-dire que les mots se succèdent, chaque mot à l'exception du premier commence par une majuscule : `monTresLongNom`.\n\nCe qui est intéressant -- le signe dollar `'$'` et l'underscore `'_'` peuvent également être utilisé dans les noms. Ce sont des symboles réguliers, tout comme les lettres, sans aucune signification particulière.\n\nCes noms sont valides :\n\n```js run untrusted\nlet $ = 1; // déclarer une variable avec le nom \"$\"\nlet _ = 2; // et maintenant une variable avec le nom \"_\"\n\nalert($ + _); // 3\n```\n\nExemples de noms de variables incorrects :\n\n```js no-beautify\nlet 1a; // ne peut pas commencer avec un chiffre\n\nlet mon-nom; // un trait d'union '-' n'est pas autorisé dans le nom\n```\n\n```smart header=\"La casse est importante\"\nDes variables nommées `apple` et `APPLE` sont deux variables différentes.\n```\n\n````smart header=\"Les lettres non latines sont autorisées mais non recommandées\"\nIl est possible d'utiliser n'importe quelle langue, y compris les lettres cyrilliques, les logogrammes chinois, etc., comme ceci :\n\n```js\nlet имя = '...';\nlet 我 = '...';\n```\n\nTechniquement, il n'y a pas d'erreur ici, ces noms sont autorisés, mais il existe une convention internationale d'utiliser l'anglais dans les noms de variables. Même si nous écrivons un petit script, sa vie peut être longue. Les personnes d'autres pays peuvent avoir besoin de les lire quelque temps.\n\n````\n\n````warn header=\"Noms réservés\"\nIl existe une [liste de mots réservés](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Grammaire_lexicale#Mots-cl%C3%A9s), qui ne peuvent pas être utilisés comme noms de variables, car ils sont utilisés par le langage lui-même.\n\nPar exemple, les mots `let`, `class`, `return`, `function` sont réservés.\n\nLe code ci-dessous donne une erreur de syntaxe :\n\n```js run no-beautify\nlet let = 5; // impossible de nommer une variable \"let\", erreur!\nlet return = 5; // on ne peut pas la nommer \"return\" aussi, erreur!\n```\n````\n\n````warn header=\"Une affectation sans : `use strict`\"\n\nNormalement, nous devons définir une variable avant de l'utiliser. Mais jadis, il était techniquement possible de créer une variable par simple affectation de la valeur, sans `let`. Cela fonctionne toujours maintenant si nous ne mettons pas `use strict`. Le comportement est conservé pour la compatibilité avec les anciens scripts.\n\n```js run no-strict\n// note : pas de \"use strict\" dans cet exemple\n\nnum = 5; // la variable \"num\" est créée si elle n'existe pas\n\nalert(num); // 5\n```\n\nC’est une mauvaise pratique, cela donne une erreur en mode strict :\n\n```js run untrusted\n\"use strict\";\n\n*!*\nnum = 5; // erreur: num n'est pas défini\n*/!*\n```\n````\n\n\n## Les Constantes\n\nPour déclarer une constante (non changeante), on peut utiliser `const` plutôt que `let` :\n\n```js\nconst myBirthday = '18.04.1982';\n```\n\nLes variables déclarées à l'aide de `const` sont appelées \"constantes\". Elles ne peuvent pas être réassignées. Une tentative de le faire provoquerait une erreur :\n\n```js run\nconst myBirthday = '18.04.1982';\n\nmyBirthday = '01.01.2001'; // erreur, ne peut pas réaffecter la constante !\n```\n\nLorsqu'un programmeur est certain que la variable ne doit jamais changer, il peut utiliser `const` pour le garantir et également le montrer clairement à tout le monde.\n\n\n### Les constantes en majuscules\n\nIl existe une pratique répandue d’utiliser des constantes comme alias pour des valeurs difficiles à mémoriser, qui sont connues avant leur exécution.\n\nCes constantes sont nommées en utilisant des majuscules et des underscores.\n\nPar exemple, créons des constantes pour les couleurs au format dit \"web\" (hexadécimal) :\n\n```js run\nconst COLOR_RED = \"#F00\";\nconst COLOR_GREEN = \"#0F0\";\nconst COLOR_BLUE = \"#00F\";\nconst COLOR_ORANGE = \"#FF7F00\";\n\n// ... quand il faut choisir une couleur\nlet color = COLOR_ORANGE;\nalert(color); // #FF7F00\n```\n\nBénéfices:\n\n- `COLOR_ORANGE` est beaucoup plus facile à retenir que `\"#FF7F00\"`.\n- Il est beaucoup plus facile de mal saisir `\"#FF7F00\"` que `COLOR_ORANGE`.\n- En lisant le code, `COLOR_ORANGE` est beaucoup plus significatif que `#FF7F00`.\n\nQuand devrions-nous utiliser les majuscules pour une constante et quand devrions-nous les nommer normalement ? Soyons clairs.\n\nÊtre une \"constante\" signifie simplement que la valeur ne change jamais. Mais il existe des constantes connues avant l'exécution (comme une valeur hexadécimale pour le rouge), et il y a celles qui sont *calculées* en temps réel, pendant l'exécution, mais ne changent pas après l'affectation.\n\nPar exemple :\n\n```js\nconst pageLoadTime = /* temps pris par une page Web pour charger */;\n```\n\nLa valeur de `pageLoadTime` n’est pas connue avant le chargement de la page, elle est donc nommée normalement. Mais cela reste une constante, car elle ne change pas après l’affectation.\n\nEn d'autres termes, les constantes nommées en majuscules ne sont utilisées que comme alias pour les valeurs \"codées en dur\".\n\n## Nommez les choses correctement\n\nEn parlant de variables, il y a une autre chose extrêmement importante.\n\nUn nom de variable doit avoir une signification claire et évidente, décrivant les données qu'elle stocke.\n\nLe nommage de variables est l’une des compétences les plus importantes et les plus complexes de la programmation. Un rapide coup d’œil sur les noms de variables peut révéler quel code est écrit par un débutant et par un développeur expérimenté.\n\nDans un projet réel, la majeure partie du temps est consacrée à la modification et à l'extension de la base de code existant, plutôt que d'écrire quelque chose de complètement séparé de zéro. Et lorsque nous revenons au code après un certain temps, il est beaucoup plus facile de trouver des informations bien étiquetées. Ou, en d'autres termes, lorsque les variables ont de bons noms.\n\nVeuillez prendre le temps de réfléchir à un nom pertinent pour une variable avant de la déclarer. Cela vous facilitera énormément la vie.\n\nVoici quelques règles à suivre :\n\n- Utilisez des noms lisibles par des humains comme `userName` ou `shoppingCart`.\n- Restez à l’écart des abréviations ou des noms courts tels que `a`, `b`, `c`, à moins que vous ne sachiez vraiment ce que vous faites.\n- Faire en sorte que le nom soit le plus descriptif et concis possible. Des exemples de noms incorrects sont `data` et `value`. Un tel nom ne dit rien. C’est tout à fait acceptable de les utiliser si le contexte dans lequel les données ou les valeurs sont impliquées est particulièrement évident.\n- S'accorder avec son équipe (et soi-même) sur les termes utilisés. Si un visiteur du site est appelé un \"utilisateur\", nous devrions nommer les variables connexes comme `currentUser` ou `newUser`, mais non `currentVisitor` ou encore `newManInTown`.\n\nCela semble simple ? En effet, ça l'est, mais la création de noms descriptifs et concis dans la pratique ne l'est pas. Fonce.\n\n```smart header=\"Réutiliser ou créer ?\"\nUne dernière note. Certains programmeurs paresseux, au lieu de déclarer une nouvelle variable, ont tendance à réutiliser ceux qui existent déjà.\n\nEn conséquence, la variable est comme une boîte où les gens jettent des choses différentes sans changer l'autocollant. Qu'est-ce qu'il y a dedans maintenant ? Qui sait … Nous devons creuser et vérifier.\n\nUn tel programmeur économise un peu sur la déclaration de variable, mais perd dix fois plus sur le débogage du code.\n\nUne variable supplémentaire est une bonne chose, cela ne complique pas le code, bien au contraire.\n\nLes minificateurs et navigateurs modernes optimisent suffisamment le code pour ne pas créer de problèmes de performances. L'utilisation de différentes variables pour différentes valeurs peut même aider le moteur à optimiser.\n```\n\n## Résumé\n\nNous pouvons déclarer des variables pour stocker des données. Cela peut être fait en utilisant `var` ou `let` ou `const`.\n\n- `let` -- est une déclaration de variable moderne.\n- `var` -- est une déclaration de variable old-school. Normalement, nous ne l’utilisons pas du tout, mais nous couvrirons les différences subtiles par rapport à `let` dans le chapitre <info:var>, juste au cas où vous en auriez besoin.\n- `const` -- est équivalent à `let`, mais la valeur de la variable ne peut pas être modifiée.\n\nLes variables doivent être nommées d’une manière qui nous permet de comprendre facilement ce qui est à l’intérieur.\n"
  },
  {
    "path": "1-js/02-first-steps/05-types/1-string-quotes/solution.md",
    "content": "\nBackticks incorpore l'expression à l'intérieur de `${...}` dans la chaîne de caractères.\n\n```js run\nlet name = \"Ilya\";\n\n// l'expression est un numéro 1\nalert( `hello ${1}` ); // hello 1\n\n// l'expression est une chaîne de caractères \"name\"\nalert( `hello ${\"name\"}` ); // hello name\n\n// l'expression est une variable, il intègre son contenu\nalert( `hello ${name}` ); // hello Ilya\n```\n"
  },
  {
    "path": "1-js/02-first-steps/05-types/1-string-quotes/task.md",
    "content": "importance: 5\n\n---\n\n# String quotes\n\nQuelle est la sortie du script ?\n\n```js\nlet name = \"Ilya\";\n\nalert( `hello ${1}` ); // ?\n\nalert( `hello ${\"name\"}` ); // ?\n\nalert( `hello ${name}` ); // ?\n```\n"
  },
  {
    "path": "1-js/02-first-steps/05-types/article.md",
    "content": "# Les types de données\n\nUne valeur en JavaScript est toujours d'un certain type. Par exemple, une chaîne de caractères ou un nombre.\n\nIl existe huit types de données de base en JavaScript. Ici, nous les couvrirons en général et dans les prochains chapitres, nous parlerons de chacun d'eux en détail.\n\nNous pouvons mettre n'importe quel type dans une variable. Par exemple, une variable peut à un moment être une chaîne de caractères puis stocker un nombre :\n\n```js\n// pas d'erreur\nlet message = \"hello\";\nmessage = 123456;\n```\n\nLes langages de programmation qui permettent de telles choses sont appelés \"typés dynamiquement\", ce qui signifie qu'il existe des types de données, mais que les variables ne sont liées à aucun d'entre eux.\n\n\n## Number\n\n```js\nlet n = 123;\nn = 12.345;\n```\n\nLe type *number* sert à la fois à des nombres entiers et à des nombres à virgule flottante.\n\nIl existe de nombreuses opérations pour les nombres, par ex. multiplication `*`, division `/`, addition `+`, soustraction `-` et ainsi de suite.\n\nOutre les nombres réguliers, il existe des \"valeurs numériques spéciales\" qui appartiennent également à ce type: `Infinity`, `-Infinity` et `NaN`.\n\n- `Infinity` représente l'[Infini](https://fr.wikipedia.org/wiki/Infini) ∞ mathématique. C'est une valeur spéciale qui est plus grande que n'importe quel nombre.\n\n    Nous pouvons l'obtenir à la suite d'une division par zéro :\n\n    ```js run\n    alert( 1 / 0 ); // Infinity\n    ```\n\n    Ou mentionnez-le simplement dans le code directement :\n\n    ```js run\n    alert( Infinity ); // Infinity\n    ```\n\n- `NaN` représente une erreur de calcul. C'est le résultat d'une opération mathématique incorrecte ou non définie, par exemple :\n\n    ```js run\n    alert( \"pas un nombre\" / 2 ); // NaN, une telle division est erronée\n    ```\n\n    `NaN` est contagieux. Toute autre opération sur `NaN` donnerait un `NaN` :\n\n    ```js run\n    alert( NaN + 1 ); // NaN\n    alert( 3 * NaN ); // NaN\n    alert( \"not a number\" / 2 - 1 ); // NaN\n    ```\n\n    Donc, s'il y a `NaN` quelque part dans une expression mathématique, il se propage à l'ensemble du résultat (il n'y a qu'une seule exception : `NaN ** 0` vaut `1`).\n\n```smart header=\"Les opérations mathématiques sont sûres\"\nFaire des maths est sans danger en JavaScript. Nous pouvons faire n'importe quoi : diviser par zéro, traiter les chaînes non numériques comme des nombres, etc.\n\nLe script ne s'arrêtera jamais avec une erreur fatale (\"die\"). Au pire, nous aurons `NaN` comme résultat.\n```\n\nLes valeurs numériques spéciales appartiennent formellement au type \"number\". Bien sûr, ce ne sont pas des nombres au sens commun de ce mot.\n\nNous allons en voir plus sur le travail avec les nombres dans le chapitre <info:number>.\n\n## BigInt [#bigint-type]\n\nEn JavaScript, le type \"number\" ne peut pas représenter des valeurs entières supérieures à <code>(2<sup>53</sup>-1)</code> (c'est `9007199254740991`), ou moins que <code>-(2<sup>53</sup>-1)</code> pour les chiffres négatifs. C'est une limitation technique causée par leur représentation interne.\n\nPour être vraiment précis, le type \"number\" peut stocker des entiers plus grands (jusqu'à <code>1.7976931348623157 * 10<sup>308</sup></code>), mais en dehors de la plage d'entiers sûrs <code>±(2 <sup>53</sup>-1)</code> il y aura une erreur de précision, car tous les chiffres ne rentrent pas dans le stockage 64 bits fixe. Ainsi, une valeur \"approximative\" peut être stockée.\n\nPar exemple, ces deux nombres (juste au-dessus de la plage de sécurité) sont identiques :\n\n```js\nconsole.log(9007199254740991 + 1); // 9007199254740992\nconsole.log(9007199254740991 + 2); // 9007199254740992\n```\n\nAinsi, tous les entiers impairs supérieurs à <code>(2<sup>53</sup>-1)</code> ne peuvent pas du tout être stockés dans le type \"number\".\n\nDans la plupart des cas, la plage <code>±(2<sup>53</sup>-1)</code> est tout à fait suffisante, mais parfois nous avons besoin de toute la plage de très grands nombres entiers, par ex. pour la cryptographie ou les horodatages de précision à la microseconde.\n\n`BigInt` a récemment été ajouté au langage pour représenter des entiers de longueur arbitraire.\n\nUne valeur `BigInt` est créé en ajoutant `n` à la fin d'un entier :\n\n```js\n// le \"n\" à la fin signifie que c'est un BigInt\nconst bigInt = 1234567890123456789012345678901234567890n;\n```\n\nComme les chiffres `BigInt` sont rarement nécessaires, nous leur avons consacré un chapitre dédié <info:bigint>. Lisez-le lorsque vous avez besoin d'aussi gros chiffres.\n\n```smart header=\"Problèmes de compatibilité\"\nÀ l'heure actuelle, `BigInt` est pris en charge dans Firefox/Chrome/Edge/Safari, mais pas dans IE.\n```\n\nYou can check [*MDN* BigInt compatibility table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) to know which versions of a browser are supported.\n\n## String\n\nUne chaîne de caractères en JavaScript doit être entre guillemets.\n\n```js\nlet str = \"Hello\";\nlet str2 = 'Single quotes are ok too';\nlet phrase = `can embed another ${str}`;\n```\n\nEn JavaScript, il existe 3 types de guillemets.\n\n1. Double quotes: `\"Hello\"`.\n2. Single quotes: `'Hello'`.\n3. Backticks: <code>&#96;Hello&#96;</code>.\n\nLes guillemets simples et doubles sont des guillemets \"simples\". Il n'y a pratiquement pas de différence entre eux en JavaScript.\n\nLes backticks sont des guillemets \"à fonctionnalité étendue\". Ils nous permettent d’intégrer des variables et des expressions dans une chaîne en les encapsulant dans `${…}`, par exemple :\n\n```js run\nlet name = \"John\";\n\n// une variable encapsulée\nalert( `Hello, *!*${name}*/!*!` ); // Hello, John!\n\n// une expression encapsulée\nalert( `the result is *!*${1 + 2}*/!*` ); // le résultat est 3\n```\n\nL'expression à l'intérieur de `${…}` est évaluée et le résultat devient une partie de la chaîne. On peut y mettre n'importe quoi : une variable comme `name` ou une expression arithmétique comme `1 + 2` ou quelque chose de plus complexe.\n\nVeuillez noter que cela ne peut être fait que dans les backticks. Les autres guillemets ne permettent pas une telle intégration !\n\n```js run\nalert( \"the result is ${1 + 2}\" ); // le résultat est ${1 + 2} (les doubles quotes ne font rien)\n```\n\nNous couvrirons les chaînes de caractères plus en détails dans le chapitre <info:string>.\n\n```smart header=\"Il n'y a pas de type *character*.\"\nDans certains langages, il existe un type spécial \"character\" pour un seul caractère. Par exemple, en langage C et en Java, il s'agit de \"char\".\n\nEn JavaScript, ce type n'existe pas. Il n'y a qu'un seul type: `string`. Une chaîne de caractères peut être composée de zéro caractère (être vide), d'un caractère ou de plusieurs d'entre eux.\n```\n\n## Boolean (type logique)\n\nLe type booléen n'a que deux valeurs: `true` et `false`.\n\nCe type est couramment utilisé pour stocker des valeurs oui / non: `true` signifie \"oui, correct\" et `false` signifie \"non, incorrect\".\n\nPar exemple :\n\n```js\nlet nameFieldChecked = true; // oui, le champ de nom est coché\nlet ageFieldChecked = false; // non, le champ d'âge n'est pas coché\n```\n\nLes valeurs booléennes résultent également de comparaisons :\n\n```js run\nlet isGreater = 4 > 1;\n\nalert( isGreater ); // true (le résultat de la comparaison est \"oui\")\n```\n\nNous couvrirons plus profondément les booléens plus tard dans le chapitre <info:logical-operators>.\n\n## La valeur \"null\"\n\nLa valeur spéciale `null` n'appartient à aucun type de ceux décrits ci-dessus.\n\nIl forme un type bien distinct qui ne contient que la valeur `null` :\n\n```js\nlet age = null;\n```\n\nEn JavaScript, `null` n'est pas une \"référence à un objet non existant\" ou un \"pointeur nul\" comme dans d'autres langages.\n\nC’est juste une valeur spéciale qui a le sens de \"rien\", \"vide\" ou \"valeur inconnue\".\n\nLe code ci-dessus indique que l'`age` est inconnu.\n\n## La valeur \"undefined\"\n\nLa valeur spéciale `undefined` se distingue des autres. C'est un type à part entière, comme `null`.\n\nLa signification de `undefined` est \"la valeur n'est pas attribuée\".\n\nSi une variable est déclarée mais non affectée, alors sa valeur est exactement `undefined` :\n\n```js run\nlet age;\n\nalert(age); // affiche \"undefined\"\n```\n\nTechniquement, il est possible d'affecter explicitement `undefined` à une variable :\n\n```js run\nlet age = 100;\n\n// change the value to undefined\nage = undefined;\n\nalert(age); // \"undefined\"\n```\n\n… Mais il n’est pas recommandé de faire cela. Normalement, nous utilisons `null` pour assigner une valeur \"vide\" ou \"inconnue\" à une variable, tandis que `undefined` est réservé comme valeur initiale par défaut pour les éléments non attribués.\n\n## Objects et Symbols\n\nLe type `object` est spécial.\n\nTous les autres types sont appelés \"primitifs\", car leurs valeurs ne peuvent contenir qu’une seule chose (que ce soit une chaîne de caractères, un nombre ou autre). À contrario, les objets servent à stocker des collections de données et des entités plus complexes.\n\nÉtant aussi important, les objets méritent un traitement spécial. Nous les traiterons plus tard dans le chapitre <info:object>, après en savoir plus sur les primitifs.\n\nLe type `symbol` est utilisé pour créer des identificateurs uniques pour les objets. Nous devons le mentionner ici par souci d'exhaustivité, mais nous allons le voir en détails après avoir étudié les objets.\n\n## L'opérateur typeof [#type-typeof]\n\nL'opérateur `typeof` renvoie le type de l'opérande. Il est utile lorsqu'on souhaite traiter différemment les valeurs de différents types ou de faire une vérification rapide.\n\nL'appel `typeof x` renvoie une chaîne de caractères avec le nom du type :\n\n```js\ntypeof undefined // \"undefined\"\n\ntypeof 0 // \"number\"\n\ntypeof 10n // \"bigint\"\n\ntypeof true // \"boolean\"\n\ntypeof \"foo\" // \"string\"\n\ntypeof Symbol(\"id\") // \"symbol\"\n\n*!*\ntypeof Math // \"object\"  (1)\n*/!*\n\n*!*\ntypeof null // \"object\"  (2)\n*/!*\n\n*!*\ntypeof alert // \"function\"  (3)\n*/!*\n```\n\nLes trois dernières lignes peuvent nécessiter des explications supplémentaires :\n\n1. `Math` est un objet interne au langage qui fournit des opérations mathématiques. Nous allons l'apprendre dans le chapitre <info:number>. Ici, il sert uniquement comme exemple d'un objet.\n2. Le résultat de `typeof null` est `\"object\"`. C'est une erreur officiellement reconnue dans `typeof`, datant des premiers jours de JavaScript et conservée pour compatibilité. Bien sûr, `null` n'est pas un objet. C'est une valeur spéciale avec un type distinct qui lui est propre. Le comportement de `typeof` est incorrect ici.\n3. Le résultat de `typeof alert` est `\"function\"`, car `alert` est une fonction. Nous étudierons les fonctions dans les chapitres suivants, et nous verrons qu’il n’y a pas de type \"fonction\" en JavaScript. Les fonctions appartiennent au type `object` mais `typeof` les traite différemment en retournant `\"function\"`. Cela vient également des débuts de JavaScript. Techniquement ce n’est pas tout à fait correct, mais très pratique à l'usage.\n\n```smart header=\"La syntaxe `typeof(x)`\"\nVous pouvez également rencontrer une autre syntaxe : `typeof(x)`. C'est la même chose que `typeof x`.\n\nPour être clair : `typeof` est un opérateur, pas une fonction. Les parenthèses ici ne font pas partie de `typeof`. C'est le genre de parenthèses utilisées pour le regroupement mathématique.\n\nHabituellement, ces parenthèses contiennent une expression mathématique, telle que `(2 + 2)`, mais ici elles ne contiennent qu'un seul argument `(x)`. Syntaxiquement, ils permettent d'éviter un espace entre l'opérateur `typeof` et son argument, et certains aiment ça.\n\nCertaines personnes préfèrent `typeof(x)`, bien que la syntaxe `typeof x` soit beaucoup plus courante.\n```\n\n## Résumé\n\nIl existe 8 types de données de base en JavaScript.\n\n- Sept types de données primitifs :\n    - `number` pour les nombres de toute nature : entier ou virgule flottante, les nombres entiers sont limités à <code>±(2<sup>53</sup>-1)</code>.\n    - `bigint` pour des nombres entiers de longueur arbitraire.\n    - `string` pour les chaînes de caractères. Une chaîne de caractères peut avoir zéro ou plusieurs caractères, il n'y a pas de type à caractère unique distinct.\n    - `boolean` pour `true`/`false` (vrai/faux).\n    - `null` pour les valeurs inconnues - un type autonome qui a une seule valeur `null`.\n    - `undefined` pour les valeurs non attribuées - un type autonome avec une valeur unique `undefined`.\n    - `symbol` pour les identifiants uniques.\n- Et un type de données non primitif :\n    - `object` pour des structures de données plus complexes.\n\nL'opérateur `typeof` nous permet de voir quel type est stocké dans la variable.\n\n- Généralement utilisé sous cette forme `typeof x`, mais `typeof(x)` est également possible.\n- Renvoie une chaîne de caractères avec le nom du type, comme `\"string\"`.\n- Pour `null` il renvoie `\"object\"` -- C’est une erreur dans le langage, ce n’est pas un objet en fait.\n\nDans les chapitres suivants, nous nous concentrerons sur les valeurs primitives et une fois que nous les connaîtrons, nous passerons aux objets.\n"
  },
  {
    "path": "1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/solution.md",
    "content": "JavaScript-code:\n\n```js demo run\nlet name = prompt(\"What is your name?\", \"\");\nalert(name);\n```\n\nLa page complète :\n\n```html\n<!DOCTYPE html>\n<html>\n<body>\n\n  <script>\n    'use strict';\n\n    let name = prompt(\"What is your name?\", \"\");\n    alert(name);\n  </script>\n\n</body>\n</html>\n```\n"
  },
  {
    "path": "1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/task.md",
    "content": "importance: 4\n\n---\n\n# Une simple page\n\nCréez une page Web qui demande un nom et l'affiche\n\n[demo]\n"
  },
  {
    "path": "1-js/02-first-steps/06-alert-prompt-confirm/article.md",
    "content": "# Interaction: alert, prompt, confirm\n\nComme nous allons utiliser le navigateur comme environnement de démonstration, voyons quelques fonctions pour interagir avec l'utilisateur : `alert`, `prompt` et `confirm`.\n\n## alert\n\nCelui-ci, nous l'avons déjà vu. Il affiche un message et attend que l'utilisateur appuie sur \"OK\".\n\nPar exemple :\n\n```js run\nalert(\"Hello\");\n```\n\nLa mini-fenêtre avec le message s'appelle une *fenêtre modale*. Le mot \"modal\" signifie que le visiteur ne peut pas interagir avec le reste de la page, appuyer sur d'autres boutons, etc., tant qu'il n'a pas traité la fenêtre. Dans ce cas -- jusqu'à ce qu'ils appuient sur \"OK\".\n\n## prompt\n\nLa fonction `prompt` accepte deux arguments :\n\n```js no-beautify\nresult = prompt(title, [default]);\n```\n\nElle affiche une fenêtre modale avec un message texte, un champ de saisie pour le visiteur et les boutons OK/Annuler.\n\n`title`\n: Le texte à afficher au visiteur.\n\n`default`\n: Un deuxième paramètre facultatif, la valeur initiale du champ d'entrée.\n\n```smart header=\"Les crochets dans la syntaxe `[...]`\"\nLes crochets autour de `default` dans la syntaxe ci-dessus indiquent que le paramètre est facultatif, non requis.\n```\n\nLe visiteur peut taper quelque chose dans le champ de saisie d'invite et appuyer sur OK. Ensuite, nous obtenons ce texte dans le `result`. Ou ils peuvent annuler l'entrée en appuyant sur Annuler ou en appuyant sur la touche `key:Esc`, puis nous obtenons `null` comme `result`.\n\nL'appel à `prompt` renvoie le texte du champ de saisie ou `null` si l'entrée a été annulée.\n\nPar exemple :\n\n```js run\nlet age = prompt('How old are you?', 100);\n\nalert(`You are ${age} years old!`); // You are 100 years old!\n```\n\n````warn header=\"Dans IE : fournissez toujours un `default`\"\nLe second paramètre est facultatif, mais si nous ne le fournissons pas, Internet Explorer insérera le texte `\"undefined\"` dans l'invite.\n\nExécutez ce code dans Internet Explorer pour voir :\n\n```js run\nlet test = prompt(\"Test\");\n```\n\nDonc, pour que les invites semblent bonnes dans IE, nous vous recommandons de toujours fournir le deuxième argument :\n\n```js run\nlet test = prompt(\"Test\", ''); // <-- pour IE\n```\n````\n\n## confirm\n\nLa syntaxe :\n\n```js\nresult = confirm(question);\n```\n\nLa fonction `confirm` affiche une fenêtre modale avec une `question` et deux boutons : OK et Annuler.\n\nLe résultat est `true` si vous appuyez sur OK et `false` dans le cas contraire.\n\nPar exemple :\n\n```js run\nlet isBoss = confirm(\"Are you the boss?\");\n\nalert( isBoss ); // true si OK est pressé\n```\n\n## Résumé\n\nNous avons couvert 3 fonctions spécifiques au navigateur pour interagir avec les visiteurs :\n\n`alert`\n: affiche un message.\n\n`prompt`\n: affiche un message demandant à l'utilisateur de saisir du texte. Il renvoie le texte ou `null`, si vous cliquez sur le bouton Annuler ou sur `key:Esc`.\n\n`confirm`\n: affiche un message et attend que l'utilisateur appuie sur \"OK\" ou \"Annuler\". Il retourne `true` pour OK et `false` pour Annuler/`key:Esc`.\n\nToutes ces méthodes sont modales: elles suspendent l'exécution du script et ne permettent pas au visiteur d'interagir avec le reste de la page tant que la fenêtre n'a pas été fermée.\n\nIl existe deux limitations partagées par toutes les méthodes ci-dessus :\n\n1. L'emplacement exact de la fenêtre modale est déterminé par le navigateur. Habituellement, c'est au centre.\n2. L'aspect exact de la fenêtre dépend également du navigateur. Nous ne pouvons pas le modifier.\n\nC'est le prix de la simplicité. Il existe d'autres moyens d'afficher des fenêtres plus jolies et une interaction plus riche avec le visiteur, mais si l'aspect graphique n'ont pas beaucoup d'importance, ces méthodes fonctionnent très bien.\n"
  },
  {
    "path": "1-js/02-first-steps/07-type-conversions/article.md",
    "content": "# Les conversions de types\n\nLa plupart du temps, les opérateurs et les fonctions convertissent automatiquement les valeurs qui leur sont attribuées dans le bon type.\n\nPar exemple, `alert` convertit automatiquement toute valeur en chaîne de caractères pour l'afficher. Les opérations mathématiques convertissent les valeurs en nombres.\n\nIl y a aussi des cas où nous devons convertir explicitement une valeur pour corriger les choses.\n\n```smart header=\"On ne parle pas encore des objets\"\nDans ce chapitre, nous ne couvrons pas encore les objets. Ici, nous étudions d'abord les primitives. \n\nPlus tard, après avoir appris les objets, nous verrons comment la conversion d’objets fonctionne dans le chapitre <info:object-toprimitive>.\n```\n\n## String Conversion\n\nLa conversion `String` se produit lorsque nous avons besoin de la forme chaîne de caractères d'une valeur.\n\nPar exemple, `alert(value)`  le fait pour afficher la valeur.\n\nNous pouvons également utiliser un appel de fonction `String(value)` pour ça :\n\n```js run\nlet value = true;\nalert(typeof value); // boolean\n\n*!*\nvalue = String(value); // maintenant la valeur est une chaîne de caractères \"true\"\nalert(typeof value); // string\n*/!*\n```\n\nLa conversion `String` est assez évidente. Un `false` devient `\"false\"`, `null` devient `\"null\"` etc.\n\n## Numeric Conversion\n\nLa conversion numérique dans les fonctions et expressions mathématiques s'effectue automatiquement.\n\nPar exemple, lorsque la division `/` est appliqué à des non-numéros :\n\n```js run\nalert( \"6\" / \"2\" ); // 3, les chaînes de caractères sont converties en nombres\n```\n\nNous pouvons utiliser une fonction `Number(value)` pour convertir explicitement une valeur :\n\n```js run\nlet str = \"123\";\nalert(typeof str); // string\n\nlet num = Number(str); // devient un nombre 123\n\nalert(typeof num); // nombre\n```\n\nUne conversion explicite est généralement requise lorsque nous lisons une valeur à partir d'une source basée sur des chaînes de caractères, par exemple un champ texte, mais qu'un nombre doit être entré.\n\nSi la chaîne de caractères n'est pas un nombre valide, le résultat de cette conversion est `NaN`, par exemple :\n\n```js run\nlet age = Number(\"une chaîne de caractères arbitraire au lieu d'un nombre\");\n\nalert(age); // NaN, la conversion a échoué\n```\n\nRègles de conversion numériques :\n\n| Valeur                               | Devient ... |\n|--------------------------------------|-------------|\n| `undefined`                          | `NaN`       |\n| `null`                               | `0`         |\n| <code>true&nbsp;et&nbsp;false</code> | `1` et `0`  |\n| `string`                              | Les espaces blancs du début et de la fin sont supprimés. Ensuite, si la chaîne restante est vide, le résultat est `0`. Sinon, le nombre est «lu» dans la chaîne. Une erreur donne `NaN`.\n\nExemples:\n\n```js run\nalert( Number(\"   123   \") ); // 123\nalert( Number(\"123z\") );      // NaN (erreur de lecture d'un nombre à \"z\")\nalert( Number(true) );        // 1\nalert( Number(false) );       // 0\n```\n\nVeuillez noter que `null` et `undefined` se comportent différemment ici : `null` devient un zéro, alors qu'`undefined` devient `NaN`.\n\nLa plupart des opérateurs mathématiques effectuent également une telle conversion, nous le verrons dans le chapitre suivant.\n\n## Boolean Conversion\n\nLa conversion booléenne est la plus simple.\n\nCela se produit dans les opérations logiques (plus tard, nous nous intéresserons aux tests de condition et à d’autres types de tests), mais nous pouvons également l’effectuer manuellement avec l’appel de `Boolean(value)`.\n\nLa règle de conversion :\n\n- Les valeurs qui sont intuitivement \"vides\", comme `0`, une chaîne de caractères vide, `null`, `undefined` `NaN` deviennent `false`.\n- Les autres valeurs deviennent `true`.\n\nPar exemple :\n\n```js run\nalert( Boolean(1) ); // true\nalert( Boolean(0) ); // false\n\nalert( Boolean(\"hello\") ); // true\nalert( Boolean(\"\") ); // false\n```\n\n````warn header=\"Veuillez noter que la chaîne de caractères avec un zero `\\\"0\\\"` est `true`\"\nCertains langages (à savoir PHP) traitent `\"0\"` comme faux. Mais en JavaScript, une chaîne non vide est toujours `true`.\n\n```js run\nalert( Boolean(\"0\") ); // true\nalert( Boolean(\" \") ); // espaces, également vrai (toute chaîne de caractères non vide est vraie)\n```\n````\n\n## Résumé\n\nLes  trois conversions de types les plus utilisées sont :  to string, to number et to boolean.\n\n**`La conversion en String`** -- Se produit lorsque nous sortons quelque chose, peut être effectué avec `String(value)`. La conversion en chaîne de caractères est généralement évidente pour les valeurs primitives.\n\n**`La conversion en Number`** -- Se produit dans les opérations mathématiques, peut être effectué avec `Number(value)`.\n\nLa conversion vers `number` suit les règles suivantes :\n\n| Valeur                              | Devient ...                                                                                                                               |\n|-------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------|\n| `undefined`                         | `NaN`                                                                                                                                     |\n| `null`                              | `0`                                                                                                                                       |\n| <code>true&nbsp;/&nbsp;false</code> | `1 / 0`                                                                                                                                   |\n| `string`                            | La chaîne de caractères est lue \"tel quel\", les espaces des deux côtés sont ignorés. Une chaîne vide devient `0`. Une erreur donne `NaN`. |\n\n**`La conversion en Boolean`** -- Se produit dans des opérations logiques, ou peut être effectué avec `Boolean(value)`.\n\nLa conversion vers `boolean` suit les règles suivantes :\n\n| Valeur                                | Devient ... |\n|---------------------------------------|-------------|\n| `0`, `null`, `undefined`, `NaN`, `\"\"` | `false`     |\n| tout autre valeur                     | `true`      |\n\n\nLa plupart de ces règles sont faciles à comprendre et à mémoriser. Les exceptions notables où les gens font généralement des erreurs sont :\n\n- `undefined` est `NaN` en tant que number, non `0`.\n- `\"0\"` et les espaces dans les chaines de caractères comme `\"   \"` sont \"true\" en booléen.\n\nLes objets ne sont pas couverts ici, nous y reviendrons plus tard dans le chapitre <info:object-toprimitive> qui est consacré exclusivement aux objets, après avoir appris plus de choses basiques sur JavaScript.\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/1-increment-order/solution.md",
    "content": "\nLa réponse est :\n\n- `a = 2`\n- `b = 2`\n- `c = 2`\n- `d = 1`\n\n```js run no-beautify\nlet a = 1, b = 1;\n\nalert( ++a ); // 2, la forme préfixe renvoie la nouvelle valeur\nalert( b++ ); // 1, la forme postfixe renvoie l'ancienne valeur\n\nalert( a ); // 2, incrémenté une fois\nalert( b ); // 2, incrémenté une fois\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/1-increment-order/task.md",
    "content": "importance: 5\n\n---\n\n# Les formes postfixes et préfixes\n\nQuelles sont les valeurs finales de toutes les variables `a`, `b`, `c` et `d` après le code ci-dessous ?\n\n```js\nlet a = 1, b = 1;\n\nlet c = ++a; // ?\nlet d = b++; // ?\n```\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/2-assignment-result/solution.md",
    "content": "La réponse est :\n\n- `a = 4` (multiplié par 2)\n- `x = 5` (calculé comme 1 + 4)\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/2-assignment-result/task.md",
    "content": "importance: 3\n\n---\n\n# Résultat d'affectation\n\nQuelles sont les valeurs de `a` et `x` après le code ci-dessous ?\n\n```js\nlet a = 2;\n\nlet x = 1 + (a *= 2);\n```\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md",
    "content": "\n```js no-beautify\n\"\" + 1 + 0 = \"10\" // (1)\n\"\" - 1 + 0 = -1 // (2)\ntrue + false = 1\n6 / \"3\" = 2\n\"2\" * \"3\" = 6\n4 + 5 + \"px\" = \"9px\"\n\"$\" + 4 + 5 = \"$45\"\n\"4\" - 2 = 2\n\"4px\" - 2 = NaN\n\"  -9  \" + 5 = \"  -9  5\" // (3)\n\"  -9  \" - 5 = -14 // (4)\nnull + 1 = 1 // (5)\nundefined + 1 = NaN // (6)\n\" \\t \\n\" - 2 = -2 // (7)\n```\n\n1. L'addition avec une chaîne de caractères `\"\" + 1` converti `1` vers une chaîne de caractères : `\"\" + 1 = \"1\"`, ensuite nous avons `\"1\" + 0`, la même règle est appliquée.\n2. La soustraction `-` (comme la plupart des opérations mathématiques) ne fonctionne qu'avec des nombres, il convertit une chaîne de caractères vide `\"\"` vers `0`.\n3. L'addition avec un string ajoute le number `5` au string.\n4. La soustraction est toujours convertie en nombres, donc elle fait de `\"  -9  \"` un number `-9` (en ignorant les espaces qui l'entourent).\n5. `null` devient `0` après la conversion numérique.\n6. `undefined` devient `NaN` après la conversion numérique.\n7. Les caractères d'espacement sont coupés au début et à la fin de la chaîne de caractères lorsque celle-ci est convertie en nombre. Ici toute la chaîne se compose d'espaces, tels que `\\t`, `\\n` et d'un espace \"normal\" entre eux. Ainsi, de manière similaire à une chaîne de caractères vide, elle devient `0`.\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/3-primitive-conversions-questions/task.md",
    "content": "importance: 5\n\n---\n\n# Les conversions de type \n\nQuels sont les résultats de ces expressions ?\n\n```js no-beautify\n\"\" + 1 + 0\n\"\" - 1 + 0\ntrue + false\n6 / \"3\"\n\"2\" * \"3\"\n4 + 5 + \"px\"\n\"$\" + 4 + 5\n\"4\" - 2\n\"4px\" - 2\n\"  -9  \" + 5\n\"  -9  \" - 5\nnull + 1\nundefined + 1\n\" \\t \\n\" - 2\n```\n\nRéfléchissez bien, notez et comparez avec la réponse.\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/4-fix-prompt/solution.md",
    "content": "La raison en est que le prompt renvoie l'entrée utilisateur sous forme de chaîne de caractères.\n\nLes variables ont donc respectivement les valeurs `\"1\"` et `\"2\"`.\n\n```js run\nlet a = \"1\"; // prompt(\"First number?\", 1);\nlet b = \"2\"; // prompt(\"Second number?\", 2);\n\nalert(a + b); // 12\n```\n\nCe que nous devons faire est de convertir les chaînes de caractères en nombres avant `+`. Par exemple, en utilisant `Number()` ou en les préfixant avec `+`.\n\nPar exemple, juste avant `prompt` :\n\n```js run\nlet a = +prompt(\"First number?\", 1);\nlet b = +prompt(\"Second number?\", 2);\n\nalert(a + b); // 3\n```\n\nOu dans l'`alert`:\n\n```js run\nlet a = prompt(\"First number?\", 1);\nlet b = prompt(\"Second number?\", 2);\n\nalert(+a + +b); // 3\n```\n\nNous utilisons à la fois unaire et binaire `+` dans le dernier code. Ça a l'air drôle, non ?\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/4-fix-prompt/task.md",
    "content": "importance: 5\n\n---\n\n# Corrigez l'addition\n\nVoici un code qui demande à l'utilisateur deux nombres et affiche leur somme.\n\nCela ne fonctionne pas correctement. La sortie dans l'exemple ci-dessous est `12` (pour les valeurs d'invite par défaut).\n\nPourquoi ? Réparez-le. Le résultat doit être `3`.\n\n```js run\nlet a = prompt(\"First number?\", 1);\nlet b = prompt(\"Second number?\", 2);\n\nalert(a + b); // 12\n```\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/article.md",
    "content": "# Opérateurs de base, mathématiques\n\nDe nombreux opérateurs nous sont connus de l'école. Ce sont les additions `+`, multiplications `*`, soustractions `-` et ainsi de suite.\n\nDans ce chapitre, nous nous concentrons sur les aspects qui ne sont pas couverts par l'arithmétique scolaire.\n\n## Termes: \"unaire\", \"binaire\", \"opérande\"\n\nAvant de continuer, saisissons la terminologie commune.\n\n- Un opérande est ce à quoi les opérateurs sont appliqués. Par exemple, dans la multiplication `5 * 2`, il y a deux opérandes : l'opérande gauche est `5` et l'opérande droit est `2`. Parfois, les gens disent \"arguments\" au lieu de \"opérandes\".\n- Un opérateur est unaire s'il a un seul opérande. Par exemple, la négation unaire `-` inverse le signe du nombre :\n\n    ```js run\n    let x = 1;\n\n    *!*\n    x = -x;\n    */!*\n    alert( x ); // -1, le moins unaire a été appliqué\n    ```\n- Un opérateur est *binaire* s'il a deux opérandes. La même négation existe également dans la forme binaire :\n\n    ```js run no-beautify\n    let x = 1, y = 3;\n    alert( y - x ); // 2, le moins binaire soustrait des valeurs\n    ```\n\n    D'un point de vue formel, dans les exemples ci-dessus, nous avons deux opérateurs différents qui partagent le même symbole : l'opérateur de négation, un opérateur unaire qui inverse le signe, et l'opérateur de soustraction, un opérateur binaire qui soustrait un nombre d'un autre.\n\n## Opérations mathématiques\n\nLes opérations mathématiques suivantes sont supportées :\n\n- Addition `+`,\n- Soustraction `-`,\n- Multiplication `*`,\n- Division `/`,\n- Reste `%`,\n- Exponentiation `**`.\n\nLes quatre premières sont assez simples, tandis que `%` et `**` nécessitent quelques explications.\n\n### Reste % (Modulo)\n\nL'opérateur reste `%`, malgré son apparence, n'est pas lié aux pourcentages.\n\nLe résultat de `a % b` est le [reste](https://fr.wikipedia.org/wiki/Reste) de la division entière de `a` par `b`.\n\nPar exemple :\n\n```js run\n\nalert( 5 % 2 ); // 1, le reste de 5 divisé par 2\nalert( 8 % 3 ); // 2, le reste de 8 divisé par 3\nalert( 8 % 4 ); // 0, le reste de 8 divisé par 4\n\n```\n\n### Exponentiation **\n\nL'opérateur d'exponentiation `a ** b` multiplie `a` par lui-même `b` fois.\nEn mathématiques à l'école, nous écrivons cela a<sup>b</sup>.\n\nPar exemple :\n\n```js run\nalert( 2 ** 2 ); // 2² = 4\nalert( 2 ** 3 ); // 2³ = 8\nalert( 2 ** 4 ); // 2⁴ = 16\n```\n\nTout comme en mathématiques, l'opérateur d'exponentiation est également défini pour les nombres non entiers.\n\nPar exemple, une racine carrée est une exponentiation de `½` :\n\n```js run\nalert( 4 ** (1/2) ); // 2 (la puissance de 1/2 équivaut à une racine carrée)\nalert( 8 ** (1/3) ); // 2 (la puissance de 1/3 équivaut à une racine cubique)\n```\n\n\n## Concaténation de chaînes de caractères, binaire `+`\n\nDécouvrons les fonctionnalités des opérateurs JavaScript qui vont au-delà de l'arithmétique scolaire.\n\nHabituellement, l'opérateur `+` additionne des chiffres.\n\nMais si le binaire `+` est appliqué aux chaînes de caractères, il les fusionne (concatène) :\n\n```js\nlet s = \"my\" + \"string\";\nalert(s); // mystring\n```\n\nNotez que si l'un des opérandes est une chaîne de caractères, l'autre est automatiquement converti en chaîne de caractères.\n\nPar exemple :\n\n```js run\nalert( '1' + 2 ); // \"12\"\nalert( 2 + '1' ); // \"21\"\n```\n\nPeu importe que le premier opérande soit une chaîne de caractères ou le second. La règle est simple : si l'un des opérandes est une chaîne de caractères, convertissez l'autre également en une chaîne de caractères.\n\nCependant, notez que les opérations se déroulent de gauche à droite. S'il y a deux nombres suivis d'une chaîne, les nombres seront ajoutés avant d'être convertis en chaîne :\n\n\n```js run\nalert(2 + 2 + '1' ); // \"41\" et non \"221\"\n```\n\nIci, les opérateurs travaillent les uns après les autres. Le premier `+` additionne deux nombres, donc il renvoie `4`, puis le `+` suivant ajoute la chaîne de caractères `1`, donc c'est comme `4 + '1' = 41`.\n\n```js run\nalert('1' + 2 + 2); // \"122\" and not \"14\"\n```\nIci, le premier opérande est une chaîne de caractères, le compilateur traite également les deux autres opérandes comme des chaînes de caractères. Le `2` est concaténé à `'1'`, donc c'est comme `'1'+ 2 = \"12\"` et `\"12\" + 2 = \"122\"`.\n\n\nLe binaire `+` est le seul opérateur qui prend en charge les chaînes de caractères de cette manière. D'autres opérateurs arithmétiques ne fonctionnent qu'avec des nombres et convertissent toujours leurs opérandes en nombres.\n\nVoici l'exemple pour la soustraction et la division :\n```js run\nalert( 6 - '2' ); // 4, convertit '2' en nombre\nalert( '6' / '2' ); // 3, convertit les deux opérandes en nombres\n```\n\n## Conversion numérique, unaire +\n\nLe plus `+` existe sous deux formes. La forme binaire que nous avons utilisée ci-dessus et la forme unaire.\n\nL’unaire plus ou, en d’autres termes, l’opérateur plus `+` appliqué à une seule valeur, ne fait rien avec les nombres, mais si l’opérande n’est pas un nombre, alors il est converti en nombre.\n\nPar exemple :\n\n```js run\n// Aucun effet sur les nombres\nlet x = 1;\nalert( +x ); // 1\n\nlet y = -2;\nalert( +y ); // -2\n\n*!*\n// Convertit les non-nombres\nalert( +true ); // 1\nalert( +\"\" );   // 0\n*/!*\n```\n\nEn fait, il fait la même chose que `Number(...)`, mais il est plus court.\n\nLa nécessité de convertir des chaînes de caractères en nombres est très fréquente. Par exemple, si nous obtenons des valeurs à partir de champs de formulaire HTML, il s’agit généralement de chaînes de caractères. Et si on veut les additionner ?\n\nLe binaire plus les ajouterait comme des chaînes de caractères :\n\n```js run\nlet apples = \"2\";\nlet oranges = \"3\";\n\nalert( apples + oranges ); // \"23\", le binaire plus concatène les chaînes de caractères\n```\n\nSi nous voulons les traiter comme des nombres, nous devons d'abord les convertir et ensuite seulement nous pouvons les additionner :\n\n```js run\nlet apples = \"2\";\nlet oranges = \"3\";\n\n*!*\n// les deux valeurs converties en nombres avant le binaire plus\nalert( +apples + +oranges ); // 5\n*/!*\n\n// c'est équivalent à cette variante plus longue\n// alert( Number(apples) + Number(oranges) ); // 5\n```\n\nDu point de vue du mathématicien, l’abondance des `+` peut sembler étrange. Mais du point de vue du programmeur, il n’y a rien de spécial : les plus unaires sont appliqués en premier, ils convertissent les chaînes de caractères en nombres, puis le plus binaire les additionne.\n\nPourquoi les plus unaires sont-ils appliqués aux valeurs avant les binaires ? Comme nous allons le voir, c’est à cause de *leur précédence supérieure*.\n\n## Précédence des opérateurs\n\nSi une expression à plusieurs opérateurs, l’ordre d’exécution est défini par leur *priorité* ou, en d’autres termes, il existe un ordre de priorité implicite entre les opérateurs.\n\nDe l'école, nous savons tous que la multiplication dans l'expression `1 + 2 * 2` devrait être calculée avant l'addition. C’est exactement cela la précédence. La multiplication est dite avoir une *précédence supérieure* à l'addition.\n\nLes parenthèses outrepassent toute priorité, donc si nous ne sommes pas satisfaits de l'ordre par défaut, nous pouvons les utiliser, comme : `(1 + 2) * 2`.\n\nIl y a beaucoup d'opérateurs en JavaScript. Chaque opérateur a un numéro correspondant à sa priorité de précédence. Celui qui est plus haut sur le tableau s'exécute en premier. Si la priorité est la même, l'ordre d'exécution est de gauche à droite.\n\nUn extrait du [tableau de précédence](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence) (vous n'avez pas besoin de vous en souvenir, mais notez que les opérateurs unaires ont une priorité plus élevée que les binaires correspondants) :\n\n| Précédence | Nom             | Symbole |\n|------------|-----------------|---------|\n| ...        | ...             | ...     |\n| 14         | plus unaire     | `+`     |\n| 14         | négation unaire | `-`     |\n| 13         | exponentiation  | `**`    |\n| 12         | multiplication  | `*`     |\n| 12         | division        | `/`     |\n| 11         | addition        | `+`     |\n| 11         | soustraction    | `-`     |\n| ...        | ...             | ...     |\n| 2          | affectation     | `=`     |\n| ...        | ...             | ...     |\n\nComme on peut le voir, le \"plus unaire\" a une priorité de `14`, ce qui est supérieur à `11` pour \"l'addition\" (plus binaire). C’est pourquoi, dans l’expression `\"+apples + +oranges\"`, les plus unaires fonctionnent avant l’addition.\n\n## Affectation\n\nNotons qu’une affectation `=` est aussi un opérateur. Il est répertorié dans le tableau des précédences avec la très faible priorité de `2`.\n\nC’est pourquoi lorsque nous assignons une variable, comme `x = 2 * 2 + 1`, les calculs sont effectués en premier, puis le `=` est évalué, stockant le résultat dans `x`.\n\n```js\nlet x = 2 * 2 + 1;\n\nalert( x ); // 5\n```\n\n### Assignment = retourne une valeur\n\nLe fait que `=` soit un opérateur, pas une construction de langage \"magique\" a une implication intéressante.\n\nTous les opérateurs en JavaScript renvoient une valeur. C'est évident pour `+` et `-`, mais aussi vrai pour `=`.\n\nL'appel `x = valeur` écrit la `valeur` dans `x` *puis la renvoie*.\n\nVoici un exemple qui utilise une affectation dans le cadre d'une expression plus complexe :\n\n```js run\nlet a = 1;\nlet b = 2;\n\n*!*\nlet c = 3 - (a = b + 1);\n*/!*\n\nalert( a ); // 3\nalert( c ); // 0\n```\n\nDans l'exemple ci-dessus, le résultat de l'expression`(a = b + 1)` est la valeur qui a été affectée à `a` (c'est-à-dire `3`). Il est ensuite utilisé pour d'autres évaluations.\n\nDrôle de code, n'est-ce pas? Nous devons comprendre comment cela fonctionne, car parfois nous le voyons dans les bibliothèques JavaScript.\n\nCependant, n'écrivez pas le code comme ça. De telles astuces ne rendent certainement pas le code plus clair ou lisible.\n\n### Affectations chaînées\n\nUne autre caractéristique intéressante est la possibilité de chaîner des affectations :\n\n```js run\nlet a, b, c;\n\n*!*\na = b = c = 2 + 2;\n*/!*\n\nalert( a ); // 4\nalert( b ); // 4\nalert( c ); // 4\n```\n\nLes affectations en chaîne sont évaluées de droite à gauche. D'abord, l'expression la plus à droite `2 + 2` est évaluée puis assignée aux variables de gauche : `c`, `b` et `a`. À la fin, toutes les variables partagent une seule valeur.\n\nEncore une fois, pour des raisons de lisibilité, il est préférable de diviser ce code en quelques lignes :\n\n```js\nc = 2 + 2;\nb = c;\na = c;\n```\nC'est plus facile à lire, en particulier lors de la numérisation rapide du code.\n\n## Modification sur place\n\nNous avons souvent besoin d'appliquer un opérateur à une variable et d'y stocker le nouveau résultat.\n\nPar exemple :\n\n```js\nlet n = 2;\nn = n + 5;\nn = n * 2;\n```\n\nCette notation peut être raccourcie en utilisant les opérateurs `+=` et `*=` :\n\n```js run\nlet n = 2;\nn += 5; // maintenant n = 7 (identique à n = n + 5)\nn *= 2; // maintenant n = 14 (identique à n = n * 2)\n\nalert( n ); // 14\n```\n\nIl existe des opérateurs de \"modification et assignation\" courts pour tous les opérateurs arithmétiques et binaires : `/=`, `-=` etc.\n\nCes opérateurs ont la même précédence qu'une affectation normale. Ils s'exécutent donc après la plupart des autres calculs :\n\n```js run\nlet n = 2;\n\nn *= 3 + 5; // la partie de droite est évaluée en premier (identique à n *= 8)\n\nalert( n ); // 16\n```\n\n## Incrémentation / décrémentation\n\n<!-- Can't use -- in title, because built-in parse turns it into – -->\n\nL'augmentation ou la diminution d'un nombre par `1` compte parmi les opérations numériques les plus courantes.\n\nIl y a donc des opérateurs spéciaux pour cela :\n\n- **Incrémentation** `++` augmente une variable de 1 :\n\n    ```js run no-beautify\n    let counter = 2;\n    counter++;        // fonctionne de la même manière que counter = counter + 1, mais c'est plus court\n    alert( counter ); // 3\n    ```\n- **Décrémentation** `--` diminue une variable de 1 :\n\n    ```js run no-beautify\n    let counter = 2;\n    counter--;        // fonctionne de la même manière que counter = counter - 1, mais c'est plus court\n    alert( counter ); // 1\n    ```\n\n```warn\nL'incrémentation / décrémentation ne peut être appliquée qu'à une variable. Une tentative pour l'utiliser sur une valeur comme `5++` donnera une erreur.\n```\n\nLes opérateurs `++` et `--` peuvent être placés à la fois après et avant la variable.\n\n- Lorsque l'opérateur va après la variable, cela s'appelle une \"forme postfixe\" : `counter++`.\n- La \"forme préfixe\" est celle où l'opérateur se place devant la variable : `++counter`.\n\nCes deux opérateurs font la même chose : augmenter le `counter` de `1`.\n\nY a-t-il une différence ? Oui, mais nous ne pouvons le voir que si nous utilisons la valeur renvoyée de `++/--`.\n\nSoyons clairs. Comme nous le savons, tous les opérateurs renvoient une valeur. L'incrémentation / décrémentation n'est pas une exception ici. La forme préfixe renvoie la nouvelle valeur, tandis que la forme postfixe renvoie l'ancienne valeur (avant l'incrémentation / décrémentation).\n\nPour voir la différence, voici un exemple :\n\n```js run\nlet counter = 1;\nlet a = ++counter; // (*)\n\nalert(a); // *!*2*/!*\n```\n\nIci, dans la ligne `(*)`, l'appel du *préfixe* `++counter` incrémente le compteur et retourne la nouvelle valeur qui est `2`. Ainsi, l'`alert` affiche `2`.\n\nMaintenant, utilisons la forme postfixe :\n\n```js run\nlet counter = 1;\nlet a = counter++; // (*) changé ++counter pour counter++\n\nalert(a); // *!*1*/!*\n```\n\nDans la ligne `(*)`, la forme postfixe `counter++` incrémente également `counter`, mais renvoie l'*ancienne* valeur (avant l'incrémentation). Donc, l'`alert` montre `1`.\n\nPour résumer :\n\n- Si le résultat de l'incrémentation/décrémentation n'est pas utilisé, alors il n'y a pas de différence dans la forme à utiliser :\n\n    ```js run\n    let counter = 0;\n    counter++;\n    ++counter;\n    alert( counter ); // 2, les lignes ci-dessus ont fait la même chose\n    ```\n- Si nous souhaitons augmenter la valeur et utiliser le résultat de l'opérateur immédiatement, nous avons besoin de la forme préfixe :\n\n    ```js run\n    let counter = 0;\n    alert( ++counter ); // 1\n    ```\n- Si nous souhaitons incrémenter, mais utiliser la valeur précédente, alors nous avons besoin de la forme postfixe :\n\n    ```js run\n    let counter = 0;\n    alert( counter++ ); // 0\n    ```\n\n````smart header=\"Incrémentation / décrémentation parmi d'autres opérateurs\"\nLes opérateurs `++/--` peuvent également être utilisés dans une expression. Leur précédence est plus élevée que la plupart des autres opérations arithmétiques.\n\nPar exemple :\n\n```js run\nlet counter = 1;\nalert( 2 * ++counter ); // 4\n```\n\nA comparer avec :\n\n```js run\nlet counter = 1;\nalert( 2 * counter++ ); // 2, counter++ renvoie \"l'ancienne\" valeur\n```\n\nBien que techniquement acceptable, une telle notation rend le code moins lisible. Une ligne fait plusieurs choses -- pas bien.\n\nLors de la lecture du code, un scan oculaire \"vertical\" rapide peut facilement manquer un tel `counter++`, et il n’est pas évident que la variable augmente.\n\nLe style \"une ligne -- une action\" est conseillé :\n\n```js run\nlet counter = 1;\nalert( 2 * counter );\ncounter++;\n```\n````\n\n## Opérateurs binaires\n\nLes opérateurs binaires traitent les arguments comme des nombres entiers de 32 bits et travaillent au niveau de leur représentation binaire.\n\nCes opérateurs ne sont pas spécifiques à JavaScript. Ils sont pris en charge dans la plupart des langages de programmation.\n\nLa liste des opérateurs :\n\n- AND ( `&` )\n- OR ( `|` )\n- XOR ( `^` )\n- NOT ( `~` )\n- LEFT SHIFT ( `<<` )\n- RIGHT SHIFT ( `>>` )\n- ZERO-FILL RIGHT SHIFT ( `>>>` )\n\nCes opérateurs sont très rarement utilisés, lorsque nous devons jouer avec des nombres au niveau le plus bas (bit à bit). Nous n'aurons pas besoin de ces opérateurs de si tôt, car le développement Web les utilise peu, mais dans certains domaines particuliers, comme la cryptographie, ils sont utiles. Vous pouvez lire le chapitre [Opérateurs binaires](https://developer.mozilla.org/fr/docs/Web/JavaScript/Guide/Expressions_et_Op%C3%A9rateurs#Op%C3%A9rateurs_binaires) sur MDN en cas de besoin.\n\n\n## Virgule\n\nL'opérateur virgule `,` est l'un des opérateurs les plus rares et les plus inhabituels. Parfois, il faut écrire un code plus court, il faut donc le connaître pour comprendre ce qui se passe.\n\nL'opérateur virgule nous permet d'évaluer plusieurs expressions en les divisant par une virgule `,`. Chacun d'eux est évalué, mais seulement le résultat de la dernière est renvoyé.\n\nPar exemple :\n\n```js run\n*!*\nlet a = (1 + 2, 3 + 4);\n*/!*\n\nalert( a ); // 7 (le résultat de 3 + 4)\n```\n\nIci, la première expression `1 + 2` est évaluée mais son résultat n'est pas utilisé, puis `3 + 4` est évalué et renvoyé comme résultat.\n\n```smart header=\"La virgule a une très faible précédence\"\nVeuillez noter que l'opérateur virgule a une précédence très basse, inférieure à `=`, donc les parenthèses sont importantes dans l'exemple ci-dessus.\n\nSans eux : `a = 1 + 2, 3 + 4` évalue d'abord `+`, additionnant les nombres dans `a = 3, 7`, ensuite l'opérateur d'affectation `=` assigne `a = 3`, et le reste est ignoré. C'est comme `(a = 1 + 2), 3 + 4`.\n```\n\nPourquoi avons-nous besoin d'un tel opérateur qui jette tout sauf la dernière partie ?\n\nParfois, les gens l'utilisent dans des constructions plus complexes pour placer plusieurs actions sur une seule ligne.\n\nPar exemple :\n\n```js\n// trois opérations en une seule ligne\nfor (*!*a = 1, b = 3, c = a * b*/!*; a < 10; a++) {\n ...\n}\n```\n\nCes astuces sont utilisées dans de nombreux frameworks JavaScript, c’est pourquoi nous les mentionnons. Mais généralement, ils n'améliorent pas la lisibilité du code, nous devrions bien réfléchir avant de les utiliser.\n"
  },
  {
    "path": "1-js/02-first-steps/09-comparison/1-comparison-questions/solution.md",
    "content": "\n\n```js no-beautify\n5 > 4 → true\n\"apple\" > \"pineapple\" → false\n\"2\" > \"12\" → true\nundefined == null → true\nundefined === null → false\nnull == \"\\n0\\n\" → false\nnull === +\"\\n0\\n\" → false\n```\n\nQuelques raisons :\n\n1. Évidemment, c'est vrai.\n2. Comparaison du dictionnaire, donc fausse. `\"a\"` est plus petit que `\"p\"`.\n3. Encore une fois, comparaison du dictionnaire, le premier caractère de `\"2\"` est plus grand que le premier caractère de `\"1\"`.\n4. Les valeurs `null` et `undefined` sont exclusivement égale entre elles.\n5. L'égalité stricte est stricte. Des types différents des deux côtés conduisent à `false`.\n6. Similaire à `(4)`, `null` n'est égale qu'à `undefined`.\n7. Egalité stricte de différents types.\n"
  },
  {
    "path": "1-js/02-first-steps/09-comparison/1-comparison-questions/task.md",
    "content": "importance: 5\n\n---\n\n# Comparaisons\n\nQuel sera le résultat pour les expressions suivantes :\n\n```js no-beautify\n5 > 4\n\"apple\" > \"pineapple\"\n\"2\" > \"12\"\nundefined == null\nundefined === null\nnull == \"\\n0\\n\"\nnull === +\"\\n0\\n\"\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/09-comparison/article.md",
    "content": "# Comparaisons\n\nIl y a de nombreux opérateurs de comparaison que nous connaissons des mathématiques :\n\n- Plus grand/petit que : <code>a &gt; b</code>, <code>a &lt; b</code>.\n- Plus grand/petit ou égal à : <code>a &gt;= b</code>, <code>a &lt;= b</code>.\n- Égalité : `a == b` (veuillez noter le signe de la double égalité `==` signifie un test d’égalité. Un seul symbole `a = b` signifierait une affectation).\n- Pas égal : en maths la notation est <code>&ne;</code>, mais en JavaScript elle est écrite comme une assignation avec un signe d’exclamation : <code>a != b</code>.\n\nDans cet article, nous en apprendrons plus sur les différents types de comparaisons, sur la façon dont JavaScript les fait, y compris sur les particularités importantes.\n\nÀ la fin, vous trouverez une bonne recette pour éviter les problèmes liés aux \"bizarreries JavaScript\".\n\n## Booléen est le résultat\n\nTout comme tous les autres opérateurs, une comparaison renvoie une valeur de type booléenne.\n\n- `true` -- signifie \"oui\", \"correct\" ou \"vrai\".\n- `false` -- signifie \"non\", \"incorrect\" ou \"faux\".\n\nPar exemple :\n\n```js run\nalert( 2 > 1 );  // true (correct)\nalert( 2 == 1 ); // false (faux)\nalert( 2 != 1 ); // true (correct)\n```\n\nUn résultat de comparaison peut être affecté à une variable, comme toute valeur :\n\n```js run\nlet result = 5 > 4; // attribue le résultat de la comparaison\nalert( result ); // true\n```\n\n## Comparaison de chaînes de caractères\n\nPour voir quelle chaîne de caractères est plus grande que l'autre, on utilise l'ordre dit \"dictionnaire\" ou \"lexicographique\".\n\nEn d'autres termes, les chaînes de caractères sont comparées lettre par lettre.\n\nPar exemple :\n\n```js run\nalert( 'Z' > 'A' ); // true\nalert( 'Glow' > 'Glee' ); // true\nalert( 'Bee' > 'Be' ); // true\nalert( '9' > '10' ); // true\n```\n\nL'algorithme pour comparer deux chaînes  de caractères est simple :\n\n1. Compare les premiers caractères des deux chaînes de caractères.\n2. Si le premier est supérieur (ou inférieur), la première chaîne de caractères est supérieure (ou inférieure) à la seconde. Nous en avons fini.\n3. Sinon, si les premiers caractères sont égaux, comparez les deuxièmes caractères de la même manière.\n4. Répéter jusqu'à la fin d'une chaîne de caractères.\n5. Si les deux chaînes de caractères se sont terminées simultanément, alors elles sont égales. Sinon, la chaîne la plus longue est plus grande.\n\nDans l'exemple ci-dessus, la comparaison `'Z' > 'A'` obtient le résultat à la première étape.\n\nLa deuxième comparaison `'Glow'` et `'Glee'` nécessite plus d'étapes car les chaînes de caractères sont comparées caractère par caractère :\n\n1. `G` est identique à `G`.\n2. `l` est identique à `l`.\n3. `o` est plus grand que `e`. On stop ici. La première chaîne de caractères est plus grande.\n\n```smart header=\"Pas vraiment un dictionnaire, mais plutôt l'ordre Unicode\"\nL'algorithme de comparaison ci-dessus est à peu près équivalent à celui utilisé dans les dictionnaires ou les annuaires téléphoniques. Mais ce n’est pas exactement la même chose.\n\nPar exemple, cette notion est importante à comprendre. Une lettre majuscule `\"A\"` n'est pas égale à la lettre minuscule `\"a\"`. Lequel est le plus grand ? En fait, le minuscule `\"a\"` l'est. Pourquoi ? Parce que le caractère minuscule a un index plus grand dans la table de codage interne (Unicode). Nous reviendrons sur les détails spécifiques et leurs conséquences dans le chapitre <info:string>.\n```\n\n## Comparaison de différents types\n\nLorsque les valeurs comparées appartiennent à différents types, elles sont converties en nombres.\n\nPar exemple :\n\n```js run\nalert( '2' > 1 ); // true, la chaîne '2' devient un numéro 2\nalert( '01' == 1 ); // true, chaîne '01' devient un numéro 1\n```\n\nPour les valeurs booléennes, `true` devient `1` et `false` devient `0`.\n\nPar exemple :\n\n```js run\nalert( true == 1 ); // true\nalert( false == 0 ); // true\n```\n\n````smart header=\"Une conséquence amusante\"\nIl est possible que dans le même temps :\n\n- Deux valeurs sont égales.\n- L'un d'eux est `vrai` comme booléen et l'autre est `faux` comme booléen.\n\nPar exemple :\n\n```js run\nlet a = 0;\nalert( Boolean(a) ); // false\n\nlet b = \"0\";\nalert( Boolean(b) ); // true\n\nalert(a == b); // true!\n```\n\nDu point de vue de JavaScript, c'est tout à fait normal. Un contrôle d'égalité convertit en utilisant la conversion numérique (par conséquent, `\"0\"` devient `0`), tandis que la `conversion booléenne` utilise un autre ensemble de règles.\n````\n\n## Égalité stricte\n\nUne vérification d'égalité régulière `==` a un problème. Elle ne peut pas faire la différence entre `0` et `false` :\n\n```js run\nalert( 0 == false ); // true\n```\n\nLa même chose avec une chaîne de caractères vide :\n\n```js run\nalert( '' == false ); // true\n```\n\nC’est parce que les opérandes de différents types sont convertis en un nombre par l’opérateur d’égalité `==`. Une chaîne de caractères vide, tout comme `false`, devient un zéro.\n\nQue faire si nous voulons différencier `0` de `faux` ?\n\n**Un opérateur d'égalité stricte `===` vérifie l'égalité sans conversion de type.**\n\nEn d'autres termes, si `a` et `b` sont de types différents, alors `a === b` renvoie immédiatement `false` sans tenter de les convertir.\n\nEssayons :\n\n```js run\nalert( 0 === false ); // false, parce que les types sont différents\n```\n\nIl existe également un opérateur de \"strict non-égalité\" `!==`, par analogie à la non-égalité `!=`.\n\nL’opérateur de vérification de l’égalité stricte est un peu plus long à écrire, mais rend évident ce qui se passe et laisse moins d’espace pour les erreurs.\n\n## Comparaison avec null et undefined\n\nIl existe un comportement non intuitif lorsque `null` ou `undefined` sont comparés à d’autres valeurs.\n\n\nPour un contrôle de strict égalité `===`\n: Ces valeurs sont différentes car chacune d’entre elles appartient à un type distinct.\n\n    ```js run\n    alert( null === undefined ); // false\n    ```\n\nPour un contrôle d'égalité non strict `==`\n: Il y a une règle spéciale. Ces deux là forment \"un beau couple\" : ils sont égaux (au sens de `==`), mais pas à d'autres valeurs.\n\n    ```js run\n    alert( null == undefined ); // true\n    ```\n\nPour les maths et autres comparaisons `<`, `>`, `<=`, `>=`\n: Les valeurs `null`/`undefined` sont converties en un nombre : `null` devient `0`, alors qu'`undefined` devient `NaN`.\n\nVoyons maintenant des choses amusantes qui se produisent lorsque nous appliquons ces règles. Et, ce qui est plus important, comment ne pas tomber dans un piège avec ces caractéristiques.\n\n### L'étrange résultat : null vs 0\n\nComparons `null` avec un zéro :\n\n```js run\nalert( null > 0 );  // (1) false\nalert( null == 0 ); // (2) false\nalert( null >= 0 ); // (3) *!*true*/!*\n```\n\nOuais, mathématiquement c'est étrange. Le dernier résultat indique que \"`null` est supérieur ou égal à zéro\". Alors que l'une des comparaisons au dessus devrait être correcte, mais les deux sont fausses.\n\nLa raison est qu'une vérification d'égalité (`==`) et les comparaisons (`<`, `>`, `<=`, `>=`) fonctionnent différemment. Les comparaisons convertissent `null` en un nombre, donc le traitent comme `0`. C'est pourquoi (3) `null >= 0` est vrai et (1) `null > 0` est faux.\n\nD’un autre côté, la vérification de l’égalité `==` pour `undefined` et `null` est définie de telle sorte que, sans aucune conversion, ils sont égaux et ne correspondent à rien d’autre. C'est pourquoi (2) `null == 0` est faux.\n\n### Un undefined incomparable\n\nLa valeur `undefined` ne doit pas du tout participer aux comparaisons :\n\n```js run\nalert( undefined > 0 );  // false (1)\nalert( undefined < 0 );  // false (2)\nalert( undefined == 0 ); // false (3)\n```\n\nPourquoi est-ce qu'il n'aime pas le zéro ? Toujours faux!\n\nNous avons ces résultats parce que :\n\n- Les comparaisons `(1)` et `(2)` renvoient `false` car `undefined` est converti en `NaN` et `NaN` est une valeur numérique spéciale qui renvoie `false` pour toutes les comparaisons.\n- Le contrôle d'égalité `(3)` renvoie `false`, car `undefined` est uniquement égal à `null` et à aucune autre valeur.\n\n### Éviter les problèmes\n\nPourquoi avons-nous observé ces exemples? Devrions-nous nous souvenir de ces particularités tout le temps ? Eh bien pas vraiment. En fait, ces notions délicates deviennent progressivement familières au fil du temps, mais il existe un moyen solide d’éviter tout problème avec elles.\n\nIl suffit de traiter toute comparaison avec `null`/`undefined` (à l'exception de la stricte égalité `===`) avec un soin exceptionnel.\n\nN'utilisez pas de comparaisons `=>`, `>`, `<`, `<=` avec une variable qui peut être `null`/`undefined`, sauf si vous êtes vraiment sûr de ce que vous faites. Si une variable peut avoir de telles valeurs, vérifiez-les séparément.\n\n## Résumé\n\n- Les opérateurs de comparaison renvoient une valeur logique.\n- Les chaînes de caractères sont comparées lettre par lettre dans l'ordre \"dictionnaire\".\n- Lorsque des valeurs de différents types sont comparées, elles sont converties en nombres (à l'exclusion d'un contrôle d'égalité strict).\n- Les valeurs `null` et `undefined` sont égales (`==`) et ne correspondent à aucune autre valeur.\n- Soyez prudent lorsque vous utilisez des comparaisons telles que `>` ou `<` avec des variables pouvant parfois être `null`/`undefined`. Faire une vérification séparée pour `null`/`undefined` est une bonne idée.\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/1-if-zero-string/solution.md",
    "content": "**Oui, il sera affiché.**\n\nToute chaîne de caractères à l'exception d'une chaîne vide (et `\"0\"` n'est pas vide) devient vraie dans le contexte logique.\n\nNous pouvons exécuter et vérifier:\n\n```js run\nif (\"0\") {\n  alert( 'Hello' );\n}\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/1-if-zero-string/task.md",
    "content": "importance: 5\n\n---\n\n# if (une chaîne de caractères avec zéro)\n\nEst-ce que `alert` sera affiché ?\n\n```js\nif (\"0\") {\n  alert( 'Hello' );\n}\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<body>\n  <script>\n    'use strict';\n\n    let value = prompt('What is the \"official\" name of JavaScript?', '');\n\n    if (value == 'ECMAScript') {\n      alert('Right!');\n    } else {\n      alert(\"You don't know? ECMAScript!\");\n    }\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/2-check-standard/solution.md",
    "content": "\n\n[html run src=\"ifelse_task2/index.html\"]\n\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/2-check-standard/task.md",
    "content": "importance: 2\n\n---\n\n# Le nom de JavaScript\n\nEn utilisant la construction `if..else`, écrivez le code qui demande : 'Quel est le nom \"officiel\" de JavaScript?'\n\nSi le visiteur entre \"ECMAScript\", alors éditez une sortie \"Bonne réponse !\", Sinon -- retourne \"Ne sait pas ? ECMAScript!\"\n\n![](ifelse_task2.svg)\n\n[demo src=\"ifelse_task2\"]\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/3-sign/if_sign/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<body>\n\n  <script>\n    'use strict';\n\n    let value = prompt('Type a number', 0);\n\n    if (value > 0) {\n      alert(1);\n    } else if (value < 0) {\n      alert(-1);\n    } else {\n      alert(0);\n    }\n  </script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/3-sign/solution.md",
    "content": "\n\n```js run\nlet value = prompt('Type a number', 0);\n\nif (value > 0) {\n  alert( 1 );\n} else if (value < 0) {\n  alert( -1 );\n} else {\n  alert( 0 );\n}\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/3-sign/task.md",
    "content": "importance: 2\n\n---\n\n# Afficher le signe\n\nEn utilisant `if..else`, écrivez le code qui obtient un numéro via le `prompt`, puis l'affiche en `alert` :\n\n- `1`, si la valeur est supérieure à zéro,\n- `-1`, si inférieur à zéro,\n- `0`, si est égal à zéro.\n\nDans cet exercice, nous supposons que l'entrée est toujours un nombre.\n\n[demo src=\"if_sign\"]\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/5-rewrite-if-question/solution.md",
    "content": "\n\n```js\nlet result = (a + b < 4) ? 'Below' : 'Over';\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/5-rewrite-if-question/task.md",
    "content": "importance: 5\n\n---\n\n# Réécrire 'if' en '?'\n\nRéécrivez ce `if` en utilisant l'opérateur conditionnel `'?'` :\n\n```js\nlet result;\n\nif (a + b < 4) {\n  result = 'Below';\n} else {\n  result = 'Over';\n}\n```\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/6-rewrite-if-else-question/solution.md",
    "content": "\n\n```js\nlet message = (login == 'Employee') ? 'Hello' :\n  (login == 'Director') ? 'Greetings' :\n  (login == '') ? 'No login' :\n  '';\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/6-rewrite-if-else-question/task.md",
    "content": "importance: 5\n\n---\n\n# Réécrire 'if..else' en '?'\n\nRéécrire ce `if..else` en utilisant plusieurs opérateurs ternaires `'?'`.\n\nPour plus de lisibilité, il est recommandé de diviser le code en plusieurs lignes.\n\n```js\nlet message;\n\nif (login == 'Employee') {\n  message = 'Hello';\n} else if (login == 'Director') {\n  message = 'Greetings';\n} else if (login == '') {\n  message = 'No login';\n} else {\n  message = '';\n}\n```\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/article.md",
    "content": "# Branche conditionnelle : if, '?'\n\nParfois, nous devons effectuer différentes actions en fonction d'une condition.\n\nPour ce faire, nous pouvons utiliser l'instruction `if` et l'opérateur conditionnel `? `, également appelé opérateur \"point d'interrogation\".\n\n## L'instruction \"if\"\n\nL'instruction `if(...)` évalue une condition entre parenthèses et, si le résultat est `true`, exécute un bloc de code.\n\nPar exemple :\n\n```js run\nlet year = prompt('In which year was ECMAScript-2015 specification published?', '');\n\n*!*\nif (year == 2015) alert( 'You are right!' );\n*/!*\n```\n\nDans l'exemple ci-dessus, la condition est une vérification d'égalité simple : `year == 2015`, mais elle peut être beaucoup plus complexe.\n\nS'il y a plus d'une instruction à exécuter, nous devons envelopper notre bloc de code entre accolades :\n\n```js\nif (year == 2015) {\n  alert( \"That's correct!\" );\n  alert( \"You're so smart!\" );\n}\n```\n\nIl est recommandé d'entourer votre bloc de code avec des accolades `{}` à chaque fois avec `if`, même s’il n’y a qu’une seule instruction. Cela améliore la lisibilité.\n\n## Conversion booléenne\n\nL'instruction `if (…)` évalue l'expression entre parenthèses et la convertit en type booléen.\n\nRappelons les règles de conversion du chapitre <info:type-conversions>:\n\n- Un nombre `0`, une chaîne de caractères vide `\"\"`, `null`, `undefined` et `NaN` deviennent `false`. À cause de cela, on dit de ces valeurs qu'elles sont \"falsy\".\n- Les autres valeurs deviennent `true`, on dit qu'elles sont \"truthy\".\n\nDonc, le code sous cette condition ne sera jamais exécuté :\n\n```js\nif (0) { // 0 est falsy\n  ...\n}\n```\n\n… Et à l'intérieur de cette condition - il fonctionne toujours :\n\n```js\nif (1) { // 1 est truthy\n  ...\n}\n```\n\nNous pouvons également transmettre une valeur booléenne pré-évaluée à `if`, comme ici :\n\n```js\nlet cond = (year == 2015); // l'égalité évalue à vrai ou faux\n\nif (cond) {\n  ...\n}\n```\n\n## La clause \"else\"\n\n\nL'instruction `if` peut contenir un bloc optionnel `else`. Il s'exécute lorsque la condition est fausse.\n\nPar exemple :\n```js run\nlet year = prompt('In which year was the ECMAScript-2015 specification published?', '');\n\nif (year == 2015) {\n  alert( 'You guessed it right!' );\n} else {\n  alert( 'How can you be so wrong?' ); // toute autre valeur que 2015\n}\n```\n\n## Plusieurs conditions : \"else if\"\n\nParfois, nous aimerions tester plusieurs variantes d'une condition. Il y a une clause `else if` pour cela.\n\nPar exemple :\n\n```js run\nlet year = prompt('En quelle année la spécification ECMAScript-2015 a-t-elle été publiée ?', '');\n\nif (year < 2015) {\n  alert( 'Too early...' );\n} else if (year > 2015) {\n  alert( 'Too late' );\n} else {\n  alert( 'Exactly!' );\n}\n```\n\nDans le code ci-dessus, JavaScript vérifie `year < 2015`. S'il est falsy, il passe à l'année de condition suivante `year > 2015` et c'est toujours `false` il passe à la dernière instruction et affiche la dernière alerte.\n\nIl peut y avoir plus de blocks `else if`. Le dernier `else` est optionnel.\n\n## Opérateur ternaire '?'\n\nParfois, nous devons attribuer une variable en fonction d'une condition.\n\nPar exemple :\n\n```js run no-beautify\nlet accessAllowed;\nlet age = prompt('How old are you?', '');\n\n*!*\nif (age > 18) {\n  accessAllowed = true;\n} else {\n  accessAllowed = false;\n}\n*/!*\n\nalert(accessAllowed);\n```\n\nL'opérateur dit \"ternaire\" ou \"point d'interrogation\" nous permet de le faire plus rapidement et plus simplement.\n\nL'opérateur est représenté par un point d'interrogation `?`. Appelé aussi \"ternaire\" parce que l'opérateur a trois opérandes. C'est en fait le seul et unique opérateur en JavaScript qui en a autant.\n\nLa syntaxe est :\n```js\nlet result = condition ? value1 : value2\n```\n\nLa `condition` est évaluée, si elle est vraie, alors `value1` est retournée, sinon -- `value2`.\n\nPar exemple :\n\n```js\nlet accessAllowed = (age > 18) ? true : false;\n```\n\nTechniquement, nous pouvons omettre les parenthèses autour de `age > 18`. L'opérateur point d'interrogation a une faible précédence, il s'exécute donc après la comparaison `>`.\n\nCet exemple fera la même chose que le précédent :\n\n```js\n// l'opérateur de comparaison \"age > 18\" s'exécute en premier quoiqu'il en soit\n// (pas besoin de l'envelopper entre parenthèses)\nlet accessAllowed = age > 18 ? true : false;\n```\n\nMais les parenthèses rendent le code plus lisible, il est donc recommandé de les utiliser.\n\n````smart\nDans l'exemple ci-dessus, il est possible d'éviter l'opérateur ternaire, parce que la comparaison elle-même renvoie un `true/false`:\n\n```js\n// la même chose\nlet accessAllowed = age > 18;\n```\n````\n\n## Multiple '?'\n\nUne séquence d'opérateurs point d'interrogation `?` permettent de renvoyer une valeur qui dépend de plusieurs conditions.\n\nPar exemple :\n```js run\nlet age = prompt('age?', 18);\n\nlet message = (age < 3) ? 'Hi, baby!' :\n  (age < 18) ? 'Hello!' :\n  (age < 100) ? 'Greetings!' :\n  'What an unusual age!';\n\nalert( message );\n```\n\nIl peut être difficile au début de comprendre ce qui se passe. Mais après un examen plus approfondi, nous constatons que ce n’est qu’une séquence de tests ordinaire.\n\n\n1. Le premier point d'interrogation vérifie si `age < 3`.\n\n2. Si vrai -- retourne `'Hi, baby!'`, Sinon, il continue avec l'expression après les deux points \":\" suivants et vérifie si `age < 18`.\n3. Si vrai -- retourne `'Hello!'`, Sinon, il continue avec l'expression après les deux points \":\" suivants et vérifie si `age < 100`.\n4. Si vrai -- retourne `'Greetings!'`, Sinon, l'expression continue après les derniers deux-points et retourne `'What an unusual age!'`.\n\nLa même logique utilisant `if..else` :\n\n```js\nif (age < 3) {\n  message = 'Hi, baby!';\n} else if (age < 18) {\n  message = 'Hello!';\n} else if (age < 100) {\n  message = 'Greetings!';\n} else {\n  message = 'What an unusual age!';\n}\n```\n\n## Utilisation non traditionnelle de '?'\n\nParfois, le point d'interrogation `?` est utilisé en remplacement de `if` :\n\n```js run no-beautify\nlet company = prompt('Which company created JavaScript?', '');\n\n*!*\n(company == 'Netscape') ?\n   alert('Right!') : alert('Wrong.');\n*/!*\n```\n\nSelon si la condition  `company == 'Netscape'` est vraie ou non, la première ou la deuxième partie après `?` s'exécute et affiche l'alerte correspondante.\n\nNous n’attribuons pas de résultat à une variable ici. L'idée est d'exécuter un code différent en fonction de la condition.\n\n**Il n'est pas recommandé d'utiliser l'opérateur ternaire de cette manière.**\n\nLa notation semble être plus courte qu'un `if`, ce qui plaît à certains programmeurs. Mais c'est moins lisible.\n\nVoici le même code avec `if` pour comparaison :\n\n```js run no-beautify\nlet company = prompt('Which company created JavaScript?', '');\n\n*!*\nif (company == 'Netscape') {\n  alert('Right!');\n} else {\n  alert('Wrong.');\n}\n*/!*\n```\n\nNos yeux scrutent le code verticalement. Les constructions qui couvrent plusieurs lignes sont plus faciles à comprendre qu'un jeu d'instructions horizontal long.\n\nL'idée d'un point d'interrogation `?` est de renvoyer l'une ou l'autre valeur selon la condition. Veuillez l'utiliser pour cela exactement. Il y a `if` pour exécuter différentes branches du code.\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/1-alert-null-2-undefined/solution.md",
    "content": "La réponse est `2`, c'est la première valeur vraie.\n\n```js run\nalert( null || 2 || undefined );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/1-alert-null-2-undefined/task.md",
    "content": "importance: 5\n\n---\n\n# Quel est le résultat de OR ?\n\nQu'est-ce que le code ci-dessous va sortir ?\n\n```js\nalert( null || 2 || undefined );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/2-alert-or/solution.md",
    "content": "La réponse : d'abord `1` puis `2`.\n\n```js run\nalert( alert(1) || 2 || alert(3) );\n```\n\nRègle importante à retenir : **L'appel de l'`alert` ne renvoie pas de valeur. Ou, en d'autres termes, il retourne `undefined`.**\n\n1. Le premier OR `||` évalue son opérande gauche `alert(1)`. Cela affiche le premier message avec `1`.\n2. L'`alert` retourne `undefined`, donc OR passe au deuxième opérande en recherchant une valeur vraie.\n3. Le deuxième opérande `2` est vrai, donc l'exécution est interrompue, `2` est renvoyé puis affiché par l'alerte externe.\n\nIl n'y aura pas de `3`, car l'évaluation n'atteint pas l'`alert (3)`.\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/2-alert-or/task.md",
    "content": "importance: 3\n\n---\n\n# Quel est le résultat des alertes OR ?\n\nQu'est-ce que le code ci-dessous va sortir ?\n\n```js\nalert( alert(1) || 2 || alert(3) );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/solution.md",
    "content": "La réponse : `null`, car c'est la première valeur `false` de la liste.\n\n```js run\nalert(1 && null && 2);\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/task.md",
    "content": "importance: 5\n\n---\n\n# Quel est le résultat de AND ?\n\nQu'est-ce que ce code va afficher ?\n\n```js\nalert( 1 && null && 2 );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/4-alert-and/solution.md",
    "content": "La réponse : `1`, et ensuite `undefined`.\n\n```js run\nalert( alert(1) && alert(2) );\n```\n\nL’appel de l'`alert` renvoie undefined (il affiche juste un message, donc il n’ya pas de retour significatif dans le code).\n\nÀ cause de cela, `&&` évalue l'opérande gauche (sortie 1), et s'arrête immédiatement, car `undefined` est une valeur `false`. Et comme `&&` recherche la première valeur fausse et la retourne, donc il s'arrête là.\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/4-alert-and/task.md",
    "content": "importance: 3\n\n---\n\n# Quel est le résultat des alertes AND ?\n\nQu’est-ce que ce code va afficher ?\n\n```js\nalert( alert(1) && alert(2) );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/5-alert-and-or/solution.md",
    "content": "La réponse : `3`.\n\n```js run\nalert( null || 2 && 3 || 4 );\n```\n\nLa priorité de AND `&&` est supérieure à OR `||`, elle s'exécute donc en premier.\n\nLe résultat de `2 && 3 = 3`, donc l'expression devient :\n\n```\nnull || 3 || 4\n```\n\nMaintenant, le résultat est la première valeur vraie : `3`.\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/5-alert-and-or/task.md",
    "content": "importance: 5\n\n---\n\n# Le résultat de OR AND OR\n\nQuel sera le résultat ?\n\n```js\nalert( null || 2 && 3 || 4 );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/6-check-if-in-range/solution.md",
    "content": "\n\n```js\nif (age >= 14 && age <= 90)\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/6-check-if-in-range/task.md",
    "content": "importance: 3\n\n---\n\n# Vérifiez la plage entre\n\nEcrivez une condition `\"if\"` pour vérifier que l’`age` est compris entre `14` et `90` ans inclus.\n\n\"Inclus\" signifie que l’`age` peut atteindre les `14` ou `90` ans.\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/7-check-if-out-range/solution.md",
    "content": "La première variante :\n\n```js\nif (!(age >= 14 && age <= 90))\n```\n\nLa seconde variante :\n\n```js\nif (age < 14 || age > 90)\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/7-check-if-out-range/task.md",
    "content": "importance: 3\n\n---\n\n# Vérifiez à l'extérieur de la plage\n\nEcrivez une condition `if` pour vérifier que l’`age` n’est PAS compris entre `14` et `90` ans inclus.\n\nCréez deux variantes: la première utilisant NOT `!`, La seconde - sans ce dernier.\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/8-if-question/solution.md",
    "content": "La réponse: le premier et le troisième vont s'exécuter.\n\nDetails:\n\n```js run\n// S'éxécute\n// le résultat de -1 || 0 = -1, vrai\nif (-1 || 0) alert( 'first' );\n\n// Ne s'éxécute pas\n// -1 && 0 = 0, faux\nif (-1 && 0) alert( 'second' );\n\n// S'éxécute\n// L'opérateur && a une précédence plus élevée que ||\n// donc -1 && 1 s'exécute en premier, nous donnant la chaîne :\n// null || -1 && 1  ->  null || 1  ->  1\nif (null || -1 && 1) alert( 'third' );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/8-if-question/task.md",
    "content": "importance: 5\n\n---\n\n# Une question à propos de \"if\"\n\nLesquelles de ces `alert`es vont s'exécuter ?\n\nQuels seront les résultats des expressions à l'intérieur de `if (...)` ?  \n\n```js\nif (-1 || 0) alert( 'first' );\nif (-1 && 0) alert( 'second' );\nif (null || -1 && 1) alert( 'third' );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/9-check-login/solution.md",
    "content": "\n\n```js run demo\nlet userName = prompt(\"Who's there?\", '');\n\nif (userName === 'Admin') {\n\n  let pass = prompt('Password?', '');\n\n  if (pass === 'TheMaster') {\n    alert( 'Welcome!' );\n  } else if (pass === '' || pass === null) {\n    alert( 'Canceled' );\n  } else {\n    alert( 'Wrong password' );\n  }\n\n} else if (userName === '' || userName === null) {\n  alert( 'Canceled' );\n} else {\n  alert( \"I don't know you\" );\n}\n```\n\nNotez les retraits verticaux à l'intérieur des blocs `if`. Ils ne sont techniquement pas nécessaires, mais rendent le code plus lisible.\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/9-check-login/task.md",
    "content": "importance: 3\n\n---\n\n# Check the login\n\nÉcrivez le code qui demande une connexion avec `prompt`.\n\nSi le visiteur entre `\"Admin\"`, puis `prompt` pour un mot de passe, si l'entrée est une ligne vide ou `key:Esc` -- affichez \"Canceled\", s'il s'agit d'une autre chaîne de caractères -- alors affichez \"I don't know you\".\n\nLe mot de passe est vérifié comme suit :\n\n- S'il est égal à \"TheMaster\", alors affichez \"Welcome!\",\n- Une autre chaînede caractères -- affichez \"Wrong password\",\n- Pour une chaîne de caractères vide ou une entrée annulée, affichez \"Canceled\".\n\nLe schéma :\n\n![](ifelse_task.svg)\n\nVeuillez utiliser des blocs `if` imbriqués. Attention à la lisibilité globale du code.\n\nAstuce: passer une entrée vide à un prompt renvoie une chaîne de caractères vide `''`. En pressant `key:ESC` lors d'un prompt cela retourne `null`.\n\n[demo]\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/article.md",
    "content": "# Opérateurs logiques\n\nIl y a trois opérateurs logiques en JavaScript : `||` (OR), `&&` (AND), `!` (NOT), `??` (Coalescence des nulles). Nous couvrons ici les trois premiers, l'opérateur `??` est dans l'article suivant.\n\nBien qu'ils soient appelés \"logiques\", ils peuvent être appliqués à des valeurs de tout type, pas seulement booléennes. Le résultat peut également être de tout type.\n\nVoyons les détails.\n\n## || (OR)\n\nL'opérateur \"OR\" est représenté avec deux symboles de ligne verticale :\n\n```js\nresult = a || b;\n```\n\nEn programmation classique, le OU logique est destiné à manipuler uniquement les valeurs booléennes. Si l'un de ses arguments est `true`, alors il renvoie `true`, sinon il renvoie `false`.\n\nEn JavaScript, l'opérateur est un peu plus compliqué et puissant. Mais voyons d’abord ce qui se passe avec les valeurs booléennes.\n\nIl existe quatre combinaisons logiques possibles :\n\n```js run\nalert( true  || true  ); // true\nalert( false || true  ); // true\nalert( true  || false ); // true\nalert( false || false ); // false\n```\n\nComme on peut le voir, le résultat est toujours `true` sauf pour le cas où les deux opérandes sont `false`.\n\nSi un opérande n'est pas booléen, il est converti en booléen pour l'évaluation.\n\nPar exemple, un nombre `1` est traité comme `true`, un nombre `0` - comme `false` :\n\n```js run\nif (1 || 0) { // fonctionne comme si ( true || false )\n  alert( 'truthy!' );\n}\n```\n\nLa plupart du temps, OR `||` est utilisé dans une instruction `if` pour tester si `l'une` des conditions données est correcte.\n\nPar exemple :\n\n```js run\nlet hour = 9;\n\n*!*\nif (hour < 10 || hour > 18) {\n*/!*\n  alert( 'The office is closed.' );\n}\n```\n\nNous pouvons passer plus de conditions :\n\n```js run\nlet hour = 12;\nlet isWeekend = true;\n\nif (hour < 10 || hour > 18 || isWeekend) {\n  alert( 'The office is closed.' ); // c'est le weekend\n}\n```\n\n### OR \"||\" cherche la première valeur vraie [#or-cherche-la-premiere-valeur-vraie]\n\nLa logique décrite ci-dessus est quelque peu classique. Maintenant, apportons les fonctionnalités \"supplémentaires\" de JavaScript.\n\nL'algorithme étendu fonctionne comme suit.\n\nEn cas de multiples valeurs liées par OR :\n\n```js\nresult = value1 || value2 || value3;\n```\n\nL'opérateur OR `||` fait ce qui suit :\n\n- Évaluez les opérandes de gauche à droite.\n- Pour chaque opérande, il le convertit en booléen. Si le résultat est `true`, arrêtez et retournez la valeur d'origine de cet opérande.\n- Si tous les autres opérandes ont été évalués (c’est-à-dire que tous étaient `false`), renvoyez le dernier opérande.\n\nUne valeur est renvoyée sous sa forme d'origine, sans conversion.\n\nEn d'autres termes, une chaîne de OR `||` renvoie la première valeur `true` ou la dernière valeur si aucune valeur `true` n'a été trouvée.\n\nPar exemple :\n\n```js run\nalert( 1 || 0 ); // 1 (1 est vrai)\n\nalert( null || 1 ); // 1 (1 est la première valeur vraie)\nalert( null || 0 || 1 ); // 1 (la première valeur vraie)\n\nalert( undefined || null || 0 ); // 0 (tous faux, renvoie la dernière valeur)\n```\n\nCela conduit à des usages intéressants par rapport à un \"OR pur, classique, booléen uniquement\".\n\n1. **Obtenir la première valeur vraie dans la liste des variables ou des expressions.**\n\n    Par exemple, nous avons les variables `firstName`, `lastName` et `nickName`, toutes optionnelles (c'est-à-dire peut être indéfini ou avoir des valeurs fausses).\n\n    Utilisons OR `||` pour choisir celui qui contient les données et l'afficher (ou `Anonymous` si rien n'est défini) :\n\n    ```js run\n    let firstName = \"\";\n    let lastName = \"\";\n    let nickName = \"SuperCoder\";\n\n    *!*\n    alert( firstName || lastName || nickName || \"Anonymous\"); // SuperCoder\n    */!*\n    ```\n\n    Si toutes les variables étaient fausses, ce serait `\"Anonymous\"` qui apparaîtrait.\n\n2. **Évaluation des courts-circuits.**\n\n    Une autre caractéristique de l'opérateur OR `||` est l'évaluation dite de \"court-circuit\".\n\n    Cela signifie que `||` traite ses arguments jusqu'à ce que la première valeur de vérité soit atteinte, puis la valeur est renvoyée immédiatement, sans même toucher l'autre argument.\n\n    L'importance de cette fonctionnalité devient évidente si un opérande n'est pas seulement une valeur, mais une expression avec un effet secondaire, comme une affectation de variable ou un appel de fonction.\n\n    Dans l'exemple ci-dessous, seul le deuxième message est imprimé :\n\n    ```js run no-beautify\n    *!*true*/!* || alert(\"not printed\");\n    *!*false*/!* || alert(\"printed\");\n    ```\n\n    Dans la première ligne, l'opérateur OR `||` arrête l'évaluation immédiatement après avoir vu `true`, de sorte que `alert` n'est pas exécuté.\n\n    Parfois, les gens utilisent cette fonctionnalité pour exécuter des commandes uniquement si la condition sur la partie gauche est fausse.\n\n## && (AND)\n\nL'opérateur AND est représenté avec deux esperluettes `&&` :\n\n```js\nresult = a && b;\n```\n\nEn programmation classique, AND retourne `true` si les deux opérandes sont `true` et `false` dans les autres cas :\n\n```js run\nalert( true  && true  ); // true\nalert( false && true  ); // false\nalert( true  && false ); // false\nalert( false && false ); // false\n```\n\nUn exemple avec `if`:\n\n```js run\nlet hour = 12;\nlet minute = 30;\n\nif (hour == 12 && minute == 30) {\n  alert( 'Time is 12:30' );\n}\n```\n\nTout comme pour OR, toute valeur est autorisée en tant qu'opérande de AND :\n\n```js run\nif (1 && 0) { // évalué comme true && false\n  alert( \"Ne marchera pas, car le résultat est faux\" );\n}\n```\n\n\n## AND \"&&\" cherche la première valeur fausse\n\nEn cas de multiples valeurs liées par AND :\n\n```js\nresult = value1 && value2 && value3;\n```\n\nL'opérateur AND `&&` effectue les opérations suivantes :\n\n- Évalue les opérandes de gauche à droite.\n- Pour chaque opérande, il le convertit en booléen. Si le résultat est `false`, arrêtez et retournez la valeur d'origine de cet opérande.\n- Si tous les autres opérandes ont été évalués (c’est-à-dire tous étaient vrais), retournez le dernier opérande.\n\nEn d'autres termes, une chaîne de AND `&&` renvoie la première valeur `false` ou la dernière valeur si aucune valeur `false` n'a été trouvée.\n\nLes règles ci-dessus sont similaires à OR. La différence est que AND retourne la première valeur `false` tandis que OR renvoie la première valeur `true`.\n\nExemples :\n\n```js run\n// si le premier opérande est vrai,\n// AND retourne le second opérande :\nalert( 1 && 0 ); // 0\nalert( 1 && 5 ); // 5\n\n// si le premier opérande est faux,\n// AND le retourne. Le deuxième opérande est ignoré\nalert( null && 5 ); // null\nalert( 0 && \"no matter what\" ); // 0\n```\n\nNous pouvons également transmettre plusieurs valeurs à la suite sur une même ligne. Voyez comment le premier faux est retourné :\n\n```js run\nalert( 1 && 2 && null && 3 ); // null\n```\n\nLorsque toutes les valeurs sont vraies, la dernière valeur est renvoyée :\n\n```js run\nalert( 1 && 2 && 3 ); // 3, la dernière\n```\n\n````smart header=\"La précédence de AND `&&` est supérieure à OR `||`\"\nLa priorité de l'opérateur AND `&&` est supérieure à OR `||`.\n\nDonc, le code `a && b || c && d` est essentiellement le même que si `&&` était entre parenthèses: `(a && b) || (c && d)`.\n````\n\n````warn header=\"Ne remplacez pas `if` par `||` ou `&&`\"\nParfois, les gens utilisent l'opérateur AND `&&` comme \"plus court pour écrire `if`\".\n\nPar exemple :\n\n```js run\nlet x = 1;\n\n(x > 0) && alert( 'Greater than zero!' );\n```\n\nL'action dans la partie droite de `&&` ne s'exécutera que si l'évaluation lui parvient. C'est-à-dire que seulement si `(x > 0)` est vrai.\n\nDonc, nous avons une analogie pour :\n\n```js run\nlet x = 1;\n\nif (x > 0) alert( 'Greater than zero!' );\n```\n\nBien que la variante avec `&&` semble plus courte, `if` est plus évidente et a tendance à être un peu plus lisible. Nous recommandons donc d'utiliser chaque construction dans son but : utilisez `if` si nous voulons `if` et utilisez `&&` si nous voulons ET.\n````\n\n\n## ! (NOT)\n\nL'opérateur booléen NOT est représenté par un point d'exclamation `!`.\n\nLa syntaxe est assez simple :\n\n```js\nresult = !value;\n```\n\nL'opérateur accepte un seul argument et effectue les opérations suivantes :\n\n1. Convertit l'opérande en type booléen : `true/false`.\n2. Renvoie la valeur inverse.\n\nPar exemple :\n\n```js run\nalert( !true ); // false\nalert( !0 ); // true\n```\n\nUn double NOT `!!` est parfois utilisé pour convertir une valeur en type booléen :\n\n```js run\nalert( !!\"non-empty string\" ); // true\nalert( !!null ); // false\n```\n\nC'est-à-dire que le premier NOT convertit la valeur en booléen et retourne l'inverse, et que le second NOT l'inverse encore. À la fin, nous avons une conversion valeur à booléen simple.\n\nIl existe un moyen un peu plus verbeux de faire la même chose -- une fonction `Boolean` intégrée :\n\n```js run\nalert( Boolean(\"non-empty string\") ); // true\nalert( Boolean(null) ); // false\n```\n\nLa précédence de NOT `!` est la plus élevée de tous les opérateurs binaire, il est donc toujours exécuté en premier, avant les autres.\n"
  },
  {
    "path": "1-js/02-first-steps/12-nullish-coalescing-operator/article.md",
    "content": "# L'opérateur de coalescence des nuls '??'\n\n[recent browser=\"new\"]\n\nL'opérateur de coalescence des nuls est écrit sous la forme de deux points d'interrogation `??`.\n\nComme il traite `null` et `undefined` de la même manière, nous utiliserons un terme spécial ici, dans cet article. Par souci de brièveté, nous dirons qu'une expression est \"définie\" lorsqu'elle n'est ni `null` ni `undefined`.\n\nLe résultat de `a ?? b` est :\n- si `a` est défini, alors `a`,\n- si `a` n'est pas défini, alors `b`.\n\nEn d'autres termes, `??` renvoie le premier argument s'il n'est pas `null`/`undefined`. Sinon, le second.\n\nL'opérateur de coalescence des nuls n'est pas complètement nouveau. C'est juste une belle syntaxe pour obtenir la première valeur \"defined\" des deux.\n\nNous pouvons réécrire `result = a ?? b` en utilisant les opérateurs que nous connaissons déjà, comme ceci :\n\n```js\nresult = (a !== null && a !== undefined) ? a : b;\n```\n\nMaintenant, il devrait être absolument clair ce que fait `??`. Voyons où cela aide.\n\nLe cas d'utilisation courant de `??` est de fournir une valeur par défaut.\n\nPar exemple, nous affichons ici `user` si sa valeur n'est pas `null`/`undefined`, sinon `Anonymous` :\n\n```js run\nlet user;\n\nalert(user ?? \"Anonymous\"); // Anonymous (user is undefined)\n```\n\nVoici l'exemple avec `user` attribué à un nom :\n\n```js run\nlet user = \"John\";\n\nalert(user ?? \"Anonymous\"); // John (user is not null/undefined)\n```\n\nNous pouvons également utiliser une séquence de `??` pour sélectionner la première valeur dans une liste qui n'est pas `null`/`undefined`.\n\nDisons que nous avons les données d'un utilisateur dans les variables `firstName`, `lastName` ou `nickName`. Tous peuvent être indéfinis, si l'utilisateur décide de ne pas entrer de valeurs correspondantes.\n\nNous aimerions afficher le nom d'utilisateur à l'aide de l'une de ces variables, ou afficher \"Anonyme\" si toutes sont `null`/`undefined`.\n\nUtilisons l'opérateur `??` pour cela :\n\n```js run\nlet firstName = null;\nlet lastName = null;\nlet nickName = \"Supercoder\";\n\n// affiche la première valeur définie :\n*!*\nalert(firstName ?? lastName ?? nickName ?? \"Anonymous\"); // Supercoder\n*/!*\n```\n\n## Comparaison avec ||\n\nL'opérateur OR `||` peut être utilisé de la même manière que `??`, comme il a été décrit dans le [chapitre précédent](info:logical-operators#or-finds-the-first-truthy-value).\n\nPar exemple, dans le code ci-dessus, nous pourrions remplacer `??` par `||` et toujours obtenir le même résultat :\n\n```js run\nlet firstName = null;\nlet lastName = null;\nlet nickName = \"Supercoder\";\n\n// affiche la première valeur vraie :\n*!*\nalert(firstName || lastName || nickName || \"Anonymous\"); // Supercoder\n*/!*\n```\n\nHistoriquement, l'opérateur OR `||` était là en premier. Il existe depuis le début de JavaScript, donc les développeurs l'utilisaient à de telles fins depuis longtemps.\n\nD'un autre côté, l'opérateur de coalescence des nuls `??` n'a été ajouté à JavaScript que récemment, et la raison en était que les gens n'étaient pas tout à fait satisfaits de `||`.\n\nLa différence importante entre eux est que :\n- `||` renvoie la première valeur *vraie*.\n- `??` renvoie la première valeur *définie*.\n\nEn d'autres termes, `||` ne fait pas la distinction entre `false`, `0`, une chaîne vide `\" \"` et `null`/`undefined`. Ce sont tous les mêmes -- des valeurs fausses. Si l'un de ceux-ci est le premier argument de `||`, alors nous obtiendrons le deuxième argument comme résultat.\n\nDans la pratique cependant, nous pouvons vouloir utiliser la valeur par défaut uniquement lorsque la variable est `null`/`undefined`. Autrement dit, lorsque la valeur est vraiment inconnue/non définie.\n\nPar exemple, considérez ceci :\n\n```js run\nlet height = 0;\n\nalert(height || 100); // 100\nalert(height ?? 100); // 0\n```\n\n- L'expression `height || 100` vérifie que `height` est une valeur fausse, et c'est `0`, elle est fausse en effet.\n    - donc le résultat de `||` est le deuxième argument, `100`.\n- L'expression `height ?? 100` vérifie que `height` est `null`/`undefined`, et ce n'est pas le cas,\n    - donc le résultat est `height` \"tel quel\", c'est-à-dire `0`.\n\nEn pratique, la hauteur zéro est souvent une valeur valide, qui ne doit pas être remplacée par la valeur par défaut. Alors `??` fait ce qu'il faut.\n\n## Priorité\n\nLa priorité de l'opérateur `??` est la même que celle de `||`. Elle est égale à `3` dans le [tableau MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table).\n\nCela signifie que, tout comme `||`, l'opérateur de coalescence des nuls `??` est évalué avant `=` et `?`, mais après la plupart des autres opérations, telles que `+`, `*`.\n\nNous devrons donc peut-être ajouter des parenthèses dans des expressions comme celle-ci :\n\n```js run\nlet height = null;\nlet width = null;\n\n// important : utilisez des parenthèses\nlet area = (height ?? 100) * (width ?? 50);\n\nalert(area); // 5000\n```\n\nSinon, si nous omettons les parenthèses, alors que `*` a une priorité plus élevée que `??`, il s'exécuterait en premier, conduisant à des résultats incorrects.\n\n```js\n// sans parenthèses\nlet area = height ?? 100 * width ?? 50;\n\n// ...fonctionne de cette façon (pas ce que nous voulons) :\nlet area = height ?? (100 * width) ?? 50;\n```\n\n### En utilisant ?? avec && ou ||\n\nPour des raisons de sécurité, JavaScript interdit d'utiliser `??` avec les opérateurs `&&` et `||`, à moins que la priorité ne soit explicitement spécifiée entre parenthèses.\n\nLe code ci-dessous déclenche une erreur de syntaxe :\n\n```js run\nlet x = 1 && 2 ?? 3; // Syntax error\n```\n\nLa limitation est sûrement discutable, mais elle a été ajoutée à la spécification du langage dans le but d'éviter les erreurs de programmation, quand les gens commencent à passer de `||` à `??`.\n\nUtilisez des parenthèses explicites pour la contourner :\n\n```js run\n*!*\nlet x = (1 && 2) ?? 3; // fonctionne\n*/!*\n\nalert(x); // 2\n```\n\n## Résumé\n\n- L'opérateur de coalescence des nuls `??` fournit un moyen court de choisir une valeur \"définie\" à partir d'une liste.\n\n    Il est utilisé pour attribuer des valeurs par défaut aux variables :\n\n    ```js\n    // configurer height = 100, si height est null ou undefined\n    height = height ?? 100;\n    ```\n\n- L'opérateur `??` a une priorité très faible, un peu plus élevée que `?` et `=`, pensez donc à ajouter des parenthèses lors de son utilisation dans une expression.\n- Il est interdit de l'utiliser avec `||` ou `&&` sans parenthèses explicites.\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/1-loop-last-value/solution.md",
    "content": "La réponse : `1`.\n\n```js run\nlet i = 3;\n\nwhile (i) {\n  alert( i-- );\n}\n```\n\nChaque itération de boucle diminue `i` de `1`. La vérification `while(i)` arrête la boucle lorsque `i = 0`.\n\nPar conséquent, les étapes de la boucle forment la séquence suivante (\"boucle décomposée\") :\n\n```js\nlet i = 3;\n\nalert(i--); // affiche 3, diminue i à 2\n\nalert(i--) // affiche 2, diminue i à 1\n\nalert(i--) // affiche 1, diminue i à 0\n\n// terminé, la vérification while(i) termine la boucle\n```\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/1-loop-last-value/task.md",
    "content": "importance: 3\n\n---\n\n# Dernière valeur de boucle\n\nQuelle est la dernière valeur affichée par ce code ? Pourquoi ?\n\n```js\nlet i = 3;\n\nwhile (i) {\n  alert( i-- );\n}\n```\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/2-which-value-while/solution.md",
    "content": "L'exercice montre comment les formes postfix/prefix peuvent conduire à des résultats différents lorsqu’ils sont utilisés dans des comparaisons.\n\n1. **De 1 à 4**\n\n    ```js run\n    let i = 0;\n    while (++i < 5) alert( i );\n    ```\n\n    La première valeur est `i=1`, parce que `++i` incrémente d'abord `i` puis renvoie la nouvelle valeur. La première comparaison est donc `1 < 5` et `alert` indique `1`.\n\n    Ensuite, viennent `2,3,4…` -- les valeurs apparaissent les unes après les autres. La comparaison utilise toujours la valeur incrémentée, car `++` est avant la variable.\n\n    Enfin, `i=4` est incrémenté à `5`, la comparaison `while(5 < 5)` échoue et la boucle s'arrête. Donc `5` n'est pas affiché.\n2. **De 1 à 5**\n\n    ```js run\n    let i = 0;\n    while (i++ < 5) alert( i );\n    ```\n\n    La première valeur est encore `i=1`. La forme postfixée de `i++` incrémente `i` puis renvoie *l'ancienne* valeur, la comparaison `i++ < 5` utilisera donc `i=0` (contrairement à `++i < 5`).\n\n    Mais l'appel d'`alert` est séparé. C’est une autre instruction qui s’exécute après l’incrémentation et la comparaison. Donc, on obtient `i=1`.\n\n    Ensuite viennent `2,3,4…`\n\n    Arrêtons-nous sur `i=4`. Le préfixe sous forme `++i` l'incrémenterait et utiliserait `5` dans la comparaison. Mais ici nous avons la forme postfixée `i++`. Donc, `i` augmente à `5`, mais renvoie l'ancienne valeur. Par conséquent, la comparaison est en réalité `while(4 < 5)` -- true, et le contrôle continue à `alert`.\n\n    La valeur `i=5` est la dernière, car à l'étape suivante `while(5 <5)` est faux. \n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/2-which-value-while/task.md",
    "content": "importance: 4\n\n---\n\n# Quelles valeurs affiche la boucle while ?\n\nA votre avis, quelles sont les valeurs affichées pour chaque boucle ?  Notez-les puis comparer avec la réponse.\n\nLes deux boucles affichent-elles les mêmes valeurs dans l'`alert` ou pas ?\n\n1. Le préfixe sous forme `++i` :\n\n    ```js\n    let i = 0;\n    while (++i < 5) alert( i );\n    ```\n2. Le postfixe sous forme `i++` :\n\n    ```js\n    let i = 0;\n    while (i++ < 5) alert( i );\n    ```\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/3-which-value-for/solution.md",
    "content": "**La réponse: de `0` à `4` dans les deux cas.**\n\n```js run\nfor (let i = 0; i < 5; ++i) alert( i );\n\nfor (let i = 0; i < 5; i++) alert( i );\n```\n\nCela peut être facilement déduit de l'algorithme de `for` :\n\n1. Exécute une fois `i = 0` avant tout (début).\n2. Vérifie l'état `i < 5`\n3. Si `true` -- execute le corps de la boucle `alert(i)`, et ensuite `i++`\n\nL'incrément `i++` est séparé de la vérification de condition (2). C’est juste une autre déclaration.\n\nLa valeur renvoyée par l’incrémentation n’est pas utilisée ici, il n’y a donc pas de différence entre `i++` et `++i`.\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/3-which-value-for/task.md",
    "content": "importance: 4\n\n---\n\n# Quelles valeurs sont affichées par la boucle \"for\" ?\n\nPour chaque boucle, notez les valeurs qui vont s'afficher. Ensuite, comparez avec la réponse.\n\nLes deux boucles `alert` les mêmes valeurs ou pas ?\n\n1. La forme postfix :\n\n    ```js\n    for (let i = 0; i < 5; i++) alert( i );\n    ```\n2. La forme préfix :\n\n    ```js\n    for (let i = 0; i < 5; ++i) alert( i );\n    ```\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/4-for-even/solution.md",
    "content": "\n\n```js run demo\nfor (let i = 2; i <= 10; i++) {\n  if (i % 2 == 0) {\n    alert( i );\n  }\n}\n```\n\nIci nous utilisons l'opérateur “modulo” `%` pour obtenir le reste et vérifier si c'est pair ou pas.\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/4-for-even/task.md",
    "content": "importance: 5\n\n---\n\n# Extraire les nombres pairs dans la boucle\n\nUtilisez la boucle `for` pour afficher les nombres pairs de `2` à `10`.\n\n[demo]\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/5-replace-for-while/solution.md",
    "content": "\n\n```js run\nlet i = 0;\nwhile (i < 3) {\n  alert( `number ${i}!` );\n  i++;\n}\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/5-replace-for-while/task.md",
    "content": "importance: 5\n\n---\n\n# Remplacer \"for\" par \"while\"\n\nRéécrivez le code en modifiant la boucle `for` en `while` sans modifier son comportement (la sortie doit rester la même).\n\n```js run\nfor (let i = 0; i < 3; i++) {\n  alert( `number ${i}!` );\n}\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/6-repeat-until-correct/solution.md",
    "content": "\n```js run demo\nlet num;\n\ndo {\n  num = prompt(\"Enter a number greater than 100?\", 0);\n} while (num <= 100 && num);\n```\n\nLa boucle `do..while` se répète tant que les deux vérifications sont vrai :\n\n1. La vérification de `num <= 100` - c’est-à-dire que la valeur entrée n’est toujours pas supérieure à `100`.\n2. La vérification que `&& num` est `false` lorsque `num` est `null` ou une chaîne vide. Ensuite, la boucle `while` s'arrête aussi.\n\nP.S. Si `num` est `null`, alors `num <= 100` est `true`. Par conséquent, sans la seconde vérification, la boucle ne s’arrêterait pas si l’utilisateur cliquait sur CANCEL. Les deux vérifications sont obligatoires.\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/6-repeat-until-correct/task.md",
    "content": "importance: 5\n\n---\n\n# Répéter jusqu'à ce que l'entrée soit correcte\n\nEcrivez une boucle qui demande un nombre supérieur à `100`. Si le visiteur saisit un autre numéro, demandez-lui de le saisir à nouveau.\n\nLa boucle doit demander un numéro jusqu'à ce que le visiteur saisisse un nombre supérieur à `100` ou annule l'entrée/entre une ligne vide.\n\nIci, nous pouvons supposer que le visiteur ne saisit que des chiffres. Il n’est pas nécessaire de mettre en œuvre un traitement spécial pour une entrée non numérique dans cette tâche.\n\n[demo]\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/7-list-primes/solution.md",
    "content": "Il existe de nombreux algorithmes pour cette tâche.\n\nUtilisons une boucle imbriquée :\n\n```js\nPour chaque i dans l'intervalle {\n  vérifier si i a un diviseur de 1..i\n  si oui => la valeur n'est pas un nombre premier\n  si non => la valeur est un nombre premier, affichez-le\n}\n```\n\nUn code utilisant un label :\n\n```js run\nlet n = 10;\n\nnextPrime:\nfor (let i = 2; i <= n; i++) { // Pour chaque i...\n\n  for (let j = 2; j < i; j++) { // cherche un diviseur ..\n    if (i % j == 0) continue nextPrime; // pas un premier, on passe au prochain i\n  }\n\n  alert( i ); // un premier\n}\n```\n\nIl y a beaucoup d’espace pour l’optimiser. Par exemple, nous pourrions rechercher les diviseurs de `2` à la racine carrée de `i`. Quoi qu’il en soit, si nous voulons être vraiment efficaces pour les grands intervalles, nous devons changer d’approche et nous baser sur des mathématiques avancées et des algorithmes complexes comme [Crible quadratique](https://fr.wikipedia.org/wiki/Crible_quadratique), [Crible algébrique](https://fr.wikipedia.org/wiki/Crible_alg%C3%A9brique) etc.\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/7-list-primes/task.md",
    "content": "importance: 3\n\n---\n\n# Extraire des nombres premiers\n\nUn nombre entier supérieur à 1 est appelé un [Nombre premier](https://fr.wikipedia.org/wiki/Nombre_premier) s'il ne peut être divisé sans reste par rien d'autre que 1 et lui-même.\n\nEn d’autres termes, `n > 1` est un nombre premier s’il ne peut être divisé de manière égale par autre chose que `1` et `n`.\n\nPar exemple, `5` est un nombre premier, car il ne peut pas être divisé sans reste par `2`, `3` et `4`.\n\n**Écrivez un code qui produit les nombres premiers dans l’intervall e 2 à n.**\n\nPour `n = 10`, le résultat sera `2`,`3`,`5`,`7`.\n\nP.S. Le code devrait fonctionner pour n'importe quel `n` et aucune valeur fixe ne doit être codé en dur.\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/article.md",
    "content": "# Boucles : while et for\n\nNous avons souvent besoin d'effectuer des actions similaires plusieurs fois de suite.\n\nPar exemple, lorsque nous devons extraire des marchandises d'une liste les unes à la suite des autres. Ou exécutez simplement le même code pour chaque numéro de 1 à 10.\n\n*Les boucles* permettent de répéter plusieurs fois la même partie du code.\n\n```smart header=\"Les boucles for..of et for..in\"\nUne petite annonce pour les lecteurs avertis.\n\nCet article ne couvre que les boucles de base : `while`, `do..while` et `for(..;..;..)`.\n\nSi vous êtes venu à cet article à la recherche d'autres types de boucles, voici les pointeurs :\n\n- Voir [for..in](info:object#forin) pour boucler sur les propriétés de l'objet.\n- Voir [for..of](info:array#loops) et [iterables](info:iterable) pour boucler sur des tableaux et des objets itérables.\n\nSinon, lisez la suite.\n```\n\n## La boucle \"while\"\n\nLa boucle `while` a la syntaxe suivante :\n\n```js\nwhile (condition) {\n  // code\n  // appelé \"loop body\" (\"corps de boucle\")\n}\n```\n\nTant que la `condition` est vraie, le `code` du corps de la boucle est exécuté.\n\nPar exemple, la boucle ci-dessous affiche `i` tant que `i < 3` :\n\n```js run\nlet i = 0;\nwhile (i < 3) { // affiche 0, puis 1, puis 2\n  alert( i );\n  i++;\n}\n```\n\nUne unique exécution du corps de la boucle est appelée **une itération**. La boucle dans l'exemple ci-dessus fait trois itérations.\n\nS'il n'y avait pas d'`i++` dans l'exemple ci-dessus, la boucle se répèterait (en théorie) pour toujours. En pratique, le navigateur fournit des moyens d’arrêter ces boucles, et pour JavaScript côté serveur, nous pouvons tuer le processus.\n\nToute expression ou variable peut être une condition de boucle, pas seulement une comparaison. Ils sont évalués et convertis en un booléen par `while`.\n\nPar exemple, le moyen le plus court d'écrire `while (i != 0)` pourrait être `while (i)` :\n\n```js run\nlet i = 3;\n*!*\nwhile (i) { // quand i devient 0, la condition devient fausse et la boucle s'arrête\n*/!*\n  alert( i );\n  i--;\n}\n```\n\n````smart header=\"Les accolades ne sont pas requis pour un corps à une seule ligne\"\nSi le corps de la boucle a une seule déclaration, nous pouvons omettre les accolades `{…}` :\n\n```js run\nlet i = 3;\n*!*\nwhile (i) alert(i--);\n*/!*\n```\n````\n\n## La boucle \"do…while\"\n\nLa vérification de la condition peut être déplacée *sous* le corps de la boucle en utilisant la syntaxe `do..while` :\n\n```js\ndo {\n  // corps de la boucle\n} while (condition);\n```\n\nLa boucle exécute d'abord le corps, puis vérifie la condition et, tant que c'est vrai, l'exécute encore et encore.\n\nPar exemple :\n\n```js run\nlet i = 0;\ndo {\n  alert( i );\n  i++;\n} while (i < 3);\n```\n\nCette forme de syntaxe est rarement utilisée, sauf lorsque vous souhaitez que le corps de la boucle s'exécute **au moins une fois**, quelle que soit la condition. Habituellement, l'autre forme est préférée : `while(…) {…}`.\n\n## La boucle \"for\"\n\nLa boucle `for` est plus complexe, mais c’est aussi la boucle la plus utilisée.\n\nCela ressemble à ceci :\n\n```js\nfor (début; condition; étape) {\n  // ... corps de la boucle ...\n}\n```\n\nApprenons la signification de ces parties par l'exemple. La boucle ci-dessous exécute `alert(i)` pour `i` en partant de `0` jusqu'à `3` (mais non compris) :\n\n```js run\nfor (let i = 0; i < 3; i++) { // affiche 0, puis 1, puis 2\n  alert(i);\n}\n```\n\nExaminons la déclaration `for` partie par partie :\n\n| partie    |            |                                                                                        |\n|-----------|------------|----------------------------------------------------------------------------------------|\n| début     | `let i = 0`    | Exécute une fois en entrant dans la boucle.                                            |\n| condition | `i < 3`    | Vérifié avant chaque itération de la boucle, en cas d'échec, la boucle s'arrête.       |\n| corps     | `alert(i)` | Exécute encore et encore tant que la condition est vraie                               |\n| étape     | `i++`      | Exécute après le corps à chaque itération |\n\n\nL'algorithme de boucle général fonctionne comme ceci :\n```\nExécuter le début\n→ (si condition → exécuter le corps et exécuter l'étape)\n→ (si condition → exécuter le corps et exécuter l'étape)\n→ (si condition → exécuter le corps et exécuter l'étape)\n→ ...\n```\n\nC'est-à-dire que `begin` est exécuté une fois, puis itéré : après chaque test de `condition`, `body` et `step` sont exécutés.\n\nSi vous débutez dans les boucles, il pourrait être utile de revenir à l'exemple et de reproduire comment elle s'exécute pas à pas sur une feuille de papier.\n\nVoici ce qui se passe exactement dans notre cas :\n\n```js\n// for (let i = 0; i < 3; i++) alert(i)\n\n// exécute début\nlet i = 0\n// si condition → exécuter le corps et exécuter l'étape\nif (i < 3) { alert(i); i++ }\n// si condition → exécuter le corps et exécuter l'étape\nif (i < 3) { alert(i); i++ }\n// si condition → exécuter le corps et exécuter l'étape\nif (i < 3) { alert(i); i++ }\n// ... fini, parce que maintenant i == 3\n```\n\n````smart header=\"Déclaration de variable en ligne\"\nIci, la variable \"counter\" `i` est déclarée directement dans la boucle. Cela s'appelle une déclaration de variable \"en ligne\". De telles variables ne sont visibles que dans la boucle.\n\n```js run\nfor (*!*let*/!* i = 0; i < 3; i++) {\n  alert(i); // 0, 1, 2\n}\nalert(i); // erreur, pas de variable\n```\n\nAu lieu de définir une variable, nous pouvons en utiliser une existante :\n\n```js run\nlet i = 0;\n\nfor (i = 0; i < 3; i++) { // utiliser une variable existante\n  alert(i); // 0, 1, 2\n}\n\nalert(i); // 3, visible, car déclaré en dehors de la boucle\n```\n````\n\n\n### Sauter des parties\n\nToute partie de `for` peut être ignorée.\n\nPar exemple, nous pouvons omettre `le début` si nous n'avons rien à faire au début de la boucle.\n\nComme ici :\n\n```js run\nlet i = 0; // nous avons i déjà déclaré et assigné\n\nfor (; i < 3; i++) { // pas besoin de \"début\"\n  alert( i ); // 0, 1, 2\n}\n```\n\nNous pouvons également supprimer la partie `étape` :\n\n```js run\nlet i = 0;\n\nfor (; i < 3;) {\n  alert( i++ );\n}\n```\n\nLa boucle est devenue identique à `while (i < 3)`.\n\nNous pouvons tout supprimer, créant ainsi une boucle infinie :\n\n```js\nfor (;;) {\n  // répète sans limites\n}\n```\n\nVeuillez noter que les deux les points-virgules `;` de `for` doivent être présents, sinon ce serait une erreur de syntaxe.\n\n## Briser la boucle\n\nNormalement, la boucle sort quand la condition devient fausse.\n\nMais nous pouvons forcer la sortie à tout moment. Il y a une directive spéciale appelée `break` pour cela.\n\nPar exemple, la boucle ci-dessous demande à l'utilisateur une série de chiffres, mais \"se casse\" quand aucun numéro n'est entré :\n\n```js run\nlet sum = 0;\n\nwhile (true) {\n\n  let value = +prompt(\"Entrez un nombre\", '');\n\n*!*\n  if (!value) break; // (*)\n*/!*\n\n  sum += value;\n\n}\nalert( 'Sum: ' + sum );\n```\n\nLa directive `break` est activée sur la ligne `(*)` si l'utilisateur entre une ligne vide ou annule l'entrée. Il arrête la boucle immédiatement, en passant le contrôle à la première ligne après la boucle. À savoir, `alert`.\n\nLa combinaison \"boucle infinie + `break` au besoin\" est idéale pour les situations où la condition doit être vérifiée non pas au début / à la fin de la boucle, mais au milieu, voire à plusieurs endroits du corps.\n\n## Continuer jusqu'à la prochaine itération [#continue]\n\nLa directive `continue` est une \"version plus légère\" de `break`. Cela n'arrête pas toute la boucle. Au lieu de cela, elle arrête l'itération en cours et force la boucle à en démarrer une nouvelle (si la condition le permet).\n\nNous pouvons l’utiliser si nous avons terminé l’itération en cours et aimerions passer à la suivante.\n\nLa boucle ci-dessous utilise `continue` pour ne produire que des valeurs impaires :\n\n```js run no-beautify\nfor (let i = 0; i < 10; i++) {\n\n  // si vrai, saute le reste du corps\n  *!*if (i % 2 == 0) continue;*/!*\n\n  alert(i); // 1, ensuite 3, 5, 7, 9\n}\n```\n\nPour les valeurs paires de `i`, la directive `continue` arrête l'exécution du corps en passant le contrôle à la prochaine itération de `for` (avec le nombre suivant). Donc, l'`alert` n'est appelée que pour les valeurs impaires.\n\n````smart header=\"La directive `continue` aide à réduire le niveau d'imbrication\"\nUne boucle affichant des valeurs impaires pourrait ressembler à ceci :\n\n```js run\nfor (let i = 0; i < 10; i++) {\n\n  if (i % 2) {\n    alert( i );\n  }\n\n}\n```\n\nD'un point de vue technique, c'est identique à l'exemple du dessus. Certes, nous pouvons simplement envelopper le code dans un bloc `if` au lieu de `continue`.\n\nMais comme effet secondaire, nous avons obtenu un niveau d'imbrication supplémentaire (l'appel de l'`alert` à l'intérieur des accolades). Si le code à l'intérieur du `if` est plus long que quelques lignes, la lisibilité globale peut en être réduite.\n````\n\n````warn header=\"Pas de `break/continue` à droite de '?'\"\nVeuillez noter que les constructions de syntaxe qui ne sont pas des expressions ne peuvent pas être utilisées avec l'opérateur ternaire `?`. Tout particulièrement les directives telles que `break/continue` ne sont pas autorisées.\n\nPar exemple, si nous prenons ce code :\n\n```js\nif (i > 5) {\n  alert(i);\n} else {\n  continue;\n}\n```\n\n… Et le réécrivons à l'aide d'un point d'interrogation :\n\n```js no-beautify\n(i > 5) ? alert(i) : *!*continue*/!*; // continue n'est pas autorisé ici\n```\n\n… Ensuite cesse de fonctionner : il y a une erreur de syntaxe.\n\n\nC’est une autre raison pour ne pas utiliser l'opérateur point d’interrogation `?` au lieu de `if`.\n````\n\n## Des labels pour break/continue\n\nParfois, nous devons sortir de plusieurs boucles imbriquées en même temps.\n\nPar exemple, dans le code ci-dessous, nous bouclons sur `i` et `j` pour demander les coordonnées `(i, j)` de `(0,0)` à `(2,2)` :\n\n```js run no-beautify\nfor (let i = 0; i < 3; i++) {\n\n  for (let j = 0; j < 3; j++) {\n\n    let input = prompt(`Value at coords (${i},${j})`, '');\n\n    // Et si nous voulons sortir d'ici à Done (ci-dessous) ?\n  }\n}\n\nalert('Done!');\n```\n\nNous avons besoin d'un moyen d'arrêter le processus si l'utilisateur annule la saisie.\n\nLe `break` ordinaire après `input` ne ferait que briser la boucle intérieure. Ce n’est pas suffisant -- les *labels* viennent à la rescousse.\n\nUne *label* est un identifiant avec deux points avant une boucle :\n\n```js\nlabelName: for (...) {\n  ...\n}\n```\n\nL'instruction `break <labelName>` dans la boucle interrompt tout le bloc de code relatif au label.\n\nComme ici :\n\n```js run no-beautify\n*!*outer:*/!* for (let i = 0; i < 3; i++) {\n\n  for (let j = 0; j < 3; j++) {\n\n    let input = prompt(`Value at coords (${i},${j})`, '');\n\n    // si une chaîne est vide ou annulée, alors rompre les deux boucles\n    if (!input) *!*break outer*/!*; // (*)\n\n    // faire quelque chose avec la valeur …\n  }\n}\n\nalert('Done!');\n```\n\nDans le code ci-dessus, `break outer` regarde vers le haut le label `outer` et sort de cette boucle.\n\nDonc, le contrôle va directement de `(*)` à `alert('Done!')`.\n\nNous pouvons également déplacer le label sur une ligne séparée :\n\n```js no-beautify\nouter:\nfor (let i = 0; i < 3; i++) { ... }\n```\n\nLa directive `continue` peut également être utilisée avec un label. Dans ce cas, l'exécution passe à l'itération suivante de la boucle labellisée.\n\n````warn header=\"Les labels ne permettent pas de \\\"sauter\\\" n'importe où\"\nLes labels ne nous permettent pas de sauter dans un endroit arbitraire du code.\n\nPar exemple, il est impossible de faire ceci :\n\n```js\nbreak label;  // saute au label ci-dessous (ne fonctionne pas)\n\nlabel: for (...)\n```\n\nUne directive `break` doit être à l'intérieur d'un bloc de code. Techniquement, tout bloc de code étiqueté fera l'affaire, par exemple :\n\n```js\nlabel: {\n  // ...\n  break label; // works\n  // ...\n}\n```\n\n... Bien que 99,9% du temps les `break` utilisés sont à l'intérieur de boucles, comme nous l'avons vu dans les exemples ci-dessus.\n\nUn `continue` n'est possible que depuis l'intérieur d'une boucle.\n````\n\n## Résumé\n\nNous avons couvert 3 types de boucles :\n\n- `while` -- La condition est vérifiée avant chaque itération.\n- `do..while` -- La condition est vérifiée après chaque itération.\n- `for (;;)` -- La condition est vérifiée avant chaque itération, des paramètres supplémentaires sont disponibles.\n\nPour créer une boucle \"infinie\", on utilise généralement la construction `while(true)`. Une telle boucle, comme toute autre, peut être stoppée avec la directive `break`.\n\nSi nous ne voulons rien faire avec l’itération actuelle et que nous souhaitons avancer jusqu'à la suivante, la directive `continue` nous permet de faire cela.\n\n`break/continue` accepte les labels précédents la boucle. Un label est le seul moyen de `break/continue` pour échapper à l'imbrication et accéder en dehors de la boucle.\n"
  },
  {
    "path": "1-js/02-first-steps/14-switch/1-rewrite-switch-if-else/solution.md",
    "content": "Pour correspondre précisément à la fonctionnalité du `switch`, le `if` doit utiliser une comparaison stricte `'==='`.\n\nCependant, pour des chaînes de caractères données, un simple `'=='` fonctionne également.\n\n```js no-beautify\nif(browser == 'Edge') {\n  alert(\"You've got the Edge!\");\n} else if (browser == 'Chrome'\n || browser == 'Firefox'\n || browser == 'Safari'\n || browser == 'Opera') {\n  alert( 'Okay we support these browsers too' );\n} else {\n  alert( 'We hope that this page looks ok!' );\n}\n```\n\nRemarque: la construction `browser == 'Chrome' || navigateur == 'Firefox'…` est divisée en plusieurs lignes pour une meilleure lisibilité.\n\nMais la construction `switch` est toujours plus propre et plus descriptive.\n"
  },
  {
    "path": "1-js/02-first-steps/14-switch/1-rewrite-switch-if-else/task.md",
    "content": "importance: 5\n\n---\n\n# Réécrire le \"switch\" dans un \"if\"\n\nÉcrivez le code en utilisant `if..else` qui correspondrait au `switch` suivant :\n\n```js\nswitch (browser) {\n  case 'Edge':\n    alert( \"You've got the Edge!\" );\n    break;\n\n  case 'Chrome':\n  case 'Firefox':\n  case 'Safari':\n  case 'Opera':\n    alert( 'Okay we support these browsers too' );\n    break;\n\n  default:\n    alert( 'We hope that this page looks ok!' );\n}\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/14-switch/2-rewrite-if-switch/solution.md",
    "content": "Les deux premiers contrôles se transforment en deux `case`. Le troisième contrôle est divisé en deux `case` :\n\n```js run\nlet a = +prompt('a?', '');\n\nswitch (a) {\n  case 0:\n    alert( 0 );\n    break;\n\n  case 1:\n    alert( 1 );\n    break;\n\n  case 2:\n  case 3:\n    alert( '2,3' );\n*!*\n    break;\n*/!*\n}\n```\n\nRemarque: le `break` en bas n'est pas requis. Mais nous le mettons pour rendre le code à l'épreuve du temps.\n\nDans le futur, il est possible que nous voulions ajouter un `case` supplémentaire, par exemple le `case 4`. Et si nous oublions d’ajouter un `break` avant, à la fin du `case 3`, il y aura une erreur. C’est donc une sorte d’assurance.\n"
  },
  {
    "path": "1-js/02-first-steps/14-switch/2-rewrite-if-switch/task.md",
    "content": "importance: 4\n\n---\n\n# Réécrire le \"if\" dans un \"switch\"\n\nRéécrivez le code ci-dessous en utilisant une seule instruction `switch` :\n\n```js run\nlet a = +prompt('a?', '');\n\nif (a == 0) {\n  alert( 0 );\n}\nif (a == 1) {\n  alert( 1 );\n}\n\nif (a == 2 || a == 3) {\n  alert( '2,3' );\n}\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/14-switch/article.md",
    "content": "# La déclaration \"switch\"\n\nUne instruction `switch` peut remplacer plusieurs vérification `if`.\n\nCela donne un moyen plus descriptif de comparer une valeur avec plusieurs variantes.\n\n## La syntaxe\n\nLe `switch` a un ou plusieurs blocs `case` (cas) et une valeur par défaut facultative.\n\nCela ressemble à ceci :\n\n```js no-beautify\nswitch(x) {\n  case 'value1':  // si (x === 'value1')\n    ...\n    [break]\n\n  case 'value2':  // si (x === 'value2')\n    ...\n    [break]\n\n  default:\n    ...\n    [break]\n}\n```\n\n- La valeur de `x` est vérifiée pour une égalité stricte avec la valeur du premier `case` (c'est-à-dire, `value1`), puis du second (`value2`) et ainsi de suite.\n- Si l'égalité est trouvée, `switch` commence à exécuter le code à partir du `case` correspondant, jusqu'au prochain `break` (ou jusqu'à la fin du switch).\n- Si aucun cas ne correspond, le code par défaut (`default`) est exécuté (s'il existe).\n\n## Un exemple\n\nUn exemple de `switch` (le code exécuté est mis en évidence) :\n\n```js run\nlet a = 2 + 2;\n\nswitch (a) {\n  case 3:\n    alert( 'Too small' );\n    break;\n*!*\n  case 4:\n    alert( 'Exactly!' );\n    break;\n*/!*\n  case 5:\n    alert( 'Too big' );\n    break;\n  default:\n    alert( \"I don't know such values\" );\n}\n```\n\nIci, le `switch` commence à comparer `a` avec le premier `case` dont la valeur est `3`. La correspondance échoue.\n\nEnsuite `4`, c’est une correspondance. L’exécution commence donc à partir du `case 4` jusqu’au prochain `break`.\n\n**S'il n'y a pas de `break`, l'exécution continue avec le `case` suivant sans aucun contrôle.**\n\nUn exemple sans `break`:\n\n```js run\nlet a = 2 + 2;\n\nswitch (a) {\n  case 3:\n    alert( 'Too small' );\n*!*\n  case 4:\n    alert( 'Exactly!' );\n  case 5:\n    alert( 'Too big' );\n  default:\n    alert( \"I don't know such values\" );\n*/!*\n}\n```\n\nDans l'exemple ci-dessus, nous verrons l'exécution séquentielle de trois `alert` :\n\n```js\nalert( 'Exactly!' );\nalert( 'Too big' );\nalert( \"I don't know such values\" );\n```\n\n````smart header=\"Toute expression peut être un argument `switch/case`\"\n`Switch` et `case` permettent des expressions arbitraires.\n\nPar exemple :\n\n```js run\nlet a = \"1\";\nlet b = 0;\n\nswitch (+a) {\n*!*\n  case b + 1:\n    alert(\"this runs, because +a is 1, exactly equals b+1\");\n    break;\n*/!*\n\n  default:\n    alert(\"this doesn't run\");\n}\n```\n\nIci `+a` donne `1`, qui est comparé à `b + 1` dans le `case`, et le code correspondant est exécuté.\n````\n\n## Groupement de \"case\"\n\nPlusieurs variantes de `case` partageant le même code peuvent être regroupées.\n\nPar exemple, si nous voulons que le même code soit exécuté pour les `case 3` et `case 5` :\n\n```js run no-beautify\nlet a = 2 + 2;\n\nswitch (a) {\n  case 4:\n    alert('Right!');\n    break;\n\n*!*\n  case 3: // (*) grouped two cases\n  case 5:\n    alert('Wrong!');\n    alert(\"Why don't you take a math class?\");\n    break;\n*/!*\n\n  default:\n    alert('The result is strange. Really.');\n}\n```\n\nMaintenant, les `3` et `5` affichent le même message.\n\nLa possibilité de \"grouper\" les `case` est un effet secondaire de la façon dont le `switch/case` fonctionne sans `break`. Ici, l’exécution du `case 3` commence à partir de la ligne `(*)` et passe par le `case 5`, car il n’y a pas de `break`.\n\n## Le type compte\n\nSoulignons que le contrôle d’égalité est toujours strict. Les valeurs doivent être du même type pour correspondre.\n\nPar exemple, considérons le code suivant :\n\n```js run\nlet arg = prompt(\"Enter a value?\");\n\nswitch (arg) {\n  case '0':\n  case '1':\n    alert( 'One or zero' );\n    break;\n\n  case '2':\n    alert( 'Two' );\n    break;\n\n  case 3:\n    alert( 'Never executes!' );\n    break;\n\n  default:\n    alert( 'An unknown value' );\n}\n```\n\n1. Pour `0`, `1`, la première `alert` est exécutée.\n2. Pour `2`, la deuxième `alert` est exécutée.\n3. Mais pour `3`, le résultat du prompt est une chaîne de caractères `\"3\"`, ce qui n’est pas strictement égal `===` au chiffre `3`. Nous avons donc un code mort dans le `case 3` ! La variante par défaut sera donc exécutée.\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/1-if-else-required/solution.md",
    "content": "Aucune différence.\n\nDans les deux cas, `return confirm('Did parents allow you?')` s'exécute exactement lorsque la condition `if` est fausse.\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/1-if-else-required/task.md",
    "content": "importance: 4\n\n---\n\n# Est-ce que \"else\" est requis ?\n\nLa fonction suivante renvoie `true` si le paramètre `age` est supérieur à `18`.\n\nSinon, il demande une confirmation et renvoie son résultat :\n\n```js\nfunction checkAge(age) {\n  if (age > 18) {\n    return true;\n*!*\n  } else {\n    // ...\n    return confirm('Did parents allow you?');\n  }\n*/!*\n}\n```\n\nLa fonction fonctionnera-t-elle différemment si `else` est supprimé ?\n\n```js\nfunction checkAge(age) {\n  if (age > 18) {\n    return true;\n  }\n*!*\n  // ...\n  return confirm('Did parents allow you?');\n*/!*\n}\n```\n\nExiste-t-il une différence dans le comportement de ces deux variantes ?\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md",
    "content": "En utilisant un opérateur point d’interrogation `'?'` :\n\n```js\nfunction checkAge(age) {\n  return (age > 18) ? true : confirm('Did parents allow you?');\n}\n```\n\nEn utilisant OU `||` (la variante la plus courte) :\n\n```js\nfunction checkAge(age) {\n  return (age > 18) || confirm('Did parents allow you?');\n}\n```\n\nNotez que les parenthèses autour de `age > 18` ne sont pas obligatoires ici. Elles existent pour une meilleure lisibilité. \n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/task.md",
    "content": "importance: 4\n\n---\n\n# Réécrivez la fonction en utilisant '?' ou '||'\n\nLa fonction suivante renvoie `true` si le paramètre `age` est supérieur à `18`.\n\nSinon, il demande une confirmation et renvoie le résultat.\n\n```js\nfunction checkAge(age) {\n  if (age > 18) {\n    return true;\n  } else {\n    return confirm('Did parents allow you?');\n  }\n}\n```\n\nRéécrivez-le, pour effectuer la même chose, mais sans `if`, et en une seule ligne.\n\nFaites deux variantes de `checkAge` :\n\n1. En utilisant un opérateur point d'interrogation `?`\n2. En utilisant OU `||`\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/3-min/solution.md",
    "content": "Une solution utilisant `if` :\n\n```js\nfunction min(a, b) {\n  if (a < b) {\n    return a;\n  } else {\n    return b;\n  }\n}\n```\n\nUne solution utilisant l'opérateur point d'interrogation `'?'` :\n\n```js\nfunction min(a, b) {\n  return a < b ? a : b;\n}\n```\n\nP.S. Dans le cas d'une égalité `a == b`, peu importe ce qu'il faut retourner.\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/3-min/task.md",
    "content": "importance: 1\n\n---\n\n# Fonction min(a, b)\n\nEcrivez une fonction `min(a, b)` qui renvoie le plus petit des deux nombres `a` et `b`.\n\nPar exemple :\n\n```js\nmin(2, 5) == 2\nmin(3, -1) == -1\nmin(1, 1) == 1\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/4-pow/solution.md",
    "content": "\n```js run demo\nfunction pow(x, n) {\n  let result = x;\n\n  for (let i = 1; i < n; i++) {\n    result *= x;\n  }\n\n  return result;\n}\n\nlet x = prompt(\"x?\", '');\nlet n = prompt(\"n?\", '');\n\nif (n < 1) {\n  alert(`Power ${n} is not supported, use a positive integer`);\n} else {\n  alert( pow(x, n) );\n}\n```\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/4-pow/task.md",
    "content": "importance: 4\n\n---\n\n# Fonction pow(x,n)\n\nEcrivez une fonction `pow(x, n)` qui renvoie `x` à la puissance `n`. Ou, autrement dit, multiplie `x` par lui-même `n` fois et renvoie le résultat.\n\n```js\npow(3, 2) = 3 * 3 = 9\npow(3, 3) = 3 * 3 * 3 = 27\npow(1, 100) = 1 * 1 * ...* 1 = 1\n```\n\nCréez une page Web qui demande (`prompt`)`x` et `n`, puis affiche le résultat de `pow(x, n)`.\n\n[demo]\n\nP.S. Dans cette tâche, la fonction ne doit prendre en charge que les valeurs naturelles de `n` : entiers supérieurs à `1`.\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/article.md",
    "content": "# Fonctions\n\nTrès souvent, nous devons effectuer une action similaire à plusieurs endroits du script.\n\nPar exemple, nous devons afficher un beau message lorsqu'un visiteur se connecte, se déconnecte et peut-être ailleurs.\n\nLes fonctions sont les principales \"composantes\" du programme. Ils permettent au code d'être appelé plusieurs fois sans répétition.\n\nNous avons déjà vu des exemples de fonctions intégrées, telles que `alert(message)`, `prompt(message, default)` et `confirm(question)`. Mais nous pouvons aussi créer nos propres fonctions.\n\n## Déclaration de fonction\n\nPour créer une fonction, nous pouvons utiliser une *déclaration de fonction*.\n\nCela ressemble à ceci :\n\n```js\nfunction showMessage() {\n  alert( 'Hello everyone!' );\n}\n```\n\nLe mot-clé `function` commence en premier, puis le *nom de la fonction*, puis une liste de *paramètres* entre les parenthèses (séparés par des virgules, vides dans l'exemple ci-dessus, nous verrons des exemples plus tard) et enfin le code de la fonction, également appelé \"le corps de la fonction\", entre des accolades.\n\n```js\nfunction name(parameter1, parameter2, ... parameterN) {\n // body\n}\n```\n\nNotre nouvelle fonction peut être appelée par son nom : `showMessage()`.\n\nPar exemple :\n\n```js run\nfunction showMessage() {\n  alert( 'Hello everyone!' );\n}\n\n*!*\nshowMessage();\nshowMessage();\n*/!*\n```\n\nL'appel `showMessage()` exécute le code de la fonction. Ici, nous verrons le message deux fois, parce qu'on l'appelle deux fois.\n\nCet exemple illustre clairement l’un des principaux objectifs des fonctions: éviter la duplication de code.\n\nSi nous devons un jour modifier le message ou son affichage, il suffit de modifier le code à un endroit: la fonction qui le renvoie.\n\n## Variables locales\n\nUne variable déclarée à l'intérieur d'une fonction n'est visible qu'à l'intérieur de cette fonction.\n\nPar exemple :\n\n```js run\nfunction showMessage() {\n*!*\n  let message = \"Hello, I'm JavaScript!\"; // variable locale\n*/!*\n\n  alert( message );\n}\n\nshowMessage(); // Hello, I'm JavaScript!\n\nalert( message ); // <-- Erreur! La variable est locale à la fonction\n```\n\n## Variables externes\n\nUne fonction peut également accéder à une variable externe, par exemple :\n\n```js run no-beautify\nlet *!*userName*/!* = 'John';\n\nfunction showMessage() {\n  let message = 'Hello, ' + *!*userName*/!*;\n  alert(message);\n}\n\nshowMessage(); // Hello, John\n```\n\nLa fonction a un accès complet à la variable externe. Cela peut aussi la modifier.\n\nPar exemple :\n\n```js run\nlet *!*userName*/!* = 'John';\n\nfunction showMessage() {\n  *!*userName*/!* = \"Bob\"; // (1) changé la variable externe\n\n  let message = 'Hello, ' + *!*userName*/!*;\n  alert(message);\n}\n\nalert( userName ); // *!*John*/!* avant l'appel de fonction\n\nshowMessage();\n\nalert( userName ); // *!*Bob*/!*, la valeur a été modifiée par la fonction\n```\n\nLa variable externe n’est utilisée que s’il n’y a pas de variable locale.\n\n\nSi une variable du même nom est déclarée à l'intérieur de la fonction, elle *eclipsera* la variable externe. Par exemple, dans le code ci-dessous, la fonction utilise le nom `userName` local. L'externe est ignoré :\n\n```js run\nlet userName = 'John';\n\nfunction showMessage() {\n*!*\n  let userName = \"Bob\"; // déclarer une variable locale\n*/!*\n\n  let message = 'Hello, ' + userName; // *!*Bob*/!*\n  alert(message);\n}\n\n// la fonction créera et utilisera son propre userName\nshowMessage();\n\nalert( userName ); // *!*John*/!*, inchangé, la fonction n'a pas accédé à la variable externe\n```\n\n```smart header=\"Variables globales\"\nLes variables déclarées en dehors de toute fonction, telle que `userName` externe dans le code ci-dessus, sont appelées *globales*.\n\nLes variables globales sont visibles depuis n'importe quelle fonction (sauf si elles sont masquées par les variables locales).\n\nC'est une bonne pratique de minimiser l'utilisation de variables globales. Le code moderne a peu ou pas de variable globales. La plupart des variables résident dans leurs fonctions. Parfois, cependant, ils peuvent être utiles pour stocker des données au niveau du projet.\n```\n\n## Arguments\n\nNous pouvons transmettre des données arbitraires à des fonctions à l'aide de paramètres.\n\nDans l'exemple ci-dessous, la fonction a deux paramètres: `from` et `text`.\n\n```js run\nfunction showMessage(*!*from, text*/!*) { // arguments : from, text\n  alert(from + ': ' + text);\n}\n\n*!*showMessage('Ann', 'Hello!');*/!* // Ann: Hello! (*)\n*!*showMessage('Ann', \"What's up?\");*/!* // Ann: What's up? (**)\n```\n\nLorsque la fonction est appelée dans les lignes `(*)` et `(**)`, les valeurs données sont copiées dans les variables locales `from` et `text`. Ensuite, la fonction les utilise.\n\nVoici un autre exemple: nous avons une variable `from` et la transmettons à la fonction. Remarque : la fonction change `from`, mais le changement n'est pas visible à l'extérieur, car une fonction obtient toujours une copie de la valeur :\n\n```js run\nfunction showMessage(from, text) {\n\n*!*\n  from = '*' + from + '*'; // améliore l'apparence de \"from\"\n*/!*\n\n  alert( from + ': ' + text );\n}\n\nlet from = \"Ann\";\n\nshowMessage(from, \"Hello\"); // *Ann*: Hello\n\n// la valeur de \"from\" est la même, la fonction a modifié une copie locale\nalert( from ); // Ann\n```\n\nLorsqu'une valeur est passée en tant que paramètre de fonction, elle est également appelée *argument*.\n\nEn d'autres termes, pour mettre ces termes au clair :\n\n- Un paramètre est la variable répertoriée entre parenthèses dans la fonction déclaration (c'est un terme du temps de la déclaration).\n- Un argument est la valeur qui est transmise à la fonction lorsqu'elle est appelée (c'est un terme du temps de l'appel).\n\nNous déclarons des fonctions en listant leurs paramètres, puis les appelons en passant des arguments.\n\nDans l'exemple ci-dessus, on pourrait dire : \"la fonction `showMessage` est déclarée avec deux paramètres, puis appelée avec deux arguments : `from` et `\"Hello\"`.\n\n\n## Les valeurs par défaut\n\nSi une fonction est appelée, mais qu'aucun argument n'est fourni, alors la valeur correspondante devient `undefined`.\n\nPar exemple, la fonction `showMessage(from, text)` mentionnée précédemment peut être appelée avec un seul argument :\n\n```js\nshowMessage(\"Ann\");\n```\n\nCe n'est pas une erreur. Un tel appel produirait `\"*Ann*: undefined\"`. Comme la valeur de `text` n'est pas transmise, elle devient `undefined`.\n\nNous pouvons spécifier la valeur dite \"par défaut\" (à utiliser si omise) pour un paramètre dans la déclaration de fonction, en utilisant `=` :\n\n```js run\nfunction showMessage(from, *!*text = \"no text given\"*/!*) {\n  alert( from + \": \" + text );\n}\n\nshowMessage(\"Ann\"); // Ann: no text given\n```\n\nMaintenant, si le paramètre `text` n'est pas passé, il obtiendra la valeur `\"no text given\"`.\n\nLa valeur par défaut saute également si le paramètre existe, mais est strictement égal à `undefined`, comme ceci :\n\n```js\nshowMessage(\"Ann\", undefined); // Ann: no text given\n```\n\nIci, `\"no text given\"` est une chaîne de caractères, mais il peut s'agir d'une expression plus complexe, qui n'est évaluée et affectée que si le paramètre est manquant. Donc, cela est également possible :\n\n```js run\nfunction showMessage(from, text = anotherFunction()) {\n  // anotherFunction() est exécuté uniquement si aucun texte n'est fourni\n  // son résultat devient la valeur de text\n}\n```\n\n```smart header=\"Évaluation des paramètres par défaut\"\n\nEn JavaScript, un paramètre par défaut est évalué chaque fois que la fonction est appelée sans le paramètre correspondant.\n\nDans l'exemple ci-dessus, `anotherFunction()` n'est pas du tout appelé, si le paramètre `text` est fourni.\n\nD'un autre côté, il est appelé indépendamment à chaque fois que `text` est manquant.\n```\n\n````smart header=\"Paramètres par défaut dans l'ancien code JavaScript\"\nIl y a plusieurs années, JavaScript ne prenait pas en charge la syntaxe des paramètres par défaut. Les gens ont donc utilisé d'autres moyens pour les spécifier.\n\nDe nos jours, on peut les croiser dans d'anciens scripts.\n\nPar exemple, une vérification explicite pour `undefined` :\n\n```js\nfunction showMessage(from, text) {\n*!*\n  if (text === undefined) {\n    text = 'no text given';\n  }\n*/!*\n\n  alert( from + \": \" + text );\n}\n```\n\n...Ou en utilisant l'opérateur `||` :\n\n```js\nfunction showMessage(from, text) {\n  // Si la valeur du texte est fausse, attribuez la valeur par défaut\n  // cela suppose que text == \"\" est identique à pas de texte du tout\n  text = text || 'no text given';\n  ...\n}\n```\n````\n\n\n### Paramètres par défaut alternatifs\n\nIl est parfois judicieux de définir des valeurs par défaut pour les paramètres non pas dans la fonction déclaration, mais à un stade ultérieur, lors de son exécution.\n\nNous pouvons vérifier si le paramètre est passé lors de l'exécution de la fonction, en le comparant avec `undefined` :\n\n```js run\nfunction showMessage(text) {\n  // ...\n\n*!*\n  if (text === undefined) { // si le paramètre est manquant\n    text = 'empty message';\n  }\n*/!*\n\n  alert(text);\n}\n\nshowMessage(); // empty message\n```\n\n...Ou nous pourrions utiliser l'opérateur `||` :\n\n```js\nfunction showMessage(text) {\n  // if text is undefined or otherwise falsy, set it to 'empty'\n  text = text || 'empty';\n  ...\n}\n```\n\nLes moteurs JavaScript modernes prennent en charge [l'opérateur de coalescence des nuls](info:nullish-coalescing-operator) `??`, c'est mieux quand des valeurs fausses, telles que `0`, sont considérées comme \"normales\" :\n\n```js run\nfunction showCount(count) {\n  // if count is undefined or null, show \"unknown\"\n  alert(count ?? \"unknown\");\n}\n\nshowCount(0); // 0\nshowCount(null); // unknown\nshowCount(); // unknown\n```\n\n\n## Renvoyer une valeur\n\nUne fonction peut renvoyer une valeur dans le code appelant en tant que résultat.\n\nL'exemple le plus simple serait une fonction qui additionne deux valeurs :\n\n```js run no-beautify\nfunction sum(a, b) {\n  *!*return*/!* a + b;\n}\n\nlet result = sum(1, 2);\nalert( result ); // 3\n```\n\nLa directive `return` peut être n'importe où dans la fonction. Lorsque l'exécution le permet, la fonction s'arrête et la valeur est renvoyée au code appelant (affecté à `result` ci-dessus).\n\nIl peut y avoir plusieurs occurrences de `return` dans une seule fonction. Par exemple :\n\n```js run\nfunction checkAge(age) {\n  if (age >= 18) {\n*!*\n    return true;\n*/!*\n  } else {\n*!*\n    return confirm('Do you have permission from your parents?');\n*/!*\n  }\n}\n\nlet age = prompt('How old are you?', 18);\n\nif ( checkAge(age) ) {\n  alert( 'Access granted' );\n} else {\n  alert( 'Access denied' );\n}\n```\n\nIl est possible d'utiliser `return` sans valeur. Cela entraîne la sortie immédiate de la fonction.\n\nPar exemple :\n\n```js\nfunction showMovie(age) {\n  if ( !checkAge(age) ) {\n*!*\n    return;\n*/!*\n  }\n\n  alert( \"Showing you the movie\" ); // (*)\n  // ...\n}\n```\n\nDans le code ci-dessus, si `checkAge(age)` renvoie `false`, alors `ShowMovie` n’effectuera pas l’`alert`.\n\n````smart header=\"Une fonction avec un `return` vide ou rien dedans retourne `undefined`\"\n\n```js run\nfunction doNothing() { /* vide */ }\n\nalert( doNothing() === undefined ); // true\n```\n\nUn `return` vide est également identique à un `return undefined` :\n\n```js run\nfunction doNothing() {\n  return;\n}\n\nalert( doNothing() === undefined ); // true\n```\n````\n\n````warn header=\"N'ajoutez jamais de nouvelle ligne entre `return` et la valeur\"\nPour une longue expression dans `return`, il pourrait être tentant de la mettre sur une ligne séparée, comme ceci :\n\n```js\nreturn\n (some + long + expression + or + whatever * f(a) + f(b))\n```\nCela ne fonctionne pas, car JavaScript suppose un point-virgule après le `return`. Cela fonctionnera comme :\n\n```js\nreturn*!*;*/!*\n (some + long + expression + or + whatever * f(a) + f(b))\n```\n\nDonc, cela devient effectivement un retour vide.\n\nSi nous voulons que l'expression renvoyée recouvre plusieurs lignes, nous devons la démarrer à la même ligne que `return`. Ou du moins mettre les parenthèses d'ouverture comme suit :\n\n```js\nreturn (\n  some + long + expression\n  + or +\n  whatever * f(a) + f(b)\n  )\n```\nEt cela fonctionnera comme prévu.\n````\n\n## Nommer une fonction [#function-naming]\n\nLes fonctions sont des actions. Donc, leur nom est généralement un verbe. Il convient de décrire brièvement, mais aussi précisément que possible, le rôle de la fonction. Pour qu'une personne qui lit le code reçoive le bon indice.\n\nC'est une pratique répandue de commencer une fonction avec un préfixe verbal qui décrit vaguement l'action. Il doit exister un accord au sein de l'équipe sur la signification des préfixes.\n\nPar exemple, les fonctions qui commencent par `\"show\"` affichent généralement quelque chose.\n\nFonction commençant par…\n\n- `\"get…\"` -- retourne une valeur,\n- `\"calc…\"` -- calcule quelque chose,\n- `\"create…\"` -- créer quelque chose,\n- `\"check…\"` -- vérifie quelque chose et retourne un booléen, etc.\n\nExemples de quelques noms :\n\n```js no-beautify\nshowMessage(..)     // affiche un message\ngetAge(..)          // renvoie l'âge (l'obtient en quelque sorte)\ncalcSum(..)         // calcule une somme et renvoie le résultat\ncreateForm(..)      // crée un formulaire (et le retourne généralement)\ncheckPermission(..) // vérifie une permission, retourne vrai/faux\n```\n\nAvec les préfixes en place, un coup d'œil sur un nom de fonction permet de comprendre le type de travail effectué et le type de valeur renvoyé.\n\n```smart header=\"Une fonction - une action\"\nUne fonction doit faire exactement ce qui est suggéré par son nom, pas plus.\n\nDeux actions indépendantes méritent généralement deux fonctions, même si elles sont généralement appelées ensemble (dans ce cas, nous pouvons créer une troisième fonction qui appelle ces deux actions).\n\nQuelques exemples de violation de cette règle :\n\n- `getAge` -- serait mauvais si elle affichait une `alert` avec l'âge (devrait seulement obtenir).\n- `createForm` -- serait mauvais s’il modifiait le document en y ajoutant un formulaire (il ne devrait que le créer et le renvoyer).\n- `checkPermission` -- serait mauvais s'il affiche le message d'accès accordé/refusé (doit uniquement effectuer la vérification et renvoyer le résultat).\n\nCes exemples supposent des significations communes de préfixes. Vous et votre équipe êtes libres de vous entendre sur d'autres sens, mais ils ne sont généralement pas très différents. Dans tous les cas, vous devez bien comprendre ce que signifie un préfixe, ce qu'une fonction préfixée peut et ne peut pas faire. Toutes les fonctions ayant le même préfixe doivent obéir aux règles. Et l'équipe devrait partager ces connaissances.\n```\n\n```smart header=\"Noms de fonction ultra-courts\"\nLes fonctions utilisées *très souvent* portent parfois des noms ultra-courts.\n\nPar exemple le framework [jQuery](https://jquery.com) définit une fonction avec `$`. La librairie [LoDash](https://lodash.com/) a nommé sa fonction principale `_`.\n\nCe sont des exceptions. En règle générale, les noms de fonctions doivent être concis et descriptifs.\n```\n\n## Fonctions == Commentaires\n\nLes fonctions doivent être courtes et faire exactement une seule chose. Si cette chose est conséquente, il vaut peut-être la peine de scinder la fonction en quelques fonctions plus petites. Parfois, suivre cette règle peut ne pas être aussi facile, mais c’est définitivement une bonne pratique.\n\nUne fonction distincte est non seulement plus facile à tester et à déboguer -- son existence même est un excellent commentaire!\n\nPar exemple, comparez les deux fonctions `showPrimes(n)` ci-dessous. Chacune extrait les [nombres premiers](https://fr.wikipedia.org/wiki/Nombre_premier) jusqu'à `n`.\n\nLa première variante utilise un label :\n\n```js\nfunction showPrimes(n) {\n  nextPrime: for (let i = 2; i < n; i++) {\n\n    for (let j = 2; j < i; j++) {\n      if (i % j == 0) continue nextPrime;\n    }\n\n    alert( i ); // un nombre premier\n  }\n}\n```\n\nLa deuxième variante utilise une fonction supplémentaire `isPrime(n)` pour tester la primalité :\n\n```js\nfunction showPrimes(n) {\n\n  for (let i = 2; i < n; i++) {\n    *!*if (!isPrime(i)) continue;*/!*\n\n    alert(i);  // un nombre premier\n  }\n}\n\nfunction isPrime(n) {\n  for (let i = 2; i < n; i++) {\n    if ( n % i == 0) return false;\n  }\n  return true;\n}\n```\n\nLa deuxième variante est plus facile à comprendre, n’est-ce pas ? Au lieu du bloc de code, nous voyons le nom de l'action (`isPrime`). Parfois, les gens se réfèrent à ce code comme étant *auto-descriptif*.\n\nDes fonctions peuvent donc être créées même si nous n’avons pas l’intention de les réutiliser. Ils structurent le code et le rendent lisible.\n\n## Résumé\n\nUne déclaration de fonction ressemble à ceci :\n\n```js\nfunction name(parameters, delimited, by, comma) {\n  /* code */\n}\n```\n\n- Les valeurs transmises à une fonction en tant que paramètres sont copiées dans ses variables locales.\n- Une fonction peut accéder à des variables externes. Mais cela ne fonctionne que de l'intérieur. Le code en dehors de la fonction ne voit pas ses variables locales.\n- Une fonction peut renvoyer une valeur. Si ce n'est pas le cas, le résultat est `undefined`.\n\nPour rendre le code propre et facile à comprendre, il est recommandé d’utiliser principalement des variables et des paramètres locaux dans la fonction, et non des variables externes.\n\nIl est toujours plus facile de comprendre une fonction qui possède des paramètres, fonctionne avec eux et renvoie un résultat, plutôt qu’une fonction qui ne comporte aucun paramètre, mais modifie des variables externes comme un effet secondaire.\n\nNommage de fonction :\n\n- Un nom doit clairement décrire le rôle de la fonction. Lorsque nous voyons un appel de fonction dans le code, un bon nom nous donne instantanément une compréhension de ce qu’elle fait et de ce qu’elle retourne.\n- Une fonction est une action, les noms de fonctions sont donc généralement verbaux.\n- Il existe de nombreux préfixes de fonctions bien connus, tels que `create…`, `show…`, `get…`, `check…` et ainsi de suite. Utilisez-les pour indiquer ce que fait une fonction.\n\nLes fonctions sont les principaux éléments constitutifs des scripts. Maintenant que nous avons couvert les bases, nous pouvons donc commencer à les créer et les utiliser. Mais ce n’est que le début du chemin. Nous allons y revenir plusieurs fois, en approfondissant leurs fonctionnalités avancées.\n"
  },
  {
    "path": "1-js/02-first-steps/16-function-expressions/article.md",
    "content": "# Fonctions Expressions\n\nEn JavaScript, une fonction n'est pas une \"structure de langage magique\", mais un type de valeur particulier.\n\nLa syntaxe utilisée précédemment s'appelle une *déclaration de fonction* :\n\n```js\nfunction sayHi() {\n  alert( \"Hello\" );\n}\n```\n\nIl existe une autre syntaxe pour créer une fonction appelée *Expression de Fonction*.\n\nCela nous permet de créer une nouvelle fonction au milieu de n'importe quelle expression.\n\nPar exemple :\n\n```js\nlet sayHi = function() {\n  alert( \"Hello\" );\n};\n```\n\nIci, nous pouvons voir une variable `sayHi` obtenir une valeur, la nouvelle fonction, créée en tant que `function() { alert(\"Hello\"); }`.\n\nComme la création de la fonction se produit dans le contexte de l'expression d'affectation (à droite de `=`), il s'agit d'une *Fonction Expression*.\n\nVeuillez noter qu'il n'y a pas de nom après le mot clé `function`. L'omission d'un nom est autorisée pour les fonctions expressions.\n\nIci, nous l'assignons immédiatement à la variable, donc la signification de ces exemples de code est la même : \"créer une fonction et la mettre dans la variable `sayHi`\".\n\nDans des situations plus avancées, que nous verrons plus tard, une fonction peut être créée et immédiatement appelée ou planifiée pour une exécution ultérieure, non stockée nulle part, restant ainsi anonyme.\n\n## La fonction est une valeur\n\nRépétons-le : quelle que soit la manière dont la fonction est créée, une fonction est une valeur. Les deux exemples ci-dessus stockent une fonction dans la variable `sayHi`.\n\nLa signification de ces exemples de code est la même : \"créer une fonction et la placer dans la variable `sayHi`\".\n\nNous pouvons même afficher cette valeur en utilisant `alert` :\n\n```js run\nfunction sayHi() {\n  alert( \"Hello\" );\n}\n\n*!*\nalert( sayHi ); // affiche le code de la fonction\n*/!*\n```\n\nVeuillez noter que la dernière ligne n'exécute pas la fonction, car il n'y a pas de parenthèses après `sayHi`. Il y a des langages de programmation où toute mention d'un nom de fonction provoque son exécution, mais JavaScript n'est pas comme ça.\n\nEn JavaScript, une fonction est une valeur, nous pouvons donc la traiter comme une valeur. Le code ci-dessus montre sa représentation sous forme de chaîne de caractères, qui est le code source.\n\nCertes, une fonction est une valeur spéciale, en ce sens que nous pouvons l'appeler comme cela `sayHi()`.\n\nMais c’est toujours une valeur. Nous pouvons donc travailler avec comme avec d’autres types de valeurs.\n\nNous pouvons copier une fonction dans une autre variable :\n\n```js run no-beautify\nfunction sayHi() {   // (1) créer\n  alert( \"Hello\" );\n}\n\nlet func = sayHi;    // (2) copier\n\nfunc(); // Hello     // (3) exécuter la copie (ça fonctionne)!\nsayHi(); // Hello    //     cela fonctionne toujours aussi (pourquoi pas)\n```\n\nVoici ce qui se passe ci-dessus en détail :\n\n1. La Déclaration de Fonction `(1)` crée la fonction et la place dans la variable nommée `sayHi`.\n2. La ligne `(2)` la copie dans la variable `func`. Veuillez noter à nouveau : il n'y a pas de parenthèses après `sayHi`. S'il y en avait, alors `func = sayHi()` écrirait *le résultat de l'appel* `sayHi()` dans `func`, et non *la fonction* `sayHi` elle-même.\n3. Maintenant, la fonction peut être appelée à la fois en tant que `sayHi()` et `func()`.\n\nNous aurions aussi pu utiliser une Fonction Expression pour déclarer `sayHi`, à la première ligne :\n\n```js\nlet sayHi = function() { // (1) create\n  alert( \"Hello\" );\n};\n\nlet func = sayHi;\n// ...\n```\n\nTout fonctionnerait de la même manière.\n\n\n````smart header=\"Pourquoi y a-t-il un point-virgule à la fin ?\"\nIl peut y avoir une question, pourquoi la Fonction Expression a un point-virgule `;` à la fin, et la Fonction Déclaration non :\n\n\n```js\nfunction sayHi() {\n  // ...\n}\n\nlet sayHi = function() {\n  // ...\n}*!*;*/!*\n```\n\nLa réponse est simple : une expression de fonction est créée ici en tant que `function(…) {…}` à l'intérieur de l'instruction d'affectation : `let sayHi = …;`. Le point-virgule `;` est recommandé à la fin de l'instruction, il ne fait pas partie de la syntaxe de la fonction.\n\nLe point-virgule serait là pour une affectation plus simple, telle que `let sayHi = 5;`, et il est également là pour une affectation de fonction.\n````\n\n## Fonctions callback (de rappel)\n\nExaminons plus d’exemples de fonctions passées en tant que valeurs et utilisant des expressions de fonction.\n\nNous allons écrire une fonction `ask(question, yes, no)` avec trois paramètres :\n\n`question`\n: Texte de la question\n\n`yes`\n: Fonction à exécuter si la réponse est “Yes”\n\n`no`\n: Fonction à exécuter si la réponse est “No”\n\nLa fonction doit poser la question et, en fonction de la réponse de l'utilisateur, appeler `yes()` ou `no()` :\n\n```js run\n*!*\nfunction ask(question, yes, no) {\n  if (confirm(question)) yes();\n  else no();\n}\n*/!*\n\nfunction showOk() {\n  alert( \"You agreed.\" );\n}\n\nfunction showCancel() {\n  alert( \"You canceled the execution.\" );\n}\n\n// utilisation: les fonctions showOk, showCancel sont transmises en tant qu'arguments à ask\nask(\"Do you agree?\", showOk, showCancel);\n```\n\nEn pratique, ces fonctions sont très utiles. La principale différence entre une demande réelle (`ask`) et l'exemple ci-dessus est que les fonctions réelles utilisent des moyens d'interagir avec l'utilisateur plus complexes que la simple confirmation (`confirm`). Dans le navigateur, une telle fonction dessine généralement une belle fenêtre de questions. Mais c'est une autre histoire.\n\n**Les arguments `showOk` et `showCancel` de `ask` s'appellent des *fonctions callback* (fonctions de rappel) ou simplement des *callbacks* (rappels).**\n\nL'idée est que nous passions une fonction et attendions qu'elle soit \"rappelée\" plus tard si nécessaire. Dans notre cas, `showOk` devient le rappel pour la réponse \"oui\" et `showCancel` pour la réponse \"non\".\n\nNous pouvons utiliser les Fonctions Expressions pour écrire la même fonction mais plus courte :\n\n```js run no-beautify\nfunction ask(question, yes, no) {\n  if (confirm(question)) yes();\n  else no();\n}\n\n*!*\nask(\n  \"Do you agree?\",\n  function() { alert(\"You agreed.\"); },\n  function() { alert(\"You canceled the execution.\"); }\n);\n*/!*\n```\n\nIci, les fonctions sont déclarées directement dans l'appel `ask(...)`. Elles n'ont pas de nom et sont donc appelées *anonymes*. De telles fonctions ne sont pas accessibles en dehors de `ask` (car elles ne sont pas affectées à des variables), mais c’est exactement ce que nous voulons ici.\n\nCe genre de code apparaît dans nos scripts très naturellement, c’est dans l’esprit de JavaScript.\n\n```smart header=\"Une fonction est une valeur représentant une \\\"action\\\"\"\nDes valeurs régulières telles que des chaînes de caractères ou des nombres représentent les *données*.\n\nUne fonction peut être perçue comme une *action*.\n\nNous pouvons tout aussi bien la passer en tant que variable ou l'exécuter si nous le voulons.\n```\n\n## Fonction Expression vs Fonction Déclaration\n\nFormulons les principales différences entre les déclarations de fonction et les expressions de fonctions.\n\nTout d'abord, la syntaxe : comment les différencier dans le code.\n\n- *La Fonction Déclaration* une fonction déclarée séparément dans le flux de code principal.\n\n    ```js\n    // Function Declaration\n    function sum(a, b) {\n      return a + b;\n    }\n    ```\n- *La Fonction Expression :* une fonction créée dans une expression ou dans une autre construction de syntaxe. Ici, la fonction est créée à droite de \"l'affectation de l'expression\" `=` :\n\n    ```js\n    // Function Expression\n    let sum = function(a, b) {\n      return a + b;\n    };\n    ```\n\nLa différence la plus subtile est *quand* une fonction est créée par le moteur JavaScript.\n\n**Une Fonction Expression est créée lorsque l’exécution l’atteint.**\n\nUne fois que le flux d'exécution passe à droite de l'affectation, `let sum = function…` , la fonction est créée et peut désormais être utilisée (assignée, appelée, etc.).\n\nLes déclarations de fonction sont différentes.\n\n**Une Fonction Déclaration peut être appelée plus tôt que sa définition.**\n\nPar exemple, une fonction déclaration globale est visible dans tout le script, peu importe où elle se trouve.\n\nCela est dû aux algorithmes internes. Lorsque JavaScript se prépare à exécuter le script, il recherche d'abord les fonctions déclarations globales et les crée. Nous pouvons considérer cela comme une \"étape d'initialisation\".\n\nUne fois que toutes les déclarations de fonctions ont été traitées, le reste du code est exécuté. Ainsi, il a accès à ces fonctions pour les appeler.\n\nPar exemple, cela fonctionne :\n\n```js run refresh untrusted\n*!*\nsayHi(\"John\"); // Hello, John\n*/!*\n\nfunction sayHi(name) {\n  alert( `Hello, ${name}` );\n}\n```\n\nLa déclaration de fonction `sayHi` est créée lorsque JavaScript est sur le point de démarrer le script et est visible partout dans celui-ci.\n\n… S’il s’agissait d’une Fonction Expression, cela ne fonctionnerait pas :\n\n```js run refresh untrusted\n*!*\nsayHi(\"John\"); // erreur!\n*/!*\n\nlet sayHi = function(name) {  // (*) plus de magie\n  alert( `Hello, ${name}` );\n};\n```\n\nLes expressions de fonction sont créées lorsque l'exécution les atteint. Cela ne se produirait que dans la ligne `(*)`. Trop tard.\n\n \nUne autre particularité des Fonctions Declaration est leur portée de bloc.\n \n\n**En mode strict, quand une Fonction Déclaration se trouve dans un bloc de code, elle est visible partout dans ce bloc. Mais pas en dehors.**\n\nPar exemple, imaginons que nous ayons besoin de déclarer une fonction `welcome()` en fonction de la variable d’`age` obtenue lors de l’exécution. Et ensuite, nous prévoyons de l'utiliser quelque temps plus tard.\n\nSi nous utilisons la fonction déclaration, cela ne fonctionnera pas comme prévu :\n\n```js run\nlet age = prompt(\"Quel est votre age ?\", 18);\n\n// déclarer conditionnellement une fonction\nif (age < 18) {\n\n  function welcome() {\n    alert(\"Hello!\");\n  }\n\n} else {\n\n  function welcome() {\n    alert(\"Greetings!\");\n  }\n\n}\n\n// ...l'utiliser plus tard\n*!*\nwelcome(); // Error: welcome is not defined\n*/!*\n```\n\nC’est parce qu’une déclaration de fonction n’est visible que dans le bloc de code dans lequel elle réside.\n\nVoici un autre exemple :\n\n```js run\nlet age = 16; // prendre 16 comme exemple\n\nif (age < 18) {\n*!*\n  welcome();               // \\   (exécution)\n*/!*\n                           //  |\n  function welcome() {     //  |\n    alert(\"Hello!\");       //  |  La déclaration de fonction est disponible\n  }                        //  |  partout dans le bloc où elle est déclarée\n                           //  |\n*!*\n  welcome();               // /   (exécution)\n*/!*\n\n} else {\n\n  function welcome() {\n    alert(\"Greetings!\");\n  }\n}\n\n// Ici, nous sommes en dehors des accolades,\n// nous ne pouvons donc pas voir les déclarations de fonction faites à l'intérieur de celles-ci.\n\n*!*\nwelcome(); // Error: welcome is not defined\n*/!*\n```\n\nQue pouvons-nous faire pour rendre `welcome` visible en dehors de `if` ?\n\nL'approche correcte consisterait à utiliser une expression de fonction et à attribuer `welcome` à la variable déclarée en dehors de `if` et offrant la visibilité appropriée.\n\nCe code fonctionne comme prévu :\n\n```js run\nlet age = prompt(\"What is your age?\", 18);\n\nlet welcome;\n\nif (age < 18) {\n\n  welcome = function() {\n    alert(\"Hello!\");\n  };\n\n} else {\n\n  welcome = function() {\n    alert(\"Greetings!\");\n  };\n\n}\n\n*!*\nwelcome(); // ok maintenant\n*/!*\n```\n\nOu nous pourrions simplifier encore davantage en utilisant un opérateur conditionnel ternaire `?` :\n\n```js run\nlet age = prompt(\"What is your age?\", 18);\n\nlet welcome = (age < 18) ?\n  function() { alert(\"Hello!\"); } :\n  function() { alert(\"Greetings!\"); };\n\n*!*\nwelcome(); // ok maintenant\n*/!*\n```\n\n\n```smart header=\"Quand choisir la fonction déclaration par rapport à la fonction expression ?\"\nEn règle générale, lorsque nous devons déclarer une fonction, la première chose à prendre en compte est la syntaxe de la fonction déclaration. Cela donne plus de liberté dans l'organisation de notre code, car nous pouvons appeler de telles fonctions avant qu'elles ne soient déclarées.\n\nC’est également meilleur pour la lisibilité, car il est plus facile de rechercher la `fonction f(…) {…}` dans le code que `let f = function(…) {…};`. Les fonction déclarations sont plus \"accrocheuses\".\n\n… Mais si une déclaration de fonction ne nous convient pas pour une raison quelconque (nous en avons vu un exemple ci-dessus), alors il convient d'utiliser une Fonction Expression.\n```\n\n\n## Résumé\n\n- Les fonctions sont des valeurs. Elles peuvent être attribuées, copiées ou déclarées à n’importe quel endroit du code.\n- Si la fonction est déclarée comme une instruction distincte dans le flux de code principal, cela s'appelle une \"déclaration de fonction\".\n- Si la fonction est créée dans le cadre d’une expression, elle est appelée \"expression de fonction\".\n- Les déclarations de fonctions sont traitées avant l'exécution du bloc de code. Elles sont visibles partout dans le bloc.\n- Les expressions de fonction sont créées lorsque le flux d’exécution les atteint.\n\nDans la plupart des cas, lorsque nous devons déclarer une fonction, une fonction déclaration est préférable parce qu'elle est visible avant la déclaration elle-même. Cela nous donne plus de flexibilité dans l'organisation du code et il est généralement plus lisible.\n\nNous devrions donc utiliser une fonction expression uniquement lorsqu'une fonction déclaration n'est pas adaptée à la tâche. Nous en avons vu quelques exemples dans ce chapitre et nous en verrons d'autres à l'avenir.\n"
  },
  {
    "path": "1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md",
    "content": "\n```js run\nfunction ask(question, yes, no) {\n  if (confirm(question)) yes();\n  else no();\n}\n\nask(\n  \"Do you agree?\",\n*!*\n  () => alert(\"You agreed.\"),\n  () => alert(\"You canceled the execution.\")\n*/!*\n);\n```\n\nÇa a l'air court et propre, non ?\n"
  },
  {
    "path": "1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md",
    "content": "\n# Réécrire avec les fonctions fléchées\n\nRemplacez les expressions de fonction par des fonctions fléchées dans le code ci-dessous :\n\n```js run\nfunction ask(question, yes, no) {\n  if (confirm(question)) yes();\n  else no();\n}\n\nask(\n  \"Do you agree?\",\n  function() { alert(\"You agreed.\"); },\n  function() { alert(\"You canceled the execution.\"); }\n);\n```\n"
  },
  {
    "path": "1-js/02-first-steps/17-arrow-functions-basics/article.md",
    "content": "# Fonctions fléchées, les bases\n\nIl existe une syntaxe plus simple et concise pour créer des fonctions, c'est souvent mieux que les Expressions de Fonction.\n\nLes \"fonctions fléchées\" sont appelées ainsi pour leur syntaxe :\n\n```js\nlet func = (arg1, arg2, ..., argN) => expression;\n```\n\nCela va créer une function `func` qui accepte les arguments `arg1...argN`, puis évalue l'`expression` sur le côté droit et retourne le résultat.\n\nC'est donc la version raccourcie de :\n\n```js\nlet func = function(arg1, arg2, ..., argN) {\n  return expression;\n};\n```\n\nVoyons un exemple concret :\n\n```js run\nlet sum = (a, b) => a + b;\n\n/* Cette fonction fléchée est la forme raccourcie de :\n\nlet sum = function(a, b) {\n  return a + b;\n};\n*/\n\nalert( sum(1, 2) ); // 3\n```\n\nComme vous pouvez le voir `(a, b) => a + b` représente une fonction qui accepte 2 arguments nommés `a` et `b`. Lors de l'exécution, elle évalue l'expression `a + b` et retourne le résultat.\n\n- Pour un argument unique, alors les parenthèses autour du paramètre peuvent être omises, rendant la fonction encore plus courte.\n\n    Par exemple:\n\n    ```js run\n    *!*\n    let double = n => n * 2;\n    // Similaire à : let double = function(n) { return n * 2 }\n    */!*\n\n    alert( double(3) ); // 6\n    ```\n\n- S’il n’y a pas d’arguments, les parenthèses seront alors vides, mais elles doivent êtres présentes :\n\n    ```js run\n    let sayHi = () => alert(\"Hello!\");\n\n    sayHi();\n    ```\n\nLes fonctions fléchées peuvent être utilisées de la même manière que les Expressions de Fonction.\n\nPar exemple pour créer une fonction dynamiquement :\n\n```js run\nlet age = prompt(\"What is your age?\", 18);\n\nlet welcome = (age < 18) ?\n  () => alert(\"Hello!\") :\n  () => alert(\"Greetings!\");\n\nwelcome(); // ok now\n```\n\nLes fonctions fléchées peuvent paraître étranges et peu lisibles au début, mais cela change rapidement avec les yeux s'habituant à cette structure.\n\nElles sont très utiles pour des actions sur une ligne et que l'on est juste paresseux d'écrire d'autres mots.\n\n## Les fonctions fléchées multiligne\n\nLes fonctions fléchées que nous avons vues jusqu'à présent étaient très simples. Elles ont pris des arguments à gauche de `=>`, les ont évalués et ont renvoyé l'expression de droite avec elles.\n\nParfois nous avons besoin de plus de complexité, comme des expressions multiples ou des déclarations. Cela est possible avec des accolades les délimitant. Il faut ensuite utiliser un `return` à l'intérieur de celles-ci.\n\nComme cela :\n\n```js run\nlet sum = (a, b) => {  // Les accolades ouvre une fonction multiligne\n  let result = a + b;\n*!*\n  return result; // si nous utilisons des accolades, nous avons besoin d'un \"return\" explicite\n*/!*\n};\n\nalert( sum(1, 2) ); // 3\n```\n\n```smart header=\"Plus à venir\"\nNous nous arrêtons ici sur les fonctions fléchées pour leur syntaxe brève mais ce n'est pas tout !\n\nLes fonctions fléchées ont d'autres particularités intéressantes.\n\nPour les apprendre en profondeur, nous devons d'abord voir d'autres aspects de JavaScript, nous reviendrons donc aux fonctions fléchées plus tard dans le chapitre <info:arrow-functions>.\n\nPour l'instant, nous pouvons les utiliser pour des actions sur une ligne ou des callbacks (rappels).\n```\n\n## Résumé\n\nLes fonctions fléchées sont pratiques pour des actions simples, en particulier pour les one-liners. Ils se déclinent en deux variantes :\n\n1. Sans accolades : `(...args) => expression` -- le côté droit est une expression : la fonction l'évalue et retourne le résultat. Les parenthèses peuvent être omises s'il n'y a qu'un seul argument, par ex. `n => n*2`.\n2. Avec accolades : `(...args) => { body }` -- les accolades nous permet des déclarations multiples au sein de la fonction, mais nous devons ajouter un `return` explicite pour retourner quelque chose.\n"
  },
  {
    "path": "1-js/02-first-steps/18-javascript-specials/article.md",
    "content": "# JavaScript specials\n\nCe chapitre récapitule brièvement les fonctionnalités de JavaScript que nous avons apprises à ce jour, en accordant une attention particulière aux moments les plus subtils.\n\n## Structure du code\n\nLes instructions sont délimitées par un point-virgule :\n\n```js run no-beautify\nalert('Hello'); alert('World');\n```\n\nHabituellement, un saut de ligne est également traité comme un séparateur, de sorte que cela fonctionnerait également :\n\n```js run no-beautify\nalert('Hello')\nalert('World')\n```\n\nCela s'appelle \"insertion automatique de point-virgule\". Parfois, cela ne fonctionne pas, par exemple :\n\n```js run\nalert(\"Il y aura une erreur après ce message\")\n\n[1, 2].forEach(alert)\n```\n\nLa plupart des guides de style de code conviennent que nous devrions mettre un point-virgule après chaque déclaration.\n\nLes points-virgules ne sont pas nécessaires après les blocs de code `{...}` et les constructions de syntaxe les utilisant comme des boucles :\n\n```js\nfunction f() {\n  // aucun point-virgule nécessaire après la déclaration de la fonction\n}\n\nfor(;;) {\n  // pas de point-virgule nécessaire après la boucle\n}\n```\n\n… Mais même si nous pouvons mettre un point-virgule supplémentaire quelque part, ce n’est pas une erreur. Ce sera ignoré.\n\nPlus d'informations dans : <info:structure>.\n\n## Mode strict\n\nPour activer pleinement toutes les fonctionnalités de JavaScript moderne, nous devrions commencer les scripts avec `\"use strict\"`.\n\n```js\n'use strict';\n\n...\n```\n\nLa directive doit être au sommet d'un script ou au début d'un corps de fonction.\n\nSans `\"use strict\"`, tout fonctionne toujours, mais certaines fonctionnalités se comportent à l'ancienne, de manière \"compatible\". Nous préférons généralement le comportement moderne.\n\nCertaines fonctionnalités modernes du langage (telles que les classes que nous étudierons dans le futur) activent implicitement le mode strict.\n\nPlus d’informations dans : <info:strict-mode>.\n\n## Variables\n\nPeut être déclaré en utilisant :\n\n- `let`\n- `const` (constant, ne peut pas être changé)\n- `var` (à l'ancienne, nous le verrons plus tard)\n\nUn nom de variable peut inclure :\n\n- Lettres et chiffres, mais le premier caractère ne peut pas être un chiffre.\n- Les caractères `$` et `_` sont normaux, à égalité avec les lettres.\n- Les alphabets et les hiéroglyphes non latins sont également autorisés, mais ils ne sont généralement pas utilisés.\n\nLes variables sont typées dynamiquement. Elles peuvent stocker n'importe quelle valeur :\n\n```js\nlet x = 5;\nx = \"John\";\n```\n\nIl y a 8 types de données :\n\n- `number` pour les nombres à virgule flottante et les nombres entiers,\n- `bigint` pour des nombres entiers de longueur arbitraire,\n- `string` pour les chaînes de caractères,\n- `boolean` pour les valeurs logiques : `true/false`,\n- `null` -- un type avec une seule valeur `null`, signifiant \"vide\" ou \"n'existe pas\",\n- `undefined` -- un type avec une seule valeur `undefined`, signifiant \"non assigné\",\n- `object` et `symbol` -- pour les structures de données complexes et les identifiants uniques, nous ne les avons pas encore appris.\n\nL'opérateur `typeof` renvoie le type d'une valeur, à deux exceptions près :\n\n```js\ntypeof null == \"object\" // erreur dans le langage\ntypeof function(){} == \"function\" // les fonctions sont traitées spécialement\n```\n\nPlus d’informations dans : <info:variables> et <info:types>.\n\n## Interaction\n\nNous utilisons un navigateur comme environnement de travail. Les fonctions de base de l'interface utilisateur sont les suivantes :\n\n[`prompt(question, [default])`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt)\n: Posez une question et retournez soit ce que le visiteur a entré, soit `null` s'il clique sur \"cancel\".\n\n[`confirm(question)`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm)\n: Posez une `question` et suggérez de choisir entre Ok et Annuler. Le choix est retourné comme `true/false`.\n\n[`alert(message)`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert)\n: Affiche un `message`.\n\nToutes ces fonctions sont *modales*, elles suspendent l'exécution du code et empêchent le visiteur d'interagir avec la page tant qu'il n'a pas répondu.\n\nPar exemple :\n\n```js run\nlet userName = prompt(\"Your name?\", \"Alice\");\nlet isTeaWanted = confirm(\"Do you want some tea?\");\n\nalert( \"Visitor: \" + userName ); // Alice\nalert( \"Tea wanted: \" + isTeaWanted ); // true\n```\n\nPlus d’informations dans : <info:alert-prompt-confirm>.\n\n## Operateurs\n\nJavaScript prend en charge les opérateurs suivants :\n\nArithmétique\n: Regulier : `* + - /`, aussi `%` pour le reste et `**` pour la puissance d'un nombre.\n\n    Le binaire plus `+` concatène des chaînes de caractères. Et si l'un des opérandes est une chaîne de caractères, l'autre est également converti en chaîne de caractères :\n\n    ```js run\n    alert( '1' + 2 ); // '12', string\n    alert( 1 + '2' ); // '12', string\n    ```\n\nAffectations\n: Il y a une assignation simple : `a = b` et des combinés comme `a *= 2`.\n\nBitwise\n: Les opérateurs au niveau du bit fonctionnent avec des entiers 32 bits au niveau du bit le plus bas : voir la [doc](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#bitwise_operators) quand ils sont nécessaires.\n\nConditionnel\n: Le seul opérateur avec trois paramètres : `cond ? resultA : resultB`. Si `cond` est vrai, retourne `resultA`, autrement `resultB`.\n\nOpérateurs logiques\n: ET logique `&&` et OU `||` effectuent une évaluation en circuit court puis renvoyent la valeur là où ils se sont arrêtés (pas nécessairement `true`/`false`). NOT logique `!` convertit l'opérande en type booléen et retourne la valeur inverse.\n\nL'opérateur de coalescence des nuls\n: L'opérateur `??` permet de choisir une valeur définie dans une liste de variables. Le résultat de `a ?? b` est `a` sauf s'il est `null`/`undefined`, alors `b`.\n\nComparaisons\n: Le contrôle d’égalité `==` pour les valeurs de types différents les convertit en un nombre (sauf `null` et `undefined`, égales entre elles et rien d’autre), elles sont donc égales :\n\n    ```js run\n    alert( 0 == false ); // true\n    alert( 0 == '' ); // true\n    ```\n\n    D'autres comparaisons sont également converties en nombre.\n\n    L’opérateur d’égalité stricte `===` ne fait pas la conversion : différents types signifient toujours différentes valeurs pour lui.\n\n    Les valeurs `null` et `undefined` sont spéciales: elles sont égales `==` les unes aux autres et n’égalent rien d’autre.\n\n    Les comparaisons supérieures/inférieures comparent des chaînes caractère par caractère, les autres types sont convertis en nombre.\n\nAutres opérateurs\n: Il y en a quelques autres, comme un opérateur de virgule.\n\nPlus d'informations dans : <info:operators>, <info:comparison>, <info:logical-operators>.\n\n## Boucles\n\n- Nous avons couvert 3 types de boucles :\n\n    ```js\n    // 1\n    while (condition) {\n      ...\n    }\n\n    // 2\n    do {\n      ...\n    } while (condition);\n\n    // 3\n    for(let i = 0; i < 10; i++) {\n      ...\n    }\n    ```\n\n- La variable déclarée dans la boucle `for(let ...)` est visible uniquement à l'intérieur de la boucle. Mais nous pouvons aussi omettre `let` et réutiliser une variable existante.\n- Les directives `break/continue` permettent de sortir complètement de la boucle / de l'itération en cours. Utilisez des labels pour rompre les boucles imbriquées.\n\nDetails dans : <info:while-for>.\n\nPlus tard, nous étudierons plus de types de boucles pour traiter des objets.\n\n## La construction \"switch\"\n\nLa construction \"switch\" peut remplacer plusieurs vérifications `if`. Il utilise `===` (égalité stricte) pour les comparaisons.\n\nPar exemple :\n\n```js run\nlet age = prompt('Your age?', 18);\n\nswitch (age) {\n  case 18:\n    alert(\"Won't work\"); // le résultat de prompt est une chaîne de caractères, pas un nombre\n\n  case \"18\":\n    alert(\"This works!\");\n    break;\n\n  default:\n    alert(\"Any value not equal to one above\");\n}\n```\n\nDetails dans : <info:switch>.\n\n## Fonctions\n\nNous avons couvert trois manières de créer une fonction en JavaScript :\n\n1. Déclaration de fonction: la fonction dans le flux de code principal\n\n    ```js\n    function sum(a, b) {\n      let result = a + b;\n\n      return result;\n    }\n    ```\n\n2. Expression de fonction : fonction dans le contexte d'une expression\n\n    ```js\n    let sum = function(a, b) {\n      let result = a + b;\n\n      return result;\n    };\n    ```\n\n3. Fonctions fléchées :\n\n    ```js\n    // expression à droite\n    let sum = (a, b) => a + b;\n\n    // ou une syntaxe multiligne avec {...}, il faut return ici :\n    let sum = (a, b) => {\n      // ...\n      return a + b;\n    }\n\n    // sans arguments\n    let sayHi = () => alert(\"Hello\");\n\n    // avec un seul argument\n    let double = n => n * 2;\n    ```\n\n\n- Les fonctions peuvent avoir des variables locales: celles déclarées dans son corps ou sa liste de paramètres. Ces variables ne sont visibles qu'à l'intérieur de la fonction.\n- Les paramètres peuvent avoir des valeurs par défaut : `function sum(a = 1, b = 2) {...}`.\n- Les fonctions retournent toujours quelque chose. Si aucune instruction `return` n’est renvoyée, le résultat est `undefined`.\n\nDetails : voir <info:function-basics>, <info:arrow-functions-basics>.\n\n## Plus à venir\n\nC’était une brève liste de fonctionnalités de JavaScript. Pour l’instant, nous n’avons étudié que les bases. Plus loin dans le tutoriel, vous trouverez plus de fonctionnalités spéciales et avancées de JavaScript.\n"
  },
  {
    "path": "1-js/02-first-steps/index.md",
    "content": "# Fondamentaux JavaScript\n\nApprenons les bases de la construction de scripts.\n"
  },
  {
    "path": "1-js/03-code-quality/01-debugging-chrome/article.md",
    "content": "# Débogage dans le navigateur\n\nAvant d’écrire un code plus complexe, parlons de débogage.\n\nLe [Debugging](https://en.wikipedia.org/wiki/Debugging) est le processus de recherche et de correction des erreurs dans un script. Tous les navigateurs modernes et la plupart des autres environnements prennent en charge les outils de débogage - une interface utilisateur spéciale dans les outils de développement facilitant grandement le débogage. Cela permet également de tracer le code étape par étape pour voir ce qui se passe exactement.\n\nNous allons utiliser Chrome ici, car il possède suffisamment de fonctionnalités, la plupart des autres navigateurs utilisent un processus similaire.\n\n## Le volet \"Sources\"\n\nVotre version de Chrome peut sembler un peu différente, mais vous devez tout de même savoir ce qui est là.\n\n- Ouvrez la [page d'exemple](debugging/index.html) dans Chrome.\n- Activer les outils de développement avec `key:F12` (Mac: `key:Cmd+Opt+I`).\n- Séléctionner le volet `Sources`.\n\nVoici ce que vous devriez voir si vous le faites pour la première fois :\n\n![](chrome-open-sources.svg)\n\nLe bouton <span class=\"devtools\" style=\"background-position:-172px -98px\"></span> ouvre le volet avec les fichiers.\n\nCliquez dessus et sélectionnez `hello.js` dans l’arborescence. Voici ce qui devrait apparaître :\n\n![](chrome-tabs.svg)\n\nIci nous pouvons voir 3 parties :\n\n1. Le volet **explorateur de fichiers** répertorie les fichiers HTML, JavaScript, CSS et autres fichiers, y compris les images jointes à la page. Des extensions Chrome peuvent également apparaître ici.\n2. Le volet **Editeur de Code** affiche le code source.\n3. Le volet **Débugueur JavaScript** est pour le débogage, nous l'explorerons bientôt.\n\nMaintenant, vous pouvez cliquer sur le même bouton <span class=\"devtools\" style=\"background-position:-172px -122px\"></span> à nouveau pour masquer la liste des ressources et laisser un peu d’espace au code.\n\n## Console\n\nSi nous appuyons sur `key:Esc`, une console s'ouvre ci-dessous. Nous pouvons taper des commandes ici et appuyer sur `key:Entrée` pour les exécuter.\n\nUne fois une instruction exécutée, son résultat est présenté ci-dessous.\n\nPar exemple, ici `1+2` donne `3`, tandis que l'appel de fonction `hello(\"debugger\")` ne renvoie rien, donc le résultat est `undefined` :\n\n![](chrome-sources-console.svg)\n\n## Breakpoints\n\nExaminons ce qui se passe dans le code de la [page d'exemple](debugging/index.html). Dans `hello.js`, cliquez sur le numéro de ligne `4`. Oui, sur le chiffre `4`, pas sur le code.\n\nFélicitations ! Vous avez défini un point d'arrêt. Veuillez également cliquer sur le numéro correspondant à la ligne `8`.\n\nCela devrait ressembler à ceci (le bleu est l'endroit où vous devez cliquer) :\n\n![](chrome-sources-breakpoint.svg)\n\nUn *breakpoint* est un point dans le code où le débogueur mettra automatiquement en pause l'exécution de JavaScript.\n\nPendant que le code est en pause, nous pouvons examiner les variables actuelles, exécuter des commandes dans la console, etc. En d'autres termes, nous pouvons le déboguer.\n\nNous pouvons toujours trouver une liste de points d'arrêt dans le volet de droite. C’est utile lorsque nous avons plusieurs points d’arrêt dans divers fichiers. Ça nous permet de :\n- Sauter rapidement au point d'arrêt dans le code (en cliquant dessus dans le volet de droite).\n- Désactiver temporairement le point d'arrêt en le décochant.\n- Supprimer le point d'arrêt en cliquant avec le bouton droit de la souris et en sélectionnant Supprimer.\n- … Et ainsi de suite.\n\n```smart header=\"Points d'arrêt conditionnels\"\n*Clic droit* sur le numéro de ligne permet de créer un point d'arrêt *conditionnel*. Il ne se déclenche que lorsque l'expression donnée, que vous devez fournir lors de sa création, est vraie.\n\nC’est pratique lorsque nous devons nous arrêter uniquement pour une certaine valeur de variable ou pour certains paramètres de fonction.\n```\n\n## La commande \"debugger\"\n\nNous pouvons également suspendre le code en utilisant la commande `debugger`, comme ceci :\n\n```js\nfunction hello(name) {\n  let phrase = `Hello, ${name}!`;\n\n*!*\n  debugger;  // <-- le débogueur s'arrête ici\n*/!*\n\n  say(phrase);\n}\n```\n\nUne telle commande ne fonctionne que lorsque les outils de développement sont ouverts, sinon le navigateur l'ignore.\n\n\n## Pause et regarder autour\n\nDans notre exemple, `hello()` est appelé lors du chargement de la page, donc le moyen le plus simple d'activer le débogueur (après avoir défini les points d'arrêt) est de recharger la page. Appuyez donc sur `key:F5` (Windows, Linux) ou sur `key:Cmd+R` (Mac).\n\nLorsque le point d'arrêt est défini, l'exécution s'interrompt à la 4ème ligne :\n\n![](chrome-sources-debugger-pause.svg)\n\nVeuillez ouvrir les menus déroulants d’information à droite (indiqués par des flèches). Ils vous permettent d'examiner l'état du code actuel :\n\n1. **`Watch` -- affiche les valeurs actuelles pour toutes les expressions.**\n\n    Vous pouvez cliquer sur le plus `+` et saisir une expression. Le débogueur affichera sa valeur, la recalculant automatiquement dans le processus d'exécution.\n\n2. **`Call Stack` -- affiche la chaîne des appels imbriqués.**\n\n    À ce moment précis, le débogueur se trouve dans l’appel `hello()`, appelé par un script dans `index.html` (aucune fonction n’est appelée, elle est donc appelée \"anonyme\").\n\n    Si vous cliquez sur un élément de la pile (ex: \"anonymous\"), le débogueur passe au code correspondant, et toutes ses variables peuvent également être examinées.\n3. **`Scope` -- variables actuelles.**\n\n    `Local` affiche les variables de fonction locales. Vous pouvez également voir leurs valeurs surlignées directement sur la source.\n\n    `Global` a des variables globales (en dehors de toutes fonctions).\n\n    Il y a aussi le mot-clé `this` que nous n’avons pas encore étudié, mais nous le ferons bientôt.\n\n## Tracer l'exécution\n\nIl est maintenant temps de *tracer* le script.\n\nIl y a des boutons pour cela en haut du volet de droite. Actionnons-les.\n<!-- https://github.com/ChromeDevTools/devtools-frontend/blob/master/front_end/Images/src/largeIcons.svg -->\n<span class=\"devtools\" style=\"background-position:-146px -168px\"></span> -- \"Reprendre\" : continue l'exécution, raccourci clavier `key:F8`.\n: Reprend l'exécution. S'il n'y a pas de points d'arrêt supplémentaires, l'exécution continue et le débogueur perd le contrôle.\n\n    Voici ce que nous pouvons voir après un clic dessus :\n\n    ![](chrome-sources-debugger-trace-1.svg)\n\n    L'exécution a repris, atteint un autre point d'arrêt à l'intérieur de `say()` et s'y est arrêtée. Jetez un coup d’œil à \"Call stack\" à droite. Il a augmenté d'un appel supplémentaire. Nous sommes à l'intérieur `say()` maintenant.\n\n<span class=\"devtools\" style=\"background-position:-200px -190px\"></span> -- \"Step\": lance la commande suivante, raccourci clavier `key:F9`.\n: Exécute la prochaine déclaration. Si nous cliquons dessus maintenant, `alert` sera affiché.\n\n    En cliquant dessus encore et encore, vous parcourrez toutes les instructions de script une par une.\n\n<span class=\"devtools\" style=\"background-position:-62px -192px\"></span> -- \"Step over\": lance la commande suivante, mais *n'entre pas dans une fonction*, raccourci clavier `key:F10`.\n : Similaire à la commande \"Step\" précédente, mais se comporte différemment si l'instruction suivante est un appel de fonction (pas une fonction intégrée, comme `alert`, mais une fonction qui nous est propre).\n\nSi nous les comparons, la commande \"Step\" entre dans un appel de fonction imbriqué et interrompt l'exécution à sa première ligne, tandis que \"Step over\" exécute l'appel de fonction imbriqué de manière invisible pour nous, en sautant les fonctions internes.\n\nL'exécution est alors suspendue immédiatement après cette fonction.\n\nC'est bien si nous ne sommes pas intéressés à voir ce qui se passe dans l'appel de fonction.\n\n<span class=\"devtools\" style=\"background-position:-4px -194px\"></span> -- \"Step into\", raccourci clavier `key:F11`.\n: Cela ressemble à \"Step\", mais se comporte différemment dans le cas d'appels de fonctions asynchrones. Si vous commencez seulement à apprendre le JavaScript, vous pouvez alors ignorer la différence, car nous n'avons pas encore d'appels asynchrones.\n\n    Pour le futur, il suffit de noter que la commande \"Step\" ignore les actions asynchrones, telles que `setTimeout` (appel de fonction planifiée), qui s'exécutent ultérieurement. Le \"Pas à pas\" entre dans leur code, les attend si nécessaire. Voir [DevTools manual](https://developers.google.com/web/updates/2018/01/devtools#async) pour plus de détails.\n\n<span class=\"devtools\" style=\"background-position:-32px -194px\"></span> -- \"Step out\": continuer l'exécution jusqu'à la fin de la fonction en cours, raccourci clavier `key:Shift+F11`.\n: Continue l'exécution et l'arrête à la toute dernière ligne de la fonction en cours. C'est pratique lorsque nous avons accidentellement entré un appel imbriqué en utilisant <span class=\"devtools\" style=\"background-position:-200px -190px\"></span>, mais cela ne nous intéresse pas et nous voulons continuer jusqu'au bout le plus tôt possible.\n\n<span class=\"devtools\" style=\"background-position:-61px -74px\"></span> -- active / désactive tous les points d'arrêt.\n: Ce bouton ne déplace pas l'exécution. Juste un ensemble de on/off pour les points d'arrêt.\n\n<span class=\"devtools\" style=\"background-position:-90px -146px\"></span> -- active/désactive la pause automatique en cas d'erreur.\n: Lorsqu'il est activé, si les outils de développement sont ouverts, une erreur lors de l'exécution du script le met automatiquement en pause. Ensuite, nous pouvons analyser les variables dans le débogueur pour voir ce qui n'a pas fonctionné. Donc, si notre script s’arrête avec une erreur, nous pouvons ouvrir le débogueur, activer cette option et recharger la page pour voir où il s’arrête et quel est le contexte à ce moment-là.\n\n```smart header=\"Continue to here\"\nUn clic droit sur une ligne de code ouvre le menu contextuel avec une excellente option appelée \"Continue to here\".\n\nC’est pratique lorsque nous voulons faire plusieurs pas en avant, mais nous sommes trop paresseux pour définir un point d’arrêt.\n```\n\n## Logging\n\nPour afficher quelque chose sur la console depuis notre code, utilisez la fonction `console.log`.\n\nPar exemple, cela affiche les valeurs de `0` à `4` sur la console :\n\n```js run\n// ouvrir la console pour visualiser\nfor (let i = 0; i < 5; i++) {\n  console.log(\"value,\", i);\n}\n```\n\nLes internautes ne voient pas cette sortie, elle se trouve dans la console. Pour la voir, ouvrez l'onglet Console des outils de développement ou appuyez sur `key:Esc` lorsque vous vous trouvez dans un autre onglet : la console en bas s'ouvre.\n\nSi nous avons assez de logging dans notre code, nous pouvons voir ce qui se passe dans les enregistrements, sans le débogueur.\n\n## Résumé\n\nComme nous pouvons le constater, il existe trois méthodes principales pour suspendre un script :\n1. Les points d'arrêt (breakpoint).\n2. Les instructions du `debugger`.\n3. Une erreur (si les outils de développement sont ouverts et le bouton <span class=\"devtools\" style=\"background-position:-90px -146px\"></span> est activé)\n\nEn pause, nous pouvons déboguer -- examiner les variables et suivre le code pour voir où l’exécution s’est mal passée.\n\nIl y a beaucoup plus d'options dans les outils de développement que celles couvertes ici. Le manuel complet est ici <https://developers.google.com/web/tools/chrome-devtools>.\n\nLes informations de ce chapitre sont suffisantes pour commencer le débogage, mais plus tard, en particulier si vous utilisez beaucoup de fonctions de navigateur, allez-y et examinez les fonctionnalités plus avancées des outils de développement.\n\nOh, et vous pouvez aussi cliquer sur différents endroits des outils de développement et voir ce qui s’affiche. C’est probablement la voie la plus rapide pour apprendre les outils de développement. N'oubliez pas le clic droit et les menus contextuels !\n"
  },
  {
    "path": "1-js/03-code-quality/01-debugging-chrome/debugging.view/hello.js",
    "content": "function hello(name) {\n  let phrase = `Hello, ${name}!`;\n\n  say(phrase);\n}\n\nfunction say(phrase) {\n  alert(`** ${phrase} **`);\n}\n"
  },
  {
    "path": "1-js/03-code-quality/01-debugging-chrome/debugging.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <script src=\"hello.js\"></script>\n\n  An example for debugging.\n\n  <script>\n  hello(\"John\");\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/01-debugging-chrome/head.html",
    "content": "<style>\nspan.devtools {\n  display: inline-block;\n  background-image: url(/article/debugging-chrome/largeIcons.svg);\n  height:18px;\n  width:18px;\n}\n</style>\n"
  },
  {
    "path": "1-js/03-code-quality/02-coding-style/1-style-errors/solution.md",
    "content": "\nVous pouvez noter ce qui suit :\n\n```js no-beautify\nfunction pow(x,n)  // <- pas d'espace entre les arguments\n{  // <- accolade sur une ligne séparée\n  let result=1;   // <- pas d'espaces des deux côtés de =\n  for(let i=0;i<n;i++) {result*=x;}   // <- pas d'espaces\n  // le contenu de {...} devrait être sur une nouvelle ligne\n  return result;\n}\n\nlet x=prompt(\"x?\",''), n=prompt(\"n?\",'') // <-- techniquement possible,\n// mais mieux vaut en faire 2 lignes, il n'y a également pas d'espaces et de ;\nif (n<=0)  // <- pas d'espaces à l'intérieur (n <= 0), et devrait être une ligne supplémentaire au-dessus\n{   // <- accolade sur une ligne séparée\n  // ci-dessous - une longue ligne, peut être utile de la scinder en 2 lignes\n  alert(`Power ${n} is not supported, please enter an integer number greater than zero`);\n}\nelse // <- pourrait l'écrire sur une seule ligne comme \"} else {\"\n{\n  alert(pow(x,n))  // pas d'espaces et ; manquant\n}\n```\n\nLa variante réparée :\n\n```js\nfunction pow(x, n) {\n  let result = 1;\n\n  for (let i = 0; i < n; i++) {\n    result *= x;\n  }\n\n  return result;\n}\n\nlet x = prompt(\"x?\", \"\");\nlet n = prompt(\"n?\", \"\");\n\nif (n <= 0) {\n  alert(`Power ${n} is not supported,\n    please enter an integer number greater than zero`);\n} else {\n  alert( pow(x, n) );\n}\n```\n"
  },
  {
    "path": "1-js/03-code-quality/02-coding-style/1-style-errors/task.md",
    "content": "importance: 4\n\n---\n\n# Mauvais style\n\nQuel est le problème avec le style de code ci-dessous ?\n\n```js no-beautify\nfunction pow(x,n)\n{\n  let result=1;\n  for(let i=0;i<n;i++) {result*=x;}\n  return result;\n}\n\nlet x=prompt(\"x?\",''), n=prompt(\"n?\",'')\nif (n<=0)\n{\n  alert(`Power ${n} is not supported, please enter an integer number greater than zero`);\n}\nelse\n{\n  alert(pow(x,n))\n}\n```\n\nRéparer le.\n"
  },
  {
    "path": "1-js/03-code-quality/02-coding-style/article.md",
    "content": "# Style de codage\n\nNotre code doit être aussi propre et lisible que possible.\n\nC’est en fait un art de la programmation -- prendre une tâche complexe et la coder de manière correcte et lisible par l’homme. Un bon style de code aide grandement à cela.\n\nUne chose à aider est le bon style de code.\n\n## Syntaxe\n\nVoici un aide-mémoire avec quelques règles suggérées (plus de détails ci-dessous) :\n\n![](code-style.svg)\n<!--\n```js\nfunction pow(x, n) {\n  let result = 1;\n\n  for (let i = 0; i < n; i++) {\n    result *= x;\n  }\n\n  return result;\n}\n\nlet x = prompt(\"x?\", \"\");\nlet n = prompt(\"n?\", \"\");\n\nif (n < 0) {\n  alert(`Power ${n} is not supported,\n    please enter a non-negative integer number`);\n} else {\n  alert( pow(x, n) );\n}\n```\n\n-->\n\nDiscutons maintenant des règles et de leurs raisons en détail.\n\n```warn header=\"Il n'y a pas de règles \\\"vous devez\\\"\"\nRien n'est figé ici. Ce sont des préférences de style, pas des dogmes religieux.\n```\n\n### Accolades\n\nDans la plupart des projets JavaScript, les accolades sont écrites sur la même ligne que le mot clé correspondant, et non sur la nouvelle ligne, dans un style dit «égyptien». Il y a aussi un espace avant un crochet d’ouverture.\n\nComme ceci :\n\n```js\nif (condition) {\n  // fait ceci\n  // ...et cela\n  // ...et cela\n}\n```\n\nUne construction sur une seule ligne, comme `if (condition) doSomething()`, est un cas important. Devrions-nous utiliser des accolades ?\n\nVoici les variantes annotées pour que vous puissiez juger de leur lisibilité :\n\n1. 😠 Les débutants font parfois cela. C'est une mauvaise pratique ! Les accolades ne sont pas nécessaires :\n    ```js\n    if (n < 0) *!*{*/!*alert(`Power ${n} is not supported`);*!*}*/!*\n    ```\n2. 😠 Lorsque vous n'utilisez pas d'accolades, évitez de passer pas à la ligne car il est facile de se tromper :\n    ```js\n    if (n < 0)\n      alert(`Power ${n} is not supported`);\n    ```\n3. 😏 Ne pas utiliser d'accolade sur une seule ligne, est acceptable tant que cela reste court :\n    ```js\n    if (n < 0) alert(`Power ${n} is not supported`);\n    ```\n4. 😃 Voici une bonne manière de faire :\n    ```js\n    if (n < 0) {\n      alert(`Power ${n} is not supported`);\n    }\n    ```\n\nPour un code tres court, une ligne est autorisée, par exemple `if (cond) return null`. Mais la variante numéro 4 est généralement plus lisible.\n\n### Longueur de la ligne\n\nPersonne n'aime lire une longue ligne horizontale de code. La meilleure pratique est de la scinder.\n\nPar exemple :\n\n```js\n// les guillemets backtick ` permettent de scinder la chaîne de caractères en plusieurs lignes\nlet str = `\n  ECMA International's TC39 is a group of JavaScript developers,\n  implementers, academics, and more, collaborating with the community\n  to maintain and evolve the definition of JavaScript.\n`;\n```\n\nEt pour les déclarations `if` :\n\n```js\nif (\n  id === 123 &&\n  moonPhase === 'Waning Gibbous' &&\n  zodiacSign === 'Libra'\n) {\n  letTheSorceryBegin();\n}\n```\n\nLa longueur de ligne maximale est convenue au niveau de l'équipe. C’est généralement 80 ou 120 caractères.\n\n### Indentations\n\nIl existe deux types d'indentations :\n\n- **Un retrait horizontal : 2 ou 4 espaces.**\n\n    Une indentation horizontale est faite en utilisant 2 ou 4 espaces ou le symbole  horizontal de tabulation (touche `key:Tab`). Lequel choisir est une vieille guerre sainte. Les espaces sont plus communs de nos jours.\n\n    Un des avantages des espaces sur les tabulations est qu’ils permettent des configurations de retrait plus flexibles que le symbole tabulation.\n\n    Par exemple, nous pouvons aligner les arguments avec la parenthèse d’ouverture, comme ceci :\n\n    ```js no-beautify\n    show(parameters,\n         aligned, // 5 espaces à gauche\n         one,\n         after,\n         another\n      ) {\n      // ...\n    }\n    ```\n\n- **Un retrait vertical: lignes vides pour fractionner le code en blocs logiques.**\n\n    Même une seule fonction peut souvent être divisée en blocs logiques. Dans l'exemple ci-dessous, l'initialisation des variables, la boucle principale et le retour du résultat sont fractionnés verticalement :\n\n    ```js\n    function pow(x, n) {\n      let result = 1;\n      //              <--\n      for (let i = 0; i < n; i++) {\n        result *= x;\n      }\n      //              <--\n      return result;\n    }\n    ```\n\n    Insérez une nouvelle ligne où cela aide à rendre le code plus lisible. Il ne devrait pas y avoir plus de neuf lignes de code sans indentation verticale.\n\n### Un point-virgule\n\nUn point-virgule doit être présent après chaque déclaration. Même si cela pourrait éventuellement être ignoré.\n\nIl y a des langages où le point-virgule est vraiment optionnel. Il est donc rarement utilisé. Mais dans JavaScript, il y a peu de cas où un saut de ligne n'est parfois pas interprété comme un point-virgule. Cela laisse place à des erreurs de programmation. Plus d'informations à ce sujet dans le chapitre <info:structure#semicolon>.\n\nSi vous êtes un programmeur JavaScript expérimenté, vous pouvez choisir un style de code sans point-virgule comme [StandardJS](https://standardjs.com/). Autrement, il est préférable d’utiliser des points-virgules pour éviter les pièges possibles. La majorité des développeurs mettent des points-virgules.\n\n### Niveaux d'imbrications\n\nIl ne devrait pas y avoir trop de niveaux d'imbrication.\n\nPar exemple, dans une boucle, c’est parfois une bonne idée d’utiliser la directive [\"continue\"](info:while-for#continue) pour éviter une imbrication supplémentaire.\n\nPar exemple, au lieu d’ajouter un `if` imbriqué conditionnel comme ceci :\n\n```js\nfor (let i = 0; i < 10; i++) {\n  if (cond) {\n    ... // <- un autre niveau d'imbrication\n  }\n}\n```\n\nNous pouvons écrire :\n\n```js\nfor (let i = 0; i < 10; i++) {\n  if (!cond) *!*continue*/!*;\n  ...  // <- pas de niveau d'imbrication supplémentaire\n}\n```\n\nUne chose similaire peut être faite avec `if`/`else` et `return`.\n\nPar exemple, les deux constructions ci-dessous sont identiques.\n\nLe premier :\n\n```js\nfunction pow(x, n) {\n  if (n < 0) {\n    alert(\"Negative 'n' not supported\");\n  } else {\n    let result = 1;\n\n    for (let i = 0; i < n; i++) {\n      result *= x;\n    }\n\n    return result;\n  }\n}\n```\n\nEt ceci :\n\n```js\nfunction pow(x, n) {\n  if (n < 0) {\n    alert(\"Negative 'n' not supported\");\n    return;\n  }\n\n  let result = 1;\n\n  for (let i = 0; i < n; i++) {\n    result *= x;\n  }\n\n  return result;\n}\n```\n\nLe second est plus lisible, parce que le \"cas marginal\" de `n < 0` est traité tôt. Une fois la vérification effectuée, nous pouvons passer au flux de code \"principal\" sans avoir besoin d'imbrication supplémentaire.\n\n## Placement des fonctions\n\nSi vous écrivez plusieurs fonctions \"helper\" (auxiliaires) et le code pour les utiliser, il existe trois façons de les placer.\n\n1. Déclarez les fonctions *au-dessus* du code qui les utilise :\n\n    ```js\n    // *!*fonctions declarations*/!*\n    function createElement() {\n      ...\n    }\n\n    function setHandler(elem) {\n      ...\n    }\n\n    function walkAround() {\n      ...\n    }\n\n    // *!*le code qui les utilise*/!*\n    let elem = createElement();\n    setHandler(elem);\n    walkAround();\n    ```\n2. Le code d'abord, puis les fonctions\n\n    ```js\n    // *!*le code qui utilise les fonctions*/!*\n    let elem = createElement();\n    setHandler(elem);\n    walkAround();\n\n    // --- *!*fonctions helper*/!* ---\n\n    function createElement() {\n      ...\n    }\n\n    function setHandler(elem) {\n      ...\n    }\n\n    function walkAround() {\n      ...\n    }\n    ```\n3. Mixte : une fonction est décrite là où elle a été utilisée pour la première fois.\n\nLa plupart du temps, la deuxième variante est préférée.\n\nC’est parce qu’en lisant du code, nous voulons d’abord savoir ce qu’il fait. Si le code commence en premier, il devient clair dès le début. Ensuite, nous n’aurons peut-être pas besoin de lire les fonctions du tout, surtout si leur nom décrit ce qu’elles font réellement.\n\n## Guides de style\n\nUn guide de style contient des règles générales sur \"comment écrire\" du code. Exemple : les quotes à utiliser, le nombre d'espaces pour indenter, la longueur de ligne maximale, etc. Beaucoup de petites choses.\n\nLorsque tous les membres d'une équipe utilisent le même guide de style, le code est uniforme. Peu importe qui l’a écrit, c’est toujours le même style.\n\nBien sûr, une équipe peut toujours écrire son propre guide de style, mais cela n’est généralement pas nécessaire. Il existe de nombreux guides existants à choisir.\n\nPar exemple :\n\n- [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html)\n- [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript)\n- [Idiomatic.JS](https://github.com/rwaldron/idiomatic.js)\n- [StandardJS](https://standardjs.com/)\n- (il y en a plus)\n\nSi vous êtes un développeur novice, commencez par le cheatsheet au début de ce chapitre. Ensuite, vous pouvez parcourir d'autres guides de style pour trouver plus d'idées et décider lequel vous préférez.\n\n## Linters automatisés\n\nLes linters sont des outils permettant de vérifier automatiquement le style de votre code et de formuler des suggestions d'amélioration.\n\nCe qui est génial avec eux, c'est que la vérification du style trouve également des bugs, comme une faute de frappe dans une variable ou un nom de fonction. En raison de cette fonctionnalité, l'utilisation d'un linter est recommandée même si vous ne souhaitez pas vous en tenir à un \"style de code\" particulier.\n\nVoici quelques linters bien connus :\n\n- [JSLint](http://www.jslint.com/) -- l'un des premiers linters.\n- [JSHint](http://www.jshint.com/) -- plus de paramètres que JSLint.\n- [ESLint](http://eslint.org/) -- probablement le plus récent.\n\nTous peuvent faire le travail. L'auteur utilise [ESLint](http://eslint.org/).\n\nLa plupart des linters sont intégrés aux éditeurs: il suffit d'activer le plug-in dans l'éditeur et de configurer le style.\n\nPar exemple, pour ESLint, vous devez procéder comme suit :\n\n1. Installer [Node.js](https://nodejs.org/).\n2. Installer ESLint avec la commande `npm install -g eslint` (npm est un gestionnaire de paquets JavaScript).\n3. Créez un fichier de configuration nommé `.eslintrc` dans la racine de votre projet JavaScript (dans le dossier contenant tous vos fichiers).\n4. Installez / activez le plug-in pour votre éditeur qui s'intègre à ESLint. La majorité des éditeurs en ont un.\n\nVoici un exemple de `.eslintrc`:\n\n```js\n{\n  \"extends\": \"eslint:recommended\",\n  \"env\": {\n    \"browser\": true,\n    \"node\": true,\n    \"es6\": true\n  },\n  \"rules\": {\n    \"no-console\": 0,\n    \"indent\": 2\n  }\n}\n```\n\nIci, la directive `\"extends\"` indique que nous nous basons sur l'ensemble de paramètres \"eslint:recommended\", puis nous spécifions les nôtres.\n\nIl est aussi possible de télécharger des ensembles de règles de style à partir du Web et de les étendre. Voir <http://eslint.org/docs/user-guide/getting-started> pour plus de détails sur l'installation.\n\nDe plus, certains IDE prennent en charge le linting nativement, ce qui peut également être bien, mais pas aussi ajustables que ESLint.\n\n## Résumé\n\nToutes les règles de syntaxe de ce chapitre et les guides de style visent à améliorer la lisibilité, elles sont donc toutes discutables.\n\nLorsque nous pensons à écrire du \"meilleur\" code, les questions que nous devrions nous poser sont les suivantes : \"Qu'est-ce qui rend le code plus lisible et plus facile à comprendre ?\" et \"Qu'est-ce qui peut nous aider à éviter les erreurs ?\". Telles sont les principales choses à garder à l'esprit lors du choix et du débat sur les styles de code.\n\nLisez les guides de style pour connaître les dernières idées à ce sujet et suivez celles que vous trouvez les meilleures.\n"
  },
  {
    "path": "1-js/03-code-quality/03-comments/article.md",
    "content": "# Commentaires\n\nComme nous le savons du chapitre <info:structure>, les commentaires peuvent être simples : à partir de `//` et multiligne : `/* ... */`.\n\nNous les utilisons normalement pour décrire comment et pourquoi le code fonctionne.\n\nDe prime abord, les commentaires peuvent sembler évidents, mais les novices en programmation les utilisent souvent à tort.\n\n## Mauvais commentaires\n\nLes novices ont tendance à utiliser des commentaires pour expliquer \"ce qui se passe dans le code\". Comme ceci :\n\n```js\n// Ce code fera cette chose (...) et cette chose (...)\n// ...Et qui sait quoi d'autre...\nvery;\ncomplex;\ncode;\n```\n\nMais en bon code, le nombre de ces commentaires \"explicatifs\" devrait être minime. Sérieusement, le code devrait être facile à comprendre sans eux.\n\nIl existe une excellente règle à ce sujet: \"Si le code est si peu clair qu’il nécessite un commentaire, il devrait peut-être être réécrit\".\n\n### Recette: refactoriser les fonctions\n\nParfois, il est avantageux de remplacer un code par une fonction, comme ici :\n\n```js\nfunction showPrimes(n) {\n  nextPrime:\n  for (let i = 2; i < n; i++) {\n\n*!*\n    // check if i is a prime number\n    for (let j = 2; j < i; j++) {\n      if (i % j == 0) continue nextPrime;\n    }\n*/!*\n\n    alert(i);\n  }\n}\n```\n\nLa meilleure variante, avec une fonction factorisée est `isPrime` :\n\n\n```js\nfunction showPrimes(n) {\n\n  for (let i = 2; i < n; i++) {\n    *!*if (!isPrime(i)) continue;*/!*\n\n    alert(i);\n  }\n}\n\nfunction isPrime(n) {\n  for (let i = 2; i < n; i++) {\n    if (n % i == 0) return false;\n  }\n\n  return true;\n}\n```\n\nMaintenant, nous pouvons comprendre le code facilement. La fonction elle-même devient le commentaire. Un tel code est appelé *auto-descriptif*.\n\n### Recette: créer des fonctions\n\nEt si nous avons une longue \"feuille de code\" comme celle-ci :\n\n```js\n// ici on ajoute du whisky\nfor(let i = 0; i < 10; i++) {\n  let drop = getWhiskey();\n  smell(drop);\n  add(drop, glass);\n}\n\n// ici on ajoute du jus\nfor(let t = 0; t < 3; t++) {\n  let tomato = getTomato();\n  examine(tomato);\n  let juice = press(tomato);\n  add(juice, glass);\n}\n\n// ...\n```\n\nCe pourrait être une meilleure variante de le refactoriser dans des fonctions comme :\n\n```js\naddWhiskey(glass);\naddJuice(glass);\n\nfunction addWhiskey(container) {\n  for(let i = 0; i < 10; i++) {\n    let drop = getWhiskey();\n    //...\n  }\n}\n\nfunction addJuice(container) {\n  for(let t = 0; t < 3; t++) {\n    let tomato = getTomato();\n    //...\n  }\n}\n```\n\nUne fois encore, les fonctions elles-mêmes racontent ce qui se passe. Il n’y a rien à commenter. Et aussi la structure du code est meilleure quand elle est divisée. C'est clair ce que chaque fonction fait, ce qu’elle nécessite et ce qu’elle renvoie.\n\nEn réalité, nous ne pouvons pas totalement éviter les commentaires «explicatifs». Il existe des algorithmes complexes. Et il existe des \"réglages\" intelligents à des fins d'optimisation. Mais généralement, nous devrions essayer de garder le code simple et auto-descriptif.\n\n## Bons commentaires\n\nAinsi, les commentaires explicatifs sont généralement mauvais. Quels commentaires sont bons ?\n\nDécrivez l'architecture\n: Fournissez une vue d’ensemble des composants, de leurs interactions, de ce que sont les flux de contrôle dans diverses situations… En bref -- une vue plongeante du code. Il existe un langage spécial [UML](https://fr.wikipedia.org/wiki/UML_(informatique)) pour les diagrammes d'architecture de haut niveau. Ça vaut vraiment la peine de l'étudier.\n\nDocumenter les paramètres de fonction et leur utilisation\n: Il y a une syntaxe spéciale [JSDoc](https://fr.wikipedia.org/wiki/JSDoc) pour documenter une fonction : utilisation, paramètres, valeur renvoyée.\n\nPar exemple :\n```js\n/**\n * Renvoie x élevé à la n-ième puissance.\n *\n * @param {number} x Le nombre à augmenter.\n * @param {number} n L'exposant doit être un nombre naturel.\n * @return {number} x élevé à la n-ème puissance.\n */\nfunction pow(x, n) {\n  ...\n}\n```\n\nDe tels commentaires nous permettent de comprendre le but de la fonction et de l’utiliser correctement sans regarder dans son code.\n\nÀ ce propos, de nombreux éditeurs comme [WebStorm](https://www.jetbrains.com/webstorm/) peut aussi les comprendre et les utiliser pour fournir une autocomplétion et une vérification automatique du code.\n\nEn outre, il existe des outils comme [JSDoc 3](https://github.com/jsdoc/jsdoc) qui peut générer une documentation HTML à partir des commentaires. Vous pouvez lire plus d'informations sur JSDoc à l'adresse <http://usejsdoc.org/>.\n\nPourquoi la tâche est-elle résolue de cette façon ?\n: Ce qui est écrit est important. Mais ce qui *n’est pas* écrit peut être encore plus important pour comprendre ce qui se passe. Pourquoi la tâche est-elle résolue exactement de cette façon ? Le code ne donne pas de réponse.\n\n    S'il y a plusieurs façons de résoudre la tâche, pourquoi celle-ci ? Surtout quand ce n’est pas la plus évidente.\n\n    Sans ces commentaires, la situation suivante est possible :\n    1. Vous (ou votre collègue) ouvrez le code écrit il y a quelque temps et constatez qu'il n'est pas optimal.\n    2. Vous pensez: \"À quel point j'étais bête à ce moment-là et à quel point je suis plus malin maintenant\", puis réécrivez en utilisant la variante \"plus évidente et correcte\".\n    3. … L'envie de réécrire était bonne. Mais dans le processus, vous constatez que la solution \"plus évidente\" fait défaut. Vous vous rappelez même vaguement pourquoi, parce que vous l'avez déjà essayé il y a longtemps. Vous revenez à la bonne variante, mais le temps a été perdu.\n\n    Les commentaires qui expliquent la solution sont très importants. Ils aident à continuer le développement de la bonne façon.\n\nLes caractéristiques subtiles du code ? Où sont-elles utilisés ?\n: Si le code a quelque chose de subtil et de contre-intuitif, cela vaut vraiment la peine de le commenter.\n\n## Résumé\n\nLes commentaires sont une caractéristique importante du bon développeur : leur présence et même leur absence.\n\nLes bons commentaires nous permettent de bien maintenir le code, d'y revenir après un délai et de l'utiliser plus efficacement.\n\n**Commentez ceci :**\n\n- Architecture globale, vue de haut niveau.\n- Utilisation de la fonction.\n- Les solutions importantes, surtout lorsqu'elles ne sont pas immédiatement évidentes.\n\n**Évitez les commentaires :**\n\n- Qui disent \"comment fonctionne le code\" et \"ce qu'il fait\".\n- Ne les mettez que s’il est impossible de rendre le code aussi simple et auto-descriptif qu’il n’en nécessite pas.\n\nLes commentaires sont également utilisés pour les outils de documentation automatique tels que JSDoc3. Ils les lisent et génèrent des documents HTML (ou des documents dans un autre format).\n"
  },
  {
    "path": "1-js/03-code-quality/04-ninja-code/article.md",
    "content": "# Ninja code\n\n\n```quote author=\"Confucius (Entretiens)\"\nApprendre sans réfléchir est vain. Réfléchir sans apprendre est dangereux.\n```\n\nLes programmeurs ninjas du passé ont utilisé ces astuces pour aiguiser l'esprit des mainteneurs de code.\n\nLes gourous de la révision de code les recherchent dans les tâches de test.\n\nLes développeurs novices les utilisent parfois encore mieux que les programmeurs ninjas.\n\nLisez-les attentivement et découvrez qui vous êtes: un ninja, un novice ou peut-être un critique de code ?\n\n\n```warn header=\"Ironie detectée\"\nBeaucoup essaient de suivre les chemins des ninjas. Peu réussissent.\n```\n\n\n## La concision est l'âme de l'esprit\n\nFaites le code aussi court que possible. Montrez à quel point vous êtes intelligent.\n\nLaissez les fonctionnalités du langage subtiles vous guider.\n\nPar exemple, jetez un oeil à cet opérateur ternaire `'?'` :\n\n```js\n// tiré d'une bibliothèque javascript bien connue\ni = i ? i < 0 ? Math.max(0, len + i) : i : 0;\n```\n\nCool, non ? Si vous écrivez comme ça, le développeur qui arrive à cette ligne et essaie de comprendre quelle est la valeur de `i` va passer un bon moment. Ensuite vient votre tour, cherchant une réponse.\n\nDites-leur que le plus court est toujours mieux. Initiez-les dans les chemins du ninja.\n\n## Variables à une lettre\n\n```quote author=\"Laozi (Tao Te Ching)\"\nLe Dao se cache sans mots. Seul le Dao est bien commencé et bien terminé.\n```\n\nUne autre façon de coder plus rapidement consiste à utiliser des noms de variable d'une seule lettre partout. Comme `a`, `b` ou `c`.\n\nUne petite variable disparaît dans le code comme un vrai ninja dans la forêt. Personne ne pourra la trouver en utilisant la \"recherche\" de l'éditeur. Et même si quelqu'un le fait, il ne pourra pas \"déchiffrer\" la signification du nom `a` ou `b`.\n\n… Mais il y a une exception. Un vrai ninja n'utilisera jamais `i` comme compteur dans une boucle `\"for\"`. N'importe où, mais pas ici. Regardez autour de vous, il y a beaucoup plus de lettres exotiques. Par exemple, `x` ou `y`.\n\nUne variable exotique en tant que compteur de boucle est particulièrement intéressante si le corps de la boucle nécessite 1 à 2 pages (rallongez-la si vous le pouvez). Ensuite, si quelqu'un regarde au fond de la boucle, il ne sera pas en mesure de comprendre rapidement que la variable nommée `x` est le compteur de boucles.\n\n## Utiliser des abréviations\n\nSi les règles de l'équipe interdisent l'utilisation de noms d'une seule lettre et de noms vagues, abrégez-les, faites des abréviations.\n\nComme ceci :\n\n- `list` -> `lst`.\n- `userAgent` -> `ua`.\n- `browser` -> `brsr`.\n- ...etc\n\nSeul celui qui a vraiment une bonne intuition sera capable de comprendre de tels noms. Essayez de tout raccourcir. Seule une personne digne de ce nom devrait être capable de soutenir le développement de votre code.\n\n## Prenez de la hauteur. Soyez abstrait.\n\n```quote author=\"Laozi (Tao Te Ching)\"\nThe great square is cornerless<br>\nThe great vessel is last complete,<br>\nThe great note is rarified sound,<br>\nThe great image has no form.\n```\n\nEn choisissant un nom, essayez d’utiliser le mot le plus abstrait. Comme `obj`, `data`, `value`, `item`, `elem` etc.\n\n- **Le nom idéal pour une variable est `data`.** Utilisez-le partout où vous le pouvez. En effet, chaque variable contient des données, non ?\n\n    … Mais que faire si `data` est déjà pris ? Essayez `value`, elle est aussi universelle. Après tout, une variable obtient finalement une *valeur*.\n\n- **Nommez une variable par son type : `str`, `num`...**\n\n    Accordez-leur une chance. Un jeune initié peut se demander : de tels noms sont-ils vraiment utiles à un ninja ? En effet, ils le sont !\n\n    Bien sûr, le nom de la variable signifie toujours quelque chose. Il indique ce qui est à l’intérieur de la variable: une chaîne de caractères, un nombre ou autre chose. Mais quand une personne essaiera de comprendre le code, elle sera surprise de constater qu’il n’y a en réalité aucune information ! Et finalement, elle ne pourra pas modifier votre code bien pensé.\n\n    Le type de valeur est facile à déterminer par le débogage. Mais quel est le sens de la variable ? Quelle chaîne de caractères/nombre est-il stocké ?\n\n    Il n’est pas possible de comprendre sans une bonne méditation !\n\n- **… Mais s'il n'y a plus de tels noms disponibles ?** Il suffit d'ajouter un numéro : `data1, item2, elem5`...\n\n## Test d'attention\n\nSeul un programmeur vraiment attentif devrait être capable de comprendre votre code. Mais comment vérifier ça ?\n\n**Une des façons - utilisez des noms de variables similaires, comme `date` et `data`.**\n\nMélangez-les où vous pouvez.\n\nUne lecture rapide de ce code devient impossible. Et quand il ya une faute de frappe… Humm… Nous sommes coincés longtemps, le temps de boire du thé.\n\n\n## Des synonymes intelligents\n\n```quote author=\"Confucius\"\nL'une des chose les plus difficiles est de trouver un chat noir dans une pièce sombre, surtout s’il n’y a pas de chat.\n```\n\nUtiliser des noms *similaires* pour les *mêmes* choses rend la vie plus intéressante et montre votre créativité au public.\n\nPar exemple, considérons les préfixes de fonction. Si une fonction affiche un message à l'écran, lancez-la avec `display…`, comme `displayMessage`. Et puis, si une autre fonction affiche à l'écran quelque chose d'autre, comme un nom d'utilisateur, lancez-le avec `show…` (comme `showName`).\n\nInsinuez qu’il existe une différence subtile entre ces fonctions, alors qu’il n’en existe aucune.\n\nFaites un pacte avec les autres ninjas de l'équipe: si John commence à \"afficher\" des fonctions avec `display` ... dans son code, Peter pourra utiliser `render` ..., et Ann - `paint` ... Notez à quel point le code est devenu plus intéressant et diversifié.\n\n… Et maintenant le tour de magie !\n\nPour deux fonctions présentant des différences importantes, utilisez le même préfixe !\n\nPar exemple, la fonction `printPage(page)` utilisera une imprimante. Et la fonction `printText(text)` mettra le texte à l'écran. Laissez un lecteur inconnu réfléchir à la fonction `printMessage`, qui porte le même nom: \"Où place-t-il le message ? Pour une imprimante ou à l'écran ?\". Pour le rendre vraiment brillant, `printMessage(message)` devrait l'extraire dans la nouvelle fenêtre!\n\n## Réutiliser des noms\n\n```quote author=\"Laozi (Tao Te Ching)\"\nUne fois que le tout est divisé, les parties<br>\nont besoin de noms.<br>\nIl y a déjà assez de noms.<br>\nIl faut savoir quand s'arrêter.\n```\n\nAjoutez une nouvelle variable uniquement lorsque cela est absolument nécessaire.\n\nAu lieu de cela, réutilisez les noms existants. Il suffit d'écrire de nouvelles valeurs en eux.\n\nDans une fonction, n'utilisez que des variables passées en paramètres.\n\nCela va rendre vraiment difficile d’identifier ce qui est exactement dans la variable maintenant. Et aussi d'où ça vient. Le but est de développer l’intuition et la mémoire de la personne qui lit le code. Une personne ayant une faible intuition devrait analyser le code ligne par ligne et suivre les modifications dans chaque branche de code.\n\n**Une variante avancée de l'approche consiste à remplacer secrètement (!) La valeur par quelque chose de similaire au milieu d'une boucle ou d'une fonction.**\n\nPar exemple :\n\n```js\nfunction ninjaFunction(elem) {\n  // 20 lignes de code fonctionnant avec elem\n\n  elem = clone(elem);\n\n  // 20 lignes supplémentaires, fonctionnant maintenant avec le clone de elem !\n}\n```\n\nUn collègue programmeur qui veut travailler avec `elem` dans la seconde moitié de la fonction sera surpris… Seulement lors du débogage, après avoir examiné le code, ils découvrira qu’il travaille avec un clone !\n\nMVu dans du code régulièrement. Mortellement efficace même contre un ninja expérimenté. \n\n## Underscores for fun\n\nPlacez les underscores `_` et `__` avant les noms de variables. Comme `_name` ou `__value`. Ce serait génial si seulement vous connaissiez leur signification. Ou, mieux, ajoutez-les juste pour le plaisir, sans signification particulière. Ou différentes significations dans différents endroits.\n\nVous faites d'une pierre deux coups. Premièrement, le code devient plus long et moins lisible, et deuxièmement, un autre développeur peut passer beaucoup de temps à essayer de comprendre ce que signifient les soulignements.\n\nUn ninja intelligent place les traits de soulignement à un endroit du code et les évite à d’autres endroits. Cela rend le code encore plus fragile et augmente la probabilité d'erreurs futures.\n\n## Montrez votre amour\n\nLaissez tout le monde voir à quel point vos entités sont magnifiques! Des noms comme `superElement`, `megaFrame` et `niceItem` illumineront définitivement le lecteur.\n\nEn effet, d’une part, quelque chose s’écrit: `super ..`, `mega ..`, `nice ..`. Mais de l’autre -- cela n’apporte aucun détail. Un lecteur peut décider de chercher un sens caché et de méditer pendant une heure ou deux de leur temps de travail rémunéré.\n\n## Chevaucher des variables externes\n\n\n\n```quote author=\"Guan Yin Zi\"\nLorsqu'on est dans la lumière, on ne peut rien voir dans l’obscurité.<br>\nLorsqu'on est dans l'obscurité, on peut tout voir dans la lumière.\n```\n\nUtilisez les mêmes noms pour les variables à l'intérieur et à l'extérieur d'une fonction. Aussi simple que cela. Pas besoin de faire des efforts pour inventer de nouveaux noms.\n\n```js\nlet *!*user*/!* = authenticateUser();\n\nfunction render() {\n  let *!*user*/!* = anotherValue();\n  ...\n  ...beaucoup de lignes...\n  ...\n  ... // <-- un programmeur veut travailler avec l'utilisateur ici et …\n  ...\n}\n```\n\nUn programmeur qui saute dans le `render` ne remarquera probablement pas qu’il ya un `user` local qui masque celui de l’extérieur.\n\nEnsuite, il essaiera de travailler avec l’`user` en supposant que c’est la variable externe, le résultat de `authenticateUser()`… Le piège est déclenché ! Bonjour debugger…\n\n\n## Effets secondaires partout !\n\nCertaines fonctions donnent l’impression de ne rien changer. Comme `isReady()`, `checkPermission()`, `findTags()`… Elles sont supposés effectuer des calculs, trouver et renvoyer les données, sans rien changer en dehors d'eux. En d'autres termes, sans \"effets secondaires\".\n\n**Une très belle astuce consiste à leur ajouter une action \"utile\", en plus de la tâche principale.**\n\n\nL’expression de surprise hébétée sur le visage de vos collègues quand ils voient une fonction nommée `is..`, `check..` ou `find...` changer quelque chose -- va certainement élargir vos limites de la raison.\n\n**Une autre façon de surprendre est de renvoyer un résultat non standard.**\n\nMontrez votre pensée originale ! Laissez l'appel de `checkPermission` renvoyer non pas `true/false`, mais un objet complexe avec les résultats de la vérification.\n\nLes développeurs qui essaient d’écrire `if(checkPermission(..))` se demanderont pourquoi cela ne fonctionne pas. Dites-leur : \"Lisez la documentation!\". Et donnez cet article.\n\n\n## Fonctions puissantes !\n\n```quote author=\"Laozi (Tao Te Ching)\"\nThe great Tao flows everywhere,<br>\nboth to the left and to the right.\n```\n\nNe limitez pas la fonction à ce qui est écrit dans son nom. Soyez plus large.\n\nPar exemple, une fonction `validateEmail(email)` pourrait (en plus de vérifier l'exactitude de l'email) afficher un message d'erreur et demander à ressaisir l'email.\n\nLes actions supplémentaires ne doivent pas être évidentes à partir du nom de la fonction. Un vrai codeur ninja ne les rendra pas évidents à partir du code non plus.\n\n**La jonction de plusieurs actions en une seule protège votre code de la réutilisation.**\n\nImaginez, un autre développeur souhaitant uniquement vérifier le courrier électronique et ne pas générer de message. Votre fonction `validateEmail(email)` qui fait les deux ne leur conviendra pas. Donc, ils ne briseront pas votre méditation en posant des questions à ce sujet.\n\n## Résumé\n\nTous les \"conseils\" ci-dessus sont tirés de code réel … Parfois écrits par des développeurs expérimentés. Peut-être même plus expérimenté que vous ;)\n\n- Suivez certains d'entre eux et votre code deviendra plein de surprises.\n- Suivez beaucoup d'entre eux, et votre code deviendra vraiment le vôtre, personne ne voudra le changer.\n- Suivez tout et votre code deviendra une leçon précieuse pour les jeunes développeurs à la recherche d'illumination.\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/3-pow-test-wrong/solution.md",
    "content": "Le test illustre l'une des tentations qu'un développeur rencontre lorsqu'il écrit des tests.\n\nCe que nous avons ici est en fait 3 tests, mais présentés comme une seule fonction avec 3 affirmations.\n\nParfois, il est plus facile d’écrire de cette façon, mais si une erreur se produit, ce qui a mal tourné est beaucoup moins évident.\n\nSi une erreur survient au beau milieu d'un flux d'exécution complexe, alors nous devrons bien comprendre les données à ce stade. Nous devrons en fait *déboguer le test*.\n\nIl serait bien préférable de diviser le test en plusieurs blocs `it` avec des entrées et des sorties clairement écrites.\n\nComme ceci :\n```js\ndescribe(\"Raises x to power n\", function() {\n  it(\"5 in the power of 1 equals 5\", function() {\n    assert.equal(pow(5, 1), 5);\n  });\n\n  it(\"5 in the power of 2 equals 25\", function() {\n    assert.equal(pow(5, 2), 25);\n  });\n\n  it(\"5 in the power of 3 equals 125\", function() {\n    assert.equal(pow(5, 3), 125);\n  });\n});\n```\n\nNous avons remplacé l'`it` unique par un `describe` et un groupe d'`it`. Si quelque chose échouait, nous verrions clairement quelles étaient les données erronées.\n\nNous pouvons également isoler un seul test et l'exécuter en mode autonome en l'écrivant `it.only` à la place de `it` :\n\n\n```js\ndescribe(\"Raises x to power n\", function() {\n  it(\"5 in the power of 1 equals 5\", function() {\n    assert.equal(pow(5, 1), 5);\n  });\n\n*!*\n  // Mocha ne va exécuter que ce code\n  it.only(\"5 in the power of 2 equals 25\", function() {\n    assert.equal(pow(5, 2), 25);\n  });\n*/!*\n\n  it(\"5 in the power of 3 equals 125\", function() {\n    assert.equal(pow(5, 3), 125);\n  });\n});\n```\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/3-pow-test-wrong/task.md",
    "content": "importance: 5\n\n---\n\n# Quel est le problème dans le test ?\n\nQu'est-ce qui ne va pas dans le test de `pow` ci-dessous ?\n\n```js\nit(\"Raises x to the power n\", function() {\n  let x = 5;\n\n  let result = x;\n  assert.equal(pow(x, 1), result);\n\n  result *= x;\n  assert.equal(pow(x, 2), result);\n\n  result *= x;\n  assert.equal(pow(x, 3), result);\n});\n```\n\nP.S. Syntaxiquement, le test est correct et réussi.\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/article.md",
    "content": "# Testing automatisé avec Mocha\n\nLes tests automatisés seront utilisés dans d'autres tâches. Ils sont également largement utilisés dans des projets réels.\n\n\n## Pourquoi avons-nous besoin de tests ?\n\nLorsque nous écrivons une fonction, nous pouvons généralement imaginer ce qu’elle doit faire : quels paramètres donnent quels résultats.\n\nAu cours du développement, nous pouvons vérifier la fonction en l'exécutant et en comparant le résultat obtenu. Par exemple, nous pouvons le faire dans la console.\n\nSi quelque chose ne va pas -- alors nous corrigeons le code, exécutons à nouveau, vérifions le résultat -- et ainsi de suite jusqu'à ce que cela fonctionne.\n\nMais de telles \"ré-exécutions\" manuelles sont imparfaites.\n\n**Lors du test manuel d’un code, il est facile de rater quelque chose.**\n\nPar exemple, nous créons une fonction `f`. On écrit du code, on teste : `f(1)` fonctionne, mais `f(2)` ne fonctionne pas. Nous corrigeons le code et maintenant `f(2)` fonctionne. Cela semble complet ? Mais nous avons oublié de re-tester `f(1).` Cela peut conduire à une erreur.\n\nC’est très typique. Lorsque nous développons quelque chose, nous gardons à l’esprit beaucoup de cas d’utilisation possibles. Mais il est difficile de s’attendre à ce qu’un programmeur les vérifie manuellement après chaque modification. Il devient donc facile de réparer une chose et d'en casser une autre.\n\n**Le test automatisé signifie que les tests sont écrits séparément, en plus du code. Ils exécutent nos fonctions de différentes manières et comparent les résultats avec les attentes.**\n\n## Behavior Driven Development (BDD)\n\nCommençons par une technique nommée [Behavior Driven Development](https://fr.wikipedia.org/wiki/Behavior-driven_development) ou, en bref, BDD. \n\n**BDD, c'est trois choses en une : les tests ET la documentation ET les exemples.**\n\nPour comprendre BDD, examinons un cas pratique de développement.\n\n## Développement de \"pow\": la spec\n\nImaginons que nous voulions créer une fonction `pow(x, n)` qui élève `x` à la puissance d'un entier `n`. Nous supposons que `n≥0`.\n\nCette tâche n’est qu’un exemple : il existe l’opérateur `**` en JavaScript qui peut le faire, mais nous nous concentrons ici sur le flux de développement pouvant également s’appliquer à des tâches plus complexes.\n\nAvant de créer le code de `pow`, nous pouvons imaginer ce que la fonction devrait faire et la décrire.\n\nCette description s'appelle une *spécification* ou, en bref, une **spec**, et contient des descriptions de cas d'utilisation ainsi que des tests pour ceux-ci, comme ceci :\n\n```js\ndescribe(\"pow\", function() {\n\n  it(\"raises to n-th power\", function() {\n    assert.equal(pow(2, 3), 8);\n  });\n\n});\n```\n\nUne spécification a trois blocs de construction principaux que vous pouvez voir ci-dessus :\n\n`describe(\"title\", function() { ... })`\n: Quelle fonctionnalité nous décrivons. Dans notre cas, nous décrivons la fonction `pow`, utilisée pour grouper les \"workers\" -- le bloc `it`.\n\n`it(\"use case description\", function() { ... })`\n: Dans le titre de `it`, nous décrivons d'une *manière lisible par l'homme* le cas particulier d'utilisation, et le deuxième argument est une fonction qui le teste.\n\n`assert.equal(value1, value2)`\n: Le code à l'intérieur du bloc `it`, si l'implémentation est correcte, doit s'exécuter sans erreur.\n\n    Les fonctions `assert.*` sont utilisées pour vérifier si `pow` fonctionne comme prévu. Ici, nous utilisons l’un d’eux - `assert.equal`, qui compare les arguments et génère une erreur s’ils ne sont pas égaux. Ici, il vérifie que le résultat de `pow(2, 3)` est égal à `8`. Nous ajouterons plus tard d'autres types de comparaisons et de contrôles.\n\nLa spécification peut être exécutée et le test spécifié dans le bloc `it` sera exécuté. Nous verrons cela plus tard.\n\n## Le flux de développement\n\nLe flux de développement ressemble généralement à ceci :\n\n1. Une spécification initiale est écrite, avec des tests pour les fonctionnalités les plus élémentaires.\n2. Une implémentation initiale est créée.\n3. Pour vérifier si cela fonctionne, nous exécutons le framework de test [Mocha](https://mochajs.org/) (plus de détails bientôt) qui exécute la spécification. Tant que la fonctionnalité n'est pas complète, des erreurs sont affichées. Nous apportons des corrections jusqu'à ce que tout fonctionne.\n4. Nous avons maintenant une implémentation initiale de travail avec des tests.\n5. Nous ajoutons d'autres cas d'utilisation à la spécification, probablement pas encore pris en charge par les implémentations. Les tests commencent à échouer.\n6. Passez à l'étape 3, mettez à jour l'implémentation jusqu'à ce que les tests ne génèrent aucune erreur.\n7. Répétez les étapes 3 à 6 jusqu'à ce que la fonctionnalité soit prête.\n\nDonc, le développement est *itératif*. Nous écrivons la spécification, la mettons en œuvre, nous nous assurons que les tests réussissent, puis rédigeons d'autres tests, nous nous assurons qu'ils fonctionnent, etc. À la fin, nous avons une implémentation qui fonctionne et des tests.\n\nVoyons ce flux de développement dans notre cas pratique.\n\nLa première étape est déjà terminée : nous avons une spécification initiale pour `pow`. Maintenant, avant de procéder à l’implémentation, utilisons quelques bibliothèques JavaScript pour exécuter les tests, histoire de voir qu’elles fonctionnent (elles échoueront toutes).\n\n## La spec en action\n\nDans ce tutoriel, nous utiliserons les bibliothèques JavaScript suivantes pour les tests :\n\n- [Mocha](https://mochajs.org/) -- le framework central : il fournit des fonctions de test communes, y compris `describe`, et `it` ainsi que la fonction principale qui exécute les tests.\n- [Chai](https://chaijs.com) -- la bibliothèque avec de nombreuses affirmations. Elle permet d’utiliser beaucoup d’affirmations différentes, pour le moment nous n’avons besoin que de `assert.equal`.\n- [Sinon](https://sinonjs.org/) -- une bibliothèque pour espionner des fonctions, émuler des fonctions intégrées et plus encore, nous en aurons besoin beaucoup plus tard.\n\nCes bibliothèques conviennent aux tests sur le navigateur et sur le serveur. Ici, nous allons considérer la variante du navigateur.\n\nLa page HTML complète avec ces frameworks et `pow` spec :\n\n```html src=\"index.html\"\n```\n\nLa page peut être divisée en quatre parties :\n\n1. Le `<head>` -- ajouter des bibliothèques et des styles tiers pour les tests.\n2. Le `<script>` avec la fonction à tester, dans notre cas - avec le code pour `pow`.\n3. Les tests - dans notre cas, un script externe `test.js` qui a `describe(\"pow\", ...)` d'en haut.\n4. L'élément HTML `<div id=\"mocha\">` sera utilisé par Mocha pour afficher les résultats.\n5. Les tests sont lancés par la commande `mocha.run()`.\n\nLe résultat :\n\n[iframe height=250 src=\"pow-1\" border=1 edit]\n\nÀ partir de là, le test échoue, il y a une erreur. C’est logique: nous avons un code de fonction vide dans `pow`, donc `pow(2,3)` renvoie `undefined` au lieu de `8`.\n\nPour l’avenir, notons qu’il existe des testeurs avancés, tels que [karma](https://karma-runner.github.io/) et d'autres, cela facilite le lancement automatique de nombreux tests différents.\n\n## Implementation initiale\n\nFaisons une simple implémentation de `pow`, pour que les tests réussissent :\n\n```js\nfunction pow(x, n) {\n  return 8; // :) we cheat!\n}\n```\n\nWow, maintenant ça marche !\n\n[iframe height=250 src=\"pow-min\" border=1 edit]\n\n## Améliorer les spécifications\n\nCe que nous avons fait est définitivement une triche. La fonction ne fonctionne pas: une tentative de calcul de `pow(3,4)` donnerait un résultat incorrect, mais les tests réussissent.\n\n… Mais la situation est assez typique, cela se produit dans la pratique. Les tests réussissent, mais la fonction ne fonctionne pas correctement. Notre spec est imparfaite. Nous devons ajouter d'autres cas d'utilisation.\n\nAjoutons encore un test pour voir si `pow(3, 4) = 81`.\n\nNous pouvons sélectionner l'une des deux manières d'organiser le test ici :\n\n1. La première variante - ajoute une autre `assert` dans le même `it` :\n\n    ```js\n    describe(\"pow\", function() {\n\n      it(\"raises to n-th power\", function() {\n        assert.equal(pow(2, 3), 8);\n    *!*\n        assert.equal(pow(3, 4), 81);\n    */!*\n      });\n\n    });\n    ```\n2. La seconde -- faire deux tests :\n\n    ```js\n    describe(\"pow\", function() {\n\n      it(\"2 raised to power 3 is 8\", function() {\n        assert.equal(pow(2, 3), 8);\n      });\n\n      it(\"3 raised to power 4 is 81\", function() {\n        assert.equal(pow(3, 4), 81);\n      });\n\n    });\n    ```\n\nLa principale différence est que, lorsque `assert` déclenche une erreur, le bloc `it` se termine immédiatement. Ainsi, dans la première variante, si le premier `assert` échoue, nous ne verrons jamais le résultat du deuxième `assert`.\n\nFaire des tests séparés est utile pour obtenir plus d’informations sur ce qui se passe, la deuxième variante est donc meilleure.\n\nCela dit, il y a encore une règle à suivre.\n\n**Un test vérifie une chose.**\n\nSi nous examinons le test et y voyons deux contrôles indépendants, il est préférable de le scinder en deux plus simples.\n\nContinuons donc avec la deuxième variante.\n\nLe résultat :\n\n[iframe height=250 src=\"pow-2\" edit border=\"1\"]\n\nComme on pouvait s'y attendre, le deuxième test a échoué. Bien sûr, notre fonction retourne toujours `8`, alors que l'`assert` en attend `81`.\n\n## Améliorer l'implémentation\n\nÉcrivons quelque chose de plus réel pour que les tests réussissent :\n\n```js\nfunction pow(x, n) {\n  let result = 1;\n\n  for (let i = 0; i < n; i++) {\n    result *= x;\n  }\n\n  return result;\n}\n```\n\nPour être sûr que la fonction fonctionne bien, testons-la pour plus de valeurs. Au lieu d’écrire manuellement les blocs, nous pouvons les générer dans `for` :\n\n```js\ndescribe(\"pow\", function() {\n\n  function makeTest(x) {\n    let expected = x * x * x;\n    it(`${x} in the power 3 is ${expected}`, function() {\n      assert.equal(pow(x, 3), expected);\n    });\n  }\n\n  for (let x = 1; x <= 5; x++) {\n    makeTest(x);\n  }\n\n});\n```\n\nLe résultat :\n\n[iframe height=250 src=\"pow-3\" edit border=\"1\"]\n\n## Description imbriquée\n\nNous allons ajouter encore plus de tests. Mais avant cela, notons que la fonction helper `makeTest` et `for` doivent être regroupées. Nous n’aurons pas besoin de faire `makeTest` dans d’autres tests, c’est nécessaire seulement pour `for` : leur tâche commune est de vérifier dans quelle mesure `pow` augmente dans la puissance donnée.\n\nLe regroupement est fait avec un `describe` imbriqué :\n\n```js\ndescribe(\"pow\", function() {\n\n*!*\n  describe(\"raises x to power 3\", function() {\n*/!*\n\n    function makeTest(x) {\n      let expected = x * x * x;\n      it(`${x} in the power 3 is ${expected}`, function() {\n        assert.equal(pow(x, 3), expected);\n      });\n    }\n\n    for (let x = 1; x <= 5; x++) {\n      makeTest(x);\n    }\n\n*!*\n  });\n*/!*\n\n  // ... plus de tests à suivre ici, les deux describe et it peuvent être ajoutés\n});\n```\n\nLa description imbriquée définit un nouveau \"sous-groupe\" de tests. Dans la sortie, nous pouvons voir l'indentation intitulée :\n\n[iframe height=250 src=\"pow-4\" edit border=\"1\"]\n\nÀ l’avenir, nous pourrons ajouter plus d'`it` et `describe` au niveau supérieur avec leurs propres fonctions helper, ils ne verront pas `makeTest`.\n\n````smart header=\"`before/after` et `beforeEach/afterEach`\"\nNous pouvons configurer les fonctions `before/after` qui s'exécutent avant/après l'exécution des tests, ainsi que les fonctions `beforeEach/afterEach` qui s'exécutent avant/après chaque `it`.\n\nPar exemple :\n\n```js no-beautify\ndescribe(\"test\", function() {\n\n  before(() => alert(\"Testing started – before all tests\"));\n  after(() => alert(\"Testing finished – after all tests\"));\n\n  beforeEach(() => alert(\"Before a test – enter a test\"));\n  afterEach(() => alert(\"After a test – exit a test\"));\n\n  it('test 1', () => alert(1));\n  it('test 2', () => alert(2));\n\n});\n```\n\nLa séquence en cours sera :\n\n```\nTest commencé - avant tous les tests (avant)\nAvant un test - entrer un test (beforeEach)\n1\nAprès un test - quitter un test (afterEach)\nAvant un test - entrer un test (beforeEach)\n2\nAprès un test - quitter un test (afterEach)\nTest terminé - après tous les tests (after)\n```\n\n[edit src=\"beforeafter\" title=\"Ouvrez l'exemple dans la sandbox.\"]\n\nHabituellement, `beforeEach/afterEach` et `before/after` sont utilisés pour effectuer l'initialisation, remettre les compteurs à zéro ou faire autre chose entre les tests (ou groupes de tests).\n````\n\n## Étendre les spécifications\n\nLa fonctionnalité de base de `pow` est complète. La première itération du développement est terminée. Quand nous aurons fini de célébrer et de boire du champagne, continuons et améliorons-le.\n\nComme il a été dit, la fonction `pow(x, n)` est censée fonctionner avec des valeurs entières positives `n`.\n\nPour indiquer une erreur mathématique, les fonctions JavaScript renvoient généralement `NaN`. Faisons de même pour les valeurs invalides de `n`.\n\nAjoutons d’abord le comportement à la spec(!) :\n\n```js\ndescribe(\"pow\", function() {\n\n  // ...\n\n  it(\"for negative n the result is NaN\", function() {\n*!*\n    assert.isNaN(pow(2, -1));\n*/!*\n  });\n\n  it(\"for non-integer n the result is NaN\", function() {\n*!*\n    assert.isNaN(pow(2, 1.5));    \n*/!*\n  });\n\n});\n```\n\nLe résultat avec de nouveaux tests :\n\n[iframe height=530 src=\"pow-nan\" edit border=\"1\"]\n\nLes tests récemment ajoutés échouent car notre implémentation ne les prend pas en charge. C’est comme cela que BDD est fait : d’abord nous écrivons des tests qui échouent, puis nous réalisons une implémentation pour eux.\n\n```smart header=\"Autres affirmations\"\n\nVeuillez noter l'affirmation `assert.isNaN` : elle vérifie `NaN`.\n\nIl y a aussi d'autres affirmations dans [Chai](https://chaijs.com), par exemple :\n\n- `assert.equal(value1, value2)` -- vérifie l'égalité `value1 == value2`.\n- `assert.strictEqual(value1, value2)` -- vérifie la stricte égalité `value1 === value2`.\n- `assert.notEqual`, `assert.notStrictEqual` -- contrôles inverses à ceux ci-dessus.\n- `assert.isTrue(value)` -- vérifie que `value === true`\n- `assert.isFalse(value)` -- vérifie que `value === false`\n- … la liste complète est dans la [doc](https://chaijs.com/api/assert/)\n```\n\nDonc, nous devrions ajouter quelques lignes à `pow`:\n\n```js\nfunction pow(x, n) {\n*!*\n  if (n < 0) return NaN;\n  if (Math.round(n) != n) return NaN;\n*/!*\n\n  let result = 1;\n\n  for (let i = 0; i < n; i++) {\n    result *= x;\n  }\n\n  return result;\n}\n```\n\nMaintenant ça fonctionne, tous les tests réussissent :\n\n[iframe height=300 src=\"pow-full\" edit border=\"1\"]\n\n[edit src=\"pow-full\" title=\"Ouvrez le dernier exemple complet dans la sandbox.\"]\n\n## Résumé\n\nDans BDD, la spécification commence, suivie de l'implémentation. À la fin, nous avons à la fois la spécification et le code.\n\nLa spécification peut être utilisée de trois manières :\n\n1. En tant que **Tests** -- garantir que le code fonctionne correctement.\n2. En tant que **Docs** -- les titres de `describe` et `it` indiquent ce que fait la fonction.\n3. En tant que **Examples** -- les tests sont en fait des exemples de travail montrant comment une fonction peut être utilisée.\n\nAvec la spécification, nous pouvons sans risque améliorer, modifier, même réécrire la fonction à partir de zéro et nous assurer qu'elle fonctionne toujours correctement.\n\nC’est particulièrement important dans les grands projets quand une fonction est utilisée dans de nombreux endroits. Lorsque nous changeons une telle fonction, il n’y a aucun moyen de vérifier manuellement si chaque endroit qui l’utilise fonctionne toujours correctement.\n\nSans tests, les gens ont deux moyens :\n\n1. Pour effectuer le changement, peu importe quoi. Et puis nos utilisateurs rencontrent des bugs, car nous ne parvenons probablement pas à vérifier quelque chose manuellement.\n2. Ou, si la punition pour les erreurs est sévère, comme il n'y a pas de tests, les gens ont peur de modifier de telles fonctions, et alors le code devient obsolète, personne ne veut y entrer. Pas bon pour le développement.\n\n**Les tests automatiques aident à éviter ces problèmes !**\n\nSi le projet est conduit par des tests, il n'y a pas de problème. Après toute modification, nous pouvons exécuter des tests et voir de nombreuses vérifications effectuées en quelques secondes.\n\n**En outre, un code bien testé a une meilleure architecture.**\n\nNaturellement, c'est parce que le code auto-testé est plus facile à modifier et à améliorer. Mais il y a aussi une autre raison.\n\nPour écrire des tests, le code doit être organisé de manière à ce que chaque fonction ait une tâche clairement décrite, des entrées et des sorties bien définies. Cela signifie une bonne architecture dès le début.\n\nDans la vraie vie, ce n’est parfois pas si facile. Parfois, il est difficile d’écrire une spécification avant le code réel, parce que son comportement n’est pas encore clair. Mais en général, les tests d’écriture rendent le développement plus rapide et plus stable.\n\n## Et maintenant ?\n\nPlus tard dans le tutoriel, vous rencontrerez de nombreuses tâches avec des tests. Vous verrez donc des exemples plus pratiques.\n\nLa rédaction de tests nécessite une bonne connaissance de JavaScript. Mais nous commençons tout juste à l’apprendre. Donc, pour l'instant vous n’êtes pas obligé d’écrire des tests, mais vous devriez déjà pouvoir les lire, même s’ils sont un peu plus complexes que dans ce chapitre.\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/beforeafter.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/beforeafter.view/test.js",
    "content": "describe(\"test\", function() {\n  \n   // Mocha usually waits for the tests for 2 seconds before considering them wrong\n  \n  this.timeout(200000); // With this code we increase this - in this case to 200,000 milliseconds\n\n  // This is because of the \"alert\" function, because if you delay pressing the \"OK\" button the tests will not pass!\n  \n  before(() => alert(\"Testing started – before all tests\"));\n  after(() => alert(\"Testing finished – after all tests\"));\n\n  beforeEach(() => alert(\"Before a test – enter a test\"));\n  afterEach(() => alert(\"After a test – exit a test\"));\n\n  it('test 1', () => alert(1));\n  it('test 2', () => alert(2));\n\n});\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    mocha.setup('bdd'); // minimal setup\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow(x, n) {\n      /* function code is to be written, empty now */\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-1.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow(x, n) {\n      /* function code is to be written, empty now */\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-1.view/test.js",
    "content": "describe(\"pow\", function() {\n\n  it(\"raises to n-th power\", function() {\n    assert.equal(pow(2, 3), 8);\n  });\n\n});\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-2.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow(x, n) {\n      return 8; // :) we cheat!\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-2.view/test.js",
    "content": "describe(\"pow\", function() {\n\n  it(\"2 raised to power 3 is 8\", function() {\n    assert.equal(pow(2, 3), 8);\n  });\n\n  it(\"3 raised to power 4 is 81\", function() {\n    assert.equal(pow(3, 4), 81);\n  });\n\n});\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-3.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow(x, n) {\n      let result = 1;\n\n      for (let i = 0; i < n; i++) {\n        result *= x;\n      }\n\n      return result;\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-3.view/test.js",
    "content": "describe(\"pow\", function() {\n\n  function makeTest(x) {\n    let expected = x * x * x;\n    it(`${x} in the power 3 is ${expected}`, function() {\n      assert.equal(pow(x, 3), expected);\n    });\n  }\n\n  for (let x = 1; x <= 5; x++) {\n    makeTest(x);\n  }\n\n});\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-4.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow(x, n) {\n      let result = 1;\n\n      for (let i = 0; i < n; i++) {\n        result *= x;\n      }\n\n      return result;\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-4.view/test.js",
    "content": "describe(\"pow\", function() {\n\n  describe(\"raises x to power 3\", function() {\n\n    function makeTest(x) {\n      let expected = x * x * x;\n      it(`${x} in the power 3 is ${expected}`, function() {\n        assert.equal(pow(x, 3), expected);\n      });\n    }\n\n    for (let x = 1; x <= 5; x++) {\n      makeTest(x);\n    }\n\n  });\n\n  // ... more tests to follow here, both describe and it can be added\n});\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-full.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow(x, n) {\n      if (n < 0) return NaN;\n      if (Math.round(n) != n) return NaN;\n\n      let result = 1;\n      for (let i = 0; i < n; i++) {\n        result *= x;\n      }\n      return result;\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-full.view/test.js",
    "content": "describe(\"pow\", function() {\n\n  describe(\"raises x to power 3\", function() {\n\n    function makeTest(x) {\n      let expected = x * x * x;\n      it(`${x} in the power 3 is ${expected}`, function() {\n        assert.equal(pow(x, 3), expected);\n      });\n    }\n\n    for (let x = 1; x <= 5; x++) {\n      makeTest(x);\n    }\n\n  });\n\n  it(\"if n is negative, the result is NaN\", function() {\n    assert.isNaN(pow(2, -1));\n  });\n\n  it(\"if n is not integer, the result is NaN\", function() {\n    assert.isNaN(pow(2, 1.5));\n  });\n\n});\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-min.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow() {\n      return 8; // :) we cheat!\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-min.view/test.js",
    "content": "describe(\"pow\", function() {\n\n  it(\"raises to n-th power\", function() {\n    assert.equal(pow(2, 3), 8);\n  });\n\n});\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-nan.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow(x, n) {\n      let result = 1;\n      for (let i = 0; i < n; i++) {\n        result *= x;\n      }\n      return result;\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-nan.view/test.js",
    "content": "describe(\"pow\", function() {\n\n  describe(\"raises x to power 3\", function() {\n\n    function makeTest(x) {\n      let expected = x * x * x;\n      it(`${x} in the power 3 is ${expected}`, function() {\n        assert.equal(pow(x, 3), expected);\n      });\n    }\n\n    for (let x = 1; x <= 5; x++) {\n      makeTest(x);\n    }\n\n  });\n\n  it(\"if n is negative, the result is NaN\", function() {\n    assert.isNaN(pow(2, -1));\n  });\n\n  it(\"if n is not integer, the result is NaN\", function() {\n    assert.isNaN(pow(2, 1.5));\n  });\n\n});\n"
  },
  {
    "path": "1-js/03-code-quality/06-polyfills/article.md",
    "content": "\n# Polyfills et transpilers\n\nLe langage JavaScript évolue régulièrement. De nouvelles propositions pour le langage apparaissent régulièrement, elles sont analysées et, si elles sont jugées utiles, elles sont ajoutées à la liste dans <https://tc39.github.io/ecma262/> et ensuite progressent vers la [specification officielle](https://www.ecma-international.org/publications-and-standards/standards/ecma-262/).\n\nLes équipes derrière les moteurs JavaScript ont leurs propres idées sur ce qu'il faut d'abord mettre en œuvre. Elles peuvent décider de mettre en œuvre des propositions qui sont en projet et reporter des éléments qui figurent déjà dans les spécifications, car ils sont moins intéressants ou tout simplement plus difficiles à faire.\n\nIl est donc assez courant pour un moteur de ne mettre en œuvre qu'une partie du standard.\n\nUne bonne page pour voir l’état actuel de la prise en charge des fonctionnalités du langage est <https://kangax.github.io/compat-table/es6/> (c’est énorme, nous avons encore beaucoup à étudier).\n\nEn tant que programmeurs, nous aimerions utiliser les fonctionnalités les plus récentes. Plus il y a de bonnes choses, mieux c'est !\n\nD'un autre côté, comment faire fonctionner le code moderne sur des moteurs plus anciens qui ne comprennent pas encore les fonctionnalités récentes ?\n\nIl existe deux outils pour cela :\n\n1. Les transpilers.\n2. Les polyfills.\n\nIci, dans ce chapitre, notre objectif est de comprendre l'essentiel de leur fonctionnement et de leur place dans le développement Web.\n\n## Les transpilers\n\nUn [transpiler](https://en.wikipedia.org/wiki/Source-to-source_compiler) est un logiciel spécial qui traduit le code source en un autre code source. Il peut analyser (\"lire et comprendre\") du code moderne et le réécrire en utilisant des constructions syntaxiques plus anciennes, de sorte qu'il fonctionnera également dans des moteurs obsolètes.\n\nPar exemple, JavaScript avant l'année 2020 n'avait pas \"l'opérateur de coalescence des nuls\" `??`. Ainsi, si un visiteur utilise un navigateur obsolète, il peut ne pas comprendre le code tel que `height = height ?? 100`.\n\nUn transpiler analyserait notre code et réécrirait `height ?? 100` en `(height !== undefined && height !== null) ? height : 100`.\n\n```js\n// avant d'exécuter le transpiler\nheight = height ?? 100;\n\n// après avoir exécuté le transpiler\nheight = (height !== undefined && height !== null) ? height : 100;\n```\n\nDésormais, le code réécrit convient aux anciens moteurs JavaScript.\n\nHabituellement, un développeur exécute le transpiler sur son propre ordinateur, puis déploie le code transpilé sur le serveur.\n\nEn parlant de noms, [Babel](https://babeljs.io) est l'un des transpileurs les plus connus.\n\nLes systèmes de construction de projets modernes, tels que [webpack](http://webpack.js.org/), fournissent des moyens pour exécuter un transpileur automatiquement à chaque changement de code, il est donc très facile à intégrer dans le processus de développement.\n\n## Les polyfills\n\nLes nouvelles fonctionnalités du langage peuvent inclure non seulement des constructions de syntaxe et des opérateurs, mais également des fonctions intégrées.\n\nPar exemple, `Math.trunc(n)` est une fonction qui \"coupe\" la partie décimale d'un nombre, par exemple `Math.trunc(1.23)` retourne `1`.\n\nDans certains moteurs JavaScript (très obsolètes), il n'y a pas de `Math.trunc`, donc un tel code échouera.\n\nComme nous parlons de nouvelles fonctions, pas de changements de syntaxe, il n'est pas nécessaire de transpiler quoi que ce soit ici. Nous avons juste besoin de déclarer la fonction manquante.\n\nUn script qui met à jour/ajoute de nouvelles fonctions est appelé \"polyfill\". Il \"comble\" le vide et ajoute les implémentations manquantes.\n\nPour ce cas particulier, le polyfill pour `Math.trunc` est un script qui l'implémente, comme ceci :\n\n```js\nif (!Math.trunc) { // si une telle fonction n'existe pas\n  // l'implémenter\n  Math.trunc = function(number) {\n    // Math.ceil et Math.floor existe même dans les anciens moteurs JavaScript\n    // ils sont traités plus tard dans le tutoriel\n    return number < 0 ? Math.ceil(number) : Math.floor(number);\n  };\n}\n```\n\nJavaScript est un langage très dynamique, les scripts peuvent ajouter/modifier toutes les fonctions, y compris celles intégrées.\n\nDeux librairies intéressantes de polyfills sont :\n- [core js](https://github.com/zloirock/core-js) qui prend en charge beaucoup de choses et permet d'inclure uniquement les fonctionnalités nécessaires.\n- [polyfill.io](https://polyfill.io) est un service qui fournit un script avec des polyfills, en fonction des fonctionnalités et du navigateur de l'utilisateur.\n\n\n## Résumé\n\nDans ce chapitre, nous aimerions vous motiver à étudier les fonctionnalités du langage modernes et même \"de pointe\", même si elles ne sont pas encore bien prises en charge par les moteurs JavaScript.\n\nN'oubliez pas d'utiliser un transpiler (si vous utilisez une syntaxe ou des opérateurs modernes) et des polyfills (pour ajouter des fonctions qui peuvent manquer). Ils veilleront à ce que le code fonctionne.\n\nPar exemple, plus tard, lorsque vous serez familiarisé avec JavaScript, vous pourrez configurer un système de création de code basé sur [webpack](http://webpack.js.org/) avec le plugin [babel-loader](https://github.com/babel/babel-loader).\n\nDe bonnes ressources qui montrent l'état actuel de la prise en charge de diverses fonctionnalités :\n- <https://kangax.github.io/compat-table/es6/> - pour du pur JavaScript.\n- <https://caniuse.com/> - pour les fonctions liées au navigateur.\n\nP.S. Google Chrome est généralement le plus à jour avec les fonctionnalités du langage, essayez-le si une démonstration d'un tutoriel échoue. La plupart des démos de didacticiels fonctionnent avec n'importe quel navigateur moderne.\n"
  },
  {
    "path": "1-js/03-code-quality/index.md",
    "content": "# Qualité du code\n\nCe chapitre explique les pratiques de codage que nous utiliserons plus loin dans le développement.\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/2-hello-object/solution.md",
    "content": "\n\n```js\nlet user = {};\nuser.name = \"John\";\nuser.surname = \"Smith\";\nuser.name = \"Pete\";\ndelete user.name;\n```\n\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/2-hello-object/task.md",
    "content": "importance: 5\n\n---\n\n# Bonjour objet\n\nÉcrivez le code, une ligne pour chaque action :\n\n1. Créer un objet vide `user`.\n2. Ajoutez la propriété `name` avec la valeur `John`.\n3. Ajoutez la propriété `surname` avec la valeur `Smith`.\n4. Changer la valeur de `name` pour `Pete`.\n5. Supprimez la propriété `name` de l'objet.\n\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js",
    "content": "function isEmpty(obj) {\n  for (let key in obj) {\n    // si la boucle a commencé, il y a une propriété\n    return false;\n  }\n  return true;\n}\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/3-is-empty/_js.view/test.js",
    "content": "describe(\"isEmpty\", function() {\n  it(\"returns true for an empty object\", function() {\n    assert.isTrue(isEmpty({}));\n  });\n\n  it(\"returns false if a property exists\", function() {\n    assert.isFalse(isEmpty({\n      anything: false\n    }));\n  });\n});"
  },
  {
    "path": "1-js/04-object-basics/01-object/3-is-empty/solution.md",
    "content": "Passez simplement une boucle sur l’objet et `return false` immédiatement s’il existe au moins une propriété."
  },
  {
    "path": "1-js/04-object-basics/01-object/3-is-empty/task.md",
    "content": "importance: 5\n\n---\n\n# Vérifier le vide\n\nEcrivez la fonction `isEmpty(obj)` qui renvoie `true` si l'objet n'a pas de propriétés, sinon `false`.\n\nDevrait fonctionner comme ça :\n\n```js\nlet schedule = {};\n\nalert( isEmpty(schedule) ); // true\n\nschedule[\"8:30\"] = \"get up\";\n\nalert( isEmpty(schedule) ); // false\n```\n\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/4-const-object/solution.md",
    "content": "Bien sûr, ça fonctionne, pas de problème.\n\nLe `const` ne protège que la variable elle-même du changement.\n\nEn d'autres termes, `user` stocke une référence à l'objet. Et cela ne peut pas être changé. Mais le contenu de l'objet peut.\n\n```js run\nconst user = {\n  name: \"John\"\n};\n\n*!*\n// fonctionne\nuser.name = \"Pete\";\n*/!*\n\n// erreur\nuser = 123;\n```\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/4-const-object/task.md",
    "content": "importance: 5\n\n---\n\n# Objets constants ?\n\nEst-il possible de changer un objet déclaré avec `const`, comment ?\n\n```js\nconst user = {\n  name: \"John\"\n};\n\n*!*\n// est-ce que ça fonctionne ?\nuser.name = \"Pete\";\n*/!*\n```\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/5-sum-object/solution.md",
    "content": "\n```js run\nlet salaries = {\n  John: 100,\n  Ann: 160,\n  Pete: 130\n};\n\nlet sum = 0;\nfor (let key in salaries) {\n  sum += salaries[key];\n}\n\nalert(sum); // 390\n```\n\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/5-sum-object/task.md",
    "content": "importance: 5\n\n---\n\n# Somme des propriétés de l'objet\n\nNous avons un objet stockant les salaires de notre équipe :\n\n```js\nlet salaries = {\n  John: 100,\n  Ann: 160,\n  Pete: 130\n}\n```\n\nÉcrivez le code pour additionner tous les salaires et les enregistrer dans la variable `sum`. Devrait être égale à `390` dans l'exemple ci-dessus.\n\nSi `salaries` est vide, le résultat doit être `0`.\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/solution.js",
    "content": "function multiplyNumeric(obj) {\n  for (let key in obj) {\n    if (typeof obj[key] == 'number') {\n      obj[key] *= 2;\n    }\n  }\n}"
  },
  {
    "path": "1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/source.js",
    "content": "let menu = {\n  width: 200,\n  height: 300,\n  title: \"My menu\"\n};\n\n\nfunction multiplyNumeric(obj) {\n  \n  /* your code */\n\n}\n\nmultiplyNumeric(menu);\n\nalert( \"menu width=\" + menu.width + \" height=\" + menu.height + \" title=\" + menu.title );\n\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/test.js",
    "content": "describe(\"multiplyNumeric\", function() {\n  it(\"multiplies all numeric properties by 2\", function() {\n    let menu = {\n      width: 200,\n      height: 300,\n      title: \"My menu\"\n    };\n    let result = multiplyNumeric(menu);\n    assert.equal(menu.width, 400);\n    assert.equal(menu.height, 600);\n    assert.equal(menu.title, \"My menu\");\n  });\n\n  it(\"returns nothing\", function() {\n    assert.isUndefined( multiplyNumeric({}) );\n  });\n\n});\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/8-multiply-numeric/solution.md",
    "content": ""
  },
  {
    "path": "1-js/04-object-basics/01-object/8-multiply-numeric/task.md",
    "content": "importance: 3\n\n---\n\n# Multipliez les valeurs de propriétés numériques par 2\n\nCréez une fonction `multiplyNumeric(obj)` qui multiplie toutes les valeurs de propriétés numériques de `obj` par `2`.\n\nPar exemple :\n\n```js\n// before the call\nlet menu = {\n  width: 200,\n  height: 300,\n  title: \"My menu\"\n};\n\nmultiplyNumeric(menu);\n\n// after the call\nmenu = {\n  width: 400,\n  height: 600,\n  title: \"My menu\"\n};\n```\n\nVeuillez noter que `multiplyNumeric` n’a pas besoin de retourner quoi que ce soit. Il devrait modifier l'objet en place.\n\nP.S. Utilisez `typeof` pour rechercher un `number` ici.\n\n\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/article.md",
    "content": "\n# Objets\n\nComme nous le savons du chapitre <info:types>, il existe huit types de données dans le langage JavaScript. Sept d'entre elles sont appelées \"primitives\", car leurs valeurs ne contiennent qu'une seule chose (que ce soit une chaîne, un nombre ou autre).\n\nEn revanche, les objets sont utilisés pour stocker des collections de données variées et d’entités plus complexes. En JavaScript, les objets pénètrent dans presque tous les aspects du langage. Nous devons donc d'abord les comprendre avant d'aller plus loin.\n\nUn objet peut être créé avec des accolades `{…}`, avec une liste optionnelle de *propriétés*. Une propriété est une paire \"clé: valeur\", dans laquelle la clé (`key`) est une chaîne de caractères (également appelée \"nom de la propriété\"), et la valeur (`value`) peut être n'importe quoi.\n\nNous pouvons imaginer un objet comme une armoire avec des fichiers signés. Chaque donnée est stockée dans son fichier par la clé. Il est facile de trouver un fichier par son nom ou d’ajouter/supprimer un fichier.\n\n![](object.svg)\n\nUn objet vide (\"armoire vide\") peut être créé en utilisant l'une des deux syntaxes suivantes :\n\n```js\nlet user = new Object(); // syntaxe \"constructeur d'objet\"\nlet user = {};  // syntaxe \"littéral objet\"\n```\n\n![](object-user-empty.svg)\n\nHabituellement, les accolades `{...}` sont utilisées. Cette déclaration s'appelle un littéral objet (*object literal*).\n\n## Littéraux et propriétés\n\nNous pouvons immédiatement inclure certaines propriétés dans `{...}` sous forme de paires \"clé: valeur\" :\n\n```js\nlet user = {     // un objet\n  name: \"John\",  // par clé \"nom\" valeur de stockage \"John\"\n  age: 30        // par clé \"age\" valeur de stockage 30\n};\n```\n\nUne propriété a une clé (également appelée \"nom\" ou \"identifiant\") avant les deux points `\":\"` et une valeur à sa droite.\n\nDans l'objet `user`, il y a deux propriétés :\n\n1. La première propriété porte le nom `\"name\"` et la valeur `\"John\"`.\n2. La seconde a le nom `\"age\"` et la valeur `30`.\n\nL'objet `user` résultant peut être imaginé comme une armoire avec deux fichiers signés intitulés \"nom\" et \"âge\".\n\n![user object](object-user.svg)\n\nNous pouvons ajouter, supprimer et lire des fichiers à tout moment.\n\nLes valeurs de propriété sont accessibles à l'aide de la notation par points :\n\n```js\n// récupère les valeurs de propriété de l'objet :\nalert( user.name ); // John\nalert( user.age ); // 30\n```\n\nLa valeur peut être de tout type. Ajoutons un booléen :\n\n```js\nuser.isAdmin = true;\n```\n\n![user object 2](object-user-isadmin.svg)\n\nPour supprimer une propriété, nous pouvons utiliser l'opérateur `delete` :\n\n```js\ndelete user.age;\n```\n\n![user object 3](object-user-delete.svg)\n\nNous pouvons également utiliser des noms de propriété multi-mots, mais ils doivent ensuite être entourés de quotes :\n\n```js\nlet user = {\n  name: \"John\",\n  age: 30,\n  \"likes birds\": true  // le nom de la propriété multi-mots doit être entourée de quotes\n};\n```\n\n![](object-user-props.svg)\n\n\nLa dernière propriété de la liste peut se terminer par une virgule :\n```js\nlet user = {\n  name: \"John\",\n  age: 30*!*,*/!*\n}\n```\nCela s'appelle une virgule  \"trailing\" ou \"hanging\". Elle facilite l'ajout/suppression/déplacement des propriétés, car toutes les lignes se ressemblent.\n\n## Crochets\n\nPour les propriétés multi-mots, l’accès par points ne fonctionne pas :\n\n```js run\n// cela donnerait une erreur de syntaxe\nuser.likes birds = true\n```\n\nJavaScript ne comprend pas cela. Il pense que nous adressons `user.likes`, ensuite il donne une erreur de syntaxe lorsqu'il rencontre des `birds` inattendus.\n\nLe point nécessite que la clé soit un identificateur de variable valide. Cela implique qu'elle ne contient aucun espace, ne commence pas par un chiffre et n'inclut pas de caractères spéciaux (`$` et `_` sont autorisés).\n\nIl existe une autre “notation entre crochets” qui fonctionne avec n’importe quelle chaîne :\n\n```js run\nlet user = {};\n\n// set\nuser[\"likes birds\"] = true;\n\n// get\nalert(user[\"likes birds\"]); // true\n\n// delete\ndelete user[\"likes birds\"];\n```\n\nMaintenant tout va bien. Veuillez noter que la chaîne de caractères entre crochets est correctement entourée de quotes (tout type de guillemets fera l'affaire).\n\nLes crochets fournissent également un moyen d'obtenir le nom de la propriété comme résultat de toute expression (par opposition à une chaîne de caractères littérale), semblable à une variable, comme ceci :\n\n```js\nlet key = \"likes birds\";\n\n// pareil que user[\"likes birds\"] = true;\nuser[key] = true;\n```\n\nIci, la variable `key` peut être calculée au moment de l'exécution ou dépendre de la saisie de l'utilisateur. Et ensuite, nous l'utilisons pour accéder à la propriété. Cela nous donne beaucoup de flexibilité.\n\nPar exemple :\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30\n};\n\nlet key = prompt(\"What do you want to know about the user?\", \"name\");\n\n// accès par variable\nalert( user[key] ); // John (si entré \"name\")\n```\n\nLa notation par points ne peut pas être utilisée de la même manière :\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30\n};\n\nlet key = \"name\";\nalert( user.key ) // undefined\n```\n\n### Propriétés calculées\n\nNous pouvons utiliser des crochets dans un objet littéral, lorsqu'on crée un objet. Cela s'appelle des propriétés calculées (*computed propertie*).\n\nPar exemple :\n\n```js run\nlet fruit = prompt(\"Which fruit to buy?\", \"apple\");\n\nlet bag = {\n*!*\n  [fruit]: 5, // le nom de la propriété est tiré de la variable fruit\n*/!*\n};\n\nalert( bag.apple ); // 5 si fruit=\"apple\"\n```\n\nLa signification d'une propriété calculée est simple: `[fruit]` signifie que le nom de la propriété doit être extrait de `fruit`.\n\nAinsi, si un visiteur entre `\"apple\"`, `bag` deviendra `{apple: 5}`.\n\nEssentiellement, cela fonctionne de la même façon que :\n```js run\nlet fruit = prompt(\"Which fruit to buy?\", \"apple\");\nlet bag = {};\n\n// prendre le nom de la propriété de la variable fruit\nbag[fruit] = 5;\n```\n\n… Mais a une meilleure apparence.\n\nNous pouvons utiliser des expressions plus complexes entre crochets :\n\n```js\nlet fruit = 'apple';\nlet bag = {\n  [fruit + 'Computers']: 5 // bag.appleComputers = 5\n};\n```\n\nLes crochets sont beaucoup plus puissants que la notation par points. Ils autorisent tous les noms de propriété et variables. Mais ils sont aussi plus lourds à écrire.\n\nAinsi, la plupart du temps, lorsque les noms de propriété sont connus et simples, le point est utilisé. Et si nous avons besoin de quelque chose de plus complexe, nous passons aux crochets.\n\n## Valeur de propriété abrégée (Property value shorthand)\n\nDans du code réel, nous utilisons souvent des variables existantes en tant que valeurs pour les noms de propriétés.\n\nPar exemple :\n\n```js run\nfunction makeUser(name, age) {\n  return {\n    name: name,\n    age: age,\n    // ...autres propriétés\n  };\n}\n\nlet user = makeUser(\"John\", 30);\nalert(user.name); // John\n```\n\nDans l'exemple ci-dessus, les propriétés portent les mêmes noms que les variables. Le cas d’utilisation de la création d’une propriété à partir d’une variable est si courant qu’il existe une valeur spéciale de propriété abrégée (*property value shorthand*) pour la rendre plus courte.\n\nAu lieu de `name:name`, nous pouvons simplement écrire `name`, comme ceci :\n\n```js\nfunction makeUser(name, age) {\n*!*\n  return {\n    name, // pareil que name: name\n    age,  // pareil que age: age\n    // ...\n  };\n*/!*\n}\n```\n\nNous pouvons utiliser à la fois des propriétés normales et des raccourcis dans le même objet :\n\n```js\nlet user = {\n  name,  // pareil que name:name\n  age: 30\n};\n```\n\n## Limitations des noms de propriété\n\nComme nous le savons déjà, une variable ne peut pas avoir un nom égal à l'un des mots réservés au langage comme \"for\", \"let\", \"return\" etc.\n\nMais pour une propriété d'objet, il n'y a pas de telle restriction :\n\n```js run\n// ces propriétés sont toutes correctes\nlet obj = {\n  for: 1,\n  let: 2,\n  return: 3\n};\n\nalert( obj.for + obj.let + obj.return );  // 6\n```\n\nEn bref, il n'y a aucune limitation sur les noms de propriété. Il peut s'agir de n'importe quelle chaîne de caractères ou symbole (un type spécial pour les identifiants, qui sera traité plus tard).\n\nLes autres types sont automatiquement convertis en chaînes de caractères.\n\nPar exemple, un nombre `0` devient une chaîne `\"0\"` lorsqu'il est utilisé comme clé de propriété :\n\n```js run\nlet obj = {\n  0: \"test\" // identique à \"0\": \"test\"\n};\n\n// les 2 alertes accèdent à la même propriété (le chiffre 0 est converti en string \"0\")\nalert( obj[\"0\"] ); // test\nalert( obj[0] ); // test (same property)\n```\n\nIl y a un problème mineur avec une propriété spéciale nommée `__proto__`. Nous ne pouvons pas le définir sur une valeur non-objet :\n\n\n```js run\nlet obj = {};\nobj.__proto__ = 5; // assignation d'un nombre\nalert(obj.__proto__); // [object Object] - la valeur est un objet, n'a pas fonctionné comme prévu\n```\n\nComme nous le voyons dans le code, l'affectation à une primitive `5` est ignorée.\n\nNous couvrirons la nature particulière de `__proto__` dans les [chapitres suivants](info:prototype-inheritance), et nous suggèrerons une [façon de corriger](info:prototype-methods) ce genre de comportement.\n\n## Test d'existence de propriété, opérateur \"in\"\n\nUne caractéristique notable des objets en JavaScript, par rapport à de nombreux autres langages, est qu'il est possible d'accéder à n'importe quelle propriété. Il n'y aura pas d'erreur si la propriété n'existe pas !\n\nLa lecture d'une propriété non existante renvoie simplement `undefined`. Nous pouvons donc facilement tester si la propriété existe :\n\n```js run\nlet user = {};\n\nalert( user.noSuchProperty === undefined ); // true signifie \"pas une telle propriété\"\n```\n\nIl existe également un opérateur spécial `\"in\"` pour cela.\n\nLa syntaxe est :\n```js\n\"key\" in object\n```\n\nPar exemple :\n\n```js run\nlet user = { name: \"John\", age: 30 };\n\nalert( \"age\" in user ); // true, user.age existe\nalert( \"blabla\" in user ); // false, user.blabla n'existe pas\n```\n\nVeuillez noter que sur le côté gauche de `in`, il doit y avoir un *nom de propriété*. C’est généralement une chaîne de caractères entre guillemets.\n\nSi nous omettons les guillemets, cela signifie qu'une variable doit contenir le nom réel à tester. Par exemple :\n\n```js run\nlet user = { age: 30 };\n\nlet key = \"age\";\nalert( *!*key*/!* in user ); // true, la propriété \"age\" existe\n```\n\nPourquoi l'opérateur `in` existe-t-il ? N'est-ce pas suffisant de comparer avec `undefined` ?\n\nEh bien, la plupart du temps, la comparaison avec `undefined` fonctionne bien. Mais il y a un cas particulier quand il échoue, mais `in` fonctionne correctement.\n\nC’est lorsque une propriété d’objet existe, mais qu'elle stocke undefined :\n\n```js run\nlet obj = {\n  test: undefined\n};\n\nalert( obj.test ); // c'est indéfini, donc - pas une telle propriété ?\n\nalert( \"test\" in obj ); // true, la propriété existe !\n```\n\n\nDans le code ci-dessus, la propriété `obj.test` existe techniquement. Donc, l'opérateur `in` fonctionne bien.\n\nDes situations comme celle-ci se produisent très rarement, parce que `undefined` n'est généralement pas attribué. Nous utilisons principalement `null` pour les valeurs \"inconnues\" ou \"vides\". Ainsi, l'opérateur `in` est un invité exotique dans le code.\n\n\n## La boucle \"for..in\" [#forin]\n\nPour parcourir toutes les clés d'un objet, il existe une forme spéciale de boucle : `for..in`. C'est une chose complètement différente de la construction `for(;;)` que nous avons étudiée auparavant.\n\nLa syntaxe :\n\n```js\nfor (key in object) {\n  // exécute le corps pour chaque clé parmi les propriétés de l'objet\n}\n```\n\nPar exemple, affichons toutes les propriétés de `user` :\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30,\n  isAdmin: true\n};\n\nfor (let key in user) {\n  // keys\n  alert( key );  // name, age, isAdmin\n  // valeurs pour les clés\n  alert( user[key] ); // John, 30, true\n}\n```\n\nNotez que toutes les constructions \"for\" nous permettent de déclarer la variable en boucle à l'intérieur de la boucle, comme `let key` ici.\n\nEn outre, nous pourrions utiliser un autre nom de variable ici au lieu de `key`. Par exemple, `for(let prop in obj)` est également largement utilisé.\n\n\n### Ordonné comme un objet\n\nLes objets sont-ils ordonnés ? En d'autres termes, si nous parcourons un objet en boucle, obtenons-nous toutes les propriétés dans le même ordre où elles ont été ajoutées ? Pouvons-nous compter sur cela ?\n\nLa réponse courte est : \"ordonné de manière spéciale\" : les propriétés des entiers sont triées, les autres apparaissent dans l'ordre de création. Nous allons voir cela en détails.\n\nPar exemple, considérons un objet avec les indicatifs de téléphone par pays :\n\n```js run\nlet codes = {\n  \"49\": \"Germany\",\n  \"41\": \"Switzerland\",\n  \"44\": \"Great Britain\",\n  // ..,\n  \"1\": \"USA\"\n};\n\n*!*\nfor(let code in codes) {\n  alert(code); // 1, 41, 44, 49\n}\n*/!*\n```\n\nL'objet peut être utilisé pour suggérer une liste d'options à l'utilisateur. Si nous créons un site principalement pour le public allemand, nous voulons probablement que `49` soit le premier.\n\nMais si nous exécutons ce code, nous voyons une image totalement différente :\n\n- USA (1) passe en premier\n- puis Switzerland (41) et ainsi de suite.\n\nLes indicatifs de téléphone sont classés par ordre croissant, car ce sont des entiers. Donc on voit `1, 41, 44, 49`.\n\n````smart header=\"Propriétés entier (integer properties) ? Qu'est-ce que c'est ?\"\nLe terme \"propriété entier\" (integer properties) désigne ici une chaîne de caractères qui peut être convertie en un nombre entier ou inversement sans changement.\n\nAinsi, `\"49\"` est un nom de propriété entier, parce que lorsqu'il est transformé en nombre entier et inversement, il reste identique. Mais `\"+49\"` et `\"1.2\"` ne le sont pas :\n\n```js run\n// Number(...) convertit explicitement en nombre\n// Math.trunc est une fonction intégrée qui supprime la partie décimale\nalert( String(Math.trunc(Number(\"49\"))) ); // \"49\", identique, propriété entière\nalert( String(Math.trunc(Number(\"+49\"))) ); // \"49\", non identique \"+49\" ⇒ propriété non entière\nalert( String(Math.trunc(Number(\"1.2\"))) ); // \"1\", non identique \"1.2\" ⇒ propriété non entière\n```\n````\n\n… Par contre, si les clés ne sont pas des entiers, elles sont listées dans l'ordre de création, par exemple :\n\n```js run\nlet user = {\n  name: \"John\",\n  surname: \"Smith\"\n};\nuser.age = 25; // Ajouter une clé de plus\n\n*!*\n// les propriétés non-entiers sont listées dans l'ordre de création\n*/!*\nfor (let prop in user) {\n  alert( prop ); // name, surname, age\n}\n```\n\nDonc, pour résoudre le problème avec les indicatifs de téléphone, nous pouvons \"tricher\" en rendant ces indicatifs non entiers. Ajouter un signe plus `\"+\"` avant chaque indicatif suffit.\n\nComme ceci :\n\n```js run\nlet codes = {\n  \"+49\": \"Germany\",\n  \"+41\": \"Switzerland\",\n  \"+44\": \"Great Britain\",\n  // ..,\n  \"+1\": \"USA\"\n};\n\nfor(let code in codes) {\n  alert( +code ); // 49, 41, 44, 1\n}\n```\n\nMaintenant, cela fonctionne comme prévu.\n\n\n## Résumé\n\nLes objets sont des tableaux associatifs dotés de plusieurs fonctionnalités spéciales.\n\nIls stockent des propriétés (paires clé-valeur), où :\n- Les clés de propriété doivent être des chaînes de caractères ou des symboles (généralement des chaînes de caractères).\n- Les valeurs peuvent être de tout type.\n\nPour accéder à une propriété, nous pouvons utiliser :\n- La notation par points : `obj.property`.\n- Notation entre crochets `obj[\"property\"]`. Les crochets permettent de prendre la clé à partir d’une variable, comme `obj[varWithKey]`.\n\nOpérateurs supplémentaires :\n- Pour supprimer une propriété : `delete obj.prop`.\n- Pour vérifier si une propriété avec la clé donnée existe : `\"key\" in obj`.\n- Pour parcourir un objet : la boucle `for (let key in obj)`.\n\nCe que nous avons étudié dans ce chapitre s’appelle un \"objet simple\" (plain object) ou juste `Object`.\nIl existe de nombreux autres types d'objets en JavaScript :\n\n- `Array` pour stocker des collections de données ordonnées,\n- `Date` pour stocker des informations sur la date et l'heure,\n- `Error` pour stocker des informations sur une erreur.\n- Etc.\n\nIls ont leurs particularités que nous étudierons plus tard. Parfois, les gens disent quelque chose comme \"type Tableau\" ou \"type Date\", mais ils ne sont pas formellement propres, mais appartiennent à un seul type de données \"objet\". Et ils l'étendent de différentes manières.\n\nLes objets en JavaScript sont très puissants. Nous venons de gratter la surface d’un sujet vraiment énorme. Nous allons travailler étroitement avec les objets et en apprendre davantage à leur sujet dans d’autres parties du tutoriel.\n"
  },
  {
    "path": "1-js/04-object-basics/02-object-copy/article.md",
    "content": "# Les références d'objet et leur copie\n\nUne des différences fondamentale des objets avec les primitives est que ceux-ci sont stockés et copiés \"par référence\", en opposition des valeurs primitives : strings, numbers, booleans, etc. -- qui sont toujours copiés comme \"valeur entière\".\n\nOn comprendra plus facilement en regardant \"sous le capot\" ce qui se passe lorsque nous copions une valeure.\n\nCommençons avec une primitive, comme une chaîne de caractères.\n\nIci nous assignons une copie de `message` dans `phrase` :\n\n```js\nlet message = \"Hello!\";\nlet phrase = message;\n```\n\nIl en résulte deux variables indépendantes, chacune stockant la chaîne de caractères `\"Hello!\"`.\n\n![](variable-copy-value.svg)\n\nUn résultat plutôt évident n'est-ce pas ?\n\nLes objets ne fonctionnent pas comme cela.\n\n**Une variable assignée à un objet ne stocke pas l'objet lui-même, mais son \"adresse en mémoire\", en d'autres termes \"une référence\" à celui-ci.**\n\nPrenons un exemple d'une telle variable :\n\n```js\nlet user = {\n  name: \"John\"\n};\n```\n\nEt ici comment elle est stockée en mémoire :\n\n![](variable-contains-reference.svg)\n\nL'objet est stocké quelque part dans la mémoire (du coté droit de l'image), tandis que la variable `user` (du coté gauche) a une référence à celui-ci.\n\nOn peut imaginer la variable d'objet, ici `user`, comme une feuille de papier avec l'adresse de l'objet écrit dessus.\n\nLorque l'on réalise une action avec l'objet, par exemple récupérer la propriété `user.name`, le moteur de JavaScript regarde à l'adresse et réalise l'opération sur l'objet actuel.\n\nEt voilà pourquoi cela est important.\n\n**Lorsqu'une variable d'objet est copiée -- la référence est copiée, l'objet lui-même n'est pas dupliqué.**\n\nPar exemple:\n\n```js no-beautify\nlet user = { name: \"John\" };\n\nlet admin = user; // copie la référence\n```\n\nMaintenant nous avons deux variables, chacune avec la référence vers le même objet :\n\n![](variable-copy-reference.svg)\n\nComme vous pouvez le voir, il n'y a toujours qu'un seul objet, mais maintenant avec deux variables qui le référence.\n\nOn peut utiliser n'importe quelle variable pour accéder à l'objet et modifier son contenu :\n\n```js run\nlet user = { name: 'John' };\n\nlet admin = user;\n\n*!*\nadmin.name = 'Pete'; // changé par la référence \"admin\"\n*/!*\n\nalert(*!*user.name*/!*); // 'Pete', les changements sont visibles sur la référence \"user\"\n```\n\nC'est comme si nous avions une armoire avec deux clés et que nous en utilisions une (`admin`) pour y entrer et y apporter des modifications. Ensuite, si nous utilisons plus tard une autre clé (`user`), nous ouvrons toujours la même armoire et pouvons accéder au contenu modifié.\n\n## Comparaison par référence\n\nDeux objets sont égaux seulement s'ils sont le même objet.\n\nPar exemple, ici `a` et `b` référencent le même objet, aussi sont-ils similaires :\n\n```js run\nlet a = {};\nlet b = a; // copie la référence\n\nalert( a == b ); // true, les deux variables référencent le même objet\nalert( a === b ); // true\n```\n\nEt ici deux objets indépendants ne sont pas égaux, même s'ils se ressemblent (les deux sont vides) :\n\n```js run\nlet a = {};\nlet b = {}; // 2 objets indépendants\n\nalert( a == b ); // false\n```\n\nPour des comparaisons comme `obj1 > obj2` ou des comparaisons avec une primitive `obj == 5`, les objets sont convertis en primitives. Nous étudierons comment les conversions d'objets fonctionnent très bientôt, mais pour dire la vérité, de telles comparaisons sont rarement nécessaires, en général elles sont le résultat d'une erreur de programmation.\n\n````smart header=\"Les objets const peuvent être modifiés\"\nUn effet secondaire important du stockage des objets en tant que références est qu'un objet déclaré comme `const` *peut* être modifié.\n\nPar exemple :\n\n```js run\nconst user = {\n  name: \"John\"\n};\n\n*!*\nuser.name = \"Pete\"; // (*)\n*/!*\n\nalert(user.name); // Pete\n```\n\nIl peut sembler que la ligne `(*)` causerait une erreur, mais ce n'est pas le cas. La valeur de `user` est constante, elle doit toujours référencer le même objet, mais les propriétés de cet objet sont libres de changer.\n\nEn d'autres termes, `const user` donne une erreur uniquement si nous essayons de définir `user=...` dans son ensemble.\n\nCela dit, si nous avons vraiment besoin de créer des propriétés d'objet constantes, c'est également possible, mais en utilisant des méthodes totalement différentes. Nous le mentionnerons dans le chapitre <info:property-descriptors>.\n````\n\n## Clonage et fusion, Object.assign [#cloning-and-merging-object-assign]\n\nCopier une variable object crée une référence en plus vers le même objet.\n\nMais que se passe-t-il si nous devons dupliquer un objet ?\n\nNous pouvons créer un nouvel objet et reproduire la structure de l'existant, en itérant sur ses propriétés et en les copiant au niveau primitif.\n\nComme cela :\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30\n};\n\n*!*\nlet clone = {}; // le nouvel object vide\n\n// on copie toutes les propritétés de user\nfor (let key in user) {\n  clone[key] = user[key];\n}\n*/!*\n\n// maintenant clone est un objet complètement indépendant avec le même contenu\nclone.name = \"Pete\"; // On change les données de celui-ci\n\nalert( user.name ); // c'est toujour John dans l'objet copié\n```\n\nOn peut aussi utiliser la méthode [Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) pour cela.\n\n\nLa syntaxe est :\n\n```js\nObject.assign(dest, ...sources)\n```\n\n- Le premier argument `dest` est l'objet cible\n- Les autres arguments sont une liste d'objets source.\n\nIl copie les propriétés de tous les objets sources dans la cible `dest`, puis les renvoie comme résultat.\n\nPar exemple, nous avons l'objet `user`, ajoutons-lui quelques autorisations :\n\n```js run\nlet user = { name: \"John\" };\n\nlet permissions1 = { canView: true };\nlet permissions2 = { canEdit: true };\n\n*!*\n// copie toutes les propriétés de permissions1 et 2 dans user\nObject.assign(user, permissions1, permissions2);\n*/!*\n\n// now user = { name: \"John\", canView: true, canEdit: true }\nalert(user.name); // John\nalert(user.canView); // true\nalert(user.canEdit); // true\n```\n\nSi la propriété copiée existe déja, elle est écrasée.\n\n```js run\nlet user = { name: \"John\" };\n\nObject.assign(user, { name: \"Pete\" });\n\nalert(user.name); // on a user = { name: \"Pete\" }\n```\n\nNous pouvons également utiliser `Object.assign` pour effectuer un simple clonage d'objet :\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30\n};\n\n*!*\nlet clone = Object.assign({}, user);\n*/!*\n\nalert(clone.name); // John\nalert(clone.age); // 30\n```\n\nIci cela copie toutes les propriétés de `user` dans l'objet vide et le retourne.\n\nIl existe également d'autres méthodes de clonage d'un objet, par ex. en utilisant la [syntaxe spread](info:rest-parameters-spread) `clone = {...user}`, abordé plus loin dans le tutoriel.\n\n## Clonage imbriqué\n\nJusqu'à maintenant on suppose que toutes les propriétés de `user` sont des primitives. Mais les propriétés peuvent être des références vers d'autres objets. Comment gérer ces cas-là ?\n\nComme ceci :\n```js run\nlet user = {\n  name: \"John\",\n  sizes: {\n    height: 182,\n    width: 50\n  }\n};\n\nalert( user.sizes.height ); // 182\n```\n\nCe n'est plus suffisant de copier `clone.sizes = user.sizes`, car `user.sizes` est un objet, il sera copié par référence. Donc `clone` et `user` partageront le même objet `sizes` :\n\n\n```js run\nlet user = {\n  name: \"John\",\n  sizes: {\n    height: 182,\n    width: 50\n  }\n};\n\nlet clone = Object.assign({}, user);\n\nalert( user.sizes === clone.sizes ); // true, c'est le même objet\n\n// user et clone partage l'objet sizes\nuser.sizes.width = 60;    // changer une propriété d'un endroit\nalert(clone.sizes.width); // 60, obtenir le résultat de l'autre\n```\n\nPour résoudre ce problème et faire en sorte que `user` et `clone` soient des objets véritablement séparés, nous devrions utiliser une boucle de clonage qui examine chaque valeur de `user[key]` et, s'il s'agit d'un objet, répliquer également sa structure. C'est ce qu'on appelle un « clonage profond » ou « clonage structuré ». Il existe une méthode [structuredClone](https://developer.mozilla.org/en-US/docs/Web/API/structuredClone) qui implémente le clonage en profondeur.\n\n\n### structuredClone\n\nL'appel `structuredClone(object)` clone l'`object` avec toutes les propriétés imbriquées.\n\nVoici comment nous pouvons l'utiliser dans notre exemple :\n\n```js run\nlet user = {\n  name: \"John\",\n  sizes: {\n    height: 182,\n    width: 50\n  }\n};\n\n*!*\nlet clone = structuredClone(user);\n*/!*\n\nalert( user.sizes === clone.sizes ); // false, c'est un objet différent\n\n// user et clone n'ont plus aucun lien entre eux\nuser.sizes.width = 60;    // changer une propriété d'un endroit\nalert(clone.sizes.width); // 50, sans lien\n```\n\nLa méthode `structuredClone` peut cloner la plupart des types de données, tels que des objets, des tableaux, des valeurs primitives.\n\nIl prend également en charge les références circulaires, lorsqu'une propriété d'objet fait référence à l'objet lui-même (directement ou via une chaîne ou des références).\n\nPar exemple :\n\n```js run\nlet user = {};\n// créons une référence circulaire :\n// user.me fait référence à l'utilisateur lui-même\nuser.me = user;\n\nlet clone = structuredClone(user);\nalert(clone.me === clone); // true\n```\n\nComme vous pouvez le voir, `clone.me` fait référence au `clone`, pas à `user` ! Ainsi, la référence circulaire a également été clonée correctement.\n\nCependant, il existe des cas où `structuredClone` échoue.\n\nPar exemple, lorsqu'un objet a une propriété de fonction :\n\n```js run\n// error\nstructuredClone({\n  f: function() {}\n});\n```\n\nLes propriétés de fonction ne sont pas prises en charge.\n\nPour gérer des cas aussi complexes, nous devrons peut-être utiliser une combinaison de méthodes de clonage, écrire du code personnalisé ou, pour ne pas réinventer la roue, prendre une implémentation existante, par exemple [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) de la bibliothèque JavaScript [lodash](https://lodash.com).\n\n## Résumé\n\nLes objets sont assignés et copiés par référence. En d'autres termes, une variable ne stocke pas la \"valeur de l'objet\" mais la \"référence\" (l'adresse en mémoire) de la valeur. Donc copier cette variable, ou la passer en argument d'une fonction, copie la référence, pas l'objet lui-même.\n\nToutes les opérations faites par une copie de la référence (comme ajouter/supprimer une propriété) sont faites sur le même objet.\n\nPour faire une \"copie réelle\" (un clone), nous pouvons utiliser `Object.assign` pour la soi-disant \"copie superficielle\" (les objets imbriqués sont copiés par référence) ou une fonction de \"clonage en profondeur\" `structuredClone` ou utiliser une implementation personnalisée de clonage, telle que [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep).\n"
  },
  {
    "path": "1-js/04-object-basics/03-garbage-collection/article.md",
    "content": "# Ramasse-miettes (garbage collection)\n\nLa gestion de la mémoire en JavaScript est effectuée automatiquement et de manière invisible pour nous. Nous créons des primitives, des objets, des fonctions… Tout cela prend de la mémoire.\n\nQue se passe-t-il quand quelque chose n'est plus nécessaire ? Comment le moteur JavaScript le découvre et le nettoie ?\n\n## Accessibilité\n\nLe concept principal de la gestion de la mémoire en JavaScript est l’*accessibilité*.\n\nEn termes simples, les valeurs \"accessibles\" sont celles qui sont accessibles ou utilisables d’une manière ou d’une autre. Elles sont garanties d'être stockés en mémoire.\n\n1. Il existe un ensemble de base de valeurs intrinsèquement accessibles, qui ne peuvent pas être supprimées pour des raisons évidentes.\n\n    Par exemple :\n\n    - Variables locales et paramètres de la fonction en cours d’exécution.\n    - Variables et paramètres pour d'autres fonctions sur la chaîne d'appels imbriqués en cours.\n    - Variables globales.\n    - (il y en a d'autres, internes aussi)\n\n    Ces valeurs s'appellent des racines (*roots*).\n\n2. Toute autre valeur est considérée comme accessible si elle est accessible depuis une racine par une référence ou par une chaîne de références.\n\n    Par exemple, s’il existe un objet dans une variable globale et que cet objet a une propriété référençant un autre objet, *cet* objet est considéré comme accessible. Et ceux auxquels il fait référence sont également accessibles. Des exemples détaillés à suivre.\n\nIl existe un processus d’arrière-plan dans le moteur JavaScript appelé [Ramasse-miettes (Garbage Collector)](https://fr.wikipedia.org/wiki/Ramasse-miettes_(informatique)). Il surveille tous les objets et supprime ceux qui sont devenus inaccessibles.\n\n## Un exemple simple\n\nVoici l'exemple le plus simple :\n\n```js\n// user a une référence à l'objet\nlet user = {\n  name: \"John\"\n};\n```\n\n![](memory-user-john.svg)\n\nIci, la flèche représente une référence d'objet. La variable globale `\"user\"` fait référence à l’objet `{name: \"John\"}` (nous l’appellerons John par souci de brièveté). La propriété `\"name\"` de John stocke une primitive, elle est donc stockée à l'intérieur de l'objet.\n\nSi la valeur de `user` est écrasée, la référence est perdue :\n\n```js\nuser = null;\n```\n\n![](memory-user-john-lost.svg)\n\nMaintenant, John devient inaccessible. Il n’y a aucun moyen d’y accéder, pas de référence. Le ramasse-miettes (garbage collector) détruit les données et libère la mémoire.\n\n## Deux références\n\nImaginons maintenant que nous ayons copié la référence de `user` à `admin` :\n\n```js\n// user a une référence à l'objet\nlet user = {\n  name: \"John\"\n};\n\n*!*\nlet admin = user;\n*/!*\n```\n\n![](memory-user-john-admin.svg)\n\nMaintenant si nous faisons la même chose :\n```js\nuser = null;\n```\n\n… Ensuite, l’objet est toujours accessible via la variable globale `admin`, il est donc encore en mémoire. Si nous écrasons également `admin`, alors il sera supprimé.\n\n## Objets liés\n\nMaintenant, un exemple plus complexe. La famille :\n\n```js\nfunction marry(man, woman) {\n  woman.husband = man;\n  man.wife = woman;\n\n  return {\n    father: man,\n    mother: woman\n  }\n}\n\nlet family = marry({\n  name: \"John\"\n}, {\n  name: \"Ann\"\n});\n```\n\nLa fonction `marry` \"marie\" deux objets en leur donnant des références et renvoie un nouvel objet les contenant tous les deux.\n\nLe résultat de la structure de mémoire :\n\n![](family.svg)\n\nÀ partir de maintenant, tous les objets sont accessibles.\n\nSupprimons maintenant deux références :\n\n```js\ndelete family.father;\ndelete family.mother.husband;\n```\n\n![](family-delete-refs.svg)\n\nIl ne suffit pas de supprimer une seule de ces deux références, car tous les objets seraient toujours accessibles.\n\nMais si nous supprimons les deux, alors nous pouvons voir que John n’a plus de référence entrante :\n\n![](family-no-father.svg)\n\nLes références sortantes importent peu. Seuls les objets entrants peuvent rendre un objet accessible. Ainsi, John est maintenant inaccessible et sera supprimé de la mémoire avec toutes ses données qui sont également devenues inaccessibles.\n\nAprès le passage du ramasse-miettes (garbage collector) :\n\n![](family-no-father-2.svg)\n\n## Île inaccessible\n\nIl est possible que toute l'île d'objets liés entre eux devienne inaccessible et soit supprimée de la mémoire.\n\nL'objet source est le même que ci-dessus. Ensuite :\n\n```js\nfamily = null;\n```\n\nL'image en mémoire devient :\n\n![](family-no-family.svg)\n\nCet exemple montre à quel point le concept d'accessibilité est important.\n\nIl est évident que John et Ann sont toujours liés, les deux ont des références entrantes. Mais cela ne suffit pas.\n\nL’ancien objet `\"family\"` a été dissocié de la racine, elle n’y fait plus référence, toute l’île devient inaccessible et sera donc supprimée.\n\n## Algorithmes internes\n\nL'algorithme de base de la récupération de place (garbage collection) s'appelle \"mark-and-sweep\".\n\nLes étapes suivantes du \"ramasse-miettes\" (garbage collection) sont régulièrement effectuées :\n\n- Le ramasse-miettes prend les racines et les \"marque\" (se souvient).\n- Ensuite, il visite et \"marque\" toutes les références.\n- Ensuite, il visite les objets marqués et marque *leurs* références. Tous les objets visités sont mémorisés afin de ne pas visiter le même objet deux fois dans le futur.\n- … Et ainsi de suite tant qu'il y a des références non consultées (accessibles depuis les racines).\n- Tous les objets sont supprimés sauf ceux qui sont marqués.\n\nPar exemple, imaginons notre structure d'objet ressembler à ceci :\n\n![](garbage-collection-1.svg)\n\nNous pouvons clairement voir une \"île inaccessible\" sur le côté droit. Voyons maintenant comment le garbage collector \"mark-and-sweep\" le gère.\n\nLa première étape marque les racines :\n\n![](garbage-collection-2.svg)\n\nEnsuite, nous suivons leurs références et marquons les objets référencés :\n\n![](garbage-collection-3.svg)\n\n...Et continuons à suivre d'autres références, dans la mesure du possible :\n\n![](garbage-collection-4.svg)\n\nDésormais, les objets qui n'ont pas pu être visités sont considérés comme inaccessibles et seront supprimés :\n\n![](garbage-collection-5.svg)\n\nNous pouvons également imaginer que le processus consiste à renverser un énorme seau de peinture à la racine, qui traverse toutes les références et marque tous les objets accessibles. Les non marqués sont ensuite supprimés.\n\nC'est le concept de la façon dont la garbage collection fonctionne. Les moteurs JavaScript appliquent de nombreuses optimisations pour accélérer l’exécution et ne pas affecter l’exécution.\n\nCertaines des optimisations :\n\n- **Collecte générationnelle** -- les objets sont divisés en deux ensembles : \"nouveaux\" et \"anciens\". Dans un code typique, de nombreux objets ont une courte durée de vie : ils apparaissent, font leur travail et meurent rapidement, il est donc logique de suivre les nouveaux objets et d'en effacer la mémoire si c'est le cas. Ceux qui survivent assez longtemps deviennent \"vieux\" et sont examinés moins souvent.\n- **Collecte incrémentielle** -- s'il y a beaucoup d'objets et que nous essayons de parcourir et de marquer l'ensemble d'objets en une seule fois, cela peut prendre un certain temps et introduire des retards visibles dans l'exécution. Ainsi, le moteur divise l'ensemble des objets existants en plusieurs parties. Et puis nettoie ces parties les unes après les autres. Il existe de nombreux petits garbage collections au lieu d'un total. Cela nécessite une comptabilité supplémentaire entre eux pour suivre les changements, mais nous obtenons de nombreux petits retards au lieu d'un gros.\n- **Collecte en cas d'inactivité** -- le garbage collector essaie de s'exécuter uniquement lorsque le processeur est inactif, afin de réduire l'effet possible sur l'exécution.\n\nIl existe d'autres optimisations et variantes d'algorithmes de récupération de place. Même si je souhaite les décrire ici, je dois m'abstenir, car différents moteurs implémentent différentes techniques et ajustements. Et, ce qui est encore plus important, les choses changent à mesure que les moteurs se développent. Donc aller plus loin de manière plus poussée, sans réel besoin, n’en vaut probablement pas la peine. À moins, bien sûr, que ce soit une question qui vous intéresse vraiment, vous trouverez quelques liens pour vous ci-dessous.\n\n## Résumé\n\nLes principales choses à savoir :\n\n- La garbage collection est effectuée automatiquement. Nous ne pouvons ni forcer ni empêcher cela.\n- Les objets sont conservés en mémoire tant qu'ils sont accessibles.\n- Être référencé n'est pas la même chose qu'être accessible (depuis une racine) : un groupe d'objets liés entre eux peut devenir inaccessible dans son ensemble.\n\nLes moteurs modernes implémentent des algorithmes avancés de récupération de place.\n\nUn livre général intitulé \"The Garbage Collection Handbook: The Art of Automatic Memory Management\" (R. Jones et al.) En parle.\n\nSi vous êtes familiarisé avec la programmation de bas niveau, les informations plus détaillées sur le garbage collector V8 se trouvent dans l'article [A tour of V8: Garbage Collection](https://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection).\n\nLe [blog V8](https://v8.dev) publie également des articles sur les modifications de la gestion de la mémoire de temps à autre. Naturellement, pour apprendre la récupération de place, vous feriez mieux de vous préparer en vous renseignant sur les éléments internes de V8 en général et en lisant le blog de [Vyacheslav Egorov](https://mrale.ph) qui a travaillé comme l'un des ingénieurs V8. Je dis: «V8», car c'est le plus couvert d'articles sur Internet. Pour d'autres moteurs, de nombreuses approches sont similaires, mais la récupération de place diffère à de nombreux égards.\n\nUne connaissance approfondie des moteurs est utile lorsque vous avez besoin d'optimisations de bas niveau. Il serait sage de planifier cela comme prochaine étape après la connaissance du langage.\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/4-object-property-this/solution.md",
    "content": "**Réponse : une erreur.**\n\nEssayez le :\n```js run\nfunction makeUser() {\n  return {\n    name: \"John\",\n    ref: this\n  };\n}\n\nlet user = makeUser();\n\nalert( user.ref.name ); // Erreur: Impossible de lire la propriété 'nom' de undefined\n```\n\nC'est parce que les règles qui définissent `this` ne prennent pas en compte la définition d'objet. Seul le moment de l'appel compte.\n\nIci, la valeur de `this` à l'intérieur de `makeUser()` est `undefined`, car elle est appelée en tant que fonction et non en tant que méthode avec la syntaxe au \"point\".\n\nLa valeur de `this` est la même pour toute la fonction, les blocs de code et les littéraux d'objet ne l'affectent pas.\n\nDonc `ref: this` prend actuellement le `this` courant de la fonction.\n\nNous pouvons réécrire la fonction et renvoyer le même `this` avec la valeur `undefined` :\n\n```js run\nfunction makeUser(){\n  return this; // cette fois il n'y a pas d'objet littéral\n}\n\nalert( makeUser().name ); // Error: Cannot read property 'name' of undefined\n```\nComme vous pouvez le constater, le résultat de `alert( makeUser().name )` est identique à celui de `alert( user.ref.name )` de l'exemple précédent.\n\nVoici le cas contraire :\n\n```js run\nfunction makeUser() {\n  return {\n    name: \"John\",\n*!*\n    ref() {\n      return this;\n    }\n*/!*\n  };\n}\n\nlet user = makeUser();\n\nalert( user.ref().name ); // John\n```\n\nMaintenant cela fonctionne parce que `user.ref()` est une méthode. Et la valeur de `this` est définie pour l'objet avant le point `.`.\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/4-object-property-this/task.md",
    "content": "importance: 5\n\n---\n\n# Utilisation de \"this\" dans le littéral d'objet\n\nIci, la fonction `makeUser` renvoie un objet.\n\nQuel est le résultat de l'accès à sa `ref` ? Pourquoi ?\n\n```js\nfunction makeUser() {\n  return {\n    name: \"John\",\n    ref: this\n  };\n}\n\nlet user = makeUser();\n\nalert( user.ref.name ); // Quel est le résultat ?\n```\n\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/7-calculator/_js.view/solution.js",
    "content": "let calculator = {\n  sum() {\n    return this.a + this.b;\n  },\n\n  mul() {\n    return this.a * this.b;\n  },\n\n  read() {\n    this.a = +prompt('a?', 0);\n    this.b = +prompt('b?', 0);\n  }\n};"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/7-calculator/_js.view/test.js",
    "content": "\n\ndescribe(\"calculator\", function() {\n  \n  context(\"when 2 and 3 entered\", function() {\n    beforeEach(function() {\n      sinon.stub(window, \"prompt\");\n\n      prompt.onCall(0).returns(\"2\");\n      prompt.onCall(1).returns(\"3\");\n\n      calculator.read();\n    });\n\n    afterEach(function() {\n      prompt.restore();\n    });\n    \n    it('the read get two values and saves them as object properties', function () {\n      assert.equal(calculator.a, 2);\n      assert.equal(calculator.b, 3);\n    });\n\n    it(\"the sum is 5\", function() {\n      assert.equal(calculator.sum(), 5);\n    });\n\n    it(\"the multiplication product is 6\", function() {\n      assert.equal(calculator.mul(), 6);\n    });\n  });\n\n});\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/7-calculator/solution.md",
    "content": "\n```js run demo solution\nlet calculator = {\n  sum() {\n    return this.a + this.b;\n  },\n\n  mul() {\n    return this.a * this.b;\n  },\n\n  read() {\n    this.a = +prompt('a?', 0);\n    this.b = +prompt('b?', 0);\n  }\n};\n\ncalculator.read();\nalert( calculator.sum() );\nalert( calculator.mul() );\n```\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/7-calculator/task.md",
    "content": "importance: 5\n\n---\n\n# Créer une calculatrice\n\nCréez un objet `calculator` avec trois méthodes :\n\n- `read()` demande deux valeurs et les enregistre en tant que propriétés d'objet avec les noms `a` et `b` respectivement.\n- `sum()` renvoie la somme des valeurs sauvegardées.\n- `mul()` multiplie les valeurs sauvegardées et renvoie le résultat.\n\n```js\nlet calculator = {\n  // ... votre code ...\n};\n\ncalculator.read();\nalert( calculator.sum() );\nalert( calculator.mul() );\n```\n\n[demo]\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/8-chain-calls/_js.view/solution.js",
    "content": "\nlet ladder = {\n  step: 0,\n  up: function() { \n    this.step++;\n    return this;\n  },\n  down: function() { \n    this.step--;\n    return this;\n  },\n  showStep: function() { \n    alert(this.step);\n    return this;\n  }\n};"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/8-chain-calls/_js.view/test.js",
    "content": "\ndescribe('Ladder', function() {\n  before(function() {\n    window.alert = sinon.stub(window, \"alert\");\n  });\n  \n  beforeEach(function() {\n    ladder.step = 0;\n  });\n\n  it('up() should return this', function() {\n    assert.equal(ladder.up(), ladder);\n  });\n\n  it('down() should return this', function() {\n    assert.equal(ladder.down(), ladder);\n  });\n\n  it('showStep() should call alert', function() {\n    ladder.showStep();\n    assert(alert.called);\n  });\n\n  it('up() should increase step', function() {\n    assert.equal(ladder.up().up().step, 2);\n  });\n\n  it('down() should decrease step', function() {\n    assert.equal(ladder.down().step, -1);\n  });\n\n  it('down().up().up().up() ', function() {\n    assert.equal(ladder.down().up().up().up().step, 2);\n  });\n\n  it('showStep() should return this', function() {\n    assert.equal(ladder.showStep(), ladder);\n  });\n \n  it('up().up().down().showStep().down().showStep()', function () {\n    assert.equal(ladder.up().up().down().showStep().down().showStep().step, 0)\n  });\n  \n  after(function() {\n    ladder.step = 0;\n    alert.restore();\n  });\n});\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md",
    "content": "La solution consiste à renvoyer l'objet lui-même à partir de chaque appel.\n\n```js run\nlet ladder = {\n  step: 0,\n  up() {\n    this.step++;\n*!*\n    return this;\n*/!*\n  },\n  down() {\n    this.step--;\n*!*\n    return this;\n*/!*\n  },\n  showStep() {\n    alert( this.step );\n*!*\n    return this;\n*/!*\n  }\n};\n\nladder.up().up().down().showStep().down().showStep(); // shows 1 then 0\n```\n\nNous pouvons également écrire un seul appel par ligne. Pour les longues chaînes, c'est plus lisible :\n\n```js \nladder\n  .up()\n  .up()\n  .down()\n  .showStep() // 1\n  .down()\n  .showStep(); // 0\n```\n\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/8-chain-calls/task.md",
    "content": "importance: 2\n\n---\n\n# Chaining\n\nIl y a un objet `ladder` qui permet de monter et descendre :\n\n```js\nlet ladder = {\n  step: 0,\n  up() { \n    this.step++;\n  },\n  down() { \n    this.step--;\n  },\n  showStep: function() { // affiche l'étape en cours\n    alert( this.step );\n  }\n};\n```\n\nMaintenant, si nous devons faire plusieurs appels en séquence, nous pouvons le faire comme ceci :\n\n```js\nladder.up();\nladder.up();\nladder.down();\nladder.showStep(); // 1\nladder.down();\nladder.showStep(); // 0\n```\n\nModifiez le code de `up` et `down` pour rendre les appels chaînables, comme ceci :\n\n```js\nladder.up().up().down().showStep().down().showStep(); // shows 1 then 0\n```\n\nCette approche est largement utilisée dans les bibliothèques JavaScript.\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/article.md",
    "content": "# Méthodes d'objet, \"this\"\n\nLes objets sont généralement créés pour représenter des entités du monde réel, comme des utilisateurs, des commandes, etc. :\n\n```js\nlet user = {\n  name: \"John\",\n  age: 30\n};\n```\n\nEt, dans le monde réel, un utilisateur peut agir : sélectionner un élément du panier, se connecter, se déconnecter, etc.\n\nLes actions sont représentées en JavaScript par des fonctions dans les propriétés.\n\n## Exemples de méthodes\n\nPour commencer, apprenons à `user` à dire bonjour :\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30\n};\n\n*!*\nuser.sayHi = function() {\n  alert(\"Hello!\");\n};\n*/!*\n\nuser.sayHi(); // Hello!\n```\n\nIci, nous venons d'utiliser une fonction expression pour créer la fonction et l'affecter à la propriété `user.sayHi` de l'objet.\n\nEnsuite, nous pouvons l'appeler comme `user.sayHi()`. L'utilisateur peut maintenant parler!\n\nUne fonction qui est la propriété d'un objet s'appelle sa *méthode*.\n\nNous avons donc ici une méthode `sayHi` de l’objet `user`.\n\nBien sûr, nous pourrions utiliser une fonction pré-déclarée comme méthode, comme ceci :\n\n```js run\nlet user = {\n  // ...\n};\n\n*!*\n// d'abord, déclarer\nfunction sayHi() {\n  alert(\"Hello!\");\n}\n\n// puis ajouter comme une méthode\nuser.sayHi = sayHi;\n*/!*\n\nuser.sayHi(); // Hello!\n```\n\n```smart header=\"Programmation orientée objet\"\nLorsque nous écrivons notre code en utilisant des objets pour représenter des entités, cela s'appelle une [programmation orientée objet](https://fr.wikipedia.org/wiki/Programmation_orient%C3%A9e_objet), en bref : \"POO\".\n\nLa programmation orientée objet est un élément important, une science intéressante en soi. Comment choisir les bonnes entités ? Comment organiser l'interaction entre elles ? C’est une architecture, et il existe d’excellents livres sur ce sujet, tels que \"Design Patterns: Elements of Reusable Object-Oriented Software\" de E. Gamma, R. Helm, R. Johnson, J. Vissides ou \"Object-Oriented Analysis and Design with Applications\" de G. Booch, et plus.\n```\n### Méthode abrégée\n\nIl existe une syntaxe plus courte pour les méthodes dans un littéral d'objet :\n\n```js\n// ces objets font la même chose\n\nuser = {\n  sayHi: function() {\n    alert(\"Hello\");\n  }\n};\n\n// la méthode abrégée semble mieux, non ?\nuser = {\n*!*\n  sayHi() { // identique à \"sayHi: function(){...}\"\n*/!*\n    alert(\"Hello\");\n  }\n};\n```\n\nComme démontré, nous pouvons omettre `\"function\"` et simplement écrire `sayHi()`.\n\nA vrai dire, les notations ne sont pas totalement identiques. Il existe des différences subtiles liées à l'héritage d'objet (à couvrir plus tard), mais pour le moment, elles importent peu. Dans presque tous les cas, la syntaxe la plus courte est préférable.\n\n## \"this\" dans les méthodes\n\nIl est courant qu'une méthode d'objet ait besoin d'accéder aux informations stockées dans l'objet pour effectuer son travail.\n\nPar exemple, le code à l'intérieur de `user.sayHi()` peut nécessiter le nom de `user`.\n\n**Pour accéder à l'objet, une méthode peut utiliser le mot-clé `this`.**\n\nLa valeur de `this` est l'objet \"avant le point\", celui utilisé pour appeler la méthode.\n\nPar exemple :\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30,\n\n  sayHi() {\n*!*\n    // \"this\" est \"l'objet actuel\"\n    alert(this.name);\n*/!*\n  }\n\n};\n\nuser.sayHi(); // John\n```\n\nIci, lors de l'exécution de `user.sayHi()`, la valeur de `this` sera `user`.\n\nTechniquement, il est également possible d’accéder à l’objet sans `this`, en le référençant via la variable externe :\n\n```js\nlet user = {\n  name: \"John\",\n  age: 30,\n\n  sayHi() {\n*!*\n    alert(user.name); // \"user\" au lieu de \"this\"\n*/!*\n  }\n\n};\n```\n\n… Mais un tel code n'est pas fiable. Si nous décidons de copier `user` dans une autre variable, par exemple `admin = user` et écraser `user` avec quelque chose d'autre, il accédera au mauvais objet.\n\nCela est démontré ci-dessous :\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30,\n\n  sayHi() {\n*!*\n    alert( user.name ); // conduit à une erreur\n*/!*\n  }\n\n};\n\n\nlet admin = user;\nuser = null; // écraser pour rendre les choses évidentes\n\n*!*\nadmin.sayHi(); // TypeError: Cannot read property 'name' of null\n*/!*\n```\n\nSi nous utilisions `this.name` au lieu de `user.name` dans l'`alert`, le code fonctionnerait.\n\n## \"this\" n'est pas lié\n\nEn JavaScript, le mot clé `this` se comporte différemment de la plupart des autres langages de programmation. Il peut être utilisé dans n'importe quelle fonction, même si ce n'est pas une méthode d'un objet.\n\nIl n’y a pas d’erreur de syntaxe dans le code suivant :\n\n```js\nfunction sayHi() {\n  alert( *!*this*/!*.name );\n}\n```\n\nLa valeur de `this` est évaluée pendant l'exécution, en fonction du contexte.\n\nPar exemple, ici la même fonction est assignée à deux objets différents et a un \"this\" différent dans les appels :\n\n```js run\nlet user = { name: \"John\" };\nlet admin = { name: \"Admin\" };\n\nfunction sayHi() {\n  alert( this.name );\n}\n\n*!*\n// utiliser la même fonction dans deux objets\nuser.f = sayHi;\nadmin.f = sayHi;\n*/!*\n\n// ces appels ont un this différent\n// \"this\" à l'intérieur de la fonction est l'objet \"avant le point\"\nuser.f(); // John  (this == user)\nadmin.f(); // Admin  (this == admin)\n\nadmin['f'](); // Admin (le point ou les crochets accèdent à la méthode - peu importe)\n```\n\nLa règle est simple : si `obj.f()` est appelé, alors `this` est `obj` pendant l'appel de `f`. C'est donc l'`user` ou l'`admin` dans l'exemple ci-dessus.\n\n````smart header=\"Appel sans objet : `this` == undefined\"\nNous pouvons même appeler la fonction sans objet du tout :\n\n```js run\nfunction sayHi() {\n  alert(this);\n}\n\nsayHi(); // undefined\n```\n\nDans ce cas, `this` est `undefined` en mode strict. Si nous essayons d'accéder à `this.name`, il y aura une erreur.\n\nEn mode non strict (si on oublie `use strict`), la valeur de `this` dans ce cas sera l’*objet global* (la fenêtre d’un navigateur, nous y reviendrons plus tard). Ceci est un comportement historique que le mode strict corrige.\n\nCe genre d'appel est généralement une erreur de programmation. Si il y a un `this` dans une fonction, il s'attend à être appelée dans un contexte d'objet.\n````\n\n```smart header=\"Les conséquences d'un `this` non lié\"\nSi vous venez d'un autre langage de programmation, vous êtes probablement habitué à l'idée d'un \"`this` lié\", où les méthodes définies dans un objet ont toujours `this` en référence à cet objet.\n\nEn JavaScript, `this` est \"libre\", sa valeur est évaluée au moment de l'appel et ne dépend pas de l'endroit où la méthode a été déclarée, mais plutôt de l'objet \"avant le point\".\n\nLe concept de temps d'exécution évalué de `this` présente à la fois des avantages et des inconvénients. D'une part, une fonction peut être réutilisée pour différents objets. D'autre part, une plus grande flexibilité ouvre la place à des erreurs.\n\nIci, notre position n'est pas de juger si cette décision de conception linguistique est bonne ou mauvaise. Nous comprendrons comment travailler avec elle, comment obtenir des avantages et éviter les problèmes.\n```\n\n## Les fonctions fléchées n'ont pas de \"this\"\n\nLes fonctions fléchées sont spéciales : elles n’ont pas leur \"propre\" `this`. Si nous faisons référence à `this` à partir d’une telle fonction, cela provient de la fonction externe \"normale\".\n\nPar exemple, ici `arrow()` utilise `this` depuis la méthode externe `user.sayHi()` :\n\n```js run\nlet user = {\n  firstName: \"Ilya\",\n  sayHi() {\n    let arrow = () => alert(this.firstName);\n    arrow();\n  }\n};\n\nuser.sayHi(); // Ilya\n```\n\nC’est une particularité des fonctions fléchées. C’est utile lorsque nous ne voulons pas réellement avoir un this distinct, mais plutôt le prendre à partir du contexte extérieur. Plus tard dans le chapitre <info:arrow-functions> nous allons approfondir les fonctions fléchées.\n\n\n## Résumé\n\n- Les fonctions stockées dans les propriétés de l'objet s'appellent des \"méthodes\".\n- Les méthodes permettent aux objets d’agir comme `object.doSomething()`.\n- Les méthodes peuvent référencer l'objet comme `this`.\n\nLa valeur de `this` est définie au moment de l'exécution.\n- Lorsqu'une fonction est déclarée, elle peut utiliser `this`, mais ce `this` n'a aucune valeur jusqu'à ce que la fonction soit appelée.\n- Une fonction peut être copiée entre des objets.\n- Lorsqu'une fonction est appelée dans la syntaxe \"méthode\" : `object.method()`, la valeur de `this` lors de l'appel est `objet`.\n\nVeuillez noter que les fonctions fléchées sont spéciales : elles n'ont pas `this`. Lorsque `this` est accédé dans une fonction fléchée, il est pris de l'extérieur.\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/solution.md",
    "content": "Oui c'est possible.\n\nSi une fonction retourne un objet alors `new` le retourne au lieu de `this`.\n\nAinsi, ils peuvent, par exemple, renvoyer le même objet défini en externe `obj` :\n\n```js run no-beautify\nlet obj = {};\n\nfunction A() { return obj; }\nfunction B() { return obj; }\n\nalert( new A() == new B() ); // true\n```\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md",
    "content": "importance: 2\n\n---\n\n# Deux fonctions - un objet\n\nEst-il possible de créer des fonctions `A` et `B` tel que `new A() == new B()` ?\n\n```js no-beautify\nfunction A() { ... }\nfunction B() { ... }\n\nlet a = new A();\nlet b = new B();\n\nalert( a == b ); // true\n```\n\nSi c'est le cas, donnez un exemple de leur code.\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/2-calculator-constructor/_js.view/solution.js",
    "content": "function Calculator() {\n\n  this.read = function() {\n    this.a = +prompt('a?', 0);\n    this.b = +prompt('b?', 0);\n  };\n\n  this.sum = function() {\n    return this.a + this.b;\n  };\n\n  this.mul = function() {\n    return this.a * this.b;\n  };\n}"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/2-calculator-constructor/_js.view/test.js",
    "content": "\ndescribe(\"calculator\", function() {\n  let calculator;\n  before(function() {\n    sinon.stub(window, \"prompt\")\n\n    prompt.onCall(0).returns(\"2\");\n    prompt.onCall(1).returns(\"3\");\n\n    calculator = new Calculator();\n    calculator.read();\n  });\n  \n  it(\"the read method asks for two values using prompt and remembers them in object properties\", function() {\n    assert.equal(calculator.a, 2);\n    assert.equal(calculator.b, 3);\n  });\n\n  it(\"when 2 and 3 are entered, the sum is 5\", function() {\n    assert.equal(calculator.sum(), 5);\n  });\n\n  it(\"when 2 and 3 are entered, the product is 6\", function() {\n    assert.equal(calculator.mul(), 6);\n  });\n\n  after(function() {\n    prompt.restore();\n  });\n});\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/2-calculator-constructor/solution.md",
    "content": "```js run demo\nfunction Calculator() {\n\n  this.read = function() {\n    this.a = +prompt('a?', 0);\n    this.b = +prompt('b?', 0);\n  };\n\n  this.sum = function() {\n    return this.a + this.b;\n  };\n\n  this.mul = function() {\n    return this.a * this.b;\n  };\n}\n\nlet calculator = new Calculator();\ncalculator.read();\n\nalert( \"Sum=\" + calculator.sum() );\nalert( \"Mul=\" + calculator.mul() );\n```\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/2-calculator-constructor/task.md",
    "content": "importance: 5\n\n---\n\n# Créer une nouvelle calculatrice\n\nCréer une fonction constructeur `Calculator` qui crée des objets avec 3 méthodes :\n\n- `read()` demande deux valeurs et les enregistre en tant que propriétés d'objet avec les noms `a` et `b` respectivement.\n- `sum()` renvoie la somme de ces propriétés.\n- `mul()` renvoie le produit de la multiplication de ces propriétés.\n\nPar exemple :\n\n```js\nlet calculator = new Calculator();\ncalculator.read();\n\nalert( \"Sum=\" + calculator.sum() );\nalert( \"Mul=\" + calculator.mul() );\n```\n\n[demo]\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/3-accumulator/_js.view/solution.js",
    "content": "function Accumulator(startingValue) {\n  this.value = startingValue;\n\n  this.read = function() {\n    this.value += +prompt('How much to add?', 0);\n  };\n\n}\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/3-accumulator/_js.view/test.js",
    "content": "describe(\"Accumulator\", function() {\n\n  beforeEach(function() {\n    sinon.stub(window, \"prompt\")\n  });\n\n  afterEach(function() {\n    prompt.restore();\n  });\n\n  it(\"initial value is the argument of the constructor\", function() {\n    let accumulator = new Accumulator(1);\n\n    assert.equal(accumulator.value, 1);\n  });\n\n  it(\"after reading 0, the value is 1\", function() {\n    let accumulator = new Accumulator(1);\n    prompt.returns(\"0\");\n    accumulator.read();\n    assert.equal(accumulator.value, 1);\n  });\n\n  it(\"after reading 1, the value is 2\", function() {\n    let accumulator = new Accumulator(1);\n    prompt.returns(\"1\");\n    accumulator.read();\n    assert.equal(accumulator.value, 2);\n  });\n});\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/3-accumulator/solution.md",
    "content": "\n\n```js run demo\nfunction Accumulator(startingValue) {\n  this.value = startingValue;\n\n  this.read = function() {\n    this.value += +prompt('How much to add?', 0);\n  };\n\n}\n\nlet accumulator = new Accumulator(1);\naccumulator.read();\naccumulator.read();\nalert(accumulator.value);\n```\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/3-accumulator/task.md",
    "content": "importance: 5\n\n---\n\n# Créer un nouvel accumulateur\n\nCréer une fonction constructeur `Accumulator(startingValue)`.\n\nL'objet qu'il crée devrait :\n\n- Stocker la \"valeur actuelle\" dans la propriété `value`. La valeur de départ est définie sur l'argument du constructeur `startingValue`.\n- La méthode `read()` devrait utiliser `prompt` pour lire un nouveau numéro et l'ajouter à `value`.\n\nEn d'autres termes, la propriété `value` est la somme de toutes les valeurs entrées par l'utilisateur avec la valeur initiale `startingValue`.\n\nVoici la démo du code :\n\n```js\nlet accumulator = new Accumulator(1); // valeur initiale 1\n\naccumulator.read(); // ajoute la valeur entrée par l'utilisateur\naccumulator.read(); // ajoute la valeur entrée par l'utilisateur\n\nalert(accumulator.value); // affiche la somme de ces valeurs\n```\n\n[demo]\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/article.md",
    "content": "# Le constructeur, l'opérateur \"new\"\n\nLa syntaxe normale `{...}` permet de créer un seul objet. Mais souvent, nous devons créer de nombreux objets similaires, tels que plusieurs utilisateurs ou éléments de menu, etc.\n\nCela peut être fait en utilisant les fonctions constructeur et l'opérateur `\"new\"`.\n\n## La function constructeur\n\nLes fonctions constructeur sont techniquement des fonctions habituelles. Il existe cependant deux conventions :\n\n1. Elles  sont nommées avec une lettre majuscule en premier.\n2. Elles ne devraient être executées qu'avec l'opérateur `\"new\"`.\n\nPar exemple :\n\n```js run\nfunction User(name) {\n  this.name = name;\n  this.isAdmin = false;\n}\n\n*!*\nlet user = new User(\"Jack\");\n*/!*\n\nalert(user.name); // Jack\nalert(user.isAdmin); // false\n```\n\nQuand une fonction est exécutée avec `new`, elle effectue les étapes suivantes :\n\n1. Un nouvel objet vide est créé et affecté à `this`.\n2. Le corps de la fonction est exécuté. Habituellement, il modifie `this`, y ajoutant de nouvelles propriétés.\n3. La valeur de `this` est retournée.\n\nEn d'autres termes, `new User(...)` fait quelque chose comme :\n\n```js\nfunction User(name) {\n*!*\n  // this = {};  (implicitement)\n*/!*\n\n  // ajoute des propriétés à this\n  this.name = name;\n  this.isAdmin = false;\n\n*!*\n  // return this;  (implicitement)\n*/!*\n}\n```\n\nDonc `let user = new User(\"Jack\")` donne le même résultat que :\n\n```js\nlet user = {\n  name: \"Jack\",\n  isAdmin: false\n};\n```\n\nMaintenant, si nous voulons créer d'autres utilisateurs, nous pouvons appeler `new User(\"Ann\")`, `new User(\"Alice\")` etc. Beaucoup plus court que d'écrire littéralement à chaque fois, et aussi facile à lire.\n\nC’est l’objectif principal des constructeurs -- implémenter du code de création d’objet réutilisable.\n\nNotons encore une fois -- techniquement, n'importe quelle fonction (à l'exception des fonctions fléchées, car elles n'ont pas de `this`) peut être utilisée comme constructeur. Elle peut être exécutée avec `new`, et elle exécutera l'algorithme ci-dessus. La \"première lettre majuscule\" est une convention, pour indiquer clairement qu'une fonction doit être exécutée avec `new`.\n\n````smart header=\"new function() { ... }\"\nSi nous avons beaucoup de lignes de code concernant la création d'un seul objet complexe, nous pouvons les envelopper dans une fonction constructeur, comme ceci :\n\n```js\n// create a function and immediately call it with new\nlet user = new function() {\n  this.name = \"John\";\n  this.isAdmin = false;\n\n  // ...autre code pour la création d'utilisateur\n  // peut-être une logique complexe et des déclarations\n  // de variables locales etc.\n};\n```\n\nCe constructeur ne peut pas être appelé à nouveau, car il n'est enregistré nulle part, juste créé et appelé. Cette astuce vise donc à encapsuler le code qui construit l'objet unique, sans réutilisation future.\n````\n\n## Constructeur mode test : new.target\n\n```smart header=\"Trucs avancés\"\nLa syntaxe de cette section est rarement utilisée, sautez-la à moins de vouloir tout savoir.\n```\n\nDans une fonction, nous pouvons vérifier si elle a été appelée avec `new` ou sans, en utilisant la propriété spéciale `new.target`.\n\nElle n'est pas définie pour les appels réguliers et équivaut à la fonction si elle est appelée avec `new` :\n\n```js run\nfunction User() {\n  alert(new.target);\n}\n\n// sans \"new\":\n*!*\nUser(); // undefined\n*/!*\n\n// avec \"new\":\n*!*\nnew User(); // function User { ... }\n*/!*\n```\n\nCela peut être utilisé dans la fonction pour savoir si elle a été appelée avec `new`, \"en mode constructeur\", ou sans \"en mode normal\".\n\nNous pouvons également faire des appels `new` et réguliers pour faire la même chose, comme ceci :\n\n```js run\nfunction User(name) {\n  if (!new.target) { // si vous m'executer sans new\n    return new User(name); // ...j'ajouterai un new pour vous\n  }\n\n  this.name = name;\n}\n\nlet john = User(\"John\"); // redirige l'appel vers un new User\nalert(john.name); // John\n```\n\nCette approche est parfois utilisée dans les librairies pour rendre la syntaxe plus flexible. Pour que les gens puissent appeler la fonction avec ou sans `new`, et que cela fonctionne toujours.\n\nCe n’est probablement pas une bonne chose à utiliser partout cependant, car l’omission de `new` rend un peu moins évident ce qui se passe. Avec `new`, nous savons tous que le nouvel objet est en cours de création.\n\n## Retour des constructeurs\n\nGénéralement, les constructeurs n'ont pas d'instruction `return`. Leur tâche consiste à écrire tous les éléments nécessaires dans `this`, qui devient automatiquement le résultat.\n\nMais s'il y a une déclaration `return`, alors la règle est simple :\n\n- Si `return` est appelé avec un object, alors il est renvoyé à la place de `this`.\n- Si `return` est appelé avec une primitive, elle est ignorée.\n\nEn d'autres termes, `return` avec un objet renvoie cet objet, dans tous les autres cas, `this` est renvoyé.\n\nPar exemple, ici `return` remplace `this` en retournant un objet :\n\n```js run\nfunction BigUser() {\n\n  this.name = \"John\";\n\n  return { name: \"Godzilla\" };  // <-- retourne cet objet\n}\n\nalert( new BigUser().name );  // Godzilla, obtenu cet objet\n```\n\nEt voici un exemple avec un `return` vide (ou nous pourrions placer une primitive après, peu importe) :\n\n```js run\nfunction SmallUser() {\n\n  this.name = \"John\";\n\n  return; // renvoie this\n}\n\nalert( new SmallUser().name );  // John\n```\n\nGénéralement, les constructeurs n’ont pas d’instruction `return`. Nous mentionnons ici le comportement spécial avec les objets renvoyés principalement dans un souci de complétude.\n\n````smart header=\"Omettre les parenthèses\"\nÀ propos, on peut omettre les parenthèses après `new` :\n\n```js\nlet user = new User; // <-- pas de parenthèses\n// identique à\nlet user = new User();\n```\n\nL'omission de parenthèses ici n'est pas considérée comme un \"bon style\", mais la syntaxe est autorisée par la spécification.\n````\n\n## Les méthodes dans les constructeurs\n\nL'utilisation de fonctions de constructeur pour créer des objets offre une grande flexibilité. La fonction constructeur peut avoir des paramètres qui définissent comment construire l'objet et ce qu'il doit y mettre.\n\nBien sûr, nous pouvons ajouter à `this` non seulement des propriétés, mais également des méthodes.\n\nPar exemple, `new User(name)` ci-dessous crée un objet avec le `name` donné et la méthode `sayHi` :\n\n```js run\nfunction User(name) {\n  this.name = name;\n\n  this.sayHi = function() {\n    alert( \"My name is: \" + this.name );\n  };\n}\n\n*!*\nlet john = new User(\"John\");\n\njohn.sayHi(); // My name is: John\n*/!*\n\n/*\njohn = {\n   name: \"John\",\n   sayHi: function() { ... }\n}\n*/\n```\n\nPour créer des objets complexes, il existe une syntaxe plus avancée, les [classes](info:classes), que nous allons couvrir plus tard.\n\n## Résumé\n\n- Les fonctions constructeur ou, plus brièvement, les constructeurs, sont des fonctions normales, mais il est généralement convenu de les nommer avec une première lettre en majuscule.\n- Les fonctions constructeur ne doivent être appelées qu'avec `new`. Un tel appel implique la création d'un objet `this` vide au début de la fonction et le renvoi de l'objet complété à la fin.\n\nNous pouvons utiliser des fonctions constructeurs pour créer plusieurs objets similaires.\n\nJavaScript fournit des fonctions constructeur pour de nombreux objets intégrés du langage : comme `Date` pour les dates, `Set` pour les ensembles et d'autres que nous prévoyons d’étudier.\n\n```smart header=\"Objets, nous reviendrons !\"\nDans ce chapitre, nous ne couvrons que les bases sur les objets et les constructeurs. Elles sont essentielles pour en savoir plus sur les types de données et les fonctions dans les chapitres suivants.\n\nAprès avoir appris cela, nous reviendrons aux objets et les couvrirons en profondeur dans les chapitres <info:prototypes> et <info:classes>.\n```\n"
  },
  {
    "path": "1-js/04-object-basics/07-optional-chaining/article.md",
    "content": "# Chaînage optionnel '?.'\n\n[recent browser=\"new\"]\n\nLe chaînage optionnel `?.` est un moyen sécurisé d'accéder aux propriétés d'objet imbriquées, même si une propriété intermédiaire n'existe pas.\n\n## Le problème de la \"propriété non existante\"\n\nSi vous venez de commencer à lire le tutoriel et à apprendre JavaScript, peut-être que le problème ne vous a pas encore touché, mais c'est assez courant.\n\nÀ titre d'exemple, disons que nous avons des objets `user` qui contiennent les informations sur nos utilisateurs.\n\nLa plupart de nos utilisateurs ont des adresses dans la propriété `user.address`, avec la rue `user.address.street`, mais certains ne les ont pas fournies.\n\nDans ce cas, lorsque nous essayons d'obtenir `user.address.street`, et que l'utilisateur se trouve sans adresse, nous obtenons une erreur :\n\n```js run\nlet user = {}; // un utilisateur sans propriété \"address\"\n\n\nalert(user.address.street); // Error!\n```\n\nC'est le résultat attendu. JavaScript fonctionne comme cela. Comme `user.address` est `undefined`, une tentative d'obtention de `user.address.street` échoue avec une erreur.\n\nDans de nombreux cas pratiques, nous préférerions obtenir `undefined` au lieu d'une erreur ici (signifiant \"pas de rue\").\n\n... Et un autre exemple. Dans le développement Web, nous pouvons obtenir un objet qui correspond à un élément de page Web à l'aide d'un appel de méthode spécial, tel que `document.querySelector('.elem')`, et il renvoie `null` lorsqu'il n'y a pas ce type d'élément.\n\n```js run\n// document.querySelector('.elem') est nul s'il n'y a pas d'élément\nlet html = document.querySelector('.elem').innerHTML; // error if it's null\n```\n\nEncore une fois, si l'élément n'existe pas, nous obtiendrons une erreur lors de l'accès à la propriété `.innerHTML` de `null`. Et dans certains cas, lorsque l'absence de l'élément est normale, nous aimerions éviter l'erreur et accepter simplement `html = null` comme résultat.\n\nComment peut-on le faire ?\n\nLa solution évidente serait de vérifier la valeur en utilisant `if` ou l'opérateur conditionnel `?`, avant d'accéder à sa propriété, comme ceci :\n\n```js\nlet user = {}; // l'utilisateur n'a pas d'adresse\n\nalert(user.address ? user.address.street : undefined);\n```\n\nCela fonctionne, il n'y a pas d'erreur... Mais c'est assez inélégant. Comme vous pouvez le voir, `\"user.address\"` apparaît deux fois dans le code.\n\nVoici à quoi ressemblerait la même chose pour `document.querySelector` :\n\n```js run\nlet html = document.querySelector('.elem') ? document.querySelector('.elem').innerHTML : null;\n```\n\nNous pouvons voir que l'élément de recherche `document.querySelector('.elem')` est en fait appelé deux fois ici. Pas bon.\n\nPour les propriétés plus profondément imbriquées, cela devient encore plus laid, car davantage de répétitions sont nécessaires.\n\nPar exemple. récupérons `user.address.street.name` de la même manière.\n\n```js\nlet user = {}; // l'utilisateur n'a pas d'adresse\n\nalert(user.address ? user.address.street ? user.address.street.name : null : null);\n```\n\nC'est juste horrible, on peut même avoir des problèmes pour comprendre un tel code.\n\nIl existe une meilleure façon de l'écrire, en utilisant l'opérateur `&&` :\n\n```js run\nlet user = {}; // l'utilisateur n'a pas d'adresse\n\nalert( user.address && user.address.street && user.address.street.name ); // undefined (no error)\n```\n\nEt le chemin complet vers la propriété garantit que tous les composants existent (sinon, l'évaluation s'arrête), mais n'est pas non plus idéal.\n\nComme vous pouvez le voir, les noms de propriétés sont toujours dupliqués dans le code. Par exemple, dans le code ci-dessus, `user.address` apparaît trois fois.\n\nC'est pourquoi le chaînage facultatif `?.` a été ajouté au langage. Pour résoudre ce problème une fois pour toutes !\n\n\n## Chaînage optionnel\n\nLe chaînage optionnel `?.` arrête l'évaluation si la valeur avant `?.` est `undefined` ou `null` et renvoie `undefined`.\n\n**Plus loin dans cet article, par souci de brièveté, nous dirons que quelque chose \"existe\" si ce n'est pas \"null\" et non \"undefined\".**\n\nEn d'autres termes, `value?.prop` :\n\n- est identique à `value.prop` si `value` existe,\n- sinon (lorsque `value` est `undefined`/`null`), il renvoie `undefined`.\n\nVoici le moyen sûr d'accéder à `user.address.street` en utilisant `?.` :\n\n```js run\nlet user = {}; // l'utilisateur n'a pas d'adresse\n\nalert( user?.address?.street ); // undefined (pas d'erreur)\n```\n\nLe code est court et propre, il n'y a aucune duplication.\n\nVoici un exemple avec `document.querySelector` :\n\n```js run\nlet html = document.querySelector('.elem')?.innerHTML; // sera undefined s'il n'y a pas d'élément\n```\n\nLa lecture de l'adresse avec `user?.address` fonctionne même si l'objet `user` n'existe pas :\n\n```js run\nlet user = null;\n\nalert( user?.address ); // undefined\nalert( user?.address.street ); // undefined\n```\n\nRemarque : la syntaxe `?.` rend facultative la valeur qui la précède, mais pas plus.\n\nPar exemple, dans `user?.address.street.name` le `?.` permet à `user` d'être en toute sécurité `null`/`undefined` (et renvoie `undefined` dans ce cas), mais ce n'est que pour `user`. D'autres propriétés sont accessibles de manière régulière. Si nous voulons que certaines d'entre elles soient optionnelles, alors nous devrons remplacer plus de `.` par `?.`.\n\n```warn header=\"N'abusez pas du chaînage optionnel\"\nNous ne devrions utiliser `?.` que là où il est normal que quelque chose n'existe pas.\n\nPar exemple, si selon notre logique de codage, l'objet `user` doit exister, mais que `address` est facultatif, alors nous devrions écrire `user.address?.street`, mais pas `user?.address?.street`.\n\nEnsuite, si `user` n'est pas défini, nous verrons une erreur de programmation à ce sujet et nous la corrigerons. Sinon, si nous abusons de `?.`, les erreurs de codage peuvent être réduites au silence là où cela n'est pas approprié et devenir plus difficiles à déboguer.\n```\n\n````warn header=\"La variable avant `?.` doit être déclarée\"\nS'il n'y a pas du tout de variable `user`, alors `user?.anything` déclenche une erreur :\n\n```js run\n// ReferenceError: user is not defined\nuser?.address;\n```\nLa variable doit être déclarée (par exemple `let/const/var user` ou en tant que paramètre de fonction). Le chaînage facultatif ne fonctionne que pour les variables déclarées.\n````\n\n## Court-circuit\n\nComme il a été dit précédemment, le `?.` arrête immédiatement (\"court-circuite\") l'évaluation si la partie gauche n'existe pas.\n\nAinsi, s'il y a d'autres appels de fonction ou opérations à droite de `?.`, elles ne seront pas effectuées.\n\nPar exemple :\n\n```js run\nlet user = null;\nlet x = 0;\n\nuser?.sayHi(x++); // pas de \"user\", donc l'exécution n'atteint pas l'appel sayHi et x++\n\n\nalert(x); // 0, la valeur n'est pas incrémenté\n```\n\n\n## Autres variantes : ?.(), ?.[]\n\n`?.` n'est pas un opérateur, mais une construction syntaxique particulière qui fonctionne aussi avec les appels de fonction et les crochets.\n\nPar exemple, `?.()` est utilisé pour exécuter une fonction seulement si elle existe.\n\nAinsi dans cet exemple la méthode `admin` n'existe pas pour tout le monde :\n\n```js run\nlet userAdmin = {\n  admin() {\n    alert(\"I am admin\");\n  }\n};\n\nlet userGuest = {};\n\n*!*\nuserAdmin.admin?.(); // I am admin\n*/!*\n\n*!*\nuserGuest.admin?.(); // rien ne se passe (aucune méthode de ce nom)\n*/!*\n```\n\nIci, dans les deux lignes, nous utilisons d'abord le point (`userAdmin.admin`) pour obtenir la propriété `admin`, car nous supposons que l'objet `user` existe, il peut donc être lu en toute sécurité.\n\nPuis `?.()` vérifie la partie gauche : si la fonction `admin` existe, alors elle s'exécute (c'est le cas pour `userAdmin`). Sinon (pour `userGuest`) l'évaluation s'arrête sans erreur.\n\nLa syntaxe `?.[]` fonctionne également, si nous voulons utiliser des crochets `[]` pour accéder aux propriétés au lieu du point `.`. Similaire aux cas précédents, il permet de lire en toute sécurité une propriété à partir d'un objet qui peut ne pas exister.\n\n```js run\nlet key = \"firstName\";\n\nlet user1 = {\n  firstName: \"John\"\n};\n\nlet user2 = null;\n\nalert( user1?.[key] ); // John\nalert( user2?.[key] ); // undefined\n```\n\nNous pouvons également utiliser `?.` avec `delete` :\n\n```js run\ndelete user?.name; // supprime user.name si user existe\n```\n\n```warn header=\"Nous pouvons utiliser `?.` pour lire et supprimer en toute sécurité, mais pas pour écrire\"\nLe chaînage optionnel `?.` n'a aucune utilité sur le côté gauche d'une affectation :\n\nPar exemple :\n\n```js run\n\nlet user = null;\n\nuser?.name = \"John\"; // Erreur, ne fonctionne pas\n// car il évalue : undefined = \"John\"\n```\nCe n'est tout simplement pas si intelligent.\n\n## Résumé\n\nLe chaînage optionnel '?.' a trois formes :\n\n1. `obj?.prop` -- retourne `obj.prop` si `obj` existe, sinon `undefined`.\n2. `obj?.[prop]` -- retourne `obj[prop]` si `obj` existe, sinon `undefined`.\n3. `obj.method?.()` -- appel `obj.method()` si `obj.method` existe, sinon retourne `undefined`.\n\nComme nous pouvons le voir, tous sont simples et simples à utiliser. Le `?.` vérifie la partie gauche pour `null`/`undefined` et permet à l'évaluation de se poursuivre si ce n'est pas le cas.\n\nUne chaîne de `?.` permet d'accéder en toute sécurité aux propriétés imbriquées.\n\nNéanmoins, nous devons appliquer `?.` avec précaution, uniquement là où il est acceptable, selon la logique de notre code, que la partie gauche n'existe pas. Pour qu'il ne nous cache pas les erreurs de programmation, si elles se produisent.\n"
  },
  {
    "path": "1-js/04-object-basics/08-symbol/article.md",
    "content": "\n# Type symbole\n\nPar spécification, seuls deux types primitifs peuvent servir de clés de propriété d'objet :\n\n- type string, ou\n- type symbol.\n\nSinon, si l'on utilise un autre type, tel que nombre, il est automatiquement converti en chaîne de caractères. Ainsi, `obj[1]` est identique à `obj[\"1\"]`, et `obj[true]` est identique à `obj[\"true\"]`.\n\nJusqu'à présent, nous n'utilisions que des chaînes de caractères.\n\nExplorons maintenant les symboles, voyons ce qu'ils peuvent faire pour nous.\n\n## Symboles\n\nUn “Symbol” représente un identifiant unique.\n\nUne valeur de ce type peut être créée en utilisant `Symbol()` :\n\n```js\nlet id = Symbol();\n```\n\nLors de la création, nous pouvons donner au symbole une description (également appelée nom de symbole), particulièrement utile pour le débogage :\n\n```js\n// id est un symbole avec la description \"id\"\nlet id = Symbol(\"id\");\n```\n\nLes symboles sont garantis d'être uniques. Même si nous créons beaucoup de symboles avec la même description, ce sont des valeurs différentes. La description est juste une étiquette qui n’affecte rien.\n\nPar exemple, voici deux symboles avec la même description -- ils ne sont pas égaux :\n\n```js run\nlet id1 = Symbol(\"id\");\nlet id2 = Symbol(\"id\");\n\n*!*\nalert(id1 == id2); // false\n*/!*\n```\n\nSi vous connaissez Ruby ou un autre langage qui comporte également une sorte de \"symboles\", attention à ne pas vous tromper. Les symboles JavaScript sont différents.\n\nDonc, pour résumer, un symbole est une \"valeur unique primitive\" avec une description facultative. Voyons où nous pouvons les utiliser.\n\n````warn header=\"Les symboles ne se convertissent pas automatiquement en chaîne de caractères\"\nLa plupart des valeurs de JavaScript prennent en charge la conversion implicite en chaîne de caractères. Par exemple, nous pouvons `alert` presque toutes les valeurs et cela fonctionnera. Les symboles sont spéciaux. Ils ne se convertissent pas automatiquement.\n\nPar exemple, cette `alert` affichera une erreur :\n\n```js run\nlet id = Symbol(\"id\");\n*!*\nalert(id); // TypeError: Impossible de convertir une valeur de symbole en chaîne de caractères\n*/!*\n```\nC'est un \"gardien du langage\" contre les erreurs, parce que les chaînes de caractères et les symboles sont fondamentalement différents et ne doivent accidentellement pas être convertis les uns en les autres.\n\nSi nous voulons vraiment afficher un symbole, nous devons appeler `.toString()` dessus, comme ici :\n\n```js run\nlet id = Symbol(\"id\");\n*!*\nalert(id.toString()); // Symbol(id), maintenant ça marche\n*/!*\n```\nOu récupérer la propriété  `symbol.description` pour afficher la description uniquement :\n\n```js run\nlet id = Symbol(\"id\");\n*!*\nalert(id.description); // id\n*/!*\n```\n\n````\n\n## Propriétés \"cachées\"\n\nLes symboles nous permettent de créer des propriétés \"cachées\" d'un objet, qu'aucune autre partie du code ne peut accéder accidentellement ou écraser.\n\nPar exemple, si nous travaillons avec des objets `user` qui appartiennent à un code tiers, nous aimerions leur ajouter des identificateurs.\n\nUtilisons une clé symbole pour cela :\n\n```js run\nlet user = { // belongs to another code\n  name: \"John\"\n};\n\nlet id = Symbol(\"id\");\n\nuser[id] = 1;\n\nalert( user[id] ); // nous pouvons accéder aux données en utilisant le symbole comme clé\n```\n\nQuel est l’avantage de l’utilisation de `Symbol(\"id\")` sur une chaîne de caractères `\"id\"` ?\n\nPoussons un peu plus loin l’exemple pour voir cela.\n\nComme les objets `user` appartiennent à une autre base de code, il n'est pas sûr de leur ajouter des champs, car nous pourrions affecter le comportement prédéfini dans cette autre base de code. Cependant, les symboles ne peuvent pas être accédés accidentellement. Le code tiers ne sera pas conscient des symboles nouvellement définis, il est donc prudent d'ajouter des symboles aux objets `user`.\n\nImaginez qu'un autre script veuille avoir son propre identifiant à l'intérieur de `user`, pour sa propre utilisation.\n\nEnsuite, ce script peut créer son propre `symbol(\"id\")`, comme ceci :\n\n```js\n// ...\nlet id = Symbol(\"id\");\n\nuser[id] = \"Their id value\";\n```\n\nIl n'y aura pas de conflit entre nos identificateurs et les leurs, car les symboles sont toujours différents, même s'ils portent le même nom.\n\nNotez que si nous utilisions une chaîne de caractère `\"id\"` au lieu d'un symbole dans le même but, il y *aurait* un conflit :\n\n\n```js\nlet user = { name: \"John\" };\n\n// Notre script utilise la propriété \"id\"\nuser.id = \"Our id value\";\n\n// ...Un autre script veut aussi \"id\" pour ses besoins …\n\nuser.id = \"Their id value\"\n// Boom! écrasé par un autre script!\n```\n\n### Symboles dans un objet littéral\n\nSi nous voulons utiliser un symbole dans un objet littéral `{...}`, nous avons besoin de crochets.\n\nComme ceci :\n\n```js\nlet id = Symbol(\"id\");\n\nlet user = {\n  name: \"John\",\n*!*\n  [id]: 123 // pas \"id\": 123\n*/!*\n};\n```\nC’est parce que nous avons besoin de la valeur de la variable `id` comme clé, pas de la chaîne de caractères \"id\".\n\n### Les symboles sont ignorés par for…in\n\nLes propriétés symboliques ne participent pas à la boucle `for..in`.\n\nPar exemple :\n\n```js run\nlet id = Symbol(\"id\");\nlet user = {\n  name: \"John\",\n  age: 30,\n  [id]: 123\n};\n\n*!*\nfor (let key in user) alert(key); // name, age (pas de symboles)\n*/!*\n\n// l'accès direct par le symbole fonctionne\nalert( \"Direct: \" + user[id] ); // Direct: 123\n```\n\n[Object.keys(user)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys) les ignore également. Cela fait partie du principe général du \"dissimulation des propriétés symboliques\". Si un autre script ou une bibliothèque parcourt notre objet, il n’accédera pas de manière inattendue à une propriété symbolique.\n\nEn revanche, [Object.assign](mdn:js/Object/assign) copie les propriétés de chaîne de caractères et de symbole :\n\n```js run\nlet id = Symbol(\"id\");\nlet user = {\n  [id]: 123\n};\n\nlet clone = Object.assign({}, user);\n\nalert( clone[id] ); // 123\n```\n\nIl n’y a pas de paradoxe ici. C'est par conception. L'idée est que lorsque nous clonons un objet ou que nous fusionnons des objets, nous souhaitons généralement que *toutes* les propriétés soient copiées (y compris les symboles tels que `id`).\n\n## Symboles globaux\n\nComme nous l’avons vu, habituellement tous les symboles sont différents, même s’ils portent les mêmes noms. Mais parfois, nous voulons que les symboles portant le même nom soient les mêmes entités. Par exemple, différentes parties de notre application veulent accéder au symbole `\"id\"` qui signifie exactement la même propriété.\n\nPour cela, il existe un *registre de symboles global*. Nous pouvons créer des symboles et y accéder ultérieurement, ce qui garantit que les accès répétés portant le même nom renvoient exactement le même symbole.\n\nPour lire (créer en cas d'absence) un symbole du registre, utilisez `Symbol.for(key)`.\n\nCet appel vérifie le registre global et, s’il existe un symbole décrit comme `key`, le renvoie, sinon il crée un nouveau symbole `Symbol(key)` et le stocke dans le registre avec la `key` donnée.\n\nPar exemple :\n\n```js run\n// lit le registre global\nlet id = Symbol.for(\"id\"); // si le symbole n'existait pas, il est créé\n\n// relit le registre (peut-être à partir d'une autre partie du code)\nlet idAgain = Symbol.for(\"id\");\n\n// le même symbole\nalert( id === idAgain ); // true\n```\n\nLes symboles à l'intérieur de ce registre sont appelés *symboles globaux*. Si nous voulons un symbole à l’échelle de l’application, accessible partout dans le code, c’est ce moyen que nous allons utiliser.\n\n```smart header=\"Cela ressemble à Ruby\"\nDans certains langages de programmation, comme Ruby, il existe un seul symbole par nom.\n\nComme nous pouvons le constater, en JavaScript, c’est vrai pour les symboles globaux.\n```\n\n### Symbol.keyFor\n\nNous avons vu que pour les symboles globaux, `Symbol.for(key)` renvoie un symbole par son nom. Pour faire le contraire -- retourner un nom par symbole global -- nous pouvons utiliser : `Symbol.keyFor(sym)` :\n\nPar exemple :\n\n```js run\n// get symbol by name\nlet sym = Symbol.for(\"name\");\nlet sym2 = Symbol.for(\"id\");\n\n// obtenir le nom par symbole\nalert( Symbol.keyFor(sym) ); // name\nalert( Symbol.keyFor(sym2) ); // id\n```\n\n`Symbol.keyFor` utilise en interne le registre de symboles global pour rechercher la clé du symbole. Donc, cela ne fonctionne pas pour les symboles non globaux. Si le symbole n’est pas global, il ne pourra pas le trouver et retournera `undefined`.\n\nCela dit, tous les symboles ont la propriété `description`.\n\nPar exemple :\n\n```js run\nlet globalSymbol = Symbol.for(\"name\");\nlet localSymbol = Symbol(\"name\");\n\nalert( Symbol.keyFor(globalSymbol) ); // name, global symbol\nalert( Symbol.keyFor(localSymbol) ); // undefined, not global\n\nalert( localSymbol.description ); // name\n```\n\n## System symbols\n\nIl existe de nombreux \"systèmes\" symboles que JavaScript utilise en interne et que nous pouvons utiliser pour affiner divers aspects de nos objets.\n\nIls sont listés dans la documentation [Well-known symbols](https://tc39.github.io/ecma262/#sec-well-known-symbols) :\n\n- `Symbol.hasInstance`\n- `Symbol.isConcatSpreadable`\n- `Symbol.iterator`\n- `Symbol.toPrimitive`\n- Etc.\n\nPar exemple, `Symbol.toPrimitive` nous permet de décrire une conversion d’objet en primitive. Nous verrons son utilisation très bientôt.\n\nNous nous familiariserons également avec d’autres symboles lorsque nous étudierons les caractéristiques du langage correspondantes.\n\n## Résumé\n\n`Symbol` est un type primitif pour les identificateurs uniques.\n\nLes symboles sont créés avec l'appel `Symbol()` ainsi qu'une description facultative.\n\nLes symboles sont toujours de valeurs différentes, même s'ils portent le même nom. Si nous voulons que les symboles portant le même nom soient égaux, nous devons utiliser le registre global : `Symbol.for(key)` renvoie (crée si nécessaire) un symbole global avec `key` comme nom.\nLes multiples appels de `Symbol.for` avec la même `key` renvoient exactement le même symbole.\n\nLes symboles ont deux principaux cas d'utilisation :\n\n1. Propriétés d'objet \"masquées\".\n    Si nous voulons ajouter une propriété à un objet qui \"appartient\" à un autre script ou à une librairie, nous pouvons créer un symbole et l'utiliser comme clé de propriété. Une propriété symbolique n’apparait pas dans `for..in`, elle ne sera donc pas traitée accidentellement avec d'autres propriétés. De plus, elle ne sera pas accessible directement, car un autre script n’a pas notre symbole. Ainsi, la propriété sera protégée contre une utilisation accidentelle ou un écrasement.\n\n    Ainsi, nous pouvons \"dissimuler\" quelque chose dans des objets dont nous avons besoin, mais que les autres ne devraient pas voir, en utilisant des propriétés symboliques.\n\n2. De nombreux symboles système utilisés par JavaScript sont accessibles en tant que `Symbol.*`. Nous pouvons les utiliser pour modifier certains comportements internes. Par exemple, plus tard dans le tutoriel, nous utiliserons `Symbol.iterator` pour [iterables](info:iterable), `Symbol.toPrimitive`, etc.\n\nTechniquement, les symboles ne sont pas cachés à 100%. Il y a une méthode intégrée [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) qui nous permet d’obtenir tous les symboles. Il y a aussi une méthode nommée [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) qui renvoie *toutes* les clés d'un objet, y compris celles symboliques. Donc, ils ne sont pas vraiment cachés. Mais la plupart des bibliothèques, fonctions intégrées et structures de syntaxe n'utilisent pas ces méthodes.\n"
  },
  {
    "path": "1-js/04-object-basics/09-object-toprimitive/article.md",
    "content": "# Conversion d'objet en primitive\n\nQue se passe-t-il lorsque des objets sont ajoutés `obj1 + obj2`, soustraits `obj1 - obj2` ou affichés à l'aide de `alert(obj)` ?\n\nJavaScript ne permet pas de personnaliser le fonctionnement des opérateurs sur les objets. Contrairement à certains autres langages de programmation, tels que Ruby ou C++, nous ne pouvons pas implémenter une méthode objet spéciale pour gérer un ajout (ou d'autres opérateurs).\n\nDans le cas de telles opérations, les objets sont auto-convertis en primitives, puis l'opération est effectuée sur ces primitives et aboutit à une valeur primitive.\n\nC'est une limitation importante, car le résultat de `obj1 + obj2` (ou toute autre opération mathématique) ne peut pas être un autre objet !\n\nPar exemple nous ne pouvons pas créer d'objets représentant des vecteurs ou des matrices (ou des réalisations ou autre), les ajouter et s'attendre à un objet \"sommé\" comme résultat. De telles prouesses architecturales sont automatiquement \"hors jeu\".\n\nDonc, parce que nous ne pouvons pas faire grand-chose ici, il n'y a pas de maths avec des objets dans de vrais projets. Lorsque cela se produit, c'est généralement à cause d'une erreur de programmation.\n\nDans ce chapitre, nous verrons comment un objet se convertit en primitive et comment le personnaliser.\n\nNous avons deux objectifs :\n\n1. Cela nous permettra de comprendre ce qui se passe en cas d'erreur de programmation, lorsqu'une telle opération s'est produite accidentellement.\n2. Il existe des exceptions, où de telles opérations sont possibles et semblent bonnes. Par exemple, soustraire ou comparer des dates (objets `Date`). Nous les verrons plus tard.\n\n## Règles de conversion\n\nDans le chapitre <info:type-conversions> nous avons vu les règles de conversion des types primitifs numériques, chaînes de caractères et booléens. Mais nous avions mis de côté les objets. Maintenant que nous connaissons les méthodes et les symboles, il devient possible de l'aborder.\n\n1. Il n'y a pas de conversion en booléen. Tous les objets sont \"true\" dans un contexte booléen, aussi simple que cela. Il n'existe que des conversions numériques et de chaînes de caractères.\n2. La conversion numérique se produit lorsque nous soustrayons des objets ou appliquons des fonctions mathématiques. Par exemple, les objets `Date` (à traiter dans le chapitre <info:date>) peut être soustrait et le résultat de `date1 - date2` est la différence de temps entre deux dates.\n3. En ce qui concerne la conversion de chaîne de caractères, cela se produit généralement lorsque nous affichons un objet tel que `alert(obj)` et dans des contextes similaires.\n\nNous pouvons implémenter nous-mêmes la conversion de chaînes de caractères et de chiffres, en utilisant des méthodes d'objet spéciales.\n\nPassons maintenant aux détails techniques, car c'est le seul moyen d'aborder le sujet en profondeur.\n\n## Hints\n\nComment JavaScript décide-t-il quelle conversion appliquer ?\n\nIl existe trois variantes de conversion de type, qui se produisent dans diverses situations. Ils sont appelés \"hints\" (indices), comme décrit dans la [spécification](https://tc39.github.io/ecma262/#sec-toprimitive) :\n\nIl existe trois variantes de conversion de type, appelées \"hints\", décrites dans la [specification](https://tc39.github.io/ecma262/#sec-toprimitive) :\n\n**`\"string\"`**\n\nPour une conversion d'un objet vers une chaîne de caractères, lorsque nous effectuons une opération sur un objet qui attend une chaîne, comme `alert` :\n\n```js\n// output\nalert(obj);\n\n// utiliser un objet comme clé de propriété\nanotherObj[obj] = 123;\n```\n\n**`\"number\"`**\n\nPour une conversion d'objet en nombre, comme lorsque nous faisons des calculs :\n\n```js\n// conversion explicite\nlet num = Number(obj);\n\n// maths (except binary plus)\nlet n = +obj; // unary plus\nlet delta = date1 - date2;\n\n// comparaison supérieur/inférieur\nlet greater = user1 > user2;\n```\n\nLa plupart des fonctions mathématiques intégrées comprennent également ce type de conversion.\n\n**`\"default\"`**\n\nSe produit dans de rares cas où l'opérateur n'est \"pas sûr\" du type auquel il doit s'attendre.\n\nPar exemple, le plus binaire `+` peut fonctionner à la fois avec des chaînes de caractères (les concaténer) et des nombres (les ajouter). Donc, si le plus binaire obtient un objet sous forme d'argument, il utilise le hint `\"default\"` pour le convertir.\n\nEn outre, si un objet est comparé à l'aide de `==` avec une chaîne de caractères, un nombre ou un symbole, il est également difficile de savoir quelle conversion doit être effectuée, par conséquent l'indicateur `\"default\"` est utilisé.\n\n```js\n// binary plus uses the \"default\" hint\nlet total = obj1 + obj2;\n\n// obj == number uses the \"default\" hint\nif (user == 1) { ... };\n```\n\nLes opérateurs de comparaison supérieurs et inférieurs, tels que `<` et `>`, peuvent également fonctionner avec des chaînes de caractères et des nombres. Néanmoins, ils utilisent l'indicateur `\"number\"`, pas `default`. C'est pour des raisons historiques.\n\nEn pratique cependant, les choses sont un peu plus simples.\n\nTous les objets intégrés à l'exception d'un cas (objet `Date`, nous l'apprendrons plus tard) implémentent la conversion `\"default\"` de la même manière que `\"number\"`. Et nous devrions probablement faire de même.\n\nPourtant, il est important de connaître les 3 hints, nous verrons bientôt pourquoi.\n\n**Pour effectuer la conversion, JavaScript essaie de trouver et d'appeler trois méthodes d'objet :**\n\n1. Appeler la méthode `obj[Symbol.toPrimitive](hint)` avec la clé symbolique `Symbol.toPrimitive` (symbole système), si une telle méthode existe.\n2. Sinon, si l'indice est `\"string\"`, essaie d'appeler `obj.toString()` puis `obj.valueOf()`, selon ce qui existe.\n3. Sinon, si l'indice est `\"number\"` ou `\"default\"`, essaie d'appeler `obj.valueOf()` puis `obj.toString()`, selon ce qui existe.\n\n## Symbol.toPrimitive\n\nCommençons par la première méthode. Il existe un symbole intégré appelé `Symbol.toPrimitive` qui devrait être utilisé pour nommer la méthode de conversion, comme ceci :\n\n```js\nobj[Symbol.toPrimitive] = function(hint) {\n  // voici le code pour convertir cet objet en une primitive\n  // il doit retourner une valeur primitive\n  // hint = \"string\" ou \"number\" ou \"default\"\n};\n```\n\nSi la méthode `Symbol.toPrimitive` existe, elle est utilisée pour tous les hints et aucune autre méthode n'est nécessaire.\n\nPar exemple, ici l'objet `user` l'implémente :\n\n```js run\nlet user = {\n  name: \"John\",\n  money: 1000,\n\n  [Symbol.toPrimitive](hint) {\n    alert(`hint: ${hint}`);\n    return hint == \"string\" ? `{name: \"${this.name}\"}` : this.money;\n  }\n};\n\n// conversions demo:\nalert(user); // hint: string -> {name: \"John\"}\nalert(+user); // hint: number -> 1000\nalert(user + 500); // hint: default -> 1500\n```\n\nComme on peut le voir d'après le code, `user` devient une chaîne de caractères auto-descriptive ou un montant d'argent en fonction de la conversion. La méthode unique `user[Symbol.toPrimitive]` gère tous les cas de conversion.\n\n## toString / valueOf\n\nS'il n'y a pas de `Symbol.toPrimitive` alors JavaScript essaie de trouver les méthodes `toString` et `valueOf` :\n\n- Pour l'indice `\"string\"` : appel à la méthode `toString`, et si elle n'existe pas ou si elle renvoie un objet au lieu d'une valeur primitive, alors appel à `valueOf` (donc `toString` a la priorité pour la conversion  de chaînes de caractères).\n- Pour les autres hints : appel à `valueOf`, et s'il n'existe pas ou s'il renvoie un objet au lieu d'une valeur primitive, alors appel à `toString` (donc `valueOf` a la priorité pour les mathématiques).\n\nLes méthodes `toString` et `valueOf` viennent des temps anciens. Ce ne sont pas des symboles (les symboles n'existaient pas il y a si longtemps), mais plutôt des méthodes nommées par des chaînes de caractères \"normales\". Ils fournissent une alternative \"à l'ancienne\" pour implémenter la conversion.\n\nCes méthodes doivent renvoyer une valeur primitive. Si `toString` ou `valueOf` renvoie un objet, il est ignoré (comme s'il n'y avait pas de méthode).\n\nPar défaut, un objet brut a les méthodes `toString` et `valueOf` suivantes :\n\n- La méthode `toString` renvoie une chaîne de caractères `\"[object Object]\"`.\n- La méthode `valueOf` renvoie l'objet en question.\n\nVoici la démo :\n\n```js run\nlet user = {name: \"John\"};\n\nalert(user); // [object Object]\nalert(user.valueOf() === user); // true\n```\n\nDonc, si nous essayons d'utiliser un objet en tant que chaîne de caractères, comme dans un `alert` ou autre chose, nous voyons par défaut `[object Object]`.\n\nEt la valeur par défaut `valueOf` n'est mentionnée ici que par souci d'exhaustivité, afin d'éviter toute confusion. Comme vous pouvez le constater, l'objet lui-même est renvoyé et est donc ignoré. Ne me demandez pas pourquoi, c'est pour des raisons historiques. Nous pouvons donc supposer que cela n'existe pas.\n\nImplémentons ces méthodes pour personnaliser la conversion.\n\nPar exemple, ici, `user` fait la même chose que ci-dessus en combinant `toString` et `valueOf` au lieu de `Symbol.toPrimitive` :\n\n```js run\nlet user = {\n  name: \"John\",\n  money: 1000,\n\n  // for hint=\"string\"\n  toString() {\n    return `{name: \"${this.name}\"}`;\n  },\n\n  // pour hint=\"number\" ou \"default\"\n  valueOf() {\n    return this.money;\n  }\n\n};\n\nalert(user); // toString -> {name: \"John\"}\nalert(+user); // valueOf -> 1000\nalert(user + 500); // valueOf -> 1500\n```\n\nComme on peut le constater, le comportement est identique à celui de l'exemple précédent avec `Symbol.toPrimitive`.\n\nNous voulons souvent un seul endroit \"fourre-tout\" pour gérer toutes les conversions primitives. Dans ce cas, nous pouvons implémenter `toString` uniquement, comme ceci :\n\n```js run\nlet user = {\n  name: \"John\",\n\n  toString() {\n    return this.name;\n  }\n};\n\nalert(user); // toString -> John\nalert(user + 500); // toString -> John500\n```\n\nEn l'absence de `Symbol.toPrimitive` et de `valueOf`, `toString` gérera toutes les conversions primitives.\n\n### Une conversion peut renvoyer n'importe quel type primitif\n\nLa chose importante à savoir sur toutes les méthodes de conversion de primitives est qu'elles ne renvoient pas nécessairement la primitive \"hinted\".\n\nIl n'y a pas de control pour vérifier si `toString` renvoie exactement une chaîne de caractères ou si la méthode `Symbol.toPrimitive` renvoie un nombre pour le hint `\"number\"`.\n\n**La seule chose obligatoire : ces méthodes doivent renvoyer une primitive, pas un objet.**\n\n```smart header=\"Notes historiques\"\nPour des raisons historiques, si `toString` ou `valueOf` renvoie un objet, il n’y a pas d’erreur, mais cette valeur est ignorée (comme si la méthode n’existait pas). C’est parce que jadis, il n’existait pas de bon concept \"d’erreur\" en JavaScript.\n\nEn revanche, `Symbol.toPrimitive` est plus strict, il *doit* retourner une primitive, sinon il y aura une erreur.\n```\n\n## Autres conversions\n\nComme nous le savons déjà, de nombreux opérateurs et fonctions effectuent des conversions de types, par exemple la multiplication `*` convertit les opérandes en nombres.\n\nSi nous passons un objet en argument, il y a deux étapes de  calcul :\n\n1. L'objet est converti en primitive (en utilisant les règles décrites ci-dessus).\n2. Si cela est nécessaire pour d'autres calculs, la primitive résultante est également convertie.\n\nPar exemple :\n\n```js run\nlet obj = {\n  // toString gère toutes les conversions en l'absence d'autres méthodes\n  toString() {\n    return \"2\";\n  }\n};\n\nalert(obj * 2); // 4, objet converti en primitive \"2\", puis la multiplication le transforme en un nombre\n```\n\n1. La multiplication `obj * 2` convertit d'abord l'objet en primitive (cela devient une chaîne de caractère `\"2\"`).\n2. Ensuite `\"2\" * 2` devient `2 * 2` (la chaîne de caractères est convertie en nombre).\n\nLe plus binaire `+` va concaténer des chaînes de caractères dans la même situation, car il accepte volontiers une chaîne de caractères :\n\n```js run\nlet obj = {\n  toString() {\n    return \"2\";\n  }\n};\n\nalert(obj + 2); // 22 (\"2\" + 2), la conversion en primitive a renvoyé une chaîne de caractères => concaténation\n```\n\n## Résumé\n\nLa conversion objet à primitive est appelée automatiquement par de nombreuses fonctions intégrées et opérateurs qui attendent une primitive en tant que valeur.\n\nIl en existe 3 types (hints) :\n\n- `\"string\"` (pour `alert` et d'autres opérations qui nécessitent une chaîne de caractères)\n- `\"number\"` (pour des maths)\n- `\"default\"` (peu d'opérateurs, généralement les objets l'implémentent de la même manière que `\"number\"`)\n\nLa spécification décrit explicitement quel opérateur utilise quel hint.\n\nL'algorithme de conversion est :\n\n1. Appeler `obj[Symbol.toPrimitive](hint)` si la méthode existe,\n2. Sinon, si l'indice est `\"string\"`, essaie `obj.toString()` puis `obj.valueOf()`, selon ce qui existe.\n3. Sinon, si l'indice est `\"number\"` ou `\"default\"`, essaie `obj.valueOf()` puis `obj.toString()`, selon ce qui existe.\n\nToutes ces méthodes doivent renvoyer une primitive pour fonctionner (si elle est définie).\n\nEn pratique, il suffit souvent d'implémenter uniquement `obj.toString()` comme méthode \"fourre-tout\" pour les conversions de chaînes de caractères qui devraient renvoyer une représentation \"lisible par l'homme\" d'un objet, à des fins de journalisation ou de débogage.\n"
  },
  {
    "path": "1-js/04-object-basics/index.md",
    "content": "# Objets: les bases\n"
  },
  {
    "path": "1-js/05-data-types/01-primitives-methods/1-string-new-property/solution.md",
    "content": "Essayez de lancer :\n\n```js run\nlet str = \"Hello\";\n\nstr.test = 5; // (*)\n\nalert(str.test);\n```\n\nSelon que vous utilisiez `use strict` ou non, le résultat peut être :\n\n1. `undefined` (pas de mode strict)\n2. une erreur (mode strict)\n\nPourquoi ? Répétons ce qui se pase à la ligne `(*)`:\n\n1. Lorsqu'on accède à une propiété de `str`, un \"wrapper d'objet\" (conteneur) est créé.\n2. En mode strict, l'écriture à l'intérieur est une erreur.\n3. Sinon, l'opération avec la propriété est poursuivie, l'objet obtient la propriété `test`. Mais après cela, \"l'objet wrapper\" disparaît, de sorte que dans la dernière ligne, `str` n'a aucune trace de la propriété `test`.\n\n**Cet exemple montre clairement que les primitives ne sont pas des objets.**\n\nIls ne peuvent pas stocker de données supplémentaires.\n"
  },
  {
    "path": "1-js/05-data-types/01-primitives-methods/1-string-new-property/task.md",
    "content": "importance: 5\n\n---\n\n# Pouvons-nous ajouter une propiété à une primitive ?\n\nConsidérons le code suivant:\n\n```js\nlet str = \"Hello\";\n\nstr.test = 5;\n\nalert(str.test);\n```\n\nPensez-vous que ça va fonctionner ? Qu'est-ce qui sera affiché ?\n"
  },
  {
    "path": "1-js/05-data-types/01-primitives-methods/article.md",
    "content": "# Méthodes des primitives\n\nJavaScript nous permet de travailler avec des primitives (chaînes de caractères, nombres, etc.) comme s'il s'agissait d'objets. Ils prévoient également des méthodes pour les appeler en tant que tel. Nous étudierons cela très bientôt, mais nous verrons d'abord comment cela fonctionne car, bien entendu, les primitives ne sont pas des objets (et nous allons rendre cela plus clair).\n\nExaminons les principales différences entre primitives et objets.\n\nUne primitive\n\n- Est une valeur de type primitif.\n- Il existe 7 types primitifs : `string`, `number`, `bigint`, `boolean`, `symbol`, `null` et `undefined`.\n\nUn objet\n\n- Est capable de stocker plusieurs valeurs en tant que propriétés.\n- Peut être créé avec `{}`, par exemple : `{name: \"John\", age: 30}`. Il existe d'autres types d'objets en JavaScript. Les fonctions, par exemple, sont des objets.\n\nL'une des meilleurs choses à propos des objets est que nous pouvons stocker une fonction en tant que l'une de ses propriétés.\n\n```js run\nlet john = {\n  name: \"John\",\n  sayHi: function() {\n    alert(\"Hi buddy!\");\n  }\n};\n\njohn.sayHi(); // Hi buddy!\n```\n\nNous avons donc crée un objet `john` avec la méthode `sayHI`.\n\nDe nombreux objets intégrés existent déjà, tels que ceux qui fonctionnent avec des dates, des erreurs, des éléments HTML, etc. Ils ont des propriétés et des méthodes différente.\n\nMais, ces fonctionnalités ont un coût !\n\nLes objets sont \"plus lourds\" que les primitives. Ils ont besoin de ressources supplémentaires pour soutenir le mécanisme interne.\n\n## Une primitive en tant qu'objet\n\nVoici le paradoxe auquel est confronté le créateur de JavaScript :\n\n- Il y a beaucoup de choses que l'on voudrait faire avec une primitive telle qu'une chaîne de caractères ou un nombre. Ce serait génial d'y avoir accès avec des méthodes.\n- Les primitives doivent être aussi rapides et légères que possible.\n\nLa solution semble peu commode, mais la voici :\n\n1. Les primitives sont toujours primitives. Une seule valeur, au choix.\n2. Le langage permet d'accéder aux méthodes et aux propriétés des chaînes de caractères, des nombres, des booléens et des symboles.\n3. Pour que cela fonctionne, un \"wrapper d'objet\" (conteneur)  spécial est crée pour fournir la fonctionnalité supplémentaire, puis il est détruit.\n\nLes \"wrapper d'objets\" (conteneurs) sont différents pour chaque type de primitive et sont appelés `String`, `Number`, `Boolean` et `Symbol`. Ainsi, ils fournissent différents ensembles de méthodes.\n\nPar exemple, il existe une méthode de string [str.toUpperCase()](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) qui renvoie une chaîne de caractères `str` en majuscule.\n\nVoici comment ça fonctionne:\n\n```js run\nlet str = \"Hello\";\n\nalert( str.toUpperCase() ); // HELLO\n```\n\nSimple, non? Voici ce qui se passe réellement dans `str.toUpperCase()`:\n\n1. La chaîne de caractères `str` est une primitive. Ainsi, au moment d'accéder à sa propriété, un objet spécial est crée, qui connaît la valeur de la chaîne de caractères et possède des méthodes utiles, comme `toUpperCase()`.\n2. Cette méthode s'exécute et retourne une nouvelle chaîne de caractères (indiquée par `alert`).\n3. L'objet spécial est détruit, laissant le primitif `str` seul.\n\nLes primitives peuvent donc fournir des méthodes, mais elles restent légères.\n\nLe moteur JavaScript optimise fortement ce processus. Il peut même ignorer la création de l'objet supplémentaire. Mais il doit toujours adhérer à la spécification et se comporter comme s'il en crée un.\n\nUn nombre a ses propres méthodes, par exemple, [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) arrondit le nombre à la précision indiquée :\n\n```js run\nlet n = 1.23456;\n\nalert( n.toFixed(2) ); // 1.23\n```\n\nNous verrons des méthodes plus spécifiques dans les chapitres [Nombres](https://javascript.info/number) et [Chaînes de caractères](https://javascript.info/string).\n\n````warn header=\"Les constructeurs `String`, `Number` et `Boolean` sont réservés à un usage interne.\"\nCertains langages comme Java nous permettent de créer des \"wrapper d'objet\" (conteneur) pour les primitives en utilisant une syntaxe telle que `new Number(1)` ou `new Boolean(false)`.\n\nEn JavaScript, cela est également possible pour des raisons historique, mais fortement **déconseillé**. Cela peut très vite se compliquer à plusieurs endroits.\n\nPar exemple :\n\n```js run\nalert( typeof 0 ); // \"number\"\n\nalert( typeof new Number(0) ); // \"object\"!\n```\n\nLes objets sont toujours vrais dans les `if`, alors l'alerte apparaîtra ici :\n\n```js run\nlet zero = new Number(0);\n\nif (zero) { // zéro est vrai, parce que c'est un objet\n  alert( \"zero is truthy!?!\" );\n}\n```\n\nPar ailleurs, utiliser les mêmes fonctions `String`, `Number` et `Boolean` sans `new` est une chose totalement valide et même recommandée. Ils convertissent une valeur dans le type correspondant : une chaîne de caractères, un nombre ou un booléen (primitive).\n\nPar exemple, ceci est entièrement valide :\n\n```js\nlet num = Number(\"123\"); // convertir une chaîne de caractères en nombre\n```\n````\n\n````warn header=\"`null` / `undefined` n'ont pas de méthode\"\nLes primitives spéciales `null` et `undefined` sont des exceptions. Elles n'ont pas de \"wrapper d'objet\" (conteneur) correspondants et ne fournissent aucune méthode. En un sens, elles sont \"les plus primitives\".\n\nUne tentative d'accès à une propriété d'une telle valeur donnerait l'erreur suivante:\n\n```js run\nalert(null.test); // error\n```\n````\n\n## Résumé\n\n- Les primitives sauf `null` et `undefined` fournissent de nombreuses méthodes utiles. Nous étudierons cela dans les prochains chapitres.\n- Officiellement, ces méthodes fonctionnent via des objets temporaires, mais les moteurs JavaScript sont bien ajustés pour optimiser cela en interne, elles ne sont donc pas coûteuses à appeler.\n"
  },
  {
    "path": "1-js/05-data-types/02-number/1-sum-interface/solution.md",
    "content": "```js run demo\nlet a = +prompt(\"Le premier numéro?\", \"\");\nlet b = +prompt(\"Le second numéro?\", \"\");\n\nalert( a + b );\n```\n\nNotez le plus unaire `+` avant le `prompt`. Il convertit immédiatement la valeur en nombre.\n\nSinon, `a` et `b` seraient des `string` et leur somme serait leur concaténation, c'est à dire: `\"1\" + \"2\" = \"12\"`.\n"
  },
  {
    "path": "1-js/05-data-types/02-number/1-sum-interface/task.md",
    "content": "importance: 5\n\n---\n\n# Somme des nombres du visiteur\n\nCréez un script qui invite le visiteur à entrer deux nombres, puis affiche leur somme.\n\n[Lancer la démo]\n\nP.S. Il y a un piège avec les types.\n"
  },
  {
    "path": "1-js/05-data-types/02-number/2-why-rounded-down/solution.md",
    "content": "En interne, la fraction décimale `6.35` est un nombre binaire sans fin. Comme toujours dans de tels cas, il est stocké avec une perte de précision.\n\nVoyons cela :\n\n```js run\nalert( 6.35.toFixed(20) ); // 6.34999999999999964473\n```\n\nLa perte de précision peut causer à la fois une augmentation et une diminution d'un nombre. Dans ce cas particulier, le nombre diminue un peu, c'est pourquoi il a été arrondi à 6.3.\n\nEt quand est-il de `1.35` ?\n\n```js run\nalert( 1.35.toFixed(20) ); // 1.35000000000000008882\n```\n\nIci, la perte de précision rend le nombre un peu plus grand, c'est pourquoi il a été arrondi à 1.4.\n\n**Comment pouvons-nous résoudre le problème avec `6.35` si nous voulons qu'il soit arrondi correctement ?**\n\nNous devons le rapprocher d'un nombre entier avant d'arrondir :\n\n```js run\nalert( (6.35 * 10).toFixed(20) ); // 63.50000000000000000000\n```\n\nNotez que `63.5` n'a aucune perte de précision. C'est parce que la partie décimale `0.5` est en réalité `1/2`. Les fractions divisées par les puissances de `2` sont représentées sans perte de précision dans le système binaire, on peut maintenant les arrondir :\n\n```js run\nalert( Math.round(6.35 * 10) / 10); // 6.35 -> 63.5 -> 64(arrondi) -> 6.4\n```\n"
  },
  {
    "path": "1-js/05-data-types/02-number/2-why-rounded-down/task.md",
    "content": "importance: 4\n\n---\n\n# Pourquoi 6.35.toFixed(1) == 6.3 ?\n\nSelon la documentation, `Math.round` et `toFixed` arrondissent tous les deux au nombre le plus proche : `0..4` arrondi par défaut (à la baisse) tantdis que `5..9` arrondi par excès (à la hausse).\n\nPar exemple :\n\n```js run\nalert( 1.35.toFixed(1) ); // 1.4\n```\n\nDans l'exemple similaire ci-dessous, pourquoi est-ce que `6.35` est arrondi à `6.3` et non `6.4` ?\n\n```js run\nalert( 6.35.toFixed(1) ); // 6.3\n```\n\nComment arrondir `6.35` de la bonne manière ?\n"
  },
  {
    "path": "1-js/05-data-types/02-number/3-repeat-until-number/_js.view/solution.js",
    "content": "\nfunction readNumber() {\n  let num;\n\n  do {\n    num = prompt(\"Enter a number please?\", 0);\n  } while ( !isFinite(num) );\n\n  if (num === null || num === '') return null;\n  \n  return +num;\n}"
  },
  {
    "path": "1-js/05-data-types/02-number/3-repeat-until-number/_js.view/test.js",
    "content": "beforeEach(function() {\n  sinon.stub(window, \"prompt\");\n});\n\nafterEach(function() {\n  prompt.restore();\n});\n\ndescribe(\"readNumber\", function() {\n\n  it(\"if a number, returns it\", function() {\n    prompt.returns(\"123\");\n    assert.strictEqual(readNumber(), 123);\n  });\n\n  it(\"if 0, returns it\", function() {\n    prompt.returns(\"0\");\n    assert.strictEqual(readNumber(), 0);\n  });\n\n  it(\"continues the loop until meets a number\", function() {\n    prompt.onCall(0).returns(\"not a number\");\n    prompt.onCall(1).returns(\"not a number again\");\n    prompt.onCall(2).returns(\"1\");\n    assert.strictEqual(readNumber(), 1);\n  });\n\n  it(\"if an empty line, returns null\", function() {\n    prompt.returns(\"\");\n    assert.isNull(readNumber());\n  });\n\n  it(\"if cancel, returns null\", function() {\n    prompt.returns(null);\n    assert.isNull(readNumber());\n  });\n\n});\n"
  },
  {
    "path": "1-js/05-data-types/02-number/3-repeat-until-number/solution.md",
    "content": "```js run demo\nfunction readNumber() {\n  let num;\n\n  do {\n    num = prompt(\"Entrez un nombre s'il vous plaît\", 0);\n  } while ( !isFinite(num) );\n\n  if (num === null || num === '') return null;\n\n  return +num;\n}\n\nalert(`Read: ${readNumber()}`);\n```\n\nLa solution est un peu plus complexe qu'elle n'y paraît car nous devons gérer des lignes `null` / vides.\n\nNous répétons donc la demande jusqu'à ce qu'il s'agisse d'un \"nombre régulier\". Les lignes `null` (cancel) et vide répondent également à cette condition car, sous forme numérique, elles valent `0`.\n\nAprès que nous nous sommes arrêtés, nous devons traiter spécialement les lignes `null` et vides (retourner `null`), car les convertir en nombre renverrait `0`.\n"
  },
  {
    "path": "1-js/05-data-types/02-number/3-repeat-until-number/task.md",
    "content": "importance: 5\n\n---\n\n# Répéter jusqu'à ce que l'entrée soit un nombre\n\nCréez une fonction `readNumber` qui invite à entrer un nombre jusqu'à ce que le visiteur saisisse une valeur numérique valide.\n\nLa valeur résultante doit être renvoyée sous forme de nombre.\n\nLe visiteur peut également arrêter le processus en entrant une ligne vide ou en appuyant sur \"CANCEL\". Dans ce cas, la fonction doit renvoyer `null`.\n\n[Lancer la démo]\n"
  },
  {
    "path": "1-js/05-data-types/02-number/4-endless-loop-error/solution.md",
    "content": "C'est parce que `i` ne sera jamais exactement égal à 10.\n\nExécutez-le pour voir les valeurs *réelles* de i :\n\n```js run\nlet i = 0;\nwhile (i < 11) {\n  i += 0.2;\n  if (i > 9.8 && i < 10.2) alert( i );\n}\n```\n\nAucun d'entre eux n'est exactement `10`.\n\nDe telles choses se produisent à cause des pertes de précision lors de l'ajout des fractions comme `0.2`.\n\nConclusion : évitez les contrôles d'égalité lorsque vous travaillez avec des fractions décimales.\n"
  },
  {
    "path": "1-js/05-data-types/02-number/4-endless-loop-error/task.md",
    "content": "importance: 4\n\n---\n\n# Une boucle infinie\n\nCette boucle est infinie. Ça ne finit jamais. Pourquoi ?\n\n```js\nlet i = 0;\nwhile (i != 10) {\n  i += 0.2;\n}\n```\n"
  },
  {
    "path": "1-js/05-data-types/02-number/8-random-min-max/solution.md",
    "content": "Nous devons \"mapper\" toutes les valeurs de l'intervalle 0...1 en valeurs de `min` à `max`.\n\nCela peut être fait en deux étapes :\n\n1. Si nous multiplions un nombre aléatoire de 0...1 par `max-min`, l'intervalle des valeurs possible augmente de `0..1` à `0..max-min`.\n2. Maintenant, si nous ajoutons `min`, l'intervalle possible devient de `min` à `max`.\n\nLa fonction :\n\n```js run\nfunction random(min, max) {\n  return min + Math.random() * (max - min);\n}\n\nalert( random(1, 5) );\nalert( random(1, 5) );\nalert( random(1, 5) );\n```\n"
  },
  {
    "path": "1-js/05-data-types/02-number/8-random-min-max/task.md",
    "content": "importance: 2\n\n---\n\n# Un nombre aléatoire de min à max\n\nLa fonction intégrée `Math.random()` crée une valeur aléatoire de 0 à 1 (1 non compris).\n\nEcrivez la fonction `random(min, max)` pour générer un nombre aléatoire compris entre `min` et `max` (max non compris).\n\nQuelques exemples :\n\n```js\nalert( random(1, 5) ); // 1.2345623452\nalert( random(1, 5) ); // 3.7894332423\nalert( random(1, 5) ); // 4.3435234525\n```\n"
  },
  {
    "path": "1-js/05-data-types/02-number/9-random-int-min-max/solution.md",
    "content": "# La solution simple mais fausse\n\nLa solution la plus simple mais fausse serait de générer une valeur de `min` à `max` et de l'arrondir :\n\n```js run\nfunction randomInteger(min, max) {\n  let rand = min + Math.random() * (max - min);\n  return Math.round(rand);\n}\n\nalert( randomInteger(1, 3) );\n```\n\nLa fonction marche, mais elle est incorrecte. La probabilité d'obtenir les valeurs `min` et `max` est deux fois inférieure à toute autre.\n\nSi vous exécutez l'exemple ci-dessous plusieurs fois, vous verrez facilement que `2` apparaît le plus souvent.\n\nCela se produit car `Math.round()` obtient des nombres aléatoires à partir de l'intervalle `1..3` et les arrondit comme ici :\n\n```js no-beautify\nvalues from 1    ... to 1.4999999999  devient 1\nvalues from 1.5  ... to 2.4999999999  devient 2\nvalues from 2.5  ... to 2.9999999999  devient 3\n```\n\nMaintenant, nous pouvons clairement voir que `1` obtient deux fois moins de valeurs que `2`. Et la même chose avec `3`.\n\n# La bonne solution\n\nIl existe de nombreuses solutions correctes à la tâche. L'une d'elles consiste à ajuster les limites d'intervalle. Pour garantir les mêmes intervalles, nous pouvons générer des valeurs comprises entre `0.5` et `3.5`, ajoutant ainsi les probabilités requises :\n\n```js run\n*!*\nfunction randomInteger(min, max) {\n  // maintenant rand est entre (min-0.5) et (max+0.5)\n  let rand = min - 0.5 + Math.random() * (max - min + 1);\n  return Math.round(rand);\n}\n*/!*\n\nalert( randomInteger(1, 3) );\n```\n\nUne autre solution pourrait être d'utiliser `Math.floor` pour un nombre aléatoie compris entre `min` et `max+1`.\n\n```js run\n*!*\nfunction randomInteger(min, max) {\n  // ici rand est de min à (max+1)\n  let rand = min + Math.random() * (max + 1 - min);\n  return Math.floor(rand);\n}\n*/!*\n\nalert( randomInteger(1, 3) );\n```\n\nMaintenant, tous les intervalles sont mappés de cette façon :\n\n```js no-beautify\nvalues from 1  ... to 1.9999999999  devient 1\nvalues from 2  ... to 2.9999999999  devient 2\nvalues from 3  ... to 3.9999999999  devient 3\n```\n\nTous les intervalles ont la même longueur, rendant la distribution finale uniforme.\n"
  },
  {
    "path": "1-js/05-data-types/02-number/9-random-int-min-max/task.md",
    "content": "importance: 2\n\n---\n\n# Un entier aléatoire de min à max\n\nCréez une fonction `randomInteger(min, max)` qui génère un nombre entier aléatoire compris entre `min` et `max` (`min` et `max` inclut).\n\nTout nombre compris dans l'intervalle `min..max` doit apparaître avec la même probabilité.\n\nQuelques exemples :\n\n```js\nalert( randomInteger(1, 5) ); // 1\nalert( randomInteger(1, 5) ); // 3\nalert( randomInteger(1, 5) ); // 5\n```\n\nVous pouvez utiliser la solution de la [tâche précédente](info:task/random-min-max) comme base.\n"
  },
  {
    "path": "1-js/05-data-types/02-number/article.md",
    "content": "# Nombres\n\nEn JavaScript moderne, il existe deux types de nombres :\n\n1. Les nombres standards en JavaScript sont stockés au format 64 bits [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754), également connu sous le nom de \"nombres à virgule flottante double précision\". Ce sont des chiffres que nous utilisons le plus souvent, et nous en parlerons dans ce chapitre.\n\n2. Les nombres BigInt pour représenter des entiers de longueur arbitraire. Ils sont parfois nécessaires, car un nombre régulier ne peut pas dépasser de manière précise <code>2<sup>53</sup></code> ou être inférieur à <code>-2<sup>53</sup></code>. Comme les bigints sont utilisés dans quelques zones spéciales, nous leur consacrons un chapitre spécial <info:bigint>.\n\nNous allons donc parler ici des nombres réguliers. Développons nos connaissances à leur sujet.\n\n## Plus de façons d'écrire un nombre\n\nImaginez que nous ayons besoin d'écrire 1 milliard. Le moyen évident est :\n\n```js\nlet milliard = 1000000000;\n```\n\nNous pouvons également utiliser l’underscore `_` comme séparateur :\n\n```js\nlet billion = 1_000_000_000;\n```\n\nIci, l'underscore `_` joue le rôle de \"[sucre syntaxique](https://en.wikipedia.org/wiki/Syntactic_sugar)\", il rend le nombre plus lisible. Le moteur JavaScript ignore simplement `_` entre les chiffres, donc c'est exactement le même milliard que ci-dessus.\n\nDans la vraie vie cependant, nous essayons d'éviter d'écrire de longues séquences de zéros. Nous sommes trop paresseux pour ça. Nous essaierons d'écrire quelque chose comme \"1 milliard\" pour un milliard ou \"7,3 milliards\" pour 7 milliards 300 millions. La même chose est vraie pour la plupart des grands nombres.\n\nEn JavaScript, nous pouvons raccourcir un nombre en y ajoutant la lettre `\"e\"` et en spécifiant le nombre de zéros :\n\n```js run\nlet milliard = 1e9;  // 1 milliard, littéralement : 1 suivi de 9 zéros\n\nalert( 7.3e9 );  // 7.3 milliards (pareil que 7300000000 ou 7_300_000_000)\n```\n\nEn d'autres termes, `e` multiplie le nombre par `1` suivi du nombre de zéros spécifié.\n\n```js\n1e3 === 1 * 1000 // e3 signifie *1000\n1.23e6 === 1.23 * 1000000 // e6 signifie *1000000\n```\n\nMaintenant, écrivons quelque chose de très petit. Disons, 1 microseconde (un millionième de seconde) :\n\n```js\nlet us = 0.000001;\n```\n\nComme avant, l'utilisation de `\"e\"` peut nous aider. Si nous voulons éviter d'écrire les zéros explicitement, nous pourrions dire la même chose comme :\n\n```js\nlet us = 1e-6; // cinq zéros à gauche de 1\n```\n\nSi nous comptons les zéros dans `0.000001`, il y en a 6. Donc logiquement, c'est `1e-6`.\n\nEn d'autres termes, un nombre négatif après `\"e\"` signifie une division par 1 suivi du nombre spécifié de zéros :\n\n```js\n// -3 divise par 1 avec 3 zéros\n1e-3 === 1 / 1000; // 0.001\n\n// -6 divise par 1 avec 6 zéros\n1.23e-6 === 1.23 / 1000000; // 0.00000123\n\n// an example with a bigger number\n1234e-2 === 1234 / 100; // 12.34, decimal point moves 2 times\n```\n\n### Nombres hexadécimaux, binaires et octaux\n\nLes nombres [Hexadécimaux](https://fr.wikipedia.org/wiki/Syst%C3%A8me_hexad%C3%A9cimal) sont souvent utilisés en JavaScript pour représenter des couleurs, encoder des caractères et pour beaucoup d'autres choses. Alors, naturellement, il existe un moyen plus court de les écrire : `0x` puis le nombre.\n\nPar exemple :\n\n```js run\nalert( 0xff ); // 255\nalert( 0xFF ); // 255 (même résultat car la casse n'a pas d'importance)\n```\n\nLes systèmes numériques binaires et octaux sont rarement utilisés, mais sont également supportés avec les préfixes `0b` et `0o` :\n\n```js run\nlet a = 0b11111111; // forme binaire de 255\nlet b = 0o377; // forme octale de 255\n\nalert( a == b ); // true, car a et b valent le même nombre, soit 255\n```\n\nCependant ça ne fonctionne qu'avec ces 3 systèmes de numération. Pour les autres systèmes numérique, nous devrions utiliser la fonction `parseInt` (que nous verrons plus loin dans ce chapitre).\n\n## La méthode toString(base)\n\nLa méthode `num.toString(base)` retourne une chaîne de caractères représentant `num` dans le système numérique de la `base` donnée.\n\nPar exemple :\n\n```js run\nlet num = 255;\n\nalert( num.toString(16) ); // ff\nalert( num.toString(2) );  // 11111111\n```\n\nLa `base` peut varier de `2` à `36`. Par défaut, il s'agit de `10`.\n\nLes cas d'utilisation courants sont :\n\n- **base=16** est utilisé pour les couleurs hexadécimales, les encodages de caractères, etc. Les chiffres peuvent être `0..9` ou `A..F`.\n- **base=2** est principalement utilisé pour le débogage d'opérations binaires, les chiffres pouvant être `0` ou `1`.\n- **base=36** est le maximum, les chiffres peuvent être `0..9` ou `A..Z`. L'alphabet latin entier est utilisé pour représenter un nombre. Un cas amusant mais utile pour la base `36` consiste à transformer un identifiant numérique long en quelque chose de plus court, par exemple pour créer une URL courte. On peut simplement le représenter dans le système numérique avec base `36` :\n\n    ```js run\n    alert( 123456..toString(36) ); // 2n9c\n    ```\n\n```warn header=\"Deux points pour appeler une méthode\"\nVeuillez noter que deux points sur `123456..toString(36)` n'est pas une faute de frappe. Si nous voulons appeler une méthode directement sur un nombre, comme `toString` dans l'exemple ci-dessus, nous devons placer deux points `..` avant la méthode.\n\nSi nous plaçions un seul point : `123456.toString(36)`, il y aurait une erreur, car la syntaxe JavaScript applique la partie décimale après le premier point et il lirait `toString(36)` comme étant la partie décimale de `123456` ce qui n'est pas le cas. Si nous plaçons un point de plus, alors JavaScript sait que la partie décimale est vide et peut maintenant appliquer la méthode.\n\nIl est aussi possible d'écrire `(123456).toString(36)`.\n```\n\n## Arrondir\n\nArrondir est l'une des opérations les plus utilisées pour travailler avec des nombres.\n\nIl existe plusieurs fonctions intégrées pour arrondir :\n\n`Math.floor`\n: Arrondis vers le bas : `3.1` devient `3`, et `-1.1` devient `-2`.\n\n`Math.ceil`\n: Arrondis ver le haut : `3.1` devient `4`, et `-1.1` devient `-1`.\n\n`Math.round`\n: Arrondit à l'entier le plus proche : `3,1` devient `3`, `3,6` devient `4` et pour le cas du milieu, `3,5` est également arrondit à `4`.\n\n`Math.trunc` (non pris en charge par Internet Explorer)\n: Supprime tout ce qui suit le point décimal : `3.1` devient `3`, `-1.1` devient `-1`.\n\nVoici le tableau pour résumer les différences entre eux :\n\n|      | `Math.floor` | `Math.ceil` | `Math.round` | `Math.trunc` |\n|------|--------------|-------------|--------------|--------------|\n| `3.1`|          `3` |         `4` |          `3` |          `3` |\n| `3.6`|          `3` |         `4` |          `4` |          `3` |\n|`-1.1`|         `-2` |        `-1` |         `-1` |         `-1` |\n|`-1.6`|         `-2` |        `-1` |         `-2` |         `-1` |\n\nCes fonctions couvrent toutes les manières possibles de traiter la partie décimale d'un nombre. Mais que se passe-t-il si nous voulons arrondir le nombre à un certain chiffre après la virgule ?\n\nPar exemple, nous avons `1.2345` et voulons l'arrondir à 2 chiffres, pour obtenir seulement `1.23`.\n\nIl y a deux façons de le faire:\n\n1. Multiplier et diviser.\n\n    Par exemple, pour arrondir le nombre au 2ème chiffre après la décimale, nous pouvons multiplier le nombre par \"100\", appeler la fonction d'arrondi puis le diviser.\n\n    ```js run\n    let num = 1.23456;\n\n    alert( Math.round(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23\n    ```\n\n2. La méthode [toFixed(n)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Number/toFixed) arrondit le nombre à `n` chiffres après le point et renvoie une chaîne de caractères du résultat.\n\n    ```js run\n    let num = 12.34;\n    alert( num.toFixed(1) ); // \"12.3\"\n    ```\n\n    Ceci arrondit à la valeur la plus proche, similaire à `Math.round` :\n\n    ```js run\n    let num = 12.36;\n    alert( num.toFixed(1) ); // \"12.4\"\n    ```\n\n    Veuillez noter que le résultat de `toFixed` est une chaîne de caractères. Si la partie décimale est plus courte qu'indiquée, des zéros sont ajoutés à la fin :\n\n    ```js run\n    let num = 12.34;\n    alert( num.toFixed(5) ); // \"12.34000\", ajout de zéros pour faire exactement 5 chiffres\n    ```\n\n    Nous pouvons le convertir en un nombre en utilisant le plus unaire `+` ou un appel `Number()` : `+num.toFixed(5)`.\n\n## Calculs imprécis\n\nEn interne, un nombre est représenté au format 64 bits [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754), il y a donc exactement 64 bits pour stocker un nombre : 52 d'entre eux sont utilisés pour stocker les chiffres, 11 d'entre eux stockent la position du point décimal (ils sont zéro pour les nombres entiers), et 1 bit est pour le signe.\n\nSi un nombre est vraiment énorme, il peut déborder du stockage 64 bits et devenir une valeur numérique spéciale `Infinity` :\n\n```js run\nalert( 1e500 ); // infini\n```\n\nCe qui est peut-être un peu moins évident, mais qui arrive souvent, est la perte de précision.\n\nConsidérez ce (faux !) test d'égalité :\n\n```js run\nalert( 0.1 + 0.2 == 0.3 ); // *!*faux*/!*\n```\n\nSi on vérifie si la somme de `0.1` et `0.2` est égale à `0.3` on obtient `faux`.\n\nÉtrange ! Qu'est-ce que c'est alors si ce n'est pas `0.3`?\n\n```js run\nalert( 0.1 + 0.2 ); // 0.30000000000000004\n```\n\nAie ! Imaginez que vous créiez un site d'e-commerce et que le visiteur mette des produits à `0,10 €` et `0,20 €` dans son panier. Le total de la commande sera de `0,30000000000000004 €`. Cela surprendrait n'importe qui.\n\nMais pourquoi cela se produit-il ?\n\nUn nombre est stocké en mémoire sous sa forme binaire, une séquence de uns et de zéros. Mais les fractions telles que `0.1`, `0.2`, qui semblent simples dans le système numérique décimal, sont en réalité des fractions sans fin dans leur forme binaire.\n\nEn d'autres termes, qu'est-ce que `0.1` ? C'est un divisé par dix `1/10`, un dixième. Dans le système numérique décimal, ces nombres sont facilement représentables. Comparez-le à un tiers : `1/3`. Cela devient une fraction sans fin `0.33333(3)`.\n\nAinsi, la division par puissances `10` est garantie de bien fonctionner dans le système décimal, mais la division par `3` ne l'est pas. Pour la même raison, dans le système de numération binaire, la division par des puissances de `2` est garantie, mais `1/10` devient une fraction binaire sans fin.\n\nIl n'existe aucun moyen de stocker **exactement 0.1** ou **exactement 0.2** à l'aide du système binaire, tout comme il n'existe aucun moyen de stocker un tiers sous forme de fraction décimale.\n\nLe format numérique IEEE-754 résout ce problème en arrondissant au nombre le plus proche possible. Ces règles d'arrondissement ne nous permettent normalement pas de voir cette \"petite perte de précision\", mais elle existe.\n\nNous pouvons voir cela en action :\n\n```js run\nalert( 0.1.toFixed(20) ); // 0.10000000000000000555\n```\n\nEt quand on additionne deux nombres, leurs \"pertes de précision\" s'additionnent.\n\nC'est pourquoi `0.1` + `0.2` n'est pas exactement `0.3`.\n\n```smart header=\"Pas seulement JavaScript\"\nLe même problème existe dans de nombreux autres langages de programmation.\n\nPHP, Java, C, Perl, Ruby donnent exactement le même résultat, car ils sont basés sur le même format numérique.\n```\n\nPouvons-nous contourner le problème ? Bien sûr, la méthode la plus fiable est d'arrondir le résultat à l'aide d'une méthode [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed):\n\n```js run\nlet sum = 0.1 + 0.2;\nalert( sum.toFixed(2) ); // \"0.30\"\n```\n\n\nVeuillez noter que `toFixed` renvoie toujours une chaîne de caractères. Il s'assure qu'il a 2 chiffres après le point décimal. C'est pratique si nous avons un magasin en ligne et devons montrer `0.30$`. Pour les autres cas, nous pouvons utiliser le plus unaire `+` pour le contraindre en un nombre :\n\n\n```js run\nlet sum = 0.1 + 0.2;\nalert( +sum.toFixed(2) ); // 0.3\n```\n\nNous pouvons également multiplier temporairement les nombres par 100 (ou un nombre plus grand) pour les transformer en nombres entiers, faire le calcul, puis rediviser. Ensuite, comme nous faisons des calculs avec des nombres entiers, l'erreur diminue quelque peu, mais nous l'obtenons toujours sur la division :\n\n```js run\nalert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3\nalert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001\n```\n\nAinsi, l'approche multiplier/diviser réduit l'erreur, mais ne la supprime pas totalement.\n\nParfois, nous pourrions essayer d'éviter les fractions complètement. Par exemple, si nous traitons avec un magasin, nous pouvons stocker les prix en cents au lieu de dollars. Mais que se passe-t-il si nous appliquons une réduction de 30 % ? En pratique, il est rarement possible d'éviter totalement les fractions. Il suffit de les arrondir pour couper les \"queues\" si nécessaire.\n\n````smart header=\"La chose amusante\"\nEssayez de lancer ceci :\n\n```js run\n// Bonjour! Je suis un nombre auto-croissant!\nalert( 9999999999999999 ); // affiche 10000000000000000\n```\n\nLa cause est encore une fois le manque de précision. Le nombre comporte 64 bits, dont 52 peuvent être utilisés pour stocker des chiffres, mais cela ne suffit pas. Alors, les chiffres les moins significatifs disparaissent.\n\nJavaScript ne déclenche pas d'erreur dans de tels événements. il fait de son mieux pour adapter le nombre au format souhaité, mais malheureusement, ce format n'est pas assez grand.\n````\n\n```smart header=\"Deux zéros\"\nUne autre conséquence amusante de la représentation interne des nombres est l'existence de deux zéros : `0` et `-0`.\n\nC'est parce que le signe est représenté par un bit, il peut donc être défini ou non pour n’importe quel nombre, y compris le zéro.\n\nDans le plupart des cas, la distinction est imperceptible, car les opérateurs peuvent les traiter de la même manière.\n```\n\n## Tests : isFinite et isNaN\n\nVous rappelez-vous de ces deux valeurs numériques spéciales ?\n\n- `Infinite` (et `-Infinite`) sont des valeurs numériques spéciales qui sont supérieures (inférieure) à tout.\n- `NaN` représente une erreur.\n\nIls appartiennent au type `number`, mais ne sont pas des numéros \"normaux\". Il existe donc des fonctions spéciales pour les vérifier :\n\n- `isNaN(valeur)` convertit son argument en un nombre et teste ensuite s'il est `NaN` :\n\n    ```js run\n    alert( isNaN(NaN) ); // true\n    alert( isNaN(\"str\") ); // true\n    ```\n\n    Mais avons-nous besoin de cette fonction ? Ne pouvons-nous pas simplement utiliser la comparaison `=== NaN` ? Malheureusement non. La valeur `NaN` est unique en ce sens qu'elle ne vaut rien, y compris elle-même :\n\n    ```js run\n    alert( NaN === NaN ); // false\n    ```\n\n- `isFinite(valeur)` convertit son argument en un nombre et renvoie true s'il s'agit d'un nombre régulier, pas de `NaN` / `Infinity` / `-Infinity` :\n\n    ```js run\n    alert( isFinite(\"15\") ); // true\n    alert( isFinite(\"str\") ); // false, car c'est une valeur non régulière: NaN\n    alert( isFinite(Infinity) ); // false, car c'est aussi une valeur non régulière: Infinity\n    ```\n\nParfois, `isFinite` est utilisé pour valider si une valeur de chaîne de caractères est un nombre régulier :\n\n```js run\nlet num = +prompt(\"Entrez un nombre\", '');\n\n// sera vrai, sauf si vous entrez Infinity, -Infinity ou NaN\nalert( isFinite(num) );\n```\n\nVeuillez noter qu'une chaîne de caractères vide ou une chaîne de caractères contenant seulement des espaces est traitée comme `0` dans toutes les fonctions numérique, y compris `isFinite`.\n\n````smart header=\"`Number.isNaN` et `Number.isFinite`\"\nLes méthodes [Number.isNaN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isNaN) et [Number.isFinite](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite) sont des versions plus \"strictes\" des fonctions `isNaN` et `isFinite`. Elles ne convertissent pas automatiquement leur argument en nombre, mais vérifient s'il appartient au type `number`.\n\n- `Number.isNaN(value)` retourne `true` si l'argument appartient au type `number` et s'il vaut `NaN`. Dans tous les autres cas, elle retourne `false`.\n\n    ```js run\n    alert( Number.isNaN(NaN) ); // true\n    alert( Number.isNaN(\"str\" / 2) ); // true\n\n    // Notez la différence :\n    alert( Number.isNaN(\"str\") ); // false, car \"str\" est de type \"string\"\n    alert( isNaN(\"str\") ); // true, car isNaN convertit la string \"str\" en un nombre et obtient NaN comme résultatde la conversion\n    ```\n\n- `Number.isFinite(value)` retourne `true` si l'argument appartient au type `number` et s'il vaut ni `NaN`, ni `Infinity`, ni `-Infinity`. Dans tous les autres cas, elle retourne `false`.\n\n    ```js run\n    alert( Number.isFinite(123) ); // true\n    alert( Number.isFinite(Infinity) ); // false\n    alert( Number.isFinite(2 / 0) ); // false\n\n    // Notez la différence :\n    alert( Number.isFinite(\"123\") ); // false, car \"123\" est de type \"string\"\n    alert( isFinite(\"123\") ); // true, car isFinite convertit la string \"123\" en un nombre 123\n    ```\n\nEn quelque sorte, les fonction `Number.isNaN` et `Number.isFinite` sont plus simples et plus directes que les fonctions `isNaN` et `isFinite`. Cependant, en pratique `isNaN` et `isFinite` sont plus souvent utilisées car elles sont plus courtes à écrire.\n````\n\n```smart header=\"Comparer avec Object.is\"\n\nIl existe une méthode intégrée spéciale [Object.is](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/is) qui compare des valeurs telles que `===`, mais qui est plus fiable pour deux cas extrêmes :\n\n1. Cela fonctionne avec `NaN` : `Object.is(NaN, NaN) === true`, c'est une bonne chose.\n2. Les valeurs `0` et `-0` sont différentes : `Object.is(0, -0) === false`, techniquement c’est vrai, car le numéro a en interne un bit de signe qui peut être différent même si tous les autres bits sont à zéro.\n\nDans tous les autres cas, `Object.is(a, b)` est identique à `a === b`.\n\nNous mentionnons `Object.is` ici, car il est souvent utilisé dans les spécifications JavaScript. Lorsqu'un algorithme interne doit comparer deux valeurs pour être exactement identiques, il utilise `Object.is` (appelé en interne [SameValue](https://tc39.github.io/ecma262/#sec-samevalue)).\n```\n\n## parseInt et parseFloat\n\nLa conversion numérique à l'aide d'un plus `+` ou `Number()` est strict. Si une valeur n'est pas exactement un nombre, elle échoue :\n\n```js run\nalert( +\"100px\" ); // NaN\n```\n\nLa seule exception concerne les espaces au début ou à la fin de la chaîne de caractères, car ils sont ignorés.\n\nMais dans la vraie vie, nous avons souvent des valeurs en unités, comme `\"100px\"` ou `\"12pt\"` en CSS. En outre, dans de nombreux pays, le symbole monétaire se situe après le montant. Nous avons donc `\"19€\"` et souhaitons en extraire une valeur numérique.\n\nC'est à quoi servent `parseInt` et `parseFloat`.\n\nIls \"lisent\" un nombre d'une chaîne jusqu'à ce qu'ils ne puissent plus. En cas d'erreur, le numéro rassemblé est renvoyé. La fonction `parseInt` renvoie un entier, tandis que `parseFloat` renvoie un nombre à virgule :\n\n```js run\nalert( parseInt('100px') ); // 100\nalert( parseFloat('12.5em') ); // 12.5\n\nalert( parseInt('12.3') ); // 12, seule la partie entière est renvoyée\nalert( parseFloat('12.3.4') ); // 12.3, le deuxième point arrête la lecture\n```\n\nIl y a des situations où `parseInt` / `parseFloat` retournera `NaN`. Cela arrive quand on ne peut lire aucun chiffre :\n\n```js run\nalert( parseInt('a123') ); // NaN, le premier symbole arrête le processus\n```\n\n````smart header=\"Le second argument de `parseInt(str, radix)`\"\nLa fonction `parseInt()` a un second paramètre optionnel. Il spécifie la base du système numérique, ce qui permet à `parseInt` d'analyser également les chaînes de nombres hexadécimaux, binaires, etc. :\n\n```js run\nalert( parseInt('0xff', 16) ); // 255\nalert( parseInt('ff', 16) ); // 255, sans 0x cela fonctionne également\n\nalert( parseInt('2n9c', 36) ); // 123456\n```\n````\n\n## Autres fonctions mathématiques\n\nJavaScript a un objet [Math](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Math) intégré qui contient une petite bibliothèque de fonctions et de constantes mathématiques.\n\nQuelques exemples :\n\n`Math.random()`\n: Retourne un nombre aléatoire de 0 à 1 (1 non compris).\n\n    ```js run\n    alert( Math.random() ); // 0.1234567894322\n    alert( Math.random() ); // 0.5435252343232\n    alert( Math.random() ); // ... (tout nombre aléatoire)\n    ```\n\n`Math.max(a, b, c...)` et `Math.min(a, b, c...)`\n: Renvoie le plus grand et le plus petit d'un nombre arbitraire d'arguments.\n\n    ```js run\n    alert( Math.max(3, 5, -10, 0, 1) ); // 5\n    alert( Math.min(1, 2) ); // 1\n    ```\n\n`Math.pow(n, power)`\n: Renvoie `n` élevé à la puissance `power` donnée.\n\n    ```js run\n    alert( Math.pow(2, 10) ); // 2 puissance 10 = 1024\n    ```\n\nIl y a plus de fonctions et de constantes dans l'objet Math, y compris la trigonométrie, que vous pouvez trouver dans la [documentation de l'objet Math](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Math).\n\n## Résumé\n\nPour écrire des nombres avec beaucoup de zéros :\n\n- Ajoutez `\"e\"` avec le nombre de zéros au nombre. Comme `123e6` est `123` avec 6 zéros soit `123000000`.\n- Un nombre négatif après le `\"e\"` entraîne la division du nombre par 1 suivi par le nombre de zéros donnés. Comme `123-e6` est `123` divisé par 1 suivi de 6 zéros, soit `0.000123` (`123` millionièmes).\n\nPour différents systèmes de numération :\n\n- Il est possible d'écrire des nombres directement dans les systèmes hex (`0x`), octal (`0o`) et binaire (`0b`).\n- `parseInt(str, base)` passe la chaîne de caractères `str` vers un système numérique avec une `base` donnée : `2 ≤ base ≤ 36`.\n- `num.toString(base)` convertit un nombre en chaîne de caractères dans le système numérique de la `base` donnée.\n\nPour les tests de nombres réguliers :\n\n- `isNaN(value)` convertit son argument en nombre puis vérifie s'il s'agit de `NaN`.\n- `Number.isNaN(value)` vérifie si son argument appartient au type `number` et, si c'est le cas, vérifie s'il s'agit de `NaN`.\n- `isFinite(value)` convertit son argument en nombre puis vérifie s'il ne s'agit pas de `NaN` / `Infinity` / `-Infinity`.\n- `Number.isFinite(value)` vérifie si son argument appartient au type `number` et, si c'est le cas, vérifie s'il ne s'agit pas de `NaN` / `Infinity` / `-Infinity`.\n\nPour convertir des valeurs telles que `12pt` et `100px` en nombres :\n\n- Utiliser `parseInt` / `parseFloat` pour la conversion \"soft\", qui lit un nombre à partir d'une chaîne de caractères, puis renvoie la valeur qu'il pouvait lire avant de rencontrer une erreur.\n\nPour les fractions :\n\n- Arrondissez en utilisant `Math.floor`, `Math.ceil`, `Math.trunc`, `Math.round` ou `num.toFixed(précision)`.\n- Assurez-vous de ne pas perdre de précision lorsque vous travaillez avec des fractions.\n\nPlus de fonctions mathématiques :\n\n- Voir l'objet [Math](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Math) quand vous en avez besoin. La bibliothèque est très petite, mais peut couvrir les besoins de base.\n"
  },
  {
    "path": "1-js/05-data-types/03-string/1-ucfirst/_js.view/solution.js",
    "content": "function ucFirst(str) {\n  if (!str) return str;\n\n  return str[0].toUpperCase() + str.slice(1);\n}"
  },
  {
    "path": "1-js/05-data-types/03-string/1-ucfirst/_js.view/test.js",
    "content": "describe(\"ucFirst\", function() {\n  it('Uppercases the first symbol', function() {\n    assert.strictEqual(ucFirst(\"john\"), \"John\");\n  });\n\n  it(\"Doesn't die on an empty string\", function() {\n    assert.strictEqual(ucFirst(\"\"), \"\");\n  });\n});"
  },
  {
    "path": "1-js/05-data-types/03-string/1-ucfirst/solution.md",
    "content": "Nous ne pouvons pas \"remplacer\" le premier caractère, car les chaînes de caractères en JavaScript sont immuables.\n\nMais nous pouvons créer une nouvelle chaîne de caractères basée sur celle existante, avec le premier caractère majuscule :\n\n```js\nlet newStr = str[0].toUpperCase() + str.slice(1);\n```\n\nIl y a un petit problème cependant. Si `str` est vide, alors `str[0]` est `undefined`, et comme `undefined` n’a pas la méthode `toUpperCase()`, nous aurons une erreur.\n\nLa solution la plus simple consiste à ajouter un test pour une chaîne vide, comme ceci :\n\n```js run demo\nfunction ucFirst(str) {\n  if (!str) return str;\n\n  return str[0].toUpperCase() + str.slice(1);\n}\n\nalert( ucFirst(\"john\") ); // John\n```\n"
  },
  {
    "path": "1-js/05-data-types/03-string/1-ucfirst/task.md",
    "content": "importance: 5\n\n---\n\n# Mettre en majuscule le premier caractère\n\nÉcrire une fonction `ucFirst(str)` qui retourne la chaîne de caractères `str` avec le premier caractère majuscule, par exemple :\n\n```js\nucFirst(\"john\") == \"John\";\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/03-string/2-check-spam/_js.view/solution.js",
    "content": "function checkSpam(str) {\n  let lowerStr = str.toLowerCase();\n\n  return lowerStr.includes('viagra') || lowerStr.includes('xxx');\n}"
  },
  {
    "path": "1-js/05-data-types/03-string/2-check-spam/_js.view/test.js",
    "content": "describe(\"checkSpam\", function() {\n  it('finds spam in \"buy ViAgRA now\"', function() {\n    assert.isTrue(checkSpam('buy ViAgRA now'));\n  });\n\n  it('finds spam in \"free xxxxx\"', function() {\n    assert.isTrue(checkSpam('free xxxxx'));\n  });\n\n  it('no spam in \"innocent rabbit\"', function() {\n    assert.isFalse(checkSpam('innocent rabbit'));\n  });\n});"
  },
  {
    "path": "1-js/05-data-types/03-string/2-check-spam/solution.md",
    "content": "Pour rendre la recherche insensible à la casse, transformons la chaîne de caractères en minuscule, puis recherchons :\n\n```js run demo\nfunction checkSpam(str) {\n  let lowerStr = str.toLowerCase();\n\n  return lowerStr.includes('viagra') || lowerStr.includes('xxx');\n}\n\nalert( checkSpam('buy ViAgRA now') );\nalert( checkSpam('free xxxxx') );\nalert( checkSpam(\"innocent rabbit\") );\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/03-string/2-check-spam/task.md",
    "content": "importance: 5\n\n---\n\n# Vérifier le spam\n\nÉcrire une fonction `checkSpam(str)` qui retourne `true` si `str` contient 'viagra' ou 'XXX', sinon `false`.\n\nLa fonction doit être sensible à la casse :\n\n```js\ncheckSpam('buy ViAgRA now') == true\ncheckSpam('free xxxxx') == true\ncheckSpam(\"innocent rabbit\") == false\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/03-string/3-truncate/_js.view/solution.js",
    "content": "function truncate(str, maxlength) {\n  return (str.length > maxlength) ? \n    str.slice(0, maxlength - 1) + '…' : str;\n}"
  },
  {
    "path": "1-js/05-data-types/03-string/3-truncate/_js.view/test.js",
    "content": "describe(\"truncate\", function() {\n  it(\"truncate the long string to the given length (including the ellipsis)\", function() {\n    assert.equal(\n      truncate(\"What I'd like to tell on this topic is:\", 20),\n      \"What I'd like to te…\"\n    );\n  });\n\n  it(\"doesn't change short strings\", function() {\n    assert.equal(\n      truncate(\"Hi everyone!\", 20),\n      \"Hi everyone!\"\n    );\n  });\n\n});\n"
  },
  {
    "path": "1-js/05-data-types/03-string/3-truncate/solution.md",
    "content": "La longueur maximale doit être `maxlength`, il faut donc la couper un peu plus courte pour laisser de la place aux ellipses.\n\nNotez qu'il existe en réalité un seul caractère Unicode pour une ellipse. Ce n’est pas trois points.\n\n```js run\nfunction truncate(str, maxlength) {\n  return (str.length > maxlength) ? \n    str.slice(0, maxlength - 1) + '…' : str;\n}\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/03-string/3-truncate/task.md",
    "content": "importance: 5\n\n---\n\n# Tronquer le texte\n\nCréer une fonction `truncate(str, maxlength)` qui vérifie la longueur de `str` et, si elle dépasse `maxlength`, remplace la fin de `str` avec le caractère des ellipses `\"…\"` pour rendre sa longueur égale à `maxlength`.\n\nLe résultat de la fonction doit être la chaîne de caractères tronquée (si nécessaire).\n\nPar exemple :\n\n```js\ntruncate(\"What I'd like to tell on this topic is:\", 20) = \"What I'd like to te…\"\n\ntruncate(\"Hi everyone!\", 20) = \"Hi everyone!\"\n```\n"
  },
  {
    "path": "1-js/05-data-types/03-string/4-extract-currency/_js.view/solution.js",
    "content": "function extractCurrencyValue(str) {\n  return +str.slice(1);\n}"
  },
  {
    "path": "1-js/05-data-types/03-string/4-extract-currency/_js.view/test.js",
    "content": "describe(\"extractCurrencyValue\", function() {\n\n  it(\"for the string $120 returns the number 120\", function() {\n    assert.strictEqual(extractCurrencyValue('$120'), 120);\n  });\n\n\n});"
  },
  {
    "path": "1-js/05-data-types/03-string/4-extract-currency/solution.md",
    "content": ""
  },
  {
    "path": "1-js/05-data-types/03-string/4-extract-currency/task.md",
    "content": "importance: 4\n\n---\n\n# Extraire l'argent\n\nNous avons un coût sous la forme `\"$120\"`. C'est-à-dire que le signe dollar commence, puis le nombre.\n\nCréer une fonction `extractCurrencyValue(str)` qui extrait la valeur numérique d'une telle chaîne de caractères et la renvoit.\n\nL'exemple :\n\n```js\nalert( extractCurrencyValue('$120') === 120 ); // true\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/03-string/article.md",
    "content": "# Strings\n\nEn JavaScript, les données de type texte sont stockées sous forme de chaînes de caractères. Il n'y a pas de type séparé pour un seul caractère.\n\nLe format interne des chaînes de caractères est toujours [UTF-16](https://en.wikipedia.org/wiki/UTF-16), il n'est pas lié au codage de la page.\n\n## Quotes\n\nRappelons les types de quotes.\n\nLes chaînes de caractères peuvent être placées entre guillemets simples, doubles ou backticks :\n\n```js\nlet single = 'single-quoted';\nlet double = \"double-quoted\";\n\nlet backticks = `backticks`;\n```\n\nLes guillemets simples et doubles sont essentiellement les mêmes. Les backticks nous permettent toutefois d’incorporer n’importe quelle expression dans la chaîne de caractères, en l'enveloppant dans `${…}` :\n\n```js run\nfunction sum(a, b) {\n  return a + b;\n}\n\nalert(`1 + 2 = ${sum(1, 2)}.`); // 1 + 2 = 3.\n```\n\nL’utilisation des backticks présente également l’avantage de permettre à une chaîne de caractères de couvrir plusieurs lignes :\n\n```js run\nlet guestList = `Guests:\n * John\n * Pete\n * Mary\n`;\n\nalert(guestList); // une liste d'invités sur plusieurs lignes\n```\n\nÇa a l'air naturel, non? Mais les guillemets simples ou doubles ne fonctionnent pas de cette façon.\n\nSi nous les utilisons et essayons d'utiliser plusieurs lignes, il y aura une erreur :\n\n```js run\nlet guestList = \"Guests: // Error: Unexpected token ILLEGAL\n  * John\";\n```\n\nLes guillemets simples et doubles proviennent d'anciens temps de la création linguistique lorsque la nécessité de chaînes multilignes n'était pas prise en compte. Les backticks sont apparus beaucoup plus tard et sont donc plus polyvalents.\n\nLes backticks nous permettent également de spécifier un \"modèle de fonction\" avant le premier backtick. La syntaxe est la suivante : <code>func&#96;string&#96;</code>. La fonction `func` est appelée automatiquement, elle reçoit la chaîne de caractères et les expressions incorporées et peut les traiter. Cette fonctionnalité est appelée \"tagged templates\", elle est rarement vue, mais vous pouvez en savoir plus à ce sujet dans la doc MDN : [Template literals](mdn:/JavaScript/Reference/Template_literals#Tagged_templates).\n\n## Caractères spéciaux\n\nIl est encore possible de créer des chaînes de caractères multilignes avec des guillemets simples et doubles en utilisant un \"caractère de nouvelle ligne\", écrit comme ceci `\\n`, qui spécifie un saut de ligne :\n\n```js run\nlet guestList = \"Guests:\\n * John\\n * Pete\\n * Mary\";\n\nalert(guestList); // une liste d'invités multiligne, pareil qu'au dessus\n```\n\nComme exemple plus simple, ces deux lignes sont égales, juste écrites différemment :\n\n```js run\nlet str1 = \"Hello\\nWorld\"; // deux lignes utilisant un \"symbole de nouvelle ligne\"\n\n// deux lignes utilisant une nouvelle ligne normale et des backticks\nlet str2 = `Hello\nWorld`;\n\nalert(str1 == str2); // true\n```\n\nIl existe d'autres caractères \"spéciaux\" moins courants.\n\nVoici la liste complète :\n\n| Caractère                                          | Description                                                                                                                                                                                      |\n|----------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `\\n`                                               | Nouvelle ligne                                                                                                                                                                                   |\n| `\\r`                                               | Dans les fichiers texte Windows, une combinaison de deux caractères `\\r\\n` représente une nouvelle pause, tandis que sur un système d'exploitation non Windows, il s'agit simplement de `\\n`. C'est pour des raisons historiques, la plupart des logiciels Windows comprennent également `\\n`. |\n| `\\'`, `\\\"`                                         | Quotes                                                                                                                                                                                           |\n| `\\\\`                                               | Backslash                                                                                                                                                                                        |\n| `\\t`                                               | Tab                                                                                                                                                                                              |\n| `\\b`, `\\f`, `\\v`                                   | Backspace, Form Feed, Vertical Tab -- conservés pour compatibilité, non utilisés de nos jours.                                                                                                   |\n\nComme vous pouvez le voir, tous les caractères spéciaux commencent par un backslash (barre oblique inversée) `\\`. On l'appelle aussi \"caractère d'échappement\".\n\nParce que c'est si spécial, si nous devons afficher une véritable barre oblique inverse `\\` dans la chaîne, nous devons la doubler :\n\n```js run\nalert( `The backslash: \\\\` ); // The backslash: \\\n```\n\nLes guillemets dits \"échappés\" `\\'`, `\\\"`, <code>\\\\`</code> sont utilisés pour insérer un guillemet dans la même chaîne entre guillemets.\n\nPar exemple :\n\n```js run\nalert( 'I*!*\\'*/!*m the Walrus!' ); // *!*I'm*/!* the Walrus!\n```\n\nComme vous pouvez le constater, nous devons précéder le simple quote intérieure du backslash `\\'`, sinon, cela indiquerait la fin de la chaîne de caractères.\n\nBien sûr, il ne faut échapper que les guillemets identiques à ceux qui les entourent. Donc, comme solution plus élégante, nous pourrions passer aux guillemets doubles ou aux backticks :\n\n```js run\nalert( \"I'm the Walrus!\" ); // I'm the Walrus!\n```\n\nOutre ces caractères spéciaux, il existe également une notation spéciale pour les codes Unicode `\\u…`, elle est rarement utilisée et est couverte dans le chapitre facultatif sur [Unicode](info:unicode).\n\n## Longueur de chaîne de caractères\n\nLa propriété `length` indique la longueur de la chaîne de caractères :\n\n```js run\nalert( `My\\n`.length ); // 3\n```\n\nNotez que `\\n` est un seul caractère \"spécial\", la longueur est donc bien `3`.\n\n```warn header=\"`length` est une propriété\"\nLes personnes ayant des connaissances dans d'autres langages peuvent parfois commettre des erreurs en l'appelant `str.length()` au lieu de `str.length`. Cela ne fonctionne pas.\n\nVeuillez noter que `str.length` est une propriété numérique et non une fonction. Il n'est pas nécessaire d'ajouter des parenthèses après. Pas `.length()`, mais `.length`.\n```\n\n## Accéder aux caractères\n\nPour obtenir un caractère à la position `pos`, utilisez des crochets `[pos]` ou appelez la méthode [str.at(pos)](mdn:js/String/at). Le premier caractère commence à la position zéro :\n\n```js run\nlet str = `Hello`;\n\n// le premier caractère\nalert( str[0] ); // H\nalert( str.at(0) ); // H\n\n// le dernier caractère\nalert( str[str.length - 1] ); // o\nalert( str.at(-1) ); // o\n```\n\nComme vous pouvez le voir, la méthode `.at(pos)` a l'avantage de permettre une position négative. Si `pos` est négatif, alors il est compté à partir de la fin de la chaîne de caractères.\n\nDonc `.at(-1)` signifie le dernier caractère, et `.at(-2)` est celui qui le précède, etc.\n\nLes crochets renvoient toujours `undefined` pour les index négatifs, par exemple :\n\n```js run\nlet str = `Hello`;\n\nalert( str[-2] ); // undefined\nalert( str.at(-2) ); // l\n```\n\nNous pouvons également parcourir les caractères en utilisant un `for..of` :\n\n```js run\nfor (let char of \"Hello\") {\n  alert(char); // H,e,l,l,o (char devient \"H\", ensuite \"e\", ensuite \"l\", etc.)\n}\n```\n\n## Les chaînes de caractères sont immuables\n\nLes chaînes de caractères ne peuvent pas être changées en JavaScript. Il est impossible de modifier un caractère.\n\nEssayons de démontrer que cela ne fonctionne pas :\n\n```js run\nlet str = 'Hi';\n\nstr[0] = 'h'; // error\nalert( str[0] ); // ne fonctionne pas\n```\n\nLa solution habituelle consiste à créer une nouvelle chaîne et à l’affecter à `str` au lieu de l’ancienne.\n\nPar exemple :\n\n```js run\nlet str = 'Hi';\n\nstr = 'h' + str[1];  // remplace la haine de caractères\n\nalert( str ); // hi\n```\n\nNous verrons plus d'exemples dans les sections suivantes.\n\n## Modifier la casse\n\nLes méthodes [toLowerCase()](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/String/toLowerCase) et [toUpperCase()](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/String/toUpperCase) modifient la casse :\n\n```js run\nalert( 'Interface'.toUpperCase() ); // INTERFACE\nalert( 'Interface'.toLowerCase() ); // interface\n```\n\nOu, si nous voulons un seul caractère minuscule :\n\n```js run\nalert( 'Interface'[0].toLowerCase() ); // 'i'\n```\n\n## Rechercher un substring (partie de la chaîne de caractères)\n\nIl existe plusieurs façons de rechercher une partie d'une chaîne de caractères.\n\n### str.indexOf\n\nLa première méthode est [str.indexOf(substr, pos)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/String/indexOf).\n\nIl cherche le `substr` dans `str`, en partant de la position donnée `pos`, et retourne la position où la correspondance a été trouvée ou `-1` si rien ne peut être trouvé.\n\nPar exemple :\n\n```js run\nlet str = 'Widget with id';\n\nalert( str.indexOf('Widget') ); // 0, parce que 'Widget' est trouvé au début\nalert( str.indexOf('widget') ); // -1, pas trouvé, la recherche est sensible à la casse\n\nalert( str.indexOf(\"id\") ); // 1, \"id\" est trouvé à la position 1 (..idget avec id)\n```\n\nLe second paramètre optionnel nous permet de rechercher à partir de la position donnée.\n\nPar exemple, la première occurrence de `\"id\"` est à la position `1`. Pour rechercher l’occurrence suivante, commençons la recherche à partir de la position `2` :\n\n```js run\nlet str = 'Widget with id';\n\nalert( str.indexOf('id', 2) ) // 12\n```\n\nSi toutes les occurrences nous intéressent, nous pouvons exécuter `indexOf` dans une boucle. Chaque nouvel appel est passé avec la position après le match précédent :\n\n```js run\nlet str = 'As sly as a fox, as strong as an ox';\n\nlet target = 'as'; // cherchons le\n\nlet pos = 0;\nwhile (true) {\n  let foundPos = str.indexOf(target, pos);\n  if (foundPos == -1) break;\n\n  alert( `Found at ${foundPos}` );\n  pos = foundPos + 1; // continue la recherche à partir de la position suivante\n}\n```\n\nLe même algorithme peut être raccourci :\n\n```js run\nlet str = \"As sly as a fox, as strong as an ox\";\nlet target = \"as\";\n\n*!*\nlet pos = -1;\nwhile ((pos = str.indexOf(target, pos + 1)) != -1) {\n  alert( pos );\n}\n*/!*\n```\n\n```smart header=\"`str.lastIndexOf(pos)`\"\nIl y a aussi une méthode similaire [str.lastIndexOf(pos)](mdn:js/String/lastIndexOf) qui cherche de la fin d'une chaîne de caractères à son début.\n\nElle liste les occurrences dans l'ordre inverse.\n```\n\nIl y a un léger inconvénient avec `indexOf` dans le test `if`. On ne peut pas le mettre dans le `if` comme ceci :\n\n```js run\nlet str = \"Widget with id\";\n\nif (str.indexOf(\"Widget\")) {\n    alert(\"We found it\"); // ne fonctionne pas !\n}\n```\n\nL’`alert` dans l’exemple ci-dessus ne s’affiche pas parce que `str.indexOf(\"Widget\")` retourne `0` (ce qui signifie qu'il a trouvé la correspondance à la position de départ). Oui, mais `if` considère que `0` est `false`.\n\nNous devrions donc effectuer la vérification avec `-1`, comme ceci :\n\n```js run\nlet str = \"Widget with id\";\n\n*!*\nif (str.indexOf(\"Widget\") != -1) {\n*/!*\n    alert(\"We found it\"); // fonctionne maintenant !\n}\n```\n\n### includes, startsWith, endsWith\n\nLa méthode plus moderne [str.includes(substr, pos)](mdn:js/String/includes) retourne `true`/`false` en fonction de si `str` contient `substr`.\n\nC’est le bon choix si nous devons tester la présence, mais n’avons pas besoin de sa position :\n\n```js run\nalert( \"Widget with id\".includes(\"Widget\") ); // true\n\nalert( \"Hello\".includes(\"Bye\") ); // false\n```\n\nLe deuxième argument optionnel de `str.includes` est la position de départ de la recherche :\n\n```js run\nalert( \"Widget\".includes(\"id\") ); // true\nalert( \"Widget\".includes(\"id\", 3) ); // false, à partir de la position 3, il n'y a pas de \"id\"\n```\n\nLes méthodes [str.startsWith](mdn:js/String/startsWith) et [str.endsWith](mdn:js/String/endsWith) font exactement ce qu'elle disent :\n\n```js run\nalert( \"*!*Wid*/!*get\".startsWith(\"Wid\") ); // true, \"Widget\" démarre avec \"Wid\"\nalert( \"Wid*!*get*/!*\".endsWith(\"get\") ); // true, \"Widget\" fini avec \"get\"\n```\n\n## Obtenir un substring (sous-chaîne de caractères)\n\nIl existe 3 méthodes en JavaScript pour obtenir un substring : `substring`, `substr` et `slice`.\n\n`str.slice(start [, end])`\n: Renvoie la partie de la chaîne de caractères de `start` jusqu'à (sans l'inclure) `end`.\n\n    Par exemple :\n\n    ```js run\n    let str = \"stringify\";\n    alert( str.slice(0, 5) ); // 'strin', le substring de 0 à 5 (sans inclure 5)\n    alert( str.slice(0, 1) ); // 's', de 0 à 1, mais sans inclure 1, donc uniquement le caractère à l'index 0\n    ```\n\n    S'il n'y a pas de second argument, `slice` va jusqu'à la fin de la chaîne de caractères :\n\n    ```js run\n    let str = \"st*!*ringify*/!*\";\n    alert( str.slice(2) ); // 'ringify', à partir de la 2e position jusqu'à la fin\n    ```\n\n    Des valeurs négatives pour `start`/`end` sont également possibles. Elles veulent dire que la position est comptée à partir de la fin de la chaîne de caractères :\n\n    ```js run\n    let str = \"strin*!*gif*/!*y\";\n\n    // commence à la 4ème position à partir de la droite, se termine au 1er à partir de la droite\n    alert( str.slice(-4, -1) ); // 'gif'\n    ```\n\n`str.substring(start [, end])`\n: Renvoie la partie de la chaîne de caractères *entre* `start` et `end` (`end` non inclus).\n\n    C'est presque la même chose que `slice`, mais cela permet à `start` d'être supérieur à `end` (dans ce cas, il échange simplement les valeurs `start` et `end`).\n\n    Par exemple :\n\n    ```js run\n    let str = \"st*!*ring*/!*ify\";\n\n    // ce sont les mêmes pour substring\n    alert( str.substring(2, 6) ); // \"ring\"\n    alert( str.substring(6, 2) ); // \"ring\"\n\n    // ...mais pas pour slice :\n    alert( str.slice(2, 6) ); // \"ring\" (le même résultat)\n    alert( str.slice(6, 2) ); // \"\" (une chaîne de caractères vide)\n\n    ```\n\n    Les arguments négatifs ne sont pas supportés (contrairement à slice), ils sont traités comme `0`.\n\n`str.substr(start [, length])`\n: Renvoie la partie de la chaîne de caractères à partir de `start`, avec le `length` (longueur) donné.\n\n    Contrairement aux méthodes précédentes, celle-ci nous permet de spécifier la longueur `length` au lieu de la position finale :\n\n    ```js run\n    let str = \"st*!*ring*/!*ify\";\n    alert( str.substr(2, 4) ); // 'ring', à partir de la 2ème position on obtient 4 caractères\n    ```\n\n    Le premier argument peut être négatif, pour compter à partir de la fin :\n\n    ```js run\n    let str = \"strin*!*gi*/!*fy\";\n    alert( str.substr(-4, 2) ); // 'gi', à partir de la 4ème position on obtient 2 caractères\n    ```\n\nCette méthode réside dans l'[Annexe B](https://tc39.es/ecma262/#sec-string.prototype.substr) de la spécification du langage. Cela signifie que seuls les moteurs JavaScript hébergés par un navigateur doivent le prendre en charge et qu'il n'est pas recommandé de l'utiliser. En pratique, il est supporté partout.\n\nRécapitulons ces méthodes pour éviter toute confusion :\n\n| méthodes                | séléction ...                           | valeurs negatives                    |\n|-------------------------|-----------------------------------------|--------------------------------------|\n| `slice(start, end)`     | de `start` à `end` (n'inclue pas `end`) | permet les négatifs                  |\n| `substring(start, end)` | entre `start` et `end`                  | les valeurs négatives signifient `0` |\n| `substr(start, length)` | de `start` obtient `length` caractères  | permet un `start` negatif            |\n\n```smart header=\"Lequel choisir ?\"\nTous peuvent faire le travail. Formellement, `substr` présente un inconvénient mineur : il n’est pas décrit dans la spécification JavaScript principale, mais dans l’Annexe B, qui couvre les fonctionnalités réservées au navigateur qui existent principalement pour des raisons historiques. Ainsi, les environnements autres que les navigateurs peuvent ne pas le prendre en charge. Mais dans la pratique, cela fonctionne partout.\n\nParmi les deux autres variantes, `slice` est un peu plus flexible, il permet des arguments négatifs et une écriture plus courte.\n\nDonc, pour une utilisation pratique, il suffit de ne retenir que \"slice\".\n```\n\n## Comparer les strings\n\nComme nous le savons du chapitre <info:comparison>, les strings sont comparées caractère par caractère dans l'ordre alphabétique.\n\nBien que, il y a quelques bizarreries.\n\n1. Une lettre minuscule est toujours plus grande qu'une majuscule :\n\n    ```js run\n    alert( 'a' > 'Z' ); // true\n    ```\n\n2. Les lettres avec des signes diacritiques sont \"hors d'usage\" :\n\n    ```js run\n    alert( 'Österreich' > 'Zealand' ); // true\n    ```\n\n    Cela peut conduire à des résultats étranges si nous trions ces noms de pays. Habituellement, les gens s'attendent à trouver `Zealand` après `Österreich` dans la liste.\n\nPour comprendre ce qui se passe, nous devons être conscients que les chaînes de caractères en JavaScript sont encodées en utilisant [UTF-16](https://en.wikipedia.org/wiki/UTF-16). C'est-à-dire que chaque caractère a un code numérique correspondant.\n\nIl existe des méthodes spéciales qui permettent d'obtenir le caractère pour le code et inversement :\n\n`str.codePointAt(pos)`\n: Renvoie un nombre décimal représentant le code du caractère à la position `pos` :\n\n    ```js run\n    // différentes lettres majuscules ont des codes différents\n    alert( \"Z\".codePointAt(0) ); // 90\n    alert( \"z\".codePointAt(0) ); // 122\n    alert( \"z\".codePointAt(0).toString(16) ); // 7a (si nous avons besoin d'une valeur hexadécimale)\n    ```\n\n`String.fromCodePoint(code)`\n: Crée un caractère par son `code` chiffre\n\n    ```js run\n    alert( String.fromCodePoint(90) ); // Z\n    alert( String.fromCodePoint(0x5a) ); // Z (nous pouvons également utiliser une valeur hexadécimale comme argument)\n    ```\n\nVoyons maintenant les caractères avec les codes `65..220` (l’alphabet latin et un peu plus) en créant une chaîne de caractères de ceux-ci :\n\n```js run\nlet str = '';\n\nfor (let i = 65; i <= 220; i++) {\n  str += String.fromCodePoint(i);\n}\nalert( str );\n// Output:\n// ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\n// ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜ\n```\n\nVous voyez ? Les caractères majuscules sont les premiers, puis quelques spéciaux, puis les minuscules, et `Ö` vers la fin de la sortie.\n\nMaintenant, cela devient évident pourquoi `a > Z`.\n\nLes caractères sont comparés par leur code numérique. Le plus grand code signifie que le caractère est plus grand. Le code pour `a` (97) est supérieur au code pour `Z` (90).\n\n- Toutes les lettres minuscules vont après les lettres majuscules car leurs codes sont plus grands.\n- Certaines lettres comme `Ö` se distinguent de l'alphabet principal. Ici, le code est supérieur à tout ce qui va de `a` à `z`.\n\n### Les comparaisons correctes [#comparaisons-correctes]\n\nL'algorithme \"approprié\" pour effectuer des comparaisons de chaînes est plus complexe qu'il n'y paraît, car les alphabets diffèrent d'une langue à l'autre.\n\nLe navigateur doit donc connaître la langue à comparer.\n\nHeureusement, les navigateurs modernes prennent en charge la norme d'internationalisation [ECMA-402](https://www.ecma-international.org/publications-and-standards/standards/ecma-402/).\n\nElle fournit une méthode spéciale pour comparer des chaînes de caractères dans différentes langues, en respectant leurs règles.\n\nL'appel [str.localeCompare(str2)](mdn:js/String/localeCompare) renvoie un entier indiquant si `str` est inférieur, égal ou supérieur à `str2` selon les règles du langage :\n\n- Renvoie un nombre négatif si `str` est inférieur à `str2`\n- Renvoie un nombre positif si `str` est supérieur à `str2`\n- Renvoie `0` s'ils sont équivalents.\n\nPar exemple :\n\n```js run\nalert( 'Österreich'.localeCompare('Zealand') ); // -1\n```\n\nCette méthode a en fait deux arguments supplémentaires spécifiés dans [la documentation](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/String/localeCompare), ce qui lui permet de spécifier la langue (par défaut, pris dans l'environnement, l'ordre des lettres dépend de la langue) et de définir des règles supplémentaires telles que la sensibilité à la casse ou doit-on traiter `\"a\"` et `\"á\"` de la même manière, etc.\n\n## Résumé\n\n- Il existe 3 types de quotes. Les backticks permettent à une chaîne de caractères de s'étendre sur plusieurs lignes et d'intégrer des expressions `${…}`.\n- Nous pouvons utiliser des caractères spéciaux, comme un saut de ligne `\\n`.\n- Pour obtenir un caractère d'une string, utilisez `[]` ou la méthode `at`.\n- Pour obtenir un substring, utilisez `slice` ou `substring`.\n- Pour mettre une chaîne de caractères en minuscule ou en majuscule, utilisez `toLowerCase` ou `toUpperCase`.\n- Pour rechercher un substring, utilisez `indexOf`, ou `includes` / `startsWith` / `endsWith` pour de simple vérifications.\n- Pour comparer les chaînes de caractères en fonction de la langue, utilisez la méthode `localeCompare`, si non ils sont comparés par les codes de caractères.\n\nIl existe plusieurs autres méthodes utiles dans les strings :\n\n- `str.trim()` -- retire les espaces (\"trims\") du début et de la fin de la chaîne de caractères.\n- `str.repeat(n)` -- répète la chaîne de caractères `n` fois.\n- … et plus. Voir le [manuel](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/String) pour plus de détails.\n\nLes strings ont aussi des méthodes pour rechercher / remplacer avec des expressions régulières. Mais c’est un sujet important, il est donc expliqué dans une autre section de ce tutoriel <info:regular-expressions>.\n\nDe plus, à partir de maintenant, il est important de savoir que les chaînes de caractères sont basées sur l'encodage Unicode, et donc il y a des problèmes avec les comparaisons. Il y a plus d'informations sur Unicode dans le chapitre <info:unicode>.\n"
  },
  {
    "path": "1-js/05-data-types/04-array/1-item-value/solution.md",
    "content": "Le résultat est `4` :\n\n```js run\nlet fruits = [\"Apples\", \"Pear\", \"Orange\"];\n\nlet shoppingCart = fruits;\n\nshoppingCart.push(\"Banana\");\n\n*!*\nalert( fruits.length ); // 4\n*/!*\n```\n\nC'est parce que les tableaux sont des objets. Donc, shoppingCart et fruits sont les références du même tableau.\n"
  },
  {
    "path": "1-js/05-data-types/04-array/1-item-value/task.md",
    "content": "importance: 3\n\n---\n\n# Le tableau est-il copié ?\n\nQu'est-ce que ce code va montrer ?\n\n```js\nlet fruits = [\"Apples\", \"Pear\", \"Orange\"];\n\n// pousser une nouvelle valeur dans la \"copie\"\nlet shoppingCart = fruits;\nshoppingCart.push(\"Banana\");\n\n// Qu'y a-t-il dans fruits ?\nalert( fruits.length ); // ?\n```\n"
  },
  {
    "path": "1-js/05-data-types/04-array/10-maximal-subarray/_js.view/solution.js",
    "content": "function getMaxSubSum(arr) {\n  let maxSum = 0;\n  let partialSum = 0;\n\n  for (let item of arr) {\n    partialSum += item;\n    maxSum = Math.max(maxSum, partialSum);\n    if (partialSum < 0) partialSum = 0;\n  }\n  return maxSum;\n}"
  },
  {
    "path": "1-js/05-data-types/04-array/10-maximal-subarray/_js.view/test.js",
    "content": "describe(\"getMaxSubSum\", function() {\n  it(\"maximal subsum of [1, 2, 3] equals 6\", function() {\n    assert.equal(getMaxSubSum([1, 2, 3]), 6);\n  });\n\n  it(\"maximal subsum of [-1, 2, 3, -9] equals 5\", function() {\n    assert.equal(getMaxSubSum([-1, 2, 3, -9]), 5);\n  });\n\n  it(\"maximal subsum of [-1, 2, 3, -9, 11] equals 11\", function() {\n    assert.equal(getMaxSubSum([-1, 2, 3, -9, 11]), 11);\n  });\n\n  it(\"maximal subsum of [-2, -1, 1, 2] equals 3\", function() {\n    assert.equal(getMaxSubSum([-2, -1, 1, 2]), 3);\n  });\n\n  it(\"maximal subsum of [100, -9, 2, -3, 5] equals 100\", function() {\n    assert.equal(getMaxSubSum([100, -9, 2, -3, 5]), 100);\n  });\n\n  it(\"maximal subsum of [] equals 0\", function() {\n    assert.equal(getMaxSubSum([]), 0);\n  });\n\n  it(\"maximal subsum of [-1] equals 0\", function() {\n    assert.equal(getMaxSubSum([-1]), 0);\n  });\n\n  it(\"maximal subsum of [-1, -2] equals 0\", function() {\n    assert.equal(getMaxSubSum([-1, -2]), 0);\n  });\n\n  it(\"maximal subsum of [2, -8, 5, -1, 2, -3, 2] equals 6\", function() {\n    assert.equal(getMaxSubSum([2, -8, 5, -1, 2, -3, 2]), 6);\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/04-array/10-maximal-subarray/solution.md",
    "content": "# Solution lente\n\nNous pouvons calculer tous les subsums possibles.\n\nLe moyen le plus simple consiste à prendre chaque élément et à calculer les sommes de tous les sous-tableaux à partir de celui-ci.\n\nPar exemple, pour `[-1, 2, 3, -9, 11]` :\n\n```js no-beautify\n// Commence à -1 :\n-1\n-1 + 2\n-1 + 2 + 3\n-1 + 2 + 3 + (-9)\n-1 + 2 + 3 + (-9) + 11\n\n// Commence à 2 :\n2\n2 + 3\n2 + 3 + (-9)\n2 + 3 + (-9) + 11\n\n// Commence à 3 :\n3\n3 + (-9)\n3 + (-9) + 11\n\n// Commence à -9 :\n-9\n-9 + 11\n\n// Commence à 11 :\n11\n```\n\nLe code est en réalité une boucle imbriquée : la boucle externe recouvrant les éléments du tableau, et l'interne compte les sous-sommes commençant par l'élément en cours.\n\n```js run\nfunction getMaxSubSum(arr) {\n  let maxSum = 0; // si on ne prend aucun élément, zéro sera retourné\n\n  for (let i = 0; i < arr.length; i++) {\n    let sumFixedStart = 0;\n    for (let j = i; j < arr.length; j++) {\n      sumFixedStart += arr[j];\n      maxSum = Math.max(maxSum, sumFixedStart);\n    }\n  }\n\n  return maxSum;\n}\n\nalert( getMaxSubSum([-1, 2, 3, -9]) ); // 5\nalert( getMaxSubSum([-1, 2, 3, -9, 11]) ); // 11\nalert( getMaxSubSum([-2, -1, 1, 2]) ); // 3\nalert( getMaxSubSum([1, 2, 3]) ); // 6\nalert( getMaxSubSum([100, -9, 2, -3, 5]) ); // 100\n```\n\nLa solution a une complexité temporelle de [O(n<sup>2</sup>)](https://en.wikipedia.org/wiki/Big_O_notation). En d'autres termes, si nous augmentons la taille du tableau 2 fois, l'algorithme fonctionnera 4 fois plus longtemps.\n\nPour les grands tableaux (1'000, 10'000 éléments ou plus), de tels algorithmes peuvent conduire à une grande lenteur.\n\n# Solution rapide\n\nParcourons le tableau et conservons la somme partielle actuelle des éléments dans la variable `s`. Si `s` devient négatif à un moment donné, assignez `s=0`. Le maximum de tous ces `s` sera la réponse.\n\nSi la description est trop vague, veuillez voir le code, il est assez court :\n\n```js run demo\nfunction getMaxSubSum(arr) {\n  let maxSum = 0;\n  let partialSum = 0;\n\n  for (let item of arr) { // pour chaque élément d'arr\n    partialSum += item; // l'ajouter à partialSum\n    maxSum = Math.max(maxSum, partialSum); // mémorise le maximum\n    if (partialSum < 0) partialSum = 0; // zéro si négatif\n  }\n\n  return maxSum;\n}\n\nalert( getMaxSubSum([-1, 2, 3, -9]) ); // 5\nalert( getMaxSubSum([-1, 2, 3, -9, 11]) ); // 11\nalert( getMaxSubSum([-2, -1, 1, 2]) ); // 3\nalert( getMaxSubSum([100, -9, 2, -3, 5]) ); // 100\nalert( getMaxSubSum([1, 2, 3]) ); // 6\nalert( getMaxSubSum([-1, -2, -3]) ); // 0\n```\n\nL'algorithme nécessite exactement 1 passage de tableau, la complexité temporelle est donc O(n).\n\nVous pouvez trouver plus d'informations détaillées sur l'algorithme ici : [Maximum subarray problem](http://en.wikipedia.org/wiki/Maximum_subarray_problem). Si la raison de ce fonctionnement n’est pas encore évidente, tracez l’algorithme à partir des exemples ci-dessus et voyez comment il fonctionne.\n"
  },
  {
    "path": "1-js/05-data-types/04-array/10-maximal-subarray/task.md",
    "content": "importance: 2\n\n---\n\n# Un sous-tableau maximal\n\nL'entrée est un tableau de nombres, par exemple `arr = [1, -2, 3, 4, -9, 6]`.\n\nLa tâche est la suivante : trouver le sous-tableau contigu de `arr` avec la somme maximale des items.\n\nÉcrire la fonction `getMaxSubSum(arr)` qui retournera cette somme.\n\nPar exemple :\n\n```js\ngetMaxSubSum([-1, *!*2, 3*/!*, -9]) == 5 (la somme des éléments en surbrillance)\ngetMaxSubSum([*!*2, -1, 2, 3*/!*, -9]) == 6\ngetMaxSubSum([-1, 2, 3, -9, *!*11*/!*]) == 11\ngetMaxSubSum([-2, -1, *!*1, 2*/!*]) == 3\ngetMaxSubSum([*!*100*/!*, -9, 2, -3, 5]) == 100\ngetMaxSubSum([*!*1, 2, 3*/!*]) == 6 (prend tout)\n```\n\nSi tous les éléments sont négatifs, cela signifie que nous n'en prenons aucun (le sous-tableau est vide), la somme est donc zéro :\n\n```js\ngetMaxSubSum([-1, -2, -3]) = 0\n```\n\nS'il vous plaît essayez de penser à une solution rapide : [O(n<sup>2</sup>)](https://en.wikipedia.org/wiki/Big_O_notation) ou même à O(n) si vous le pouvez.\n"
  },
  {
    "path": "1-js/05-data-types/04-array/2-create-array/solution.md",
    "content": "```js run\nlet styles = [\"Jazz\", \"Blues\"];\nstyles.push(\"Rock-n-Roll\");\nstyles[Math.floor((styles.length - 1) / 2)] = \"Classics\";\nalert( styles.shift() );\nstyles.unshift(\"Rap\", \"Reggae\");\n```\n"
  },
  {
    "path": "1-js/05-data-types/04-array/2-create-array/task.md",
    "content": "importance: 5\n\n---\n\n# Opérations de tableaux\n\nEssayons 5 opérations de tableau.\n\n1. Créez un tableau `styles` avec les éléments \"Jazz\" et \"Blues\".\n2. Ajoutez \"Rock-n-Roll\" à la fin.\n3. Remplacez la valeur au milieu par \"Classiques\". Votre code pour trouver la valeur moyenne devrait fonctionner pour tous les tableaux de longueur impaire.\n4. Extrayez la première valeur du tableau et affichez-la.\n5. Ajoutez `Rap` et `Reggae` au tableau.\n\nLe processus du tableau :\n\n```js no-beautify\nJazz, Blues\nJazz, Blues, Rock-n-Roll\nJazz, Classics, Rock-n-Roll\nClassics, Rock-n-Roll\nRap, Reggae, Classics, Rock-n-Roll\n```\n"
  },
  {
    "path": "1-js/05-data-types/04-array/3-call-array-this/solution.md",
    "content": "L'appel de `arr[2]()` est syntaxiquement le bon vieux `obj[method]()`, dans le rôle de `obj` on a `arr`, et dans le rôle de `method` on a `2`.\n\nNous avons donc un appel de la fonction `arr[2]` comme méthode d'objet. Naturellement, il reçoit `this` en référençant l'objet `arr` et sort le tableau :\n\n```js run\nlet arr = [\"a\", \"b\"];\n\narr.push(function() {\n  alert( this );\n})\n\narr[2](); // a,b,function(){...}\n```\n\nLe tableau a 3 valeurs. Il en avait initialement deux, plus la fonction.\n"
  },
  {
    "path": "1-js/05-data-types/04-array/3-call-array-this/task.md",
    "content": "importance: 5\n\n---\n\n# Appel dans un contexte de tableau\n\nQuel est le résultat ? Et pourquoi ?\n\n```js\nlet arr = [\"a\", \"b\"];\n\narr.push(function() {\n  alert( this );\n});\n\narr[2](); // ?\n```\n"
  },
  {
    "path": "1-js/05-data-types/04-array/5-array-input-sum/solution.md",
    "content": "Veuillez noter le détail subtile mais important de la solution. Nous ne convertissons pas instantanément `value` en nombre après le `prompt`, parce qu'après `value = +value` nous ne pourrions pas distinguer une chaîne vide (signe d’arrêt) du zéro (nombre valide). Nous le faisons plus tard à la place.\n\n```js run demo\nfunction sumInput() {\n\n  let numbers = [];\n\n  while (true) {\n\n    let value = prompt(\"A number please?\", 0);\n\n    // devrions-nous annuler ?\n    if (value === \"\" || value === null || !isFinite(value)) break;\n\n    numbers.push(+value);\n  }\n\n  let sum = 0;\n  for (let number of numbers) {\n    sum += number;\n  }\n  return sum;\n}\n\nalert( sumInput() );\n```\n"
  },
  {
    "path": "1-js/05-data-types/04-array/5-array-input-sum/task.md",
    "content": "importance: 4\n\n---\n\n# Somme des nombres saisis\n\nÉcrivez la fonction `sumInput()` qui :\n\n- Demande à l'utilisateur des valeurs utilisant `prompt` et stocke les valeurs dans le tableau.\n- Finit de demander lorsque l'utilisateur entre une valeur non numérique, une chaîne vide ou appuie sur \"Annuler\".\n- Calcule et retourne la somme des éléments du tableau.\n\nP.S. Un zéro `0` est un nombre valide, donc s'il vous plaît n'arrêtez pas l'entrée sur zéro.\n\n[demo]\n"
  },
  {
    "path": "1-js/05-data-types/04-array/article.md",
    "content": "# Arrays\n\nLes objets vous permettent de stocker des collections de valeurs à clé. C'est très bien.\n\nMais assez souvent, nous trouvons qu'il nous faut une *collection ordonnée*, où nous avons un 1er, un 2ème, un 3ème élément, etc. Par exemple, nous avons besoin de cela pour stocker une liste de quelque chose : utilisateurs, trucs, éléments HTML, etc.\n\nIl n'est pas pratique d'utiliser un objet ici, car il ne fournit aucune méthode pour gérer l'ordre des éléments. Nous ne pouvons pas insérer une nouvelle propriété \"entre\" celles existantes. Les objets ne sont tout simplement pas destinés à un tel usage.\n\nIl existe une structure de données spéciale appelée `Array` (tableau), pour stocker les collections ordonnées.\n\n## Déclaration\n\nIl existe deux syntaxes pour créer un tableau vide :\n\n```js\nlet arr = new Array();\nlet arr = [];\n```\n\nLa plupart du temps c'est la deuxième syntaxe qui est utilisée. Nous pouvons fournir des éléments initiaux entre parenthèses :\n\n```js\nlet fruits = [\"Apple\", \"Orange\", \"Plum\"];\n```\n\nLes éléments de tableau sont numérotés en commençant par zéro.\n\nOn peut obtenir un élément par son numéro grace aux crochets :\n\n```js run\nlet fruits = [\"Apple\", \"Orange\", \"Plum\"];\n\nalert( fruits[0] ); // Apple\nalert( fruits[1] ); // Orange\nalert( fruits[2] ); // Plum\n```\n\nNous pouvons remplacer un élément :\n\n```js\nfruits[2] = 'Pear'; // maintenant [\"Apple\", \"Orange\", \"Pear\"]\n```\n\n...Ou en ajouter un nouveau au tableau :\n\n```js\nfruits[3] = 'Lemon'; // maintenant [\"Apple\", \"Orange\", \"Pear\", \"Lemon\"]\n```\n\nLe nombre total d'éléments dans le tableau est sa `length` (longueur) :\n\n```js run\nlet fruits = [\"Apple\", \"Orange\", \"Plum\"];\n\nalert( fruits.length ); // 3\n```\n\nNous pouvons également utiliser un `alert` pour afficher l'ensemble du tableau :\n\n```js run\nlet fruits = [\"Apple\", \"Orange\", \"Plum\"];\n\nalert( fruits ); // Apple,Orange,Plum\n```\n\nUn tableau peut stocker des éléments de tout type.\n\nPar exemple :\n\n```js run no-beautify\n// mélange de valeurs\nlet arr = [ 'Apple', { name: 'John' }, true, function() { alert('hello'); } ];\n\n// récupère l'objet à l'index 1 et montre ensuite son nom\nalert( arr[1].name ); // John\n\n// affiche la fonction à l'index 3 et l'exécute la\narr[3](); // hello\n```\n\n````smart header=\"Trailing comma (virgule de fin)\"\nUn tableau, comme pour un objet, peut se terminer par une virgule :\n```js\nlet fruits = [\n  \"Apple\",\n  \"Orange\",\n  \"Plum\"*!*,*/!*\n];\n```\n\nLe style \"virgule de fin\" facilite l'insertion et la suppression d'éléments, car toutes les lignes se ressemblent.\n````\n\n## Récupérer les derniers éléments avec \"at\"\n\n[recent browser=\"new\"]\n\nDisons que nous voulons le dernier élément du tableau.\n\nCertains langages de programmation permettent l'utilisation d'index négatifs pour ça, comme `fruits[-1]`.\n\nTandis qu'en JavaScript ça ne fonctionnera pas. Le résultat sera `undefined`, parce que l'index dans les crochets est traité littéralement.\n\nNous pouvons calculer explicitement l'index du dernier élément et donc y accéder: `fruits[fruits.length - 1]`.\n\n```js run\nlet fruits = [\"Apple\", \"Orange\", \"Plum\"];\n\nalert( fruits[fruits.length-1] ); // Plum\n```\n\nUn peu lourd, n'est-ce pas ? Nous devons écrire le même nom de variable deux fois.\n\nHeureusement, il y a une syntaxe plus courte : `fruits.at(-1)` :\n\n```js run\nlet fruits = [\"Apple\", \"Orange\", \"Plum\"];\n\n// Identique à fruits[fruits.length-1]\nalert( fruits.at(-1) ); // Plum\n```\n\nEn d'autres termes, `arr.at(i)`:\n\n- est exactement identique à `arr[i]`, si `i >= 0`.\n- pour les valeurs négatives de `i`, ça recule depuis la fin du tableau.\n\n## Les méthodes pop/push, shift/unshift\n\nUne [queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) (file d'attente) est l'une des utilisations les plus courantes pour les tableaux. En informatique, cela signifie une collection ordonnée d’éléments qui supporte deux opérations :\n\n- `push` ajoute un élément à la fin.\n- `shift` enlève un élément depuis le début, en faisant avancer la file d'attente, de sorte que le deuxième élément devienne le premier.\n\n![](queue.svg)\n\nLes tableaux prennent en charge les deux opérations.\n\nEn pratique, nous en avons besoin très souvent. Par exemple, une file d'attente de messages devant être affichés à l'écran.\n\nIl y a un autre cas d'utilisation pour les tableaux -- la structure de données nommée [stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)).\n\nIl supporte deux opérations :\n\n- `push` ajoute un élément à la fin.\n- `pop` enlève un élément de la fin.\n\nAinsi, de nouveaux éléments sont ajoutés ou enlevés toujours à partir de la \"fin\".\n\nUn stack (pile) est généralement illustrée par un jeu de cartes. De nouvelles cartes sont ajoutées ou enlevées par le haut :\n\n![](stack.svg)\n\nPour les stacks (piles), le dernier élément envoyé est reçu en premier, c'est le principe LIFO (Last-In-First-Out, dernier entré, premier sorti). Pour les files d'attente, nous avons FIFO (First-In-First-Out, premier entré, premier sorti).\n\nLes tableaux en JavaScript peuvent fonctionner à la fois en queue et en stack. Ils vous permettent d'ajouter ou supprimer des éléments à la fois par le début ou par la fin.\n\nEn informatique, la structure de données qui permet cela s'appelle [deque](https://en.wikipedia.org/wiki/Double-ended_queue).\n\n**Méthodes qui fonctionnent avec la fin du tableau :**\n\n`pop`\n: Extrait le dernier élément du tableau et le renvoie :\n\n    ```js run\n    let fruits = [\"Apple\", \"Orange\", \"Pear\"];\n\n    alert( fruits.pop() ); // supprime \"Pear\" et l'alerte\n\n    alert( fruits ); // Apple, Orange\n    ```\n\n    Les deux méthodes `fruits.pop()` et `fruits.at(-1)` renvoient le dernier élément du tableau, mais `fruits.pop()` modifie également le tableau en supprimant l'élément.\n\n`push`\n: Ajoute l'élément à la fin du tableau :\n\n    ```js run\n    let fruits = [\"Apple\", \"Orange\"];\n\n    fruits.push(\"Pear\");\n\n    alert( fruits ); // Apple, Orange, Pear\n    ```\n\n    L'appel de `fruits.push(...)` est égal à `fruits[fruits.length] = ...`.\n\n**Méthodes qui fonctionnent avec le début du tableau :**\n\n`shift`\n: Extrait le premier élément du tableau et le renvoie :\n\n    ```js run\n    let fruits = [\"Apple\", \"Orange\", \"Pear\"];\n\n    alert( fruits.shift() ); // supprime \"Apple\" et l'alerte\n\n    alert( fruits ); // Orange, Pear\n    ```\n\n`unshift`\n: Ajoute l'élément au début du tableau :\n\n    ```js run\n    let fruits = [\"Orange\", \"Pear\"];\n\n    fruits.unshift(\"Apple\");\n\n    alert( fruits ); // Apple, Orange, Pear\n    ```\n\nLes méthodes `push` et `unshift` peuvent ajouter plusieurs éléments à la fois :\n\n```js run\nlet fruits = [\"Apple\"];\n\nfruits.push(\"Orange\", \"Peach\");\nfruits.unshift(\"Pineapple\", \"Lemon\");\n\n// [\"Pineapple\", \"Lemon\", \"Apple\", \"Orange\", \"Peach\"]\nalert( fruits );\n```\n\n## Les internes\n\nUn tableau est un type d'objet spécial. Les crochets utilisés pour accéder à la propriété `arr[0]` proviennent en fait de la syntaxe de l'objet. C'est essentiellement la même chose que `obj[key]`, où `arr` est l'objet, tandis que les nombres sont utilisés comme clés.\n\nIls étendent les objets en fournissant des méthodes spéciales pour travailler avec des collections ordonnées de données ainsi que la propriété `length`. Mais au fond c'est toujours un objet.\n\nN'oubliez pas qu'il n'y a que huit types de base en JavaScript (voir le chapitre [Les types de données](info:types) pour plus d'infos). `Array` est un objet et se comporte donc comme un objet.\n\nPar exemple, il est copié par référence :\n\n```js run\nlet fruits = [\"Banana\"]\n\nlet arr = fruits; // copier par référence (deux variables font référence au même tableau)\n\nalert( arr === fruits ); // true\n\narr.push(\"Pear\"); // modifie le tableau par référence\n\nalert( fruits ); // Banana, Pear - 2 items maintenant\n```\n\n...Mais ce qui rend les tableaux vraiment spéciaux, c'est leur représentation interne. Le moteur tente de stocker ses éléments dans une zone de mémoire contiguë, l'un après l'autre, exactement comme le montrent les illustrations de ce chapitre. Il existe également d'autres optimisations permettant de faire fonctionner les tableaux très rapidement.\n\nMais ils se cassent tous si nous arrêtons de travailler avec un tableau comme avec une \"collection ordonnée\" et commençons à le travailler comme s'il s'agissait d'un objet normal.\n\nPar exemple, techniquement, nous pouvons faire ceci :\n\n```js\nlet fruits = []; // créer un tableau\n\nfruits[99999] = 5; // assigne une propriété avec un index beaucoup plus grand que sa longueur\n\nfruits.age = 25; // créer une propriété avec un nom arbitraire\n```\n\nC'est possible, car les tableaux sont des objets à leur base. Nous pouvons leur ajouter des propriétés.\n\nMais le moteur verra que nous travaillons avec le tableau comme avec un objet normal. Les optimisations spécifiques à un tableau ne sont pas adaptées à ce type de situation et seront désactivées. Leurs avantages disparaissent.\n\nLes moyens de casser un tableau :\n\n- Ajouter une propriété non numérique comme `arr.test = 5`.\n- Faire des trous, comme ajouter `arr[0]` et ensuite `arr[1000]` (et rien entre eux).\n- Remplire le tableau dans l'ordre inverse, comme `arr[1000]`, `arr[999]` etc.\n\nVeuillez considérer les tableaux comme des structures spéciales pour travailler avec les *données ordonées*. Ils fournissent des méthodes spéciales pour cela. Les tableaux sont soigneusement réglés dans les moteurs JavaScript pour fonctionner avec des données ordonnées contiguës, veuillez les utiliser de cette manière. Et si vous avez besoin de clés arbitraires, il y a de fortes chances pour que vous ayez réellement besoin d'un objet régulier `{}`.\n\n## Performance\n\nLes méthodes `push`/`pop` vont vite, alors que `shift`/`unshift` sont lentes.\n\n![](array-speed.svg)\n\nPourquoi est-il plus rapide de travailler avec la fin d'un tableau qu'avec son début ? Voyons ce qui se passe pendant l'exécution :\n\n```js\nfruits.shift(); // prends 1 élément du début\n```\n\nIl ne suffit pas de prendre l'élément avec le nombre `0`. D'autres éléments doivent également être renumérotés.\n\nL'opération `shift` doit faire 3 choses :\n\n1. Supprimer l'élément avec l'index `0`.\n2. Déplacer tous les éléments à gauche, les renuméroter de l'index `1` à `0`, de`2` à `1`, etc.\n3. Mettre à jour la propriété `length`.\n\n![](array-shift.svg)\n\n**Plus il y a d'éléments dans le tableau, plus il y faut de temps pour les déplacer, plus il y a d'opérations en mémoire.**\n\nLa même chose se produit avec `unshift`. Pour ajouter un élément au début du tableau, nous devons d’abord déplacer les éléments existants vers la droite, en augmentant leur index.\n\nEt qu’en est-il avec `push`/`pop` ? Ils n'ont pas besoin de déplacer quoi que ce soit. Pour extraire un élément de la fin, la méthode `pop` nettoie l'index et raccourcit `length`.\n\nLes actions pour l'opération `pop` :\n\n```js\nfruits.pop(); // enleve 1 élément de la fin\n```\n\n![](array-pop.svg)\n\n**La méthode `pop` n'a pas besoin de déplacer quoi que ce soit, car les autres éléments conservent leurs index. C'est pourquoi c'est extrêmement rapide.**\n\nLa même chose avec la méthode `push`.\n\n## Boucles\n\nL'une des méthodes les plus anciennes pour cycler des éléments de tableau est la boucle `for` sur les index :\n\n```js run\nlet arr = [\"Apple\", \"Orange\", \"Pear\"];\n\n*!*\nfor (let i = 0; i < arr.length; i++) {\n*/!*\n  alert( arr[i] );\n}\n```\n\nMais pour les tableaux, il existe une autre forme de boucle, `for..of` :\n\n```js run\nlet fruits = [\"Apple\", \"Orange\", \"Plum\"];\n\n// itère sur des éléments de tableau\nfor (let fruit of fruits) {\n  alert( fruit );\n}\n```\n\nLe `for..of` ne donne pas accès au numéro de l'élément actuel, mais à sa valeur, mais dans la plupart des cas, cela suffit. Et c'est plus court.\n\nTechniquement, comme les tableaux sont des objets, il est également possible d'utiliser `for..in` :\n\n```js run\nlet arr = [\"Apple\", \"Orange\", \"Pear\"];\n\n*!*\nfor (let key in arr) {\n*/!*\n  alert( arr[key] ); // Apple, Orange, Pear\n}\n```\n\nMais c'est en fait une mauvaise idée. Il y a des problèmes potentiels avec cela :\n\n1. La boucle `for..in` itère sur *toutes les propriétés*, pas seulement les propriétés numériques.\n\n    Il existe des objets dits \"array-like\" dans le navigateur et dans d'autres environnements, qui *ressemblent à des tableaux*. C'est-à-dire qu'ils ont les propriétés `length` et index, mais ils peuvent également avoir d'autres propriétés et méthodes non numériques, dont nous n'avons généralement pas besoin. La boucle `for..in` les listera cependant. Donc, si nous devons travailler avec des objets de type tableau, ces propriétés \"supplémentaires\" peuvent devenir un problème.\n\n2. La boucle `for..in` est optimisée pour les objets génériques, pas pour les tableaux, elle est 10-100 fois plus lente. Bien sûr, c'est encore très rapide. L'accélération peut n'importer que dans les goulots d'étranglement ou sembler hors de propos. Mais il faut quand même être conscient de la différence.\n\nEn règle générale, nous ne devrions pas utiliser `for..in` pour les tableaux.\n\n## Un mot à propos de \"length\"\n\nLa propriété `length` est automatiquement mise à jour lorsque nous modifions le tableau. Pour être précis, il ne s'agit pas du nombre de valeurs du tableau, mais du plus grand index numérique plus un.\n\nPar exemple, un seul élément avec un grand index donne une grande longueur :\n\n```js run\nlet fruits = [];\nfruits[123] = \"Apple\";\n\nalert( fruits.length ); // 124\n```\n\nNotez que nous n'utilisons généralement pas de tableaux de ce type.\n\nUne autre chose intéressante à propos de la propriété `length` est qu'elle est accessible en écriture.\n\nSi nous l'augmentons manuellement, rien d'intéressant ne se produit. Mais si nous le diminuons, le tableau est tronqué. Le processus est irréversible, voici l'exemple :\n\n```js run\nlet arr = [1, 2, 3, 4, 5];\n\narr.length = 2; // tronque à 2 éléments\nalert( arr ); // [1, 2]\n\narr.length = 5; // retourne la length d'origine\nalert( arr[3] ); // undefined: les valeurs ne reviennent pas\n```\n\nAinsi, le moyen le plus simple pour effacer le tableau est `arr.length = 0;`.\n\n## new Array() [#new-array]\n\nIl y a une syntaxe supplémentaire pour créer un tableau :\n\n```js\nlet arr = *!*new Array*/!*(\"Apple\", \"Pear\", \"etc\");\n```\n\nIl est rarement utilisé, car les crochets `[]` sont plus courts. En outre, il comporte une caractéristique délicate.\n\nSi `new Array` est appelé avec un seul argument qui est un nombre, il crée un tableau *sans éléments, mais avec la longueur donnée*.\n\nVoyons comment on peut se tirer une balle dans le pied :\n\n```js run\nlet arr = new Array(2); // va-t-il créer un tableau de [2] ?\n\nalert( arr[0] ); // undefined! pas d'éléments.\n\nalert( arr.length ); // length 2\n```\n\nPour éviter de telles surprises, nous utilisons généralement des crochets, sauf si nous savons vraiment ce que nous faisons.\n\n## Tableaux multidimensionnels\n\nLes tableaux peuvent avoir des éléments qui sont aussi des tableaux. On peut l'utiliser pour des tableaux multidimensionnels, pour stocker des matrices :\n\n```js run\nlet matrix = [\n  [1, 2, 3],\n  [4, 5, 6],\n  [7, 8, 9]\n];\n\nalert( matrix[1][1] ); // 5, l'élément central\n```\n\n## toString\n\nLes tableaux ont leur propre implémentation de la méthode `toString` qui renvoie une liste d'éléments séparés par des virgules.\n\nPar exemple :\n\n```js run\nlet arr = [1, 2, 3];\n\nalert( arr ); // 1,2,3\nalert( String(arr) === '1,2,3' ); // true\n```\n\nAussi, essayons ceci :\n\n```js run\nalert( [] + 1 ); // \"1\"\nalert( [1] + 1 ); // \"11\"\nalert( [1,2] + 1 ); // \"1,21\"\n```\n\nLes tableaux n'ont pas de `Symbol.toPrimitive`, ni de `valueOf` viable, ils implémentent uniquement la conversion `toString`, donc ici `[]` devient une chaîne vide, `[1]` devient `\"1\"` et `[1,2]` devient `\"1,2\"`.\n\nLorsque l'opérateur binaire plus `+` ajoute quelque chose à une chaîne, il la convertit également en chaîne, de sorte que l'étape suivante se présente comme suit :\n\n```js run\nalert( \"\" + 1 ); // \"1\"\nalert( \"1\" + 1 ); // \"11\"\nalert( \"1,2\" + 1 ); // \"1,21\"\n```\n\n## Ne comparez pas les tableaux avec ==\n\nLes tableaux en JavaScript, contrairement à certains autres langages de programmation, ne doivent pas être comparés avec l'opérateur `==`.\n\nCet opérateur n'a pas de traitement spécial pour les tableaux, il fonctionne avec eux comme avec n'importe quel objet.\n\nRappelons les règles :\n\n- Deux objets sont égaux `==` uniquement s'ils font référence au même objet.\n- Si l'un des arguments de `==` est un objet, et l'autre est une primitive, alors l'objet est converti en primitif, comme expliqué dans le chapitre <info:object-toprimitive>.\n- ...À l'exception de `null` et `undefined` qui s'égalent `==` l'un l'autre et rien d'autre.\n\nLa comparaison stricte `===` est encore plus simple, car elle ne convertit pas les types.\n\nDonc, si nous comparons des tableaux avec `==`, ils ne sont jamais les mêmes, sauf si nous comparons deux variables qui référencent exactement le même tableau.\n\nPar exemple :\n\n```js run\nalert( [] == [] ); // false\nalert( [0] == [0] ); // false\n```\n\nCes tableaux sont des objets techniquement différents. Donc, ils ne sont pas égaux. L'opérateur `==` ne fait pas de comparaison élément par élément.\n\nLa comparaison avec les primitives peut également donner des résultats apparemment étranges :\n\n```js run\nalert( 0 == [] ); // true\n\nalert('0' == [] ); // false\n```\n\nIci, dans les deux cas, nous comparons une primitive à un objet tableau. Ainsi, le tableau `[]` est converti en primitive à des fins de comparaison et devient une chaîne vide `''`.\n\nEnsuite, le processus de comparaison se poursuit avec les primitives, comme décrit dans le chapitre <info:type-conversions> :\n\n```js run\n// après que [] soit converti vers ''\nalert( 0 == '' ); // true, car '' est converti en nombre 0\n\nalert('0' == '' ); // false, pas de conversion de type, différentes chaînes de caractères\n```\n\nAlors, comment comparer des tableaux ?\n\nC'est simple, n'utilisez pas l'opérateur `==`. Au lieu de cela, comparez-les élément par élément dans une boucle ou en utilisant les méthodes d'itération expliquées dans le chapitre suivant.\n\n## Résumé\n\nArray est un type d’objet spécial, adapté au stockage et à la gestion des éléments de données ordonnées.\n\n- La déclaration :\n\n    ```js\n    // crochets (habituel)\n    let arr = [item1, item2...];\n\n    // new Array (exceptionnellement rare)\n    let arr = new Array(item1, item2...);\n    ```\n\n    L'appel de `new Array(number)` crée un tableau de longueur donnée, mais sans éléments.\n\n- La propriété `length` est la longueur du tableau ou, plus précisément, son dernier index numérique plus un. Il est auto-ajusté par les méthodes de tableau.\n- Si nous raccourcissons `length` manuellement, le tableau est tronqué.\n\nObtenir les éléments :\n\n- nous pouvons obtenir un élément par son index, comme `arr[0]`\n- nous pouvons également utiliser la méthode `at(i)` qui autorise les index négatifs. Pour les valeurs négatives de `i`, il recule à partir de la fin du tableau. Si `i >= 0`, cela fonctionne comme `arr[i]`.\n\nNous pouvons utiliser un tableau comme deque avec les opérations suivantes :\n\n- `push(...items)` ajoute `items` à la fin.\n- `pop()` supprime l'élément de la fin et le renvoie.\n- `shift()` supprime l'élément du début et le renvoie.\n- `unshift(... items)` ajoute des `items` au début.\n\nPour boucler sur les éléments du tableau :\n\n- `for (let i = 0; i <arr.length; i++)` -- fonctionne le plus rapidement, compatible avec les anciens navigateurs.\n- `for (let item of arr)` -- la syntaxe moderne pour les éléments uniquement.\n- `pour (let i in arr)` -- ne jamais utiliser.\n\nPour comparer des tableaux, n'utilisez pas l'opérateur `==` (ainsi que `>`, `<` et autres), car ils n'ont pas de traitement spécial pour les tableaux. Ils les traitent comme n'importe quel objet, et ce n'est pas ce que nous voulons habituellement.\n\nA la place, vous pouvez utiliser la boucle `for..of` pour comparer les tableaux élément par élément.\n\nNous continuerons avec les tableaux et étudierons d'autres méthodes pour ajouter, supprimer, extraire des éléments et trier des tableaux dans le prochain chapitre <info:array-methods>.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/1-camelcase/_js.view/solution.js",
    "content": "function camelize(str) {\n  return str\n    .split('-') // divise 'my-long-word' en tableau ['my', 'long', 'word']\n    .map( \n\t// capitalise les premières lettres de tous les éléments du tableau sauf le premier\n     \t// convertit ['my', 'long', 'word'] en ['my', 'Long', 'Word']\n      (word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1)\n    )\n    .join(''); // rejoint ['my', 'Long', 'Word'] en -> myLongWord\n}\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/1-camelcase/_js.view/test.js",
    "content": "describe(\"camelize\", function() {\n\n  it(\"leaves an empty line as is\", function() {\n    assert.equal(camelize(\"\"), \"\");\n  });\n\n  it(\"turns background-color into backgroundColor\", function() {\n    assert.equal(camelize(\"background-color\"), \"backgroundColor\");\n  });\n\n  it(\"turns list-style-image into listStyleImage\", function() {\n    assert.equal(camelize(\"list-style-image\"), \"listStyleImage\");\n  });\n\n  it(\"turns -webkit-transition into WebkitTransition\", function() {\n    assert.equal(camelize(\"-webkit-transition\"), \"WebkitTransition\");\n  });\n\n});"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/1-camelcase/solution.md",
    "content": ""
  },
  {
    "path": "1-js/05-data-types/05-array-methods/1-camelcase/task.md",
    "content": "importance: 5\n\n---\n\n# Traduit border-left-width en borderLeftWidth\n\nEcrivez la fonction `camelize(str)` qui change les mots séparés par des tirets comme \"my-short-string\" en camel-cased \"myShortString\".\n\nLa fonction doit donc supprimer tous les tirets et mettre en majuscule la première lettre de chaque mot à partir du deuxième mot.\n\nExemples :\n\n```js\ncamelize(\"background-color\") == 'backgroundColor';\ncamelize(\"list-style-image\") == 'listStyleImage';\ncamelize(\"-webkit-transition\") == 'WebkitTransition';\n```\n\nP.S. Astuce : utilisez `split` pour scinder la chaîne dans un tableau, transformer la et ensuite utilisez `join`.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/10-average-age/solution.md",
    "content": "```js run\nfunction getAverageAge(users) {\n  return users.reduce((prev, user) => prev + user.age, 0) / users.length;\n}\n\nlet john = { name: \"John\", age: 25 };\nlet pete = { name: \"Pete\", age: 30 };\nlet mary = { name: \"Mary\", age: 29 };\n\nlet arr = [ john, pete, mary ];\n\nalert( getAverageAge(arr) ); // 28\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/10-average-age/task.md",
    "content": "importance: 4\n\n---\n\n# Obtenir l'âge moyen\n\nEcrivez la fonction `getAverageAge(users)` qui obtient un tableau d'objets avec la propriété `age` et qui ensuite retourne l'age moyen.\n\nLa formule pour la moyenne est `(age1 + age2 + ... + ageN) / N`.\n\nPar exemple:\n\n```js no-beautify\nlet john = { name: \"John\", age: 25 };\nlet pete = { name: \"Pete\", age: 30 };\nlet mary = { name: \"Mary\", age: 29 };\n\nlet arr = [ john, pete, mary ];\n\nalert( getAverageAge(arr) ); // (25 + 30 + 29) / 3 = 28\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/11-array-unique/_js.view/solution.js",
    "content": "function unique(arr) {\n  let result = [];\n\n  for (let str of arr) {\n    if (!result.includes(str)) {\n      result.push(str);\n    }\n  }\n\n  return result;\n}\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/11-array-unique/_js.view/test.js",
    "content": "describe(\"unique\", function() {\n  it(\"removes non-unique elements\", function() {\n    let strings = [\"Hare\", \"Krishna\", \"Hare\", \"Krishna\",\n      \"Krishna\", \"Krishna\", \"Hare\", \"Hare\", \":-O\"\n    ];\n\n    assert.deepEqual(unique(strings), [\"Hare\", \"Krishna\", \":-O\"]);\n  });\n\n  it(\"does not change the source array\", function() {\n    let strings = [\"Krishna\", \"Krishna\", \"Hare\", \"Hare\"];\n    unique(strings);\n    assert.deepEqual(strings, [\"Krishna\", \"Krishna\", \"Hare\", \"Hare\"]);\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/11-array-unique/solution.md",
    "content": "Parcourons les éléments du tableau:\n- Pour chaque élément, nous vérifierons si le tableau résultant contient déjà cet élément.\n- S'il en est ainsi, alors ignorez-le, sinon ajoutez aux résultats.\n\n```js run\nfunction unique(arr) {\n  let result = [];\n\n  for (let str of arr) {\n    if (!result.includes(str)) {\n      result.push(str);\n    }\n  }\n\n  return result;\n}\n\nlet strings = [\"Hare\", \"Krishna\", \"Hare\", \"Krishna\",\n  \"Krishna\", \"Krishna\", \"Hare\", \"Hare\", \":-O\"\n];\n\nalert( unique(strings) ); // Hare, Krishna, :-O\n```\n\nLe code fonctionne, mais il comporte un problème de performances potentiel.\n\nLa méthode `result.includes(str)` parcourt en interne le tableau `result` et compare chaque élément à `str` pour trouver la correspondance.\n\nDonc, s'il y a `100` éléments dans `result` et que personne ne correspond à `str`, alors il parcourra tout le `result` et fera exactement les `100` comparaisons. Et si `result` est grand, exemple `10000`, alors il y aura des `10000` comparaisons .\n\nCe n'est pas un problème en soi, parce que les moteurs JavaScript sont très rapides, alors parcourir un tableau de `10000` éléments  est une question de microsecondes.\n\nMais nous faisons ce test pour chaque élément de `arr`, dans la boucle `for`.\n\nDonc, si `arr.length` vaut `10000`, nous aurons quelque chose comme `10000*10000` = 100 millions de comparaisons. C'est beaucoup.\n\nLa solution n’est donc valable que pour les petits tableaux.\n\nPlus loin dans le chapitre <info:map-set>, nous verrons comment l'optimiser.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/11-array-unique/task.md",
    "content": "importance: 4 \n\n---\n\n# Filtrer les membres uniques du tableau\n\n`arr` est un tableau.\n\nCréez une fonction `unique(arr)` qui devrait renvoyer un tableau avec des éléments uniques de `arr`.\n\nPar exemple:\n\n```js\nfunction unique(arr) {\n  /* votre code */\n}\n\nlet strings = [\"Hare\", \"Krishna\", \"Hare\", \"Krishna\",\n  \"Krishna\", \"Krishna\", \"Hare\", \"Hare\", \":-O\"\n];\n\nalert( unique(strings) ); // Hare, Krishna, :-O\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/12-reduce-object/_js.view/solution.js",
    "content": "function groupById(array) {\n  return array.reduce((obj, value) => {\n    obj[value.id] = value;\n    return obj;\n  }, {})\n}\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/12-reduce-object/_js.view/test.js",
    "content": "describe(\"groupById\", function() {\n\n  it(\"creates an object grouped by id\", function() {\n    let users = [\n      {id: 'john', name: \"John Smith\", age: 20},\n      {id: 'ann', name: \"Ann Smith\", age: 24},\n      {id: 'pete', name: \"Pete Peterson\", age: 31},\n    ];\n\n    assert.deepEqual(groupById(users), {\n      john: {id: 'john', name: \"John Smith\", age: 20},\n      ann: {id: 'ann', name: \"Ann Smith\", age: 24},\n      pete: {id: 'pete', name: \"Pete Peterson\", age: 31},\n    });\n  });\n\n  it(\"works with an empty array\", function() {\n    users = [];\n    assert.deepEqual(groupById(users), {});\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/12-reduce-object/solution.md",
    "content": ""
  },
  {
    "path": "1-js/05-data-types/05-array-methods/12-reduce-object/task.md",
    "content": "importance: 4\n\n---\n\n# Create keyed object from array\n\nLet's say we received an array of users in the form `{id:..., name:..., age:... }`.\n\nCreate a function `groupById(arr)` that creates an object from it, with `id` as the key, and array items as values.\n\nFor example:\n\n```js\nlet users = [\n  {id: 'john', name: \"John Smith\", age: 20},\n  {id: 'ann', name: \"Ann Smith\", age: 24},\n  {id: 'pete', name: \"Pete Peterson\", age: 31},\n];\n\nlet usersById = groupById(users);\n\n/*\n// after the call we should have:\n\nusersById = {\n  john: {id: 'john', name: \"John Smith\", age: 20},\n  ann: {id: 'ann', name: \"Ann Smith\", age: 24},\n  pete: {id: 'pete', name: \"Pete Peterson\", age: 31},\n}\n*/\n```\n\nSuch function is really handy when working with server data.\n\nIn this task we assume that `id` is unique. There may be no two array items with the same `id`.\n\nPlease use array `.reduce` method in the solution.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/2-filter-range/_js.view/solution.js",
    "content": "\nfunction filterRange(arr, a, b) {\n  // added brackets around the expression for better readability\n  return arr.filter(item => (a <= item && item <= b));\n}"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/2-filter-range/_js.view/test.js",
    "content": "describe(\"filterRange\", function() {\n\n  it(\"returns the filtered values\", function() {\n\n    let arr = [5, 3, 8, 1];\n\n    let filtered = filterRange(arr, 1, 4); \n\n    assert.deepEqual(filtered, [3, 1]);\n  });\n\n  it(\"doesn't change the array\", function() {\n\n    let arr = [5, 3, 8, 1];\n\n    let filtered = filterRange(arr, 1, 4); \n\n    assert.deepEqual(arr, [5,3,8,1]);\n  });\n\n});"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/2-filter-range/solution.md",
    "content": "```js run demo\nfunction filterRange(arr, a, b) {\n  // ajout de crochets autour de l'expression pour une meilleure lisibilité\n  return arr.filter(item => (a <= item && item <= b));\n}\n\nlet arr = [5, 3, 8, 1];\n\nlet filtered = filterRange(arr, 1, 4);\n\nalert( filtered ); // 3,1 (valeur correspondate)\n\nalert( arr ); // 5,3,8,1 (non modifié)\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/2-filter-range/task.md",
    "content": "importance: 4\n\n---\n\n# Filter range\n\nEcrivez une fonction `filterRange(arr, a, b)` qui obtient un tableau `arr`, recherche les éléments avec des valeurs supérieures ou égales à `a` et inférieures ou égales à `b` et retourne un résultat sous forme de tableau.\n\nLa fonction ne doit pas modifier le tableau. Elle doit juste retourner le nouveau tableau.\n\nPar exemple :\n\n```js\nlet arr = [5, 3, 8, 1];\n\nlet filtered = filterRange(arr, 1, 4);\n\nalert( filtered ); // 3,1 (valeurs correspondantes)\n\nalert( arr ); // 5,3,8,1 (non modifié)\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js",
    "content": "\nfunction filterRangeInPlace(arr, a, b) {\n\n  for (let i = 0; i < arr.length; i++) {\n    let val = arr[i];\n\t  \n\t// enleve si en dehors de l'intervalle\n    if (val < a || val > b) {\n      arr.splice(i, 1);\n      i--;\n    }\n  }\n\n}\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/test.js",
    "content": "describe(\"filterRangeInPlace\", function() {\n\n  it(\"returns the filtered values\", function() {\n\n    let arr = [5, 3, 8, 1];\n\n    filterRangeInPlace(arr, 2, 5); \n\n    assert.deepEqual(arr, [5, 3]);\n  });\n\n  it(\"doesn't return anything\", function() {\n    assert.isUndefined(filterRangeInPlace([1,2,3], 1, 4)); \n  });\n\n});\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md",
    "content": "```js run demo\nfunction filterRangeInPlace(arr, a, b) {\n\n  for (let i = 0; i < arr.length; i++) {\n    let val = arr[i];\n\n    // enleve si en dehors de l'intervalle\n    if (val < a || val > b) {\n      arr.splice(i, 1);\n      i--;\n    }\n  }\n\n}\n\nlet arr = [5, 3, 8, 1];\n\nfilterRangeInPlace(arr, 1, 4); // supprime les nombres sauf de 1 à 4\n\nalert( arr ); // [3, 1]\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/3-filter-range-in-place/task.md",
    "content": "importance: 4\n\n---\n\n# Filter range \"in place\"\n\nEcrivez une fonction `filterRangeInPlace(arr, a, b)` qui obtient un tableau `arr` et en supprime toutes les valeurs, sauf celles comprises entre `a` et `b`. Le test est : `a ≤ arr[i] ≤ b`.\n\nLa fonction doit juste modifier que le tableau. Elle ne doit rien retourner.\n\nPar exemple :\n\n```js\nlet arr = [5, 3, 8, 1];\n\nfilterRangeInPlace(arr, 1, 4); // supprime les nombres qui ne sont pas entre 1 et 4\n\nalert( arr ); // [3, 1]\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/4-sort-back/solution.md",
    "content": "```js run\nlet arr = [5, 2, 1, -10, 8];\n\narr.sort((a, b) => b - a);\n\nalert( arr );\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/4-sort-back/task.md",
    "content": "importance: 4\n\n---\n\n# Trier par ordre décroissant\n\n```js\nlet arr = [5, 2, 1, -10, 8];\n\n// ...  votre code pour le trier par ordre décroissant\n\nalert( arr ); // 8, 5, 2, 1, -10\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/5-copy-sort-array/solution.md",
    "content": "Nous pouvons utiliser `slice()` pour faire une copie et exécuter le tri sur celle-ci :\n\n```js run\nfunction copySorted(arr) {\n  return arr.slice().sort();\n}\n\nlet arr = [\"HTML\", \"JavaScript\", \"CSS\"];\n\n*!*\nlet sorted = copySorted(arr);\n*/!*\n\nalert( sorted );\nalert( arr );\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/5-copy-sort-array/task.md",
    "content": "importance: 5\n\n---\n\n# Copier et trier le tableau\n\nNous avons un tableau de chaînes `arr`. Nous aimerions en avoir une copie triée, mais sans modifier `arr`.\n\nCréez une fonction `copySorted(arr)` qui renvoie une copie triée.\n\n```js\nlet arr = [\"HTML\", \"JavaScript\", \"CSS\"];\n\nlet sorted = copySorted(arr);\n\nalert( sorted ); // CSS, HTML, JavaScript\nalert( arr ); // HTML, JavaScript, CSS (aucune modification)\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/6-array-get-names/solution.md",
    "content": "```js run\n\nlet john = { name: \"John\", age: 25 };\nlet pete = { name: \"Pete\", age: 30 };\nlet mary = { name: \"Mary\", age: 28 };\n\nlet users = [ john, pete, mary ];\n\nlet names = users.map(item => item.name);\n\nalert( names ); // John, Pete, Mary\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/6-array-get-names/task.md",
    "content": "importance: 5\n\n---\n\n# Map en noms\n\nVous avez un tableau d'objets `user`, chacun ayant `user.name`. Écrivez le code qui le convertit en un tableau de noms.\n\nPar exemple :\n\n```js no-beautify\nlet john = { name: \"John\", age: 25 };\nlet pete = { name: \"Pete\", age: 30 };\nlet mary = { name: \"Mary\", age: 28 };\n\nlet users = [ john, petemary ];\n\nlet names = /* ... votre code */\n\nalert( names ); // John, Pete, Mary\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/6-calculator-extendable/_js.view/solution.js",
    "content": "function Calculator() {\n\n  this.methods = {\n    \"-\": (a, b) => a - b,\n    \"+\": (a, b) => a + b\n  };\n\n  this.calculate = function(str) {\n\n    let split = str.split(' '),\n      a = +split[0],\n      op = split[1],\n      b = +split[2];\n\n    if (!this.methods[op] || isNaN(a) || isNaN(b)) {\n      return NaN;\n    }\n\n    return this.methods[op](a, b);\n  };\n\n  this.addMethod = function(name, func) {\n    this.methods[name] = func;\n  };\n}\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/6-calculator-extendable/_js.view/test.js",
    "content": "describe(\"Calculator\", function() {\n  let calculator;\n\n  before(function() {\n    calculator = new Calculator;\n  });\n\n  it(\"calculate(12 + 34) = 46\", function() {\n    assert.equal(calculator.calculate(\"12 + 34\"), 46);\n  });\n\n  it(\"calculate(34 - 12) = 22\", function() {\n    assert.equal(calculator.calculate(\"34 - 12\"), 22);\n  });\n\n  it(\"add multiplication: calculate(2 * 3) = 6\", function() {\n    calculator.addMethod(\"*\", (a, b) => a * b);\n    assert.equal(calculator.calculate(\"2 * 3\"), 6);\n  });\n\n  it(\"add power: calculate(2 ** 3) = 8\", function() {\n    calculator.addMethod(\"**\", (a, b) => a ** b);\n    assert.equal(calculator.calculate(\"2 ** 3\"), 8);\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/6-calculator-extendable/solution.md",
    "content": "\n- Please note how methods are stored. They are simply added to `this.methods` property.\n- All tests and numeric conversions are done in the `calculate` method. In future it may be extended to support more complex expressions.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/6-calculator-extendable/task.md",
    "content": "importance: 5\n\n---\n\n# Create an extendable calculator\n\nCreate a constructor function `Calculator` that creates \"extendable\" calculator objects.\n\nThe task consists of two parts.\n\n1. First, implement the method `calculate(str)` that takes a string like `\"1 + 2\"` in the format \"NUMBER operator NUMBER\" (space-delimited) and returns the result. Should understand plus `+` and minus `-`.\n\n    Usage example:\n\n    ```js\n    let calc = new Calculator;\n\n    alert( calc.calculate(\"3 + 7\") ); // 10\n    ```\n\n2. Then add the method `addMethod(name, func)` that teaches the calculator a new operation. It takes the operator `name` and the two-argument function `func(a,b)` that implements it.\n\n    For instance, let's add the multiplication `*`, division `/` and power `**`:\n\n    ```js\n    let powerCalc = new Calculator;\n    powerCalc.addMethod(\"*\", (a, b) => a * b);\n    powerCalc.addMethod(\"/\", (a, b) => a / b);\n    powerCalc.addMethod(\"**\", (a, b) => a ** b);\n\n    let result = powerCalc.calculate(\"2 ** 3\");\n    alert( result ); // 8\n    ```\n\n- No parentheses or complex expressions in this task.\n- The numbers and the operator are delimited with exactly one space.\n- There may be error handling if you'd like to add it.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/7-map-objects/solution.md",
    "content": "\n```js run no-beautify\nlet john = { name: \"John\", surname: \"Smith\", id: 1 };\nlet pete = { name: \"Pete\", surname: \"Hunt\", id: 2 };\nlet mary = { name: \"Mary\", surname: \"Key\", id: 3 };\n\nlet users = [ john, pete, mary ];\n\n*!*\nlet usersMapped = users.map(user => ({\n  fullName: `${user.name} ${user.surname}`,\n  id: user.id\n}));\n*/!*\n\n/*\nusersMapped = [\n  { fullName: \"John Smith\", id: 1 },\n  { fullName: \"Pete Hunt\", id: 2 },\n  { fullName: \"Mary Key\", id: 3 }\n]\n*/\n\nalert( usersMapped[0].id ); // 1\nalert( usersMapped[0].fullName ); // John Smith\n```\n\nPlease note that in the arrow functions we need to use additional brackets. \n\nWe can't write like this:\n```js\nlet usersMapped = users.map(user => *!*{*/!*\n  fullName: `${user.name} ${user.surname}`,\n  id: user.id\n});\n```\n\nAs we remember, there are two arrow functions: without body `value => expr` and with body `value => {...}`.\n\nHere JavaScript would treat `{` as the start of function body, not the start of the object. The workaround is to wrap them in the \"normal\" brackets:\n\n```js\nlet usersMapped = users.map(user => *!*({*/!*\n  fullName: `${user.name} ${user.surname}`,\n  id: user.id\n}));\n```\n\nNow fine.\n\n\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/7-map-objects/task.md",
    "content": "importance: 5\n\n---\n\n# Map en objets\n\nVous avez un tableau d'objets `user`, chacun ayant `name`, `surname` et `id`.\n\nEcrivez le code pour créer un autre tableau à partir de celui-ci, avec les objets `id` et `fullName`, où `fullName` est généré à partir de `name` et `surname`.\n\nPar exemple:\n\n```js no-beautify\nlet john = { name: \"John\", surname: \"Smith\", id: 1 };\nlet pete = { name: \"Pete\", surname: \"Hunt\", id: 2 };\nlet mary = { name: \"Mary\", surname: \"Key\", id: 3 };\n\nlet users = [ john, pete, mary ];\n\n*!*\nlet usersMapped = /* ... votre code ... */\n*/!*\n\n/*\nusersMapped = [\n  { fullName: \"John Smith\", id: 1 },\n  { fullName: \"Pete Hunt\", id: 2 },\n  { fullName: \"Mary Key\", id: 3 }\n]\n*/\n\nalert( usersMapped[0].id ) // 1\nalert( usersMapped[0].fullName ) // John Smith\n```\n\nDonc, en réalité, vous devez mapper un tableau d'objets sur un autre. Essayez d'utiliser `=>` ici. Il y a une petite prise.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/8-sort-objects/solution.md",
    "content": "```js run no-beautify\nfunction sortByAge(arr) {\n  arr.sort((a, b) => a.age - b.age);\n}\n\nlet john = { name: \"John\", age: 25 };\nlet pete = { name: \"Pete\", age: 30 };\nlet mary = { name: \"Mary\", age: 28 };\n\nlet arr = [ john, pete, mary ];\n\nsortByName(arr);\n\n// maitenant trié il est: [john, mary, pete]\nalert(arr[0].name); // John\nalert(arr[1].name); // Mary\nalert(arr[2].name); // Pete\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/8-sort-objects/task.md",
    "content": "importance: 5\n\n---\n\n# Trier les objets\n\nEcrivez la fonction `sortByName(users)` qui obtient un tableau d'objets avec la propriété `name` et le trie.\n\nPar exemple:\n\n```js no-beautify\nlet john = { name: \"John\", age: 25 };\nlet pete = { name: \"Pete\", age: 30 };\nlet mary = { name: \"Mary\", age: 28 };\n\nlet arr = [ john, pete, mary ];\n\nsortByName(arr);\n\n// maintenant: [john, mary, pete]\nalert(arr[0].name); // John\nalert(arr[1].name); // Mary\nalert(arr[2].name); // Pete\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/9-shuffle/solution.md",
    "content": "The simple solution could be:\n\n```js run\n*!*\nfunction shuffle(array) {\n  array.sort(() => Math.random() - 0.5);\n}\n*/!*\n\nlet arr = [1, 2, 3];\nshuffle(arr);\nalert(arr);\n```\n\nThat somewhat works, because `Math.random() - 0.5` is a random number that may be positive or negative, so the sorting function reorders elements randomly.\n\nBut because the sorting function is not meant to be used this way, not all permutations have the same probability.\n\nFor instance, consider the code below. It runs `shuffle` 1000000 times and counts appearances of all possible results:\n\n```js run\nfunction shuffle(array) {\n  array.sort(() => Math.random() - 0.5);\n}\n\n// counts of appearances for all possible permutations\nlet count = {\n  '123': 0,\n  '132': 0,\n  '213': 0,\n  '231': 0,\n  '321': 0,\n  '312': 0\n};\n\nfor (let i = 0; i < 1000000; i++) {\n  let array = [1, 2, 3];\n  shuffle(array);\n  count[array.join('')]++;\n}\n\n// show counts of all possible permutations\nfor (let key in count) {\n  alert(`${key}: ${count[key]}`);\n}\n```\n\nAn example result (depends on JS engine):\n\n```js\n123: 250706\n132: 124425\n213: 249618\n231: 124880\n312: 125148\n321: 125223\n```\n\nWe can see the bias clearly: `123` and `213` appear much more often than others.\n\nThe result of the code may vary between JavaScript engines, but we can already see that the approach is unreliable.\n\nWhy it doesn't work? Generally speaking, `sort` is a \"black box\": we throw an array and a comparison function into it and expect the array to be sorted. But due to the utter randomness of the comparison the black box goes mad, and how exactly it goes mad depends on the concrete implementation that differs between engines.\n\nThere are other good ways to do the task. For instance, there's a great algorithm called [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle). The idea is to walk the array in the reverse order and swap each element with a random one before it:\n\n```js\nfunction shuffle(array) {\n  for (let i = array.length - 1; i > 0; i--) {\n    let j = Math.floor(Math.random() * (i + 1)); // random index from 0 to i\n\n    // swap elements array[i] and array[j]\n    // we use \"destructuring assignment\" syntax to achieve that\n    // you'll find more details about that syntax in later chapters\n    // same can be written as:\n    // let t = array[i]; array[i] = array[j]; array[j] = t\n    [array[i], array[j]] = [array[j], array[i]];\n  }\n}\n```\n\nLet's test it the same way:\n\n```js run\nfunction shuffle(array) {\n  for (let i = array.length - 1; i > 0; i--) {\n    let j = Math.floor(Math.random() * (i + 1));\n    [array[i], array[j]] = [array[j], array[i]];\n  }\n}\n\n// counts of appearances for all possible permutations\nlet count = {\n  '123': 0,\n  '132': 0,\n  '213': 0,\n  '231': 0,\n  '321': 0,\n  '312': 0\n};\n\nfor (let i = 0; i < 1000000; i++) {\n  let array = [1, 2, 3];\n  shuffle(array);\n  count[array.join('')]++;\n}\n\n// show counts of all possible permutations\nfor (let key in count) {\n  alert(`${key}: ${count[key]}`);\n}\n```\n\nThe example output:\n\n```js\n123: 166693\n132: 166647\n213: 166628\n231: 167517\n312: 166199\n321: 166316\n```\n\nLooks good now: all permutations appear with the same probability.\n\nAlso, performance-wise the Fisher-Yates algorithm is much better, there's no \"sorting\" overhead.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/9-shuffle/task.md",
    "content": "importance: 3\n\n---\n\n# Mélanger un tableau\n\nEcrivez la fonction `shuffle(array)` qui mélange les éléments (de manière aléatoire) du tableau.\n\nLes exécutions multiples de `shuffle` peuvent conduire à différents ordres d'éléments. Par exemple:\n\n```js\nlet arr = [1, 2, 3];\n\nshuffle(arr);\n// arr = [3, 2, 1]\n\nshuffle(arr);\n// arr = [2, 1, 3]\n\nshuffle(arr);\n// arr = [3, 1, 2]\n// ...\n```\n\nTous les ordres d'éléments doivent avoir une probabilité égale. Par exemple, `[1,2,3]` peut être réorganisé comme `[1,2,3]` ou `[1,3,2]` ou `[3,1,2]` etc., avec une probabilité égale de chaque cas.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/article.md",
    "content": "# Méthodes de tableau\n\nLes tableaux viennent avec beaucoup de méthodes. Pour faciliter les choses, dans ce chapitre, ils ont été divisés en groupes.\n\n## Ajouter/Supprimer des éléments\n\nNous connaissons déjà des méthodes qui ajoutent et suppriment des éléments au début ou à la fin :\n\n- `arr.push(...items)` -- ajoute des éléments à la fin,\n- `arr.pop()` -- supprime un élément à la fin,\n- `arr.shift()` -- supprime un élément au début,\n- `arr.unshift(...items)` -- ajouter des éléments au début.\n\nEn voici quelques autres.\n\n### splice\n\nComment supprimer un élément du tableau ?\n\nLes tableaux sont des objets, nous pouvons donc utiliser `delete` :\n\n```js run\nlet arr = [\"I\", \"go\", \"home\"];\n\ndelete arr[1]; // supprime \"go\"\n\nalert( arr[1] ); // undefined\n\n// maintenant arr = [\"I\",  , \"home\"];\nalert( arr.length ); // 3\n```\n\nL'élément a été supprimé, mais le tableau a toujours 3 éléments, on peut voir que `arr.length == 3`\n\nC'est normal, car `delete obj.key` supprime une valeur par la `clé`. C'est tout ce que ça fait. C'est donc parfait pour les objets. Mais pour les tableaux, nous souhaitons généralement que le reste des éléments se déplace et occupe la place libérée. Nous nous attendons à avoir un tableau plus court maintenant.\n\nDes méthodes spéciales doivent donc être utilisées.\n\nLa méthode [arr.splice](mdn:js/Array/splice) est un couteau suisse pour les tableaux. Elle peut tout faire : ajouter, supprimer et remplacer des éléments.\n\nLa syntaxe est la suivante :\n\n```js\narr.splice(start[, deleteCount, elem1, ..., elemN])\n```\n\nIl a modifié `arr` à partir de l'index `start` : supprime les éléments `deleteCount` puis insère `elem1, ..., elemN` à leur place. Renvoie le tableau des éléments supprimés.\n\nCette méthode est facile à comprendre avec des exemples.\n\nCommençons par la suppression :\n\n```js run\nlet arr = [\"I\", \"study\", \"JavaScript\"];\n\n*!*\narr.splice(1, 1); // À partir de l'index 1 supprime 1 élément\n*/!*\n\nalert( arr ); // [\"I\", \"JavaScript\"]\n```\n\nFacile, non ? À partir de l'index 1, il a supprimé 1 élément.\n\nDans l'exemple suivant, nous supprimons 3 éléments et les remplaçons par les deux autres :\n\n```js run\nlet arr = [*!*\"I\", \"study\", \"JavaScript\",*/!* \"right\", \"now\"];\n\n// supprime les 3 premiers éléments et les remplace par d'autre\narr.splice(0, 3, \"Let's\", \"dance\");\n\nalert( arr ) // maintenant [*!*\"Let's\", \"dance\"*/!*, \"right\", \"now\"]\n```\n\nNous pouvons voir ici que `splice` renvoie le tableau des éléments supprimés :\n\n```js run\nlet arr = [*!*\"I\", \"study\",*/!* \"JavaScript\", \"right\", \"now\"];\n\n// supprime les 2 premiers éléments\nlet removed = arr.splice(0, 2);\n\nalert( removed ); // \"I\", \"study\" <-- tableau des éléments supprimés\n```\n\nLa méthode `splice` est également capable d'insérer les éléments sans aucune suppression. Pour cela, nous devons définir `nombreDeSuppression` sur 0 :\n\n```js run\nlet arr = [\"I\", \"study\", \"JavaScript\"];\n\n// de l'index 2\n// supprime 0\n// et ajoute \"complex\" et \"language\"\narr.splice(2, 0, \"complex\", \"language\");\n\nalert( arr ); // \"I\", \"study\", \"complex\", \"language\", \"JavaScript\"\n```\n\n````smart header=\"Index négatifs autorisés\"\nIci et dans d'autres méthodes de tableau, les index négatifs sont autorisés. Ils spécifient la position à partir de la fin du tableau, comme ici :\n\n```js run\nlet arr = [1, 2, 5];\n\n// de l'index -1 (un déplacement à partir de la fin)\n// supprime 0 éléments,\n// puis insère 3 et 4\narr.splice(-1, 0, 3, 4);\n\nalert( arr ); // 1,2,3,4,5\n```\n````\n\n### slice\n\nLa méthode [arr.slice](mdn:js/Array/slice) est beaucoup plus simple et est similaire à la méthode `arr.splice`.\n\nLa syntaxe est la suivante :\n\n```js\narr.slice([start], [end])\n```\n\nIl retourne un nouveau tableau dans lequel il copie tous les éléments index qui commencent de `start` à `end` (sans compter `end`). Les deux `start` et `end` peuvent être négatifs, dans ce cas, la position depuis la fin du tableau est supposée.\n\nCela ressemble à une méthode string `str.slice`, mais au lieu de sous-chaînes de caractères, cela crée des sous-tableaux.\n\nPar exemple :\n\n```js run\nlet arr = [\"t\", \"e\", \"s\", \"t\"];\n\nalert( arr.slice(1, 3) ); // e,s (copie de 1 à 3, 3 non compris)\n\nalert( arr.slice(-2) ); // s,t (copie de -2 jusqu'à la fin)\n```\n\nNous pouvons aussi l'appeler sans arguments : `arr.slice()` pour créer une copie de `arr`. Cela est souvent utilisé pour obtenir une copie pour d'autres transformations qui ne devraient pas affecter le tableau d'origine.\n\n### concat\n\nLa méthode [arr.concat](mdn:js/Array/concat) crée un nouveau tableau qui inclut les valeurs d'autres tableaux et des éléments supplémentaires.\n\nLa syntaxe est la suivante :\n\n```js\narr.concat(arg1, arg2...)\n```\n\nIl accepte n'importe quel nombre d'arguments -- des tableaux ou des valeurs.\n\nLe résultat est un nouveau tableau contenant les éléments `arr`, puis `arg1`, `arg2`, etc.\n\nSi un argument `argN` est un tableau, alors tous ses éléments sont copiés. Sinon, l'argument lui-même est copié.\n\nPar exemple :\n\n```js run\nlet arr = [1, 2];\n\n// créer un tableau à partir de arr et [3,4]\nalert( arr.concat([3, 4]) ); // 1,2,3,4\n\n// créer un tableau à partir de arr et [3,4] et [5,6]\nalert( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6\n\n// créer un tableau à partir de arr et [3,4], puis ajoute les valeurs 5 et 6\nalert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6\n```\n\nNormalement, il ne copie que les éléments des tableaux. Les autres objets, même s'ils ressemblent à des tableaux, sont ajoutés dans leur ensemble :\n\n```js run\nlet arr = [1, 2];\n\nlet arrayLike = {\n  0: \"something\",\n  length: 1\n};\n\nalert( arr.concat(arrayLike) ); // 1,2,[object Object]\n```\n\n… Mais si un objet de type tableau (array-like) a une propriété spéciale `Symbol.isConcatSpreadable`, alors il est traité comme un tableau par `concat` : ses éléments sont ajoutés à la place :\n\n```js run\nlet arr = [1, 2];\n\nlet arrayLike = {\n  0: \"something\",\n  1: \"else\",\n*!*\n  [Symbol.isConcatSpreadable]: true,\n*/!*\n  length: 2\n};\n\nalert( arr.concat(arrayLike) ); // 1,2,something,else\n```\n\n## Itérer: forEach (pourChaque)\n\nLa méthode [arr.forEach](mdn:js/Array/forEach) permet d’exécuter une fonction pour chaque élément du tableau.\n\nLa syntaxe :\n\n```js\narr.forEach(function(item, index, array) {\n  // ... fait quelques chose avec l'élément\n});\n```\n\nPar exemple, cela montre chaque élément du tableau :\n\n```js run\n// pour chaque élément appel l'alerte\n[\"Bilbo\", \"Gandalf\", \"Nazgul\"].forEach(alert);\n```\n\nEt ce code est plus élaboré sur leurs positions dans le tableau cible :\n\n```js run\n[\"Bilbo\", \"Gandalf\", \"Nazgul\"].forEach((item, index, array) => {\n  alert(`${item} est à l'index ${index} dans ${array}`);\n});\n```\n\nLe résultat de la fonction (s'il en renvoie) est jeté et ignoré.\n\n## Recherche dans le tableau\n\nVoyons maintenant les méthodes de recherche dans un tableau.\n\n### indexOf/lastIndexOf et includes\n\nLes méthodes [arr.indexOf](mdn:js/Array/indexOf), et [arr.includes](mdn:js/Array/includes) ont la même syntaxe et utilisent essentiellement la même chose que leurs équivalents de chaîne, mais fonctionnent sur des éléments au lieu de caractères :\n\n- `arr.indexOf(item, from)` recherche l'élément `item` à partir de l'index `from`, et retourne l'index où il a été trouvé, sinon il retourne `-1`.\n- `arr.includes(item, from)` -- recherche l'élément `item` en commençant par l'index `from`, retourne `true` si il est trouvé.\n\nHabituellement, ces méthodes sont utilisées avec un seul argument : l'élément à rechercher. Par défaut, la recherche s'effectue depuis le début.\n\nPar exemple :\n\n```js run\nlet arr = [1, 0, false];\n\nalert( arr.indexOf(0) ); // 1\nalert( arr.indexOf(false) ); // 2\nalert( arr.indexOf(null) ); // -1\n\nalert( arr.includes(1) ); // true\n```\n\nVeuillez noter que `indexOf` utilise l'égalité stricte `===` pour la comparaison. Donc, si nous cherchons \"faux\", il trouve exactement \"faux\" et non le zéro.\n\nSi nous voulons vérifier si `item` existe dans le tableau et n'avons pas besoin de l'index, alors `arr.includes` est préféré.\n\nLa méthode [arr.lastIndexOf](mdn:js/Array/lastIndexOf) est la même que `indexOf`, mais recherche de droite à gauche.\n\n```js run\nlet fruits = ['Apple', 'Orange', 'Apple']\n\nalert( fruits.indexOf('Apple') ); // 0 (first Apple)\nalert( fruits.lastIndexOf('Apple') ); // 2 (last Apple)\n```\n\n````smart header=\"La méthode `includes` gère `NaN` correctement\"\nUne caractéristique mineure mais remarquable de `includes` est qu'il gère correctement `NaN`, contrairement à `indexOf` :\n\n```js run\nconst arr = [NaN];\nalert( arr.indexOf(NaN) ); // -1 (faux, devrait être 0)\nalert( arr.includes(NaN) ); // true (correct)\n```\nC'est parce que `includes` a été ajouté à JavaScript beaucoup plus tard et utilise l'algorithme de comparaison le plus à jour en interne.\n````\n\n### find et findIndex/findLastIndex\n\nImaginez que nous ayons un tableau d'objets. Comment pouvons-nous trouver un objet avec une condition spécifique ?\n\nIci la méthode [arr.find(fn)](mdn:js/Array/find) se révèle vraiment pratique.\n\nLa syntaxe est la suivante :\n\n```js\nlet result = arr.find(function(item, index, array) {\n // devrait retourner true si l'élément correspond à ce que nous recherchons\n // pour le scénario de falsy (fausseté), renvoie undefined\n});\n```\n\nLa fonction est appelée pour chaque élément du tableau, l'un après l'autre :\n\n- `item` est l'élément.\n- `index` est sont index.\n- `array` est le tableau lui même.\n\nS'il renvoie `true`, la recherche est arrêtée, l'`item` est renvoyé. Si rien n'est trouvé, `undefined` est renvoyé.\n\nPar exemple, nous avons un tableau d’utilisateurs, chacun avec les champs `id` et `name`. Trouvons le premier avec l'`id == 1` :\n\n```js run\nlet users = [\n  {id: 1, name: \"John\"},\n  {id: 2, name: \"Pete\"},\n  {id: 3, name: \"Mary\"}\n];\n\nlet user = users.find(item => item.id == 1);\n\nalert(user.name); // John\n```\n\nDans la vie réelle, les tableaux d'objets sont une chose courante, la méthode `find` est donc très utile.\n\nNotez que dans l'exemple, nous fournissons à `find` la fonction `item => item.id == 1` avec un argument. C'est typique, les autres arguments de cette fonction sont rarement utilisés.\n\nLa méthode [arr.findIndex](mdn:js/Array/findIndex) est essentiellement la même, mais elle retourne l'index où l'élément a été trouvé à la place de l'élément lui-même. La valeur de `-1` est retournée si rien n'est trouvé.\n\nLa méthode [arr.findLastIndex](mdn:js/Array/findLastIndex) est comme `findIndex`, mais recherche de droite à gauche, similaire à `lastIndexOf`.\n\nVoici un exemple :\n\n```js run\nlet users = [\n  {id: 1, name: \"John\"},\n  {id: 2, name: \"Pete\"},\n  {id: 3, name: \"Mary\"},\n  {id: 4, name: \"John\"}\n];\n\n// Trouver l'index du premier John\nalert(users.findIndex(user => user.name == 'John')); // 0\n\n// Trouver l'index du dernier John\nalert(users.findLastIndex(user => user.name == 'John')); // 3\n```\n\n### filter\n\nLa méthode `find` recherche un seul (le premier) élément qui rend la fonction true.\n\nS'il y en a plusieurs, nous pouvons utiliser [arr.filter(fn)](mdn:js/Array/filter).\n\nLa syntaxe est à peu près identique à celle de `find`, mais `filter` renvoie un tableau d'éléments correspondants :\n\n```js\nlet results = arr.filter(function(item, index, array) {\n  // si true, l'item est poussé vers résultats et l'itération continue\n  // retourne un tableau vide si rien n'est trouvé\n});\n```\n\nPar exemple :\n\n```js run\nlet users = [\n  {id: 1, name: \"John\"},\n  {id: 2, name: \"Pete\"},\n  {id: 3, name: \"Mary\"}\n];\n\n// retourne les tableaux des deux premiers users\nlet someUsers = users.filter(item => item.id < 3);\n\nalert(someUsers.length); // 2\n```\n\n## Transformer un tableau\n\nPassons aux méthodes qui transforment et réorganisent un tableau.\n\n### map\n\nLa méthode [arr.map](mdn:js/Array/map) est l’une des plus utiles et des plus utilisées.\n\nElle appelle la fonction pour chaque élément du tableau et renvoie le tableau de résultats.\n\nLa syntaxe est :\n\n```js\nlet result = arr.map(function(item, index, array) {\n  // renvoie la nouvelle valeur au lieu de l'item\n});\n```\n\nPar exemple, ici nous transformons chaque élément en sa longueur :\n\n```js run\nlet lengths = [\"Bilbo\", \"Gandalf\", \"Nazgul\"].map(item => item.length)\nalert(lengths); // 5,7,6\n```\n\n### sort(fn)\n\nLa méthode [arr.sort](mdn:js/Array/sort) trie le tableau *en place*, en changeant son ordre d'élément.\n\nElle renvoie également le tableau trié, mais la valeur renvoyée est généralement ignorée, comme `arr` est lui-même modifié.\n\nPar exemple :\n\n```js run\nlet arr = [ 1, 2, 15 ];\n\n// la méthode réordonne le contenu de arr\narr.sort();\n\nalert( arr );  // *!*1, 15, 2*/!*\n```\n\nAvez-vous remarqué quelque chose d'étrange dans le résultat ?\n\nL'ordre est devenu `1, 15, 2`. C'est incorrect. Mais pourquoi ?\n\n**Les éléments sont triés en tant que chaînes par défaut.**\n\nLittéralement, tous les éléments sont convertis en chaînes de caractères pour comparaisons. Pour les chaînes de caractères, l’ordre lexicographique est appliqué et donc `\"2\" > \"15\"`.\n\nPour utiliser notre propre ordre de tri, nous devons fournir une fonction comme argument de `arr.sort()`.\n\nLa fonction doit comparer deux valeurs arbitraires et renvoyer le résultat :\n\n```js\nfunction compare(a, b) {\n  if (a > b) return 1; // if the first value is greater than the second\n  if (a == b) return 0; // if values are equal\n  if (a < b) return -1; // if the first value is less than the second\n}\n```\n\nPar exemple, pour trier sous forme de nombres :\n\n```js run\nfunction compareNumeric(a, b) {\n  if (a > b) return 1;\n  if (a == b) return 0;\n  if (a < b) return -1;\n}\n\nlet arr = [ 1, 2, 15 ];\n\n*!*\narr.sort(compareNumeric);\n*/!*\n\nalert(arr);  // *!*1, 2, 15*/!*\n```\n\nMaintenant, ça fonctionne comme nous l'avons prévu.\n\nMettons cela de côté et regardons ce qui se passe. L'`arr` peut être un tableau de n'importe quoi, non ? Il peut contenir des nombres, des chaînes de caractères, des objets ou autre. Nous avons donc un ensemble de *quelques items*. Pour le trier, nous avons besoin d’une *fonction de classement* qui sache comment comparer ses éléments. La valeur par défaut est un ordre de chaîne de caractères.\n\n\nLa méthode `arr.sort(fn)` intégre l'implémentation d'un algorithme générique de tri. Nous n'avons pas besoin de nous préoccuper de son fonctionnement interne (c'est un [tri rapide optimisé](https://fr.wikipedia.org/wiki/Tri_rapide) la plupart du temps). Il va parcourir le tableau, comparer ses éléments à l'aide de la fonction fournie et les réorganiser. Tout ce dont nous avons besoin est de fournir la `fn` qui effectue la comparaison.\n\n\nÀ propos, si nous voulons savoir quels éléments sont comparés, rien ne nous empêche de les alerter :\n\n```js run\n[1, -2, 15, 2, 0, 8].sort(function(a, b) {\n  alert( a + \" <> \" + b );\n  return a - b;\n});\n```\n\nL'algorithme peut comparer un élément à plusieurs autres dans le processus, mais il essaie de faire le moins de comparaisons possible.\n\n````smart header=\"Une fonction de comparaison peut renvoyer n'importe quel nombre\"\nEn réalité, une fonction de comparaison est requise uniquement pour renvoyer un nombre positif pour dire \"plus grand\" et un nombre négatif pour dire \"plus petit\".\n\nCela permet d'écrire des fonctions plus courtes :\n\n```js run\nlet arr = [ 1, 2, 15 ];\n\narr.sort(function(a, b) { return a - b; });\n\nalert(arr);  // *!*1, 2, 15*/!*\n```\n````\n\n````smart header=\"Fonction fléchée pour le meilleur\"\nSouvenez-vous des [fonctions fléchées](info:arrow-functions-basics) ? Nous pouvons les utiliser ici pour un tri plus net :\n\n```js\narr.sort( (a, b) => a - b );\n```\n\nCela fonctionne exactement comme la version longue ci-dessus.\n````\n\n````smart header=\"Utiliser `localeCompare` pour les chaînes de caractères\"\nSouvenez-vous de l'algorithme de comparaison [des chaînes de caractères](info:string#correct-comparisons) ? Il compare les lettres par leurs codes par défaut.\n\nPour de nombreux alphabets, il est préférable d'utiliser la méthode `str.localeCompare` pour trier correctement les lettres, comme `Ö`.\n\nPar exemple, trions quelques pays en allemand :\n\n```js run\nlet countries = ['Österreich', 'Andorra', 'Vietnam'];\n\nalert( countries.sort( (a, b) => a > b ? 1 : -1) ); // Andorra, Vietnam, Österreich (wrong)\n\nalert( countries.sort( (a, b) => a.localeCompare(b) ) ); // Andorra,Österreich,Vietnam (correct!)\n```\n````\n\n### reverse\n\nLa méthode [arr.reverse](mdn:js/Array/reverse) inverse l'ordre des éléments dans l'`arr`.\n\nPar exemple :\n\n```js run\nlet arr = [1, 2, 3, 4, 5];\narr.reverse();\n\nalert( arr ); // 5,4,3,2,1\n```\n\nIl retourne également le tableau `arr` après l'inversion.\n\n### split et join\n\nVoici une situation réele. Nous écrivons une application de messagerie et la personne entre dans la liste des destinataires délimités par des virgules : `John, Pete, Mary`. Mais pour nous, un tableau de noms serait beaucoup plus confortable qu'une simple chaîne de caractères. Alors, comment l'obtenir ?\n\nLa méthode [str.split(separator)](mdn:js/String/split) fait exactement cela. Elle divise la chaîne en un tableau selon le délimiteur `separator` donné.\n\nDans l'exemple ci-dessous, nous les séparons par une virgule suivie d'un espace :\n\n```js run\nlet names = 'Bilbo, Gandalf, Nazgul';\n\nlet arr = names.split(', ');\n\nfor (let name of arr) {\n  alert( `Un message à ${name}.` ); // Un message à Bilbo  (ainsi que les autres noms)\n}\n```\n\nLa méthode `split` a un deuxième argument numérique facultatif -- une limite sur la longueur du tableau. S'il est fourni, les éléments supplémentaires sont ignorés. En pratique, il est rarement utilisé cependant :\n\n```js run\nlet arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2);\n\nalert(arr); // Bilbo, Gandalf\n```\n\n````smart header=\"Divisé en lettres\"\nL'appel de `split(s)` avec un `s` vide diviserait la chaîne en un tableau de lettres :\n\n```js run\nlet str = \"test\";\n\nalert( str.split('') ); // t,e,s,t\n```\n````\n\nL'appel de [arr.join(separator)](mdn:js/Array/join) fait l'inverse de `split`. Elle crée une chaîne de caractères avec les éléments de `arr` joints entre eux par `separator`.\n\nPar exemple :\n\n```js run\nlet arr = ['Bilbo', 'Gandalf', 'Nazgul'];\n\nlet str = arr.join(';'); // joint les éléments du tableau en une string en utilisant le caractère \";\"\n\nalert( str ); // Bilbo;Gandalf;Nazgul\n```\n\n### reduce/reduceRight\n\nLorsque nous devons parcourir un tableau, nous pouvons utiliser `forEach`, `for` ou `for..of`.\n\nLorsque nous devons itérer et renvoyer les données pour chaque élément, nous pouvons utiliser `map`.\n\nLes méthodes [arr.reduce](mdn:js/Array/reduce) et [arr.reduceRight](mdn:js/Array/reduceRight) appartiennent également à cette famille, mais sont un peu plus complexes. Ces méthodes sont utilisées pour calculer une valeur unique basée sur un tableau.\n\nLa syntaxe est la suivante :\n\n```js\nlet value = arr.reduce(function(accumulator, item, index, array) {\n  // ...\n}, [initial]);\n```\n\nLa fonction est appliquée à tous les éléments du tableau les uns après les autres et \"reporte\" son résultat à l'appel suivant.\n\nLes arguments :\n\n- `accumulator` -- est le résultat de l'appel de fonction précédent, égal à `initial` la première fois (si `initial` est fourni).\n- `item` -- est l'élément actuel du tableau.\n- `index` -- est sa position.\n- `array` -- est le tableau.\n\nLorsque la fonction est appliquée, le résultat de l'appel de fonction précédent est transmis au suivant en tant que premier argument.\n\nAinsi, le premier argument est l'accumulateur qui stocke le résultat combiné de toutes les exécutions précédentes. À la fin, il devient le résultat de la fonction `reduce`.\n\nCela semble compliqué ?\n\nLe moyen le plus simple pour comprendre c'est avec un exemple.\n\nIci nous obtenons la somme d'un tableau sur une ligne :\n\n```js run\nlet arr = [1, 2, 3, 4, 5];\n\nlet result = arr.reduce((sum, current) => sum + current, 0);\n\nalert(result); // 15\n```\n\nLa fonction passée à `reduce` utilise seulement 2 arguments, c’est généralement suffisant.\n\nVoyons en détails ce qu'il se passe.\n\n1. Lors du premier passage, `sum` prend la valeur de `initial` (le dernier argument de `reduce`), égale à `0`, et `current` correspond au premier élément du tableau, égal à `1`. Donc le résultat de la fonction est `1`.\n2. Lors du deuxième passage, `sum = 1`, nous y ajoutons le deuxième élément du tableau (`2`) et `sum` est retourné.\n3. Au troisième passage, `sum = 3` et nous y ajoutons un élément supplémentaire, et ainsi de suite...\n\nLe flux de calcul :\n\n![](reduce.svg)\n\nOu sous la forme d'un tableau, où chaque ligne représente un appel de fonction sur l'élément de tableau suivant :\n\n|   |`sum`|`current`|result|\n|---|-----|---------|------|\n|premier appel|`0`|`1`|`1`|\n|deuxième appel|`1`|`2`|`3`|\n|troisième appel|`3`|`3`|`6`|\n|quatrième appel|`6`|`4`|`10`|\n|cinquième appel|`10`|`5`|`15`|\n\nIci, nous pouvons clairement voir comment le résultat de l'appel précédent devient le premier argument du suivant.\n\nNous pouvons également omettre la valeur initiale :\n\n```js run\nlet arr = [1, 2, 3, 4, 5];\n\n// Suppression de la valeur initiale de reduce (pas de 0)\nlet result = arr.reduce((sum, current) => sum + current);\n\nalert( result ); // 15\n```\n\nLe résultat est le même. En effet, s'il n'y a pas de valeur initiale, alors `reduce` prend le premier élément du tableau comme valeur initiale et lance l'itération à partir du deuxième élément.\n\nLe tableau de calcul est le même que celui ci-dessus, sans la première ligne.\n\nMais une telle utilisation nécessite une extrême prudence. Si le tableau est vide, alors `reduce` appelé sans valeur initiale génèrera une erreur.\n\nVoici un exemple :\n\n```js run\nlet arr = [];\n\n// Erreur : Réduction du tableau vide sans valeur initiale\n// si la valeur initiale existait, reduce la renverrait pour l'arr vide.\narr.reduce((sum, current) => sum + current);\n```\n\nIl est donc conseillé de toujours spécifier la valeur initiale.\n\nLa méthode [arr.reduceRight](mdn:js/Array/reduceRight) fait la même chose, mais va de droite à gauche.\n\n## Array.isArray\n\nLes tableaux ne forment pas un type distinct du langage. Ils sont basés sur des objets.\n\nDonc son `typeof` ne permet pas de distinguer un objet brut d'un tableau :\n\n```js run\nalert(typeof {}); // object\nalert(typeof []); // object (pareil)\n```\n\n...Mais les tableaux sont utilisés si souvent qu'il existe une méthode spéciale pour cela : [Array.isArray(value)](mdn:js/Array/isArray). Il renvoie `true` si la `value` est un tableau, sinon il renvoie `false`.\n\n```js run\nalert(Array.isArray({})); // false\n\nalert(Array.isArray([])); // true\n```\n\n## La plupart des méthodes supportent \"thisArg\"\n\nPresque toutes les méthodes de tableau qui appellent des fonctions -- comme `find`, `filter`, `map`, à l'exception de `sort`, acceptent un paramètre supplémentaire facultatif `thisArg`.\n\nCe paramètre n'est pas expliqué dans les sections ci-dessus, car il est rarement utilisé. Mais pour être complet, nous devons quand même le voir.\n\nVoici la syntaxe complète de ces méthodes :\n\n```js\narr.find(func, thisArg);\narr.filter(func, thisArg);\narr.map(func, thisArg);\n// ...\n// thisArg est le dernier argument optionnel\n```\n\nLa valeur du paramètre `thisArg` devient `this` pour `func`.\n\nPar exemple, nous utilisons ici une méthode de l'objet `army` en tant que filtre et `thisArg` passe le contexte :\n\n```js run\nlet army = {\n  minAge: 18,\n  maxAge: 27,\n  canJoin(user) {\n    return user.age >= this.minAge && user.age < this.maxAge;\n  }\n};\n\nlet users = [\n  {age: 16},\n  {age: 20},\n  {age: 23},\n  {age: 30}\n];\n\n*!*\n// trouve les utilisateurs pour qui army.canJoin retourne true\nlet soldiers = users.filter(army.canJoin, army);\n*/!*\n\nalert(soldiers.length); // 2\nalert(soldiers[0].age); // 20\nalert(soldiers[1].age); // 23\n```\n\nSi, dans l'exemple ci-dessus, nous utilisions `users.filter(army.canJoin)`, alors `army.canJoin` serait appelée en tant que fonction autonome, avec `this = undefined`, ce qui entraînerait une erreur instantanée.\n\nUn appel à `users.filter(army.canJoin, army)` peut être remplacé par `users.filter(user => army.canJoin(user))`, qui fait la même chose. Le premier est utilisé plus souvent, car il est un peu plus facile à comprendre pour la plupart des gens.\n\n## Résumé\n\nUn cheat sheet des méthodes de tableau :\n\n- Pour ajouter / supprimer des éléments :\n  - `push(...items)` -- ajoute des éléments à la fin,\n  - `pop()` -- extrait un élément en partant de la fin,\n  - `shift()` -- extrait un élément depuis le début,\n  - `unshift(...items)` -- ajoute des éléments au début.\n  - `splice(pos, deleteCount, ...items)` -- à l'index `pos` supprime `deleteCount` éléments et insert les éléments `items`.\n  - `slice(start, end)` -- crée un nouveau tableau, y copie les éléments de `start` jusqu'à `end` (non inclus).\n  - `concat(...items)` -- retourne un nouveau tableau : copie tous les membres du groupe actuel et lui ajoute des éléments. Si un des `items` est un tableau, ses éléments sont pris.\n- Pour rechercher parmi des éléments :\n  - `indexOf/lastIndexOf(item, pos)` -- cherche l'`item` à partir de la position `pos`, retourne l'index `-1` s'il n'est pas trouvé.\n  - `includes(value)` -- retourne `true` si le tableau contient une `value`, sinon `false`.\n  - `find/filter(func)` -- filtre les éléments à travers la fonction, retourne la première / toutes les valeurs qui retournent `true`.\n  - `findIndex` est similaire à `find`, mais renvoie l'index au lieu d'une valeur.\n- Pour parcourir les éléments :\n  - `forEach(func)` -- appelle `func` pour chaque élément, ne retourne rien.\n- Pour transformer le tableau :\n  - `map(func)` -- crée un nouveau tableau à partir des résultats de `func` pour chaque élément.\n  - `sort(func)` -- trie le tableau courant, puis le renvoie.\n  - `reverse()` -- inverse le tableau courant, puis le renvoie.\n  - `split/join` -- convertit une chaîne en tableau et inversement.\n  - `reduce(func, initial)` -- calcule une valeur unique sur le tableau en appelant `func` pour chaque élément et en transmettant un résultat intermédiaire entre les appels.\n- Aditionellement :\n  - `Array.isArray(value)` vérifie que `value` est un tableau, si c'est le cas, renvoie `true`, sinon `false`.\n\nVeuillez noter que les méthodes `sort`, `reverse` et `splice` modifient le tableau lui-même.\n\nCes méthodes sont les plus utilisées, elles couvrent 99% des cas d'utilisation. Mais il y en a encore d'autres :\n\n- [arr.some(fn)](mdn:js/Array/some)/[arr.every(fn)](mdn:js/Array/every) vérifie le tableau.\n\n  La fonction `fn` est appelée sur chaque élément du tableau comme pour `map`. Si n'importe quel / tous les résultats sont `true`, il retourne `true`, sinon il retourne `false`.\n\n  La fonction `fn` est appelée sur chaque élément du tableau similaire à `map`. Si un/tous les résultats sont `true`, renvoie `true`, sinon `false`.\n\n  Ces méthodes se comportent en quelque sorte comme les opérateurs `||` et `&&` : si `fn` renvoie une valeur vraie, `arr.some()` renvoie immédiatement `true` et arrête de parcourir les autres éléments ; si `fn` renvoie une valeur fausse, `arr.every()`retourne immédiatement `false` et arrête également d'itérer sur les autres éléments.\n\n  On peut utiliser `every` pour comparer les tableaux :\n\n  ```js run\n  function arraysEqual(arr1, arr2) {\n    return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]);\n  }\n\n  alert( arraysEqual([1, 2], [1, 2])); // true\n  ```\n\n- [arr.fill(value, start, end)](mdn:js/Array/fill) -- remplit le tableau avec une répétition de `value` de l'index `start` à `end`.\n\n- [arr.copyWithin(target, start, end)](mdn:js/Array/copyWithin) -- copie ses éléments en lui-même de la position `start` jusqu'à la position `end`, à la position `target` (écrase les éléments éxistants).\n\n- [arr.flat(depth)](mdn:js/Array/flat)/[arr.flatMap(fn)](mdn:js/Array/flatMap) créer un nouveau tableau plat à partir d'un tableau multidimensionnel.\n\nPour la liste complète, consultez le [manuel](mdn:js/Array).\n\nÀ première vue, vous pouvez penser qu’il existe de nombreuses méthodes difficiles à retenir. Mais en réalité, c'est beaucoup plus facile qu'il n'y paraît.\n\nParcourez le cheat sheet et essayer de vous en souvenir. Ensuite, faites les exercices de ce chapitre afin de vous familiariser avec les méthodes de tableau.\n\nEnsuite, chaque fois que vous avez besoin de faire quelque chose avec un tableau, et que vous ne savez plus comment - revenez ici, regardez le cheatsheet et trouvez la bonne méthode. Des exemples vous aideront à l'écrire correctement. Bientôt, à force de pratiquer, vous vous souviendrez automatiquement des méthodes, sans efforts particuliers.\n"
  },
  {
    "path": "1-js/05-data-types/06-iterable/article.md",
    "content": "# Iterables\n\nLes objets *Iterable* sont une généralisation des tableaux. C'est un concept qui permet de rendre n'importe quel objet utilisable dans une boucle `for..of`.\n\nBien sûr, les tableaux sont itérables. Mais il existe de nombreux autres objets intégrés, qui sont également itérables. Par exemple, les chaînes de caractères sont également itérables.\n\nSi un objet n'est pas techniquement un tableau, mais représente une collection (liste, set) de quelque chose, alors `for..of` est une excellente syntaxe pour boucler dessus, voyons comment le faire fonctionner.\n\n## Symbol.iterator\n\nNous pouvons facilement saisir le concept des itérables en faisant le nôtre.\n\nPar exemple, nous avons un objet qui n'est pas un tableau, mais qui semble convenir pour une boucle `for..of`.\n\nComme un objet `range` qui représente un intervalle de nombres :\n\n```js\nlet range = {\n  from: 1,\n  to: 5\n};\n\n// Nous voulons que le for..of fonctionne :\n// for (let num of range) ... num=1,2,3,4,5\n```\n\nPour rendre la `range` itérable (et donc laisser `for..of` faire son travail), nous devons ajouter une méthode à l'objet nommé `Symbol.iterator` (un symbole intégré spécial que pour cela).\n\n1. Lorsque `for..of` démarre, il appelle cette méthode une fois (ou des erreurs si il n'est pas trouvé). La méthode doit retourner un *iterator* -- un objet avec la méthode `next`.\n2. À partir de là, `for..of` ne fonctionne *qu'avec cet objet retourné*.\n3. Quand `for..of` veut la valeur suivante, il appelle `next()` sur cet objet.\n4. Le résultat de `next()` doit avoir la forme `{done: Boolean, value: any}`, où `done = true` signifie que l'itération est terminée, sinon `value` doit être la nouvelle valeur.\n\nVoici l'implémentation complète de `range` avec les remarques :\n\n```js run\nlet range = {\n  from: 1,\n  to: 5\n};\n\n// 1. l'appel d'un for..of appelle initialement ceci\nrange[Symbol.iterator] = function() {\n\n  // ...il retourne l'objet itérateur :\n  // 2. À partir de là, for..of fonctionne uniquement avec cet itérateur, lui demandant les valeurs suivantes\n  return {\n    current: this.from,\n    last: this.to,\n\n    // 3. next() est appelée à chaque itération par la boucle for..of\n    next() {\n      // 4. il devrait renvoyer la valeur sous forme d'objet {done: .., valeur: ...}\n      if (this.current <= this.last) {\n        return { done: false, value: this.current++ };\n      } else {\n        return { done: true };\n      }\n    }\n  };\n};\n\n// maintenant ça marche !\nfor (let num of range) {\n  alert(num); // 1, ensuite 2, 3, 4, 5\n}\n```\n\nVeuillez noter la fonctionnalité principale des iterables : separation of concerns (séparation des préoccupations).\n\n- Le `range` lui-même n'a pas la méthode `next()`.\n- Au lieu de cela, un autre objet, appelé \"iterator\", est créé par l'appel à `range[Symbol.iterator]()`, et sa méthode `next()` génère des valeurs pour l'itération.\n\nAinsi, l'objet itérateur est séparé de l'objet sur lequel il est itéré.\n\nTechniquement, nous pouvons les fusionner et utiliser `range` lui-même comme itérateur pour simplifier le code.\n\nComme ça :\n\n```js run\nlet range = {\n  from: 1,\n  to: 5,\n\n  [Symbol.iterator]() {\n    this.current = this.from;\n    return this;\n  },\n\n  next() {\n    if (this.current <= this.to) {\n      return { done: false, value: this.current++ };\n    } else {\n      return { done: true };\n    }\n  }\n};\n\nfor (let num of range) {\n  alert(num); // 1, ensuite 2, 3, 4, 5\n}\n```\n\nMaintenant, `range[Symbol.iterator]()` renvoie l'objet `range` lui-même : il dispose de la méthode `next()` et se souvient de la progression de l'itération en cours dans `this.current`. C'est plus court? Oui. Et parfois c'est aussi bien.\n\nL'inconvénient est qu'il est maintenant impossible d'avoir deux boucles `for..of` s'exécutant simultanément sur l'objet: elles partageront l'état d'itération, car il n'y a qu'un seul itérateur -- l'objet lui-même. Cependant, il est rare de disposer de deux for-of parallèles, faisables avec certains scénarios asynchrones.\n\n```smart header=\"Itérateurs infinis\"\nDes itérateurs infinis sont également possibles. Par exemple, `range` devient infini pour `range.to = Infinity`. Ou nous pouvons créer un objet itérable qui génère une suite infinie de nombres pseudo-aléatoires. Il peut être aussi utile.\n\nIl n'y a pas de limitation sur `next`, il peut renvoyer de plus en plus de valeurs, c'est normal.\n\nBien sûr, la boucle `for..of` sur une telle itération serait sans fin. Mais on peut toujours l'arrêter en utilisant `break`.\n```\n\n## String est iterable\n\nLes tableaux et les chaînes de caractères sont les iterables intégrés les plus largement utilisés.\n\nPour une chaîne de caractères, `for..of` boucle sur ses caractères :\n\n```js run\nfor (let char of \"test\") {\n  // se déclenche 4 fois: une fois pour chaque caractère\n  alert( char ); // t, ensuite e, ensuite s, ensuite t\n}\n```\n\nEt cela fonctionne correctement avec les paires de substitution !\n\n```js run\nlet str = '𝒳😂';\nfor (let char of str) {\n    alert( char ); // 𝒳, et ensuite 😂\n}\n```\n\n## Appeler explicitement un itérateur\n\nPour une compréhension plus approfondie, voyons comment utiliser explicitement un itérateur.\n\nNous allons parcourir une chaîne de caractères de la même manière que `for..of`, mais avec des appels directs. Ce code crée un itérateur de chaîne de caractères et en récupère la valeur \"manuellement\" :\n\n```js run\nlet str = \"Hello\";\n\n// fait la même chose que\n// for (let char of str) alert(char);\n\n*!*\nlet iterator = str[Symbol.iterator]();\n*/!*\n\nwhile (true) {\n  let result = iterator.next();\n  if (result.done) break;\n  alert(result.value); // affiche les caractères un par un\n}\n```\n\nCela est rarement nécessaire, mais nous donne plus de contrôle sur le processus que `for..of`. Par exemple, nous pouvons scinder le processus d'itération : itérer un peu, puis arrêter, faire autre chose, puis reprendre plus tard.\n\n## Iterables et array-likes [#array-like]\n\nIl existe deux termes officiels qui se ressemblent mais qui sont très différents. Assurez-vous de bien les comprendre pour éviter la confusion.\n\n- *Iterables* sont des objets qui implémentent la méthode `Symbol.iterator`, comme décrit ci-dessus.\n- *Array-likes* sont des objets qui ont des index et des `length`, ils ressemblent donc à des tableaux.\n\nLorsque nous utilisons JavaScript pour des tâches pratiques dans un navigateur ou d'autres environnements, il est possible que nous rencontrions des objets qui sont iterables ou des array-like, ou les deux.\n\nPar exemple, les chaînes de caractères sont à la fois iterables (`for..of` fonctionne dessus) et des array-likes (elles ont des index numériques et une longueur).\n\nMais un itérable peut ne pas ressembler à un array-like. Et inversement, un array-like peut ne pas être itérable.\n\nPar exemple, la `range` dans l'exemple ci-dessus est itérable, mais pas comme un array-like, car elle n'a pas de propriétés indexées et de `length`.\n\nEt voici l'objet qui ressemble à un tableau, mais pas itérable :\n\n```js run\nlet arrayLike = { // a des index et une longueur => semblable à un tableau\n  0: \"Hello\",\n  1: \"World\",\n  length: 2\n};\n\n*!*\n// Erreur (pas de Symbol.iterator)\nfor (let item of arrayLike) {}\n*/!*\n```\n\nLes iterables et les array-likes ne sont généralement *pas des tableaux*, ils n'ont pas `push`, `pop`, etc. C'est plutôt gênant si nous avons un tel objet et que nous voulons travailler avec lui comme avec un tableau. Par exemple, nous aimerions travailler avec une plage en utilisant les méthodes de tableau. Comment y parvenir ?\n\n## Array.from\n\nIl existe une méthode universelle [Array.from](mdn:js/Array/from) qui prend une valeur itérable ou array-like et en fait un \"vrai\" `Array`. Ensuite, nous pouvons appeler des méthodes de tableau dessus.\n\nPar exemple :\n\n```js run\nlet arrayLike = {\n  0: \"Hello\",\n  1: \"World\",\n  length: 2\n};\n\n*!*\nlet arr = Array.from(arrayLike); // (*)\n*/!*\nalert(arr.pop()); // World (la méthode fonctionne)\n```\n\n`Array.from` à la ligne `(*)` prend l'objet, l'examine comme étant un objet itérable ou un array-like, crée ensuite un nouveau tableau et y copie tous les éléments.\n\nIl en va de même pour un itérable :\n\n```js run\n// en supposant que cette \"range\" est tirée de l'exemple ci-dessus\nlet arr = Array.from(range);\nalert(arr); // 1,2,3,4,5 (array toString conversion fonctionne)\n```\n\nLa syntaxe complète de `Array.from` permet aussi de fournir une fonction optionnelle de \"mapping\" :\n\n```js\nArray.from(obj[, mapFn, thisArg])\n```\n\nLe second argument `mapFn` peut être une fonction à appliquer à chaque élément avant de l'ajouter au tableau, et `thisArg` permet d'en définir le `this`.\n\nPar exemple :\n\n```js run\n// en supposant que cette \"range\" est tirée de l'exemple ci-dessus\n\n// met au carré chaque nombre\nlet arr = Array.from(range, num => num * num);\n\nalert(arr); // 1,4,9,16,25\n```\n\nIci, nous utilisons `Array.from` pour transformer une chaîne en un tableau de caractères :\n\n```js run\nlet str = '𝒳😂';\n\n// divise une chaîne en un tableau de caractères\nlet chars = Array.from(str);\n\nalert(chars[0]); // 𝒳\nalert(chars[1]); // 😂\nalert(chars.length); // 2\n```\n\nContrairement à `str.split`, il repose sur la nature itérable de la chaîne et donc, tout comme `for..of`, fonctionne correctement avec des paires de substitution.\n\nTechniquement, il fait la même chose que :\n\n```js run\nlet str = '𝒳😂';\n\nlet chars = []; // Array.from en interne fait la même boucle\nfor (let char of str) {\n  chars.push(char);\n}\n\nalert(chars);\n```\n\n...Mais c'est plus court.\n\nNous pouvons même créer une fonction `slice` qui prend en compte les paires de substitution :\n\n```js run\nfunction slice(str, start, end) {\n  return Array.from(str).slice(start, end).join('');\n}\n\nlet str = '𝒳😂𩷶';\n\nalert( slice(str, 1, 3) ); // 😂𩷶\n\n// les méthodes native ne supporte pas les paires de substitution\nalert( str.slice(1, 3) ); // ordures (deux pièces de paires de substitution différentes)\n```\n\n## Résumé\n\nLes objets pouvant être utilisés dans `for..of` s'appellent *iterable*.\n\n- Techniquement, les iterables doivent implémenter la méthode nommée `Symbol.iterator`.\n    - Le résultat de `obj[Symbol.iterator]()` s'appelle un *itérateur*. Il gère le processus d'itération ultérieur.\n    - Un itérateur doit avoir la méthode nommée `next()` qui retourne un objet `{done: Boolean, value: any}`, ici `done: true` dénote la fin du processus de l'itération, sinon `value` est la valeur suivante.\n- La méthode `Symbol.iterator` est appelée automatiquement par `for..of`, mais nous pouvons aussi le faire directement.\n- Les iterables intégrés tels que des chaînes de caractères ou des tableaux implémentent également `Symbol.iterator`.\n- L'itérateur de chaîne de caractères connaît les paires de substitution.\n\nLes objets qui ont des propriétés indexées et des `length` sont appelés *array-like*. De tels objets peuvent également avoir d'autres propriétés et méthodes, mais ne possèdent pas les méthodes intégrées des tableaux.\n\nSi nous regardons à l'intérieur de la spécification -- nous verrons que la plupart des méthodes intégrées supposent qu'elles fonctionnent avec des éléments iterables ou des array-like au lieu de \"vrais\" tableaux, car c'est plus abstrait.\n\n`Array.from(obj[, mapFn, thisArg])` créer un véritable `Array` à partir d'un `obj` itérable ou array-like, et nous pouvons ensuite utiliser des méthodes de tableau sur celui-ci. Les arguments optionnels `mapFn` et `thisArg` nous permettent d'appliquer une fonction à chaque élément.\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/01-array-unique-map/_js.view/solution.js",
    "content": "function unique(arr) {\n  return Array.from(new Set(arr));\n}\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/01-array-unique-map/_js.view/test.js",
    "content": "describe(\"unique\", function() {\n  it(\"removes non-unique elements\", function() {\n    let strings = [\"Hare\", \"Krishna\", \"Hare\", \"Krishna\",\n      \"Krishna\", \"Krishna\", \"Hare\", \"Hare\", \":-O\"\n    ];\n\n    assert.deepEqual(unique(strings), [\"Hare\", \"Krishna\", \":-O\"]);\n  });\n\n  it(\"does not change the source array\", function() {\n    let strings = [\"Krishna\", \"Krishna\", \"Hare\", \"Hare\"];\n    unique(strings);\n    assert.deepEqual(strings, [\"Krishna\", \"Krishna\", \"Hare\", \"Hare\"]);\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/01-array-unique-map/solution.md",
    "content": ""
  },
  {
    "path": "1-js/05-data-types/07-map-set/01-array-unique-map/task.md",
    "content": "importance: 5\n\n---\n\n# Filtrer les membres uniques du tableau\n\nDisons que `arr` est un tableau.\n\nCréez une fonction `unique(arr)` qui devrait renvoyer un tableau avec les éléments uniques d'arr.\n\nPar exemple :\n\n```js\nfunction unique(arr) {\n  /* your code */\n}\n\nlet values = [\"Hare\", \"Krishna\", \"Hare\", \"Krishna\",\n  \"Krishna\", \"Krishna\", \"Hare\", \"Hare\", \":-O\"\n];\n\nalert( unique(values) ); // Hare, Krishna, :-O\n```\n\nP.S. Ici, les chaînes de caractères sont utilisées, mais elles peuvent être des valeurs de n'importe quel type.\n\nP.P.S. Utilisez `Set` pour stocker des valeurs uniques.\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/02-filter-anagrams/_js.view/solution.js",
    "content": "\nfunction aclean(arr) {\n  let map = new Map();\n\n  for(let word of arr) {\n    let sorted = word.toLowerCase().split(\"\").sort().join(\"\");\n    map.set(sorted, word);\n  }\n\n  return Array.from(map.values());\n}"
  },
  {
    "path": "1-js/05-data-types/07-map-set/02-filter-anagrams/_js.view/test.js",
    "content": "function intersection(arr1, arr2) {\n  return arr1.filter(item => arr2.includes(item));\n}\n\ndescribe(\"aclean\", function() {\n\n  it(\"returns exactly 1 word from each anagram set\", function() {\n    let arr = [\"nap\", \"teachers\", \"cheaters\", \"PAN\", \"ear\", \"era\", \"hectares\"];\n\n    let result = aclean(arr);\n    assert.equal(result.length, 3);\n\n    assert.equal(intersection(result, [\"nap\", \"PAN\"]).length, 1);\n    assert.equal(intersection(result, [\"teachers\", \"cheaters\", \"hectares\"]).length, 1);\n    assert.equal(intersection(result, [\"ear\", \"era\"]).length, 1);\n\n  });\n\n  it(\"is case-insensitive\", function() {\n    let arr = [\"era\", \"EAR\"];\n    assert.equal(aclean(arr).length, 1);\n  });\n\n});"
  },
  {
    "path": "1-js/05-data-types/07-map-set/02-filter-anagrams/solution.md",
    "content": "Pour trouver tous les anagrammes, divisons chaque mot en lettres et trions-les. Lorsque ils sont triés par lettres, tous les anagrammes sont identiques.\n\nPar exemple :\n\n```\nnap, pan -> anp\near, era, are -> aer\ncheaters, hectares, teachers -> aceehrst\n...\n```\n\nNous allons utiliser les variantes triées par lettre comme clés de map pour stocker une seule valeur pour chaque clé :\n\n```js run\nfunction aclean(arr) {\n  let map = new Map();\n\n  for (let word of arr) {\n    // diviser le mot en lettres, les trier et les rejoindre\n*!*\n    let sorted = word.toLowerCase().split('').sort().join(''); // (*)\n*/!*\n    map.set(sorted, word);\n  }\n\n  return Array.from(map.values());\n}\n\nlet arr = [\"nap\", \"teachers\", \"cheaters\", \"PAN\", \"ear\", \"era\", \"hectares\"];\n\nalert( aclean(arr) );\n```\n\nLe tri des lettres se fait par la chaîne d'appels en ligne `(*)`.\n\nPour plus de commodité, divisons-le en plusieurs lignes :\n\n```js\nlet sorted = word // PAN\n  .toLowerCase() // pan\n  .split('') // ['p','a','n']\n  .sort() // ['a','n','p']\n  .join(''); // anp\n```\n\nDeux mots différents, `'PAN'` et `'nap'`, reçoivent la même forme de lettre triée `'anp'`.\n\nLa ligne suivante place le mot dans le map :\n\n```js\nmap.set(sorted, word);\n```\n\nSi nous rencontrons à nouveau un mot trié de la même manière, il écrasera la valeur précédente avec la même clé dans le map. Nous aurons donc toujours au maximum un mot par lettre.\n\nÀ la fin `Array.from(map.values())` prends un itérable sur les valeurs du `Map` (nous n'avons pas besoin des clés dans le résultat) et renvoi un tableau avec celles-ci.\n\nIci, nous pourrions également utiliser un objet simple au lieu du `Map`, car les clés sont des chaînes de caractères.\n\nVoilà à quoi la solution peut ressembler :\n\n```js run demo\nfunction aclean(arr) {\n  let obj = {};\n\n  for (let i = 0; i < arr.length; i++) {\n    let sorted = arr[i].toLowerCase().split(\"\").sort().join(\"\");\n    obj[sorted] = arr[i];\n  }\n\n  return Object.values(obj);\n}\n\nlet arr = [\"nap\", \"teachers\", \"cheaters\", \"PAN\", \"ear\", \"era\", \"hectares\"];\n\nalert( aclean(arr) );\n```\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/02-filter-anagrams/task.md",
    "content": "importance: 4 \n\n---\n\n# Filter les anagrams\n\n[Anagrams](https://fr.wikipedia.org/wiki/Anagramme) sont des mots qui ont le même nombre de mêmes lettres, mais dans un ordre différent.\n\nPar exemple :\n\n```\nnap - pan\near - are - era\ncheaters - hectares - teachers\n```\n\nEcrivez une fonction `aclean(arr)` qui retourne un tableau nettoyé des anagrammes.\n\nPar exemple :\n\n```js\nlet arr = [\"nap\", \"teachers\", \"cheaters\", \"PAN\", \"ear\", \"era\", \"hectares\"];\n\nalert( aclean(arr) ); // \"nap,teachers,ear\" ou \"PAN,cheaters,era\"\n```\n\nDe chaque groupe d’anagrammes ne devrait rester qu’un mot, peu importe lequel.\n\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/03-iterable-keys/solution.md",
    "content": "\nC’est parce que `map.keys()` retourne un itérable, mais pas un tableau.\n\nNous pouvons le convertir en tableau en utilisant `Array.from` :\n\n\n```js run\nlet map = new Map();\n\nmap.set(\"name\", \"John\");\n\n*!*\nlet keys = Array.from(map.keys());\n*/!*\n\nkeys.push(\"more\");\n\nalert(keys); // name, more\n```\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/03-iterable-keys/task.md",
    "content": "importance: 5\n\n---\n\n# Clés Iterables\n\nNous voulons obtenir un tableau de `map.keys()` dans une variable puis lui appliquer des méthodes spécifiques aux tableaux, par ex: `.push`.\n\nMais cela ne fonctionne pas :\n\n```js run\nlet map = new Map();\n\nmap.set(\"name\", \"John\");\n\nlet keys = map.keys();\n\n*!*\n// Error: keys.push is not a function\nkeys.push(\"more\");\n*/!*\n```\n\nPourquoi ? Comment pouvons-nous corriger le code pour que `keys.push` fonctionne ?\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/article.md",
    "content": "# Map et Set\n\nJusqu'à présent, nous avons découvert les structures de données complexes suivantes :\n\n- Les objets sont utilisés pour stocker des collections de clés.\n- Les tableaux sont utilisés pour stocker des collections ordonnées.\n\nMais ce n'est pas suffisant pour la vie réelle. C'est pourquoi `Map` et `Set` existent également.\n\n## Map\n\nUne [Map](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Map) est une collection d'éléments de données saisis, tout comme un `Object`. Mais la principale différence est que `Map` autorise les clés de tout type.\n\nVoici les méthodes et les propriétés d'une `Map` :\n\n- [`new Map()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/Map) -- créer la map.\n- [`map.set(key, value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set) -- stocke la valeur par la clé.\n- [`map.get(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) -- renvoie la valeur par la clé, `undefined` si `key` n'existe pas dans la map.\n- [`map.has(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has) -- retourne `true` si la `key` existe, `false` sinon.\n- [`map.delete(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete) -- supprime l'élément (la paire clé/valeur) par la clé.\n- [`map.clear()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear) -- supprime tout de la map.\n- [`map.size`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size) -- renvoie le nombre d'éléments actuel.\n\nPar exemple :\n\n```js run\nlet map = new Map();\n\nmap.set('1', 'str1');   // une clé de type chaîne de caractère\nmap.set(1, 'num1');     // une clé de type numérique\nmap.set(true, 'bool1'); // une clé de type booléenne\n\n// souvenez-vous, dans un `Object`, les clés sont converties en chaîne de caractères\n// alors que `Map` conserve le type d'origine de la clé,\n// c'est pourquoi les deux appels suivants retournent des valeurs  :\n\nalert( map.get(1)   ); // 'num1'\nalert( map.get('1') ); // 'str1'\n\nalert( map.size ); // 3\n```\n\nAu travers de cet exemple nous pouvons voir, qu'à la différence des `Objects`, les clés ne sont pas converties en chaîne de caractère.\nIl est donc possible d'utiliser n'importe quel type.\n\n```smart header=\"`map[key]` n'est pas la bonne façon d'utiliser un `Map`\"\nBien que `map[key]` fonctionne également, par exemple nous pouvons définir `map[key] = 2`, cela traite `map` comme un objet JavaScript simple, ce qui implique toutes les limitations correspondantes (uniquement des clés chaîne de caractères/symbol etc.).\n\nNous devons donc utiliser les méthodes de `map` : `set`, `get` et ainsi de suite.\n```\n\n**Map peut également utiliser des objets comme clés.**\n\nPar exemple :\n\n```js run\nlet john = { name: \"John\" };\n\n// pour chaque utilisateur, nous stockons le nombre de visites\nlet visitsCountMap = new Map();\n\n// john est utilisé comme clé dans la map\nvisitsCountMap.set(john, 123);\n\nalert( visitsCountMap.get(john) ); // 123\n```\n\nUtiliser des objets comme clés est l'une des fonctionnalités les plus notables et les plus importantes de `Map`. La même chose ne compte pas pour `Object`. Une chaîne de caractères comme clé dans `Object` est très bien, mais nous ne pouvons pas utiliser un autre `Object` comme clé dans `Object`.\n\nEssayons de faire comme l'exemple précédent directement avec un `Object` :\n\n```js run\nlet john = { name: \"John\" };\nlet ben = { name: \"Ben\" };\n\nlet visitsCountObj = {}; // on créé notre object\n\nvisitsCountObj[ben] = 234; // essayez d'utiliser l'objet ben comme clé\nvisitsCountObj[john] = 123; // essayez d'utiliser l'objet john comme clé, l'objet ben sera remplacé\n\n*!*\n// C'est ce qui a été écrit!\nalert( visitsCountObj[\"[object Object]\"] ); // 123\n*/!*\n```\n\nComme `visitesCountObj` est un objet, il convertit toutes les clés `Object`, telles que `john` et `ben` ci-dessus, en la même chaîne de caractères `\"[object Object]\"`. Certainement pas ce que nous voulons.\n\n```smart header=\"Comment `Map` compare les clés\"\n\nPour tester l'égalité entre les clés, `Map` se base sur l'algorithme [SameValueZero](https://tc39.github.io/ecma262/#sec-samevaluezero).\nC'est grosso modo la même chose que l'opérateur de stricte égalité `===`, à la différence que `NaN` est considéré comme étant égal à `NaN`.\n`NaN` peut donc être utilisé comme clé.\n\nCet algorithme ne peut pas être modifié.\n```\n\n````smart header=\"Chaînage\"\n\nChaque appel à `map.set` retourne la `map` elle-même, ce qui nous permet d'enchaîner les appels :\n\n```js\nmap.set('1', 'str1')\n  .set(1, 'num1')\n  .set(true, 'bool1');\n```\n````\n\n## Itération dans Map\n\nIl existe 3 façons de parcourir les éléments d'une `map` :\n\n- [`map.keys()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/keys) -- renvoie un itérable pour les clés,\n- [`map.values()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/values) -- renvoie un itérable pour les valeurs,\n- [`map.entries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries) -- renvoie un itérable pour les entrées `[key, value]`, il est utilisé par défaut dans `for..of`.\n\nPar exemple :\n\n```js run\nlet recipeMap = new Map([\n  ['cucumber', 500],\n  ['tomatoes', 350],\n  ['onion',    50]\n]);\n\n// on parcourt les clés (les légumes)\nfor (let vegetable of recipeMap.keys()) {\n  alert(vegetable); // cucumber, tomatoes, onion\n}\n\n// on parcourt les valeurs (les montants)\nfor (let amount of recipeMap.values()) {\n  alert(amount); // 500, 350, 50\n}\n\n// on parcourt les entries (couple [clé, valeur])\nfor (let entry of recipeMap) { // équivalent à : recipeMap.entries()\n  alert(entry); // cucumber,500 (etc.)\n}\n```\n\n```smart header=\"L'ordre d'insertion est conservé\"\nContraitement aux `Object`, `Map` conserve l'ordre d'insertion des valeurs.\n```\n\nIl est aussi possible d'utiliser `forEach` avec `Map` comme on pourrait le faire avec un tableau :\n\n```js\n// exécute la fonction pour chaque couple (key, value)\nrecipeMap.forEach( (value, key, map) => {\n  alert(`${key}: ${value}`); // cucumber: 500 etc.\n});\n```\n\n## Object.entries: Créer une Map à partir d'un objet\n\nLorsqu'une `Map` est créée, nous pouvons passer un tableau (ou un autre itérable) contenant des paires clé/valeur pour l'initialisation, comme ceci :\n\n```js run\n// tableau de paires [clé, valeur]\nlet map = new Map([\n  ['1',  'str1'],\n  [1,    'num1'],\n  [true, 'bool1']\n]);\n\nalert( map.get('1') ); // str1\n```\n\nSi nous avons un objet simple et que nous souhaitons en créer une `Map`, nous pouvons utiliser la méthode intégrée [Object.entries(obj)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries) qui renvoie un tableau de paires clé/valeur pour un objet exactement dans ce format.\n\nNous pouvons donc créer une `Map` à partir d'un objet de la manière suivante :\n\n```js run\nlet obj = {\n  name: \"John\",\n  age: 30\n};\n\n*!*\nlet map = new Map(Object.entries(obj));\n*/!*\n\nalert( map.get('name') ); // John\n```\n\nIci, `Object.entries` renvoie le tableau de paires clé/valeur : `[ [\"name\", \"John\"], [\"age\", 30] ]`. C'est ce dont a besoin la `Map`.\n\n## Object.fromEntries: Objet à partir d'une Map\n\nNous venons de voir comment créer une `Map` à partir d'un objet simple avec `Object.entries(obj)`.\n\nIl existe une méthode `Object.fromEntries` qui fait l'inverse : étant donné un tableau de paires `[clé, valeur]`, elle crée un objet à partir de ces paires :\n\n```js run\nlet prices = Object.fromEntries([\n  ['banana', 1],\n  ['orange', 2],\n  ['meat', 4]\n]);\n\n// maintenant, prices = { banana: 1, orange: 2, meat: 4 }\n\nalert(prices.orange); // 2\n```\n\nNous pouvons utiliser `Object.fromEntries` pour obtenir un objet simple à partir d'une `Map`.\n\nPar exemple, nous stockons les données dans une `Map`, mais nous devons les transmettre à un code tiers qui attend un objet simple.\n\nVoici comment procéder :\n\n```js run\nlet map = new Map();\nmap.set('banana', 1);\nmap.set('orange', 2);\nmap.set('meat', 4);\n\n*!*\nlet obj = Object.fromEntries(map.entries()); // créer un objet simple (*)\n*/!*\n\n// terminé!\n// obj = { banana: 1, orange: 2, meat: 4 }\n\nalert(obj.orange); // 2\n```\n\nUn appel à `map.entries()` renvoie un itérable de paires clé/valeur, exactement dans le bon format pour `Object.fromEntries`.\n\nNous pourrions également raccourcir la ligne (*) :\n\n```js\nlet obj = Object.fromEntries(map); // .entries() omis\n```\n\nC'est la même chose, car `Object.fromEntries` attend un objet itérable en argument. Pas nécessairement un tableau. Et l'itération standard pour une `map` renvoie les mêmes paires clé/valeur que `map.entries()`. Ainsi, nous obtenons un objet simple avec les mêmes clés/valeurs que la `map`.\n\n## Set\n\nUn [`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) est une collection de types spéciaux - \"ensemble de valeurs\" (sans clés), où chaque valeur ne peut apparaître qu'une seule fois.\n\nSes principales méthodes sont :\n\n- [`new Set([iterable])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/Set) -- crée le set et si un objet `iterable` est fourni (généralement un tableau), en copie les valeurs dans le set.\n- [`set.add(value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/add) -- ajoute une valeur, renvoie le set lui-même.\n- [`set.delete(value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/delete) -- supprime la valeur, renvoie `true` si `value` existait au moment de l'appel, sinon `false`.\n- [`set.has(value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/has) -- renvoie `true` si la valeur existe dans le set sinon `false`.\n- [`set.clear()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/clear) -- supprime tout du set.\n- [`set.size`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/size) -- c'est le nombre d'éléments.\n\nCe qu'il faut surtout savoir c'est que lorsque l'on appelle plusieurs fois `set.add(value)` avec la même valeur, la méthode ne fait rien.\nC'est pourquoi chaque valeur est unique dans un `Set`.\n\nPar exemple, nous souhaitons nous souvenir de tous nos visiteurs. Mais chaque visiteurs doit être unique.\n\n`Set` est exactement ce qu'il nous faut :\n\n```js run\nlet set = new Set();\n\nlet john = { name: \"John\" };\nlet pete = { name: \"Pete\" };\nlet mary = { name: \"Mary\" };\n\n// visites, certains utilisateurs viennent plusieurs fois\nset.add(john);\nset.add(pete);\nset.add(mary);\nset.add(john);\nset.add(mary);\n\n// set conserve une fois chaque visiteurs\nalert( set.size ); // 3\n\nfor (let user of set) {\n  alert(user.name); // John (puis Pete et Mary)\n}\n```\n\nL'alternative à `Set` aurait pu être un tableau d'utilisateurs en vérifiant avant chaque insertion que l'élément n'existe pas en utilisant [arr.find](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/find). Cependant les performances auraient été moins bonnes car cette méthode parcours chaque élément du tableau. `Set` est beaucoup plus efficace car il est optimisé en interne pour vérifier l'unicité des valeurs.\n\n## Parcourir un Set\n\nNous pouvons parcourir les éléments d'un Set avec `for..of` ou en utilisant `forEach` :\n\n```js run\nlet set = new Set([\"oranges\", \"apples\", \"bananas\"]);\n\nfor (let value of set) alert(value);\n\n// même chose en utilisant forEach:\nset.forEach((value, valueAgain, set) => {\n  alert(value);\n});\n```\n\nA noter que la fonction de callback utilisée par `forEach` prend 3 arguments en paramètres : une `value`, puis *la même valeur* `valueAgain`, et enfin le set lui-même.\n\n\nC'est pour la compatibilité avec `Map` où le callback `forEach` passé possède trois arguments. Ça a l'air un peu étrange, c'est sûr. Mais cela peut aider à remplacer facilement `Map` par `Set` dans certains cas, et vice versa.\n\nLes méthodes pour parcourir les éléments d'une `Map` peuvent être utilisées :\n\n- [`set.keys()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/keys) -- renvoie un objet itérable pour les valeurs,\n- [`set.values()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/values) -- identique à `set.keys()`, pour compatibilité avec `Map`,\n- [`set.entries()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/entries) -- renvoie un objet itérable pour les entrées `[value, value]`, existe pour la compatibilité avec `Map`.\n\n## Résumé\n\n[`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) -- est une collection de valeurs-clés.\n\nMéthodes et propriétés :\n\n- [`new Map([iterable])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/Map) -- crée la map, avec un `iterable` facultatif (par exemple un tableau) de paires `[key, value]` pour l'initialisation.\n- [`map.set(key, value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/set) -- stocke la valeur par la clé, renvoie la map elle-même.\n- [`map.get(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/get) -- renvoie la valeur par la clé, `undefined` si `key` n'existe pas dans la map.\n- [`map.has(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/has) -- retourne `true` si la `key` existe, `false` sinon.\n- [`map.delete(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/delete) -- supprime l'élément par la clé, renvoie `true` si `key` existait au moment de l'appel, sinon `false`.\n- [`map.clear()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear) -- supprime tout de la map.\n- [`map.size`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/size) -- renvoie le nombre d'éléments actuel.\n\nLa différence entre `Map` avec un objet traditionel :\n\n- N'importe quel type peut être utilisé comme clé.\n- Accès à des méthodes tels que `size`.\n\n[`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) -- est une collection de valeurs uniques.\n\nMéthodes et propriétés :\n\n- [`new Set([iterable])`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/Set) -- crée le set avec un `iterable` facultatif (par exemple un tableau) de valeurs pour l'initialisation.\n- [`set.add(value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/add) -- ajoute une valeur (ne fait rien si `value` existe), renvoie l'ensemble lui-même.\n- [`set.delete(value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/delete) -- supprime la valeur, renvoie `true` si `value` existait au moment de l'appel, sinon `false`.\n- [`set.has(value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/has) -- renvoie `true` si la valeur existe dans l'ensemble, sinon `false`.\n- [`set.clear()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/clear) -- supprime tout du set.\n- [`set.size`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/size) -- c'est le nombre d'éléments.\n\nOn ne peut pas dire que les éléments dans une `Map` ou un `Set` sont désordonnés car ils sont toujours parcourut par ordre d'insertion.\nIl est cependant impossible de réorganiser les éléments ou bien de les retrouver par leur index.\n"
  },
  {
    "path": "1-js/05-data-types/08-weakmap-weakset/01-recipients-read/solution.md",
    "content": "Stockons les messages lus dans `WeakSet`:\n\n```js run\nlet messages = [\n  {text: \"Hello\", from: \"John\"},\n  {text: \"How goes?\", from: \"John\"},\n  {text: \"See you soon\", from: \"Alice\"}\n];\n\nlet readMessages = new WeakSet();\n\n// deux messages ont été lus\nreadMessages.add(messages[0]);\nreadMessages.add(messages[1]);\n// readMessages a 2 éléments\n\n// ...Relisons le premier message !\nreadMessages.add(messages[0]);\n// readMessages a encore 2 éléments uniques\n\n// réponse : le message[0] a-t-il été lu ?\nalert(\"Read message 0: \" + readMessages.has(messages[0])); // true\n\nmessages.shift();\n// maintenant readMessages a 1 élément (techniquement, la mémoire peut être nettoyée plus tard)\n```\n\nLe `WeakSet` permet de stocker un ensemble de messages et de vérifier facilement l’existence d’un message dedans.\n\nIl se nettoie automatiquement. Le compromis est que nous ne pouvons pas le parcourir, nous ne pouvons pas obtenir \"tous les messages lus\" directement. Mais nous pouvons le faire en parcourant tous les messages et en filtrant ceux qui sont dans le set.\n\nUne autre solution pourrait consister à ajouter une propriété telle que `message.isRead = true` à un message après sa lecture. Comme les objets de messages sont gérés par un autre code, cela est généralement déconseillé, mais nous pouvons utiliser une propriété symbolique pour éviter les conflits.\n\nComme ceci :\n```js\n// la propriété symbolique n'est connue que de notre code\nlet isRead = Symbol(\"isRead\");\nmessages[0][isRead] = true;\n```\n\nMaintenant, le code tiers ne verra probablement pas notre propriété supplémentaire.\n\nBien que les symboles permettent de réduire la probabilité de problèmes, l’utilisation de `WeakSet` est préférable du point de vue de l’architecture.\n"
  },
  {
    "path": "1-js/05-data-types/08-weakmap-weakset/01-recipients-read/task.md",
    "content": "importance: 5\n\n---\n\n# Stocker les messages non-lus\n\nIl y a un tableau de messages :\n\n```js\nlet messages = [\n  {text: \"Hello\", from: \"John\"},\n  {text: \"How goes?\", from: \"John\"},\n  {text: \"See you soon\", from: \"Alice\"}\n];\n```\n\nVotre code peut y accéder, mais les messages sont gérés par le code d’une autre personne. De nouveaux messages sont ajoutés, les anciens sont régulièrement supprimés par ce code et vous ne connaissez pas le moment exact où cela se produit.\n\nMaintenant, quelle structure de données pouvez-vous utiliser pour stocker des informations si le message \"a été lu\" ? La structure doit être bien adaptée pour donner la réponse \"a-t-il été lu ?\" Pour l'objet de message donné.\n\nP.S. Lorsqu'un message est supprimé des `messages`, il doit également disparaître de votre structure.\n\nP.P.S. Nous ne devrions pas modifier les objets de message, leur ajouter nos propriétés. Comme ils sont gérés par le code de quelqu'un d'autre, cela peut avoir de mauvaises conséquences.\n"
  },
  {
    "path": "1-js/05-data-types/08-weakmap-weakset/02-recipients-when-read/solution.md",
    "content": "\nPour stocker une date, nous pouvons utiliser `WeakMap`:\n\n```js\nlet messages = [\n  {text: \"Hello\", from: \"John\"},\n  {text: \"How goes?\", from: \"John\"},\n  {text: \"See you soon\", from: \"Alice\"}\n];\n\nlet readMap = new WeakMap();\n\nreadMap.set(messages[0], new Date(2017, 1, 1));\n// objet Date que nous étudierons plus tard\n```\n"
  },
  {
    "path": "1-js/05-data-types/08-weakmap-weakset/02-recipients-when-read/task.md",
    "content": "importance: 5\n\n---\n\n# Stocker les dates de lectures\n\nIl existe un tableau de messages comme dans la [previous task](info:task/recipients-read). La situation est similaire.\n\n```js\nlet messages = [\n  {text: \"Hello\", from: \"John\"},\n  {text: \"How goes?\", from: \"John\"},\n  {text: \"See you soon\", from: \"Alice\"}\n];\n```\n\nLa question qui se pose maintenant est la suivante : quelle structure de données suggérez-vous pour stocker les informations : \"quand le message a-t-il été lu ?\".\n\nDans la tâche précédente, nous n'avions besoin que de stocker le fait \"oui/non\". Nous devons maintenant stocker la date et elle ne doit rester en mémoire que tant que le message n’a pas été nettoyé.\n\nP.S. Les dates peuvent être stockées en tant qu'objets de la classe intégrée `Date`, que nous couvrirons plus tard.\n"
  },
  {
    "path": "1-js/05-data-types/08-weakmap-weakset/article.md",
    "content": "# WeakMap et WeakSet\n\nComme nous le savons du chapitre <info:garbage-collection>, le moteur JavaScript stocke une valeur en mémoire pendant qu'elle est accessible et peut potentiellement être utilisée.\n\nPar exemple :\n\n```js\nlet john = { name: \"John\" };\n\n// l'objet est accessible, john en est la référence\n\n// écraser la référence\njohn = null;\n\n*!*\n// l'objet sera supprimé de la mémoire\n*/!*\n```\n\nHabituellement, les propriétés d'un objet ou des éléments d'un tableau ou d'une autre structure de données sont considérées comme accessibles et conservées en mémoire pendant que cette structure de données est en mémoire.\n\nPar exemple, si nous mettons un objet dans un tableau, alors que le tableau est vivant, l'objet sera également vivant, même s'il n'y a pas d'autres références.\n\nComme ceci :\n\n```js\nlet john = { name: \"John\" };\n\nlet array = [ john ];\n\njohn = null; // écraser la référence\n\n*!*\n// l'objet précédemment référencé par john est stocké dans le tableau\n// donc il ne sera pas nettoyé\n// nous pouvons l'obtenir sous forme de array[0]\n*/!*\n```\n\nSemblable à cela, si nous utilisons un objet comme clé dans un `Map` classique, alors que le `Map` existe, cet objet existe également. Il occupe de la mémoire et ne peut pas être nettoyé (garbage collected).\n\nPar example :\n\n```js\nlet john = { name: \"John\" };\n\nlet map = new Map();\nmap.set(john, \"...\");\n\njohn = null; // écraser la référence\n\n*!*\n// John est stocké à l'intérieur du map\n// nous pouvons l'obtenir en utilisant map.keys()\n*/!*\n```\n\n[`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) est fondamentalement différent à cet égard. Cela n'empêche pas le garbage collection des objets clés.\n\nVoyons ce que cela signifie sur des exemples.\n\n## WeakMap\n\nLa première différences entre [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) et [`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) est que les clés doivent être des objets, pas des valeurs primitives :\n\n```js run\nlet weakMap = new WeakMap();\n\nlet obj = {};\n\nweakMap.set(obj, \"ok\"); // fonctionne bien (object key)\n\n*!*\n// ne peut pas utiliser une chaîne de caractères comme clé\nweakMap.set(\"test\", \"Whoops\"); // Erreur, parce que \"test\" n'est pas un objet\n*/!*\n```\n\nMaintenant, si nous utilisons un objet comme clé, et qu'il n'y a pas d'autres références à cet objet -- il sera automatiquement supprimé de la mémoire (et du map).\n\n```js\nlet john = { name: \"John\" };\n\nlet weakMap = new WeakMap();\nweakMap.set(john, \"...\");\n\njohn = null; // on écrase la référence\n\n// John est supprimé de la mémoire !\n```\n\nComparez-le avec l'exemple du `Map` ci-dessus. Maintenant, si `john` n'existe que comme clé de `WeakMap` -- il sera automatiquement supprimé du map (et de la mémoire).\n\n`WeakMap` ne prend pas en charge l'itération et les méthodes `keys()`, `values()`, `entries()`, il n'y a donc aucun moyen d'en obtenir toutes les clés ou valeurs.\n\n`WeakMap` n'a que les méthodes suivantes :\n\n- [`weakMap.set(key, value)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/set)\n- [`weakMap.get(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/get)\n- [`weakMap.delete(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/delete)\n- [`weakMap.has(key)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap/has)\n\nPourquoi une telle limitation ? C'est pour des raisons techniques. Si un objet a perdu toutes les autres références (comme `john` dans le code ci-dessus), il doit être automatiquement nettoyé. Mais techniquement, ce n'est pas exactement spécifié *quand le nettoyage a lieu*.\n\nLe moteur JavaScript décide de cela. Il peut choisir d'effectuer le nettoyage de la mémoire immédiatement ou d'attendre et de faire le nettoyage plus tard lorsque d'autres suppressions se produisent. Donc, techniquement, le nombre d'éléments actuel d'un `WeakMap` n'est pas connu. Le moteur peut l'avoir nettoyé ou non, ou l'a fait partiellement. Pour cette raison, les méthodes qui accèdent à toutes les clés/valeurs ne sont pas prises en charge.\n\nMaintenant, où avons-nous besoin d'une telle structure de données ?\n\n## Cas d'utilisation : données supplémentaires\n\nLe principal domaine d'application de `WeakMap` est un *stockage de données supplémentaire*.\n\nSi nous travaillons avec un objet qui \"appartient\" à un autre code, peut-être même une bibliothèque tierce, et que nous souhaitons stocker certaines données qui lui sont associées, cela ne devrait exister que lorsque l'objet est vivant -- alors `WeakMap` est exactement ce qu'il nous faut.\n\nNous plaçons les données dans un `WeakMap`, en utilisant l'objet comme clé, et lorsque l'objet est nettoyé, ces données disparaissent automatiquement également.\n\n```js\nweakMap.set(john, \"secret documents\");\n// si John meurt, les documents secrets seront détruits automatiquement\n```\n\nRegardons un exemple.\n\nPar exemple, nous avons un code qui conserve un nombre de visites pour les utilisateurs. Les informations sont stockées dans un map : un objet utilisateur est la clé et le nombre de visites est la valeur. Lorsqu'un utilisateur quitte (son objet est nettoyé), nous ne voulons plus stocker son nombre de visites.\n\nVoici un exemple d'une fonction de comptage avec `Map` :\n\n```js\n// 📁 visitsCount.js\nlet visitsCountMap = new Map(); // map: user => visits count\n\n// augmentons le nombre de visites\nfunction countUser(user) {\n  let count = visitsCountMap.get(user) || 0;\n  visitsCountMap.set(user, count + 1);\n}\n```\n\nEt voici une autre partie du code, peut-être un autre fichier qui l'utilise :\n\n```js\n// 📁 main.js\nlet john = { name: \"John\" };\n\ncountUser(john); // compter ses visites\n\n// plus tard, John nous quitte\njohn = null;\n```\n\nMaintenant, l'objet `john` doit être nettoyé, mais cependant, il reste en mémoire, parce que c'est une clé dans `visitesCountMap`.\n\nNous devons nettoyer `visitesCountMap` lorsque nous supprimons des utilisateurs, sinon il augmentera indéfiniment en mémoire. Un tel nettoyage peut devenir une tâche fastidieuse dans des architectures complexes.\n\nNous pouvons éviter cela en utilisant `WeakMap` :\n\n```js\n// 📁 visitsCount.js\nlet visitsCountMap = new WeakMap(); // weakmap: user => visits count\n\n// augmentons le nombre de visites\nfunction countUser(user) {\n  let count = visitsCountMap.get(user) || 0;\n  visitsCountMap.set(user, count + 1);\n}\n```\n\nMaintenant, nous n'avons plus à nettoyer `visitesCountMap`. Après que l'objet `john` devienne inaccessible autrement que en tant que clé de `WeakMap`, il est supprimé de la mémoire, en même temps que les informations de cette clé dans `WeakMap`.\n\n## Cas d'utilisation : mise en cache\n\nUn autre exemple courant est la mise en cache. Nous pouvons stocker (\"cache\") les résultats d'une fonction, afin que les futurs appels sur le même objet puissent le réutiliser.\n\nPour y parvenir, nous pouvons utiliser `Map` (scénario non optimal) :\n\n```js run\n// 📁 cache.js\nlet cache = new Map();\n\n// calculons et mémorisons le résultat\nfunction process(obj) {\n  if (!cache.has(obj)) {\n    let result = /* calculs du résultat pour */ obj;\n\n    cache.set(obj, result);\n    return result;\n  }\n\n  return cache.get(obj);\n}\n\n*!*\n// Maintenant, utilisons process() dans un autre fichier :\n*/!*\n\n// 📁 main.js\nlet obj = {/* disons que nous avons un objet */};\n\nlet result1 = process(obj); // calculé\n\n// … plus tard, d'un autre endroit du code …\nlet result2 = process(obj); // résultat mémorisé provenant du cache\n\n// … plus tard, lorsque l'objet n'est plus nécessaire :\nobj = null;\n\nalert(cache.size); // 1 (Ouch ! L'objet est toujours dans le cache, prenant de la mémoire !)\n```\n\nPour plusieurs appels de `process(obj)` avec le même objet, il ne calcule le résultat que la première fois, puis le prend simplement dans `cache`. L'inconvénient est que nous devons nettoyer le `cache` lorsque l'objet n'est plus nécessaire.\n\nSi nous remplaçons `Map` par `WeakMap`, alors ce problème disparaît : le résultat mis en cache sera automatiquement supprimé de la mémoire une fois que l'objet sera nettoyé.\n\n```js run\n// 📁 cache.js\n*!*\nlet cache = new WeakMap();\n*/!*\n\n// calculons et mémorisons le résultat\nfunction process(obj) {\n  if (!cache.has(obj)) {\n    let result = /* calculer le résultat pour */ obj;\n\n    cache.set(obj, result);\n    return result;\n  }\n\n  return cache.get(obj);\n}\n\n// 📁 main.js\nlet obj = {/* un objet */};\n\nlet result1 = process(obj);\nlet result2 = process(obj);\n\n// … plus tard, lorsque l'objet n'est plus nécessaire :\nobj = null;\n\n// Impossible d'obtenir cache.size, car c'est un WeakMap,\n// mais c'est 0 ou bientôt 0\n// Lorsque obj est nettoyé, les données mises en cache seront également supprimées\n```\n\n## WeakSet\n\n`WeakSet` se comporte de la même manière :\n\n- Il est analogue à `Set`, mais nous pouvons seulement ajouter des objets à `WeakSet` (pas de primitives).\n- Un objet existe dans le set tant qu'il est accessible ailleurs.\n- Comme `Set`, il prend en charge [`add`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Weakset/add), [`has`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Weakset/has) et [`delete`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Weakset/delete), mais pas `size`, `keys()` et aucune itération.\n\nÉtant \"weak\" (faible), il sert également de stockage supplémentaire. Mais pas pour des données arbitraires, mais plutôt pour des faits \"oui/non\". Une appartenance à `WeakSet` peut signifier quelque chose à propos de l'objet.\n\nPar exemple, nous pouvons ajouter des utilisateurs à `WeakSet` pour garder une trace de ceux qui ont visité notre site :\n\n```js run\nlet visitedSet = new WeakSet();\n\nlet john = { name: \"John\" };\nlet pete = { name: \"Pete\" };\nlet mary = { name: \"Mary\" };\n\nvisitedSet.add(john); // John nous a rendu visite\nvisitedSet.add(pete); // Ensuite Pete\nvisitedSet.add(john); // John encore\n\n// visitedSet a 2 utilisateurs maintenant\n\n// vérifions si John est venu\nalert(visitedSet.has(john)); // true\n\n// vérifions si Mary est venue\nalert(visitedSet.has(mary)); // false\n\njohn = null;\n\n// visitedSet sera nettoyé automatiquement\n```\n\nLa limitation la plus notable de `WeakMap` et `WeakSet` est l'absence d'itérations et l'impossibilité d'obtenir tout le contenu actuel. Cela peut sembler gênant, mais n'empêche pas `WeakMap`/`WeakSet` de faire leur travail principal -- être un stockage \"supplémentaire\" de données pour les objets qui sont stockés/gérés à un autre endroit.\n\n## Résumé\n\n[`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) est une sorte de collection `Map` qui n'autorise que des objets comme clés et les supprime avec la valeur associée une fois qu'ils deviennent inaccessibles par d'autres moyens.\n\n\n[`WeakSet`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet) est une sorte de collection `Set` qui ne stocke que des objets et les supprime une fois qu'ils deviennent inaccessibles par d'autres moyens.\n\nLeurs principaux avantages sont qu'ils ont une faible référence aux objets, de sorte qu'ils peuvent facilement être supprimés par le garbage collector.\n\nCela se fait au prix de ne pas avoir de support pour `clear`, `size`, `keys`, `values`...\n\n`WeakMap` et `WeakSet` sont utilisées comme structures de données \"secondaires\" en plus du stockage d'objets \"principal\". Une fois que l'objet est retiré du stockage principal, s'il n'est trouvé que comme clé de `WeakMap` ou dans un `WeakSet`, il sera nettoyé automatiquement.\n"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/01-sum-salaries/_js.view/solution.js",
    "content": "function sumSalaries(salaries) {\n\n  let sum = 0;\n  for (let salary of Object.values(salaries)) {\n    sum += salary;\n  }\n\n  return sum;\n}\n\n"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/01-sum-salaries/_js.view/test.js",
    "content": "describe(\"sumSalaries\", function() {\n  it(\"returns sum of salaries\", function() {\n    let salaries = {\n      \"John\": 100,\n      \"Pete\": 300,\n      \"Mary\": 250\n    };\n\n    assert.equal( sumSalaries(salaries), 650 );\n  });\n\n  it(\"returns 0 for the empty object\", function() {\n    assert.strictEqual( sumSalaries({}), 0);\n  });\n});"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/01-sum-salaries/solution.md",
    "content": "```js run demo\nfunction sumSalaries(salaries) {\n\n  let sum = 0;\n  for (let salary of Object.values(salaries)) {\n    sum += salary;\n  }\n\n  return sum; // 650\n}\n\nlet salaries = {\n  \"John\": 100,\n  \"Pete\": 300,\n  \"Mary\": 250\n};\n\nalert( sumSalaries(salaries) ); // 650\n```\nOu, éventuellement, nous pourrions aussi obtenir la somme en utilisant `Object.values` et `reduce`:\n\n```js\n// boucles de reduce sur les salaires,\n// en les additionnant\n// et retourne le résultat\nfunction sumSalaries(salaries) {\n  return Object.values(salaries).reduce((a, b) => a + b, 0) // 650\n}\n```\n"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/01-sum-salaries/task.md",
    "content": "importance: 5\n\n---\n\n# Additionner les propriétés\n\nIl existe un objet `salaries` avec un nombre arbitraire de salaires.\n\nEcrivez la fonction `sumSalaries(salaries)` qui retourne la somme des salaires en utilisant `Object.values` et la boucle `for..of`.\n\nSi `salaries` est vide, le résultat doit être `0`.\n\nPar exemple:\n\n```js\nlet salaries = {\n  \"John\": 100,\n  \"Pete\": 300,\n  \"Mary\": 250\n};\n\nalert( sumSalaries(salaries) ); // 650\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/02-count-properties/_js.view/solution.js",
    "content": "function count(obj) { \n  return Object.keys(obj).length;\n}\n\n"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/02-count-properties/_js.view/test.js",
    "content": "describe(\"count\", function() {\n  it(\"counts the number of properties\", function() {\n    assert.equal( count({a: 1, b: 2}), 2 );\n  });\n\n  it(\"returns 0 for an empty object\", function() {\n    assert.equal( count({}), 0 );\n  });\n\n  it(\"ignores symbolic properties\", function() {\n    assert.equal( count({ [Symbol('id')]: 1 }), 0 );\n  });\n});"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/02-count-properties/solution.md",
    "content": ""
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/02-count-properties/task.md",
    "content": "importance: 5\n\n---\n\n# Compter les propriétés\n\nEcrivez la fonction `count(obj)` qui retourne le nombre de propriétés qu'il y a dans l'objet:\n\n```js\nlet user = {\n  name: 'John',\n  age: 30\n};\n\nalert( count(user) ); // 2\n```\n\nEssayer d'écrire le code le plus petit possible.\n\nP.S: Ignorez les propriétés symboliques, ne comptez que les propriétés \"normales\".\n\n"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/article.md",
    "content": "# Object.keys, values, entries\n\nÉloignons-nous des structures de données individuelles et parlons des itérations les concernant.\n\nDans le chapitre précédent, nous avons vu les méthodes `map.keys()`, `map.values()`, `map.entries()`.\n\nCes méthodes sont génériques, il existe une convention de les utiliser pour les structures de données. Si nous devions créer notre propre structure de données, nous devrions aussi les implémenter.\n\nIls sont pris en charge par :\n\n- `Map`\n- `Set`\n- `Array`\n\nLes objets simples prennent également en charge des méthodes similaires, mais la syntaxe est un peu différente.\n\n## Object.keys, values, entries\n\nPour les objets simples, les méthodes suivantes sont disponibles :\n\n- [Object.keys(obj)](mdn:js/Object/keys) -- retourne un tableau de clés.\n- [Object.values(obj)](mdn:js/Object/values) -- retourne un tableau de valeurs.\n- [Object.entries(obj)](mdn:js/Object/entries) -- retourne un tableau de paires `[clé, valeur]`.\n\nVeuillez noter les différences (par rapport à map par exemple) :\n\n|                 | Map           | Object                                    |\n|-----------------|---------------|-------------------------------------------|\n| Syntaxe d'appel | `map.keys()`  | `Object.keys(obj)`, mais pas `obj.keys()` |\n| Retours         | iterable      | \"vrai\" Array                              |\n\nLa première différence est que nous devons appeler `Object.keys(obj)` et non `obj.keys()`.\n\nPourquoi cela ? La principale raison est la flexibilité. N'oubliez pas que les objets sont une base de toutes les structures complexes en JavaScript. Ainsi, nous pouvons avoir un objet à nous comme `data` qui implémente sa propre méthode `data.values()`. Et nous pouvons toujours appeler `Object.values(data)`.\n\nLa seconde différence réside dans le fait que les méthodes `Object.*` retourne de \"réels\" objets de tableau, et pas seulement des objets itératifs. C'est principalement pour des raisons historiques.\n\nPar exemple :\n\n```js\nlet user = {\n  name: \"John\",\n  age: 30\n};\n```\n\n- `Object.keys(user) = [name, age]`\n- `Object.values(user) = [\"John\", 30]`\n- `Object.entries(user) = [ [\"name\",\"John\"], [\"age\",30] ]`\n\nVoici un exemple d'utilisation d'`Object.values` pour boucler les valeurs de propriété :\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30\n};\n\n// boucle sur les valeurs\nfor (let value of Object.values(user)) {\n  alert(value); // John, ensuite 30\n}\n```\n\n```warn header=\"Object.keys/values/entries ignorer les propriétés symboliques\"\n\nTout comme une boucle `for..in`, ces méthodes ignorent les propriétés qui utilisent `Symbol(...)` comme clés.\n\nD'habitude c'est pratique. Mais si nous voulons aussi des clés symboliques, il existe une méthode distincte [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) qui retourne un tableau composé uniquement de clés symboliques. De plus, la méthde [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) renvoie *toutes* les clés.\n```\n\n## Transformer des objets\n\nLes objets manquent de nombreuses méthodes existantes pour les tableaux, par exemple `map`, `filter` et autres.\n\nSi nous souhaitons leur appliquer ces méthodes, nous pouvons utiliser `Object.entries` suivis par `Object.fromEntries` :\n\n1. Utilisons `Object.entries(obj)` pour obtenir un tableau de paires clé / valeur de `obj`.\n2. Utilisons des méthodes de tableau sur ce tableau, par exemple `map`, pour transformer ces paires clé / valeur.\n3. Utilisons `Object.fromEntries(array)` sur le tableau résultant pour le reconvertir en objet.\n\nPar exemple, nous avons un objet avec des prix et aimerions les doubler :\n\n```js run\nlet prices = {\n  banana: 1,\n  orange: 2,\n  meat: 4,\n};\n\n*!*\nlet doublePrices = Object.fromEntries(\n  // convertir les prix en tableau, mapper chaque paire clé / valeur dans une autre paire\n  // puis fromEntries restitue l'objet\n  Object.entries(prices).map(entry => [entry[0], entry[1] * 2])\n);\n*/!*\n\nalert(doublePrices.meat); // 8\n```\n\nCela peut sembler difficile au premier abord, mais cela devient facile à comprendre après l’avoir utilisé une ou deux fois. Nous pouvons faire de puissantes chaînes de transformations de cette façon.\n"
  },
  {
    "path": "1-js/05-data-types/10-destructuring-assignment/1-destruct-user/solution.md",
    "content": "\n```js run\nlet user = {\n  name: \"John\",\n  years: 30\n};\n\nlet {name, years: age, isAdmin = false} = user;\n\nalert( name ); // John\nalert( age ); // 30\nalert( isAdmin ); // false\n```"
  },
  {
    "path": "1-js/05-data-types/10-destructuring-assignment/1-destruct-user/task.md",
    "content": "importance: 5\n\n---\n\n# L'affectation par décomposition\n\nNous avons un objet :\n\n```js\nlet user = {\n  name: \"John\",\n  years: 30\n};\n```\n\nÉcrivez l'affectation par décomposition qui se lit comme suit :\n\n- La propriété `name` dans la variable `name`.\n- La propriété `years` dans la variable `age`.\n- La propriété `isAdmin` dans la variable `isAdmin` (false si absent)\n\nVoici un exemple des valeurs après votre affectation :\n\n```js\nlet user = { name: \"John\", years: 30 };\n\n// votre code à gauche ::\n// ... = user\n\nalert( name ); // John\nalert( age ); // 30\nalert( isAdmin ); // false\n```\n"
  },
  {
    "path": "1-js/05-data-types/10-destructuring-assignment/6-max-salary/_js.view/solution.js",
    "content": "function topSalary(salaries) {\n\n  let maxSalary = 0;\n  let maxName = null;\n\n  for(const [name, salary] of Object.entries(salaries)) {\n    if (maxSalary < salary) {\n      maxSalary = salary;\n      maxName = name;\n    }\n  }\n\n  return maxName;\n}"
  },
  {
    "path": "1-js/05-data-types/10-destructuring-assignment/6-max-salary/_js.view/test.js",
    "content": "describe(\"topSalary\", function() {\n  it(\"returns top-paid person\", function() {\n    let salaries = {\n      \"John\": 100,\n      \"Pete\": 300,\n      \"Mary\": 250\n    };\n\n    assert.equal( topSalary(salaries), \"Pete\" );\n  });\n\n  it(\"returns null for the empty object\", function() {\n    assert.isNull( topSalary({}) );\n  });\n});"
  },
  {
    "path": "1-js/05-data-types/10-destructuring-assignment/6-max-salary/solution.md",
    "content": ""
  },
  {
    "path": "1-js/05-data-types/10-destructuring-assignment/6-max-salary/task.md",
    "content": "importance: 5\n\n---\n\n# Le salaire maximal\n\nIl y a un objet `salaries` : \n\n```js\nlet salaries = {\n  \"John\": 100,\n  \"Pete\": 300,\n  \"Mary\": 250\n};\n```\n\nCréer la fonction `topSalary(salaries)` qui renvoie le nom de la personne la mieux payée.\n\n- Si `salaries` est vide, devrait retourner `null`.\n- S'il y a plusieurs personnes les mieux payées, renvoyez-en une.\n\nP.S. Utilisez `Object.entries` et la décomposition pour parcourir les paires clé / valeur.\n"
  },
  {
    "path": "1-js/05-data-types/10-destructuring-assignment/article.md",
    "content": "# L'affectation par décomposition\n\nLes deux structures de données les plus utilisées en JavaScript sont `Object` et `Array`.\n\n- Les objets nous permettent de créer une seule entité qui stocke les éléments de données par clé.\n- Les tableaux nous permettent de rassembler des éléments de données dans une liste ordonnée.\n\nMais lorsque nous transmettons ceux-ci à une fonction, il se peut que celle-ci n'ait pas besoin d'un objet / tableau dans son ensemble, mais plutôt de morceaux individuels.\n\n*L'affectation par décomposition*  est une syntaxe spéciale qui nous permet de \"décompresser\" des tableaux ou des objets dans un ensemble de variables, ce qui est parfois plus pratique.\n\nLa décomposition fonctionne également très bien avec des fonctions complexes comportant de nombreux paramètres, valeurs par défaut, etc.\n\n## Décomposition d'un tableau\n\nVoici un exemple de la façon dont un tableau est décomposé en variables :\n\n```js\n// nous avons un tableau avec le prénom et le nom\nlet arr = [\"John\", \"Smith\"]\n\n*!*\n// l'affectation par décomposition\n// sets firstName = arr[0]\n// and surname = arr[1]\nlet [firstName, surname] = arr;\n*/!*\n\nalert(firstName); // John\nalert(surname);  // Smith\n```\n\nMaintenant, nous pouvons travailler avec des variables plutôt que des parties du tableau.\n\nCela a l'air pas mal quand c'est combiné avec `split` ou tout autre méthode de renvoi de tableau :\n\n```js run\nlet [firstName, surname] = \"John Smith\".split(' ');\nalert(firstName); // John\nalert(surname);  // Smith\n```\n\nComme vous pouvez le voir, la syntaxe est simple. Il y a cependant plusieurs détails particuliers. Voyons plus d'exemples, pour mieux le comprendre.\n\n````smart header=\"\\\"Décomposition\\\" ne veut pas dire \\\"destruction\\\".\"\nCette manipulation est appelée \"affectation par décomposition\", car elle \"se décompose\" en copiant ses éléments dans des variables. Mais le tableau lui-même n'est pas modifié.\n\nC’est juste une façon plus courte d’écrire :\n```js\n// let [firstName, surname] = arr;\nlet firstName = arr[0];\nlet surname = arr[1];\n```\n````\n\n````smart header=\"Ignorer les éléments en utilisant des virgules\"\nLes éléments indésirables du tableau peuvent également être supprimés via une virgule supplémentaire :\n\n```js run\n*!*\n// second element is not needed\nlet [firstName, , title] = [\"Julius\", \"Caesar\", \"Consul\", \"of the Roman Republic\"];\n*/!*\n\nalert( title ); // Consul\n```\n\nDans le code ci-dessus, le deuxième élément du tableau est ignoré, le troisième est attribué à `title` et le reste du tableau est également ignoré (car il n'y a pas de variables pour eux).\n````\n\n````smart header=\"Fonctionne avec n'importe quel itérable à droite\"\n\n...En fait, nous pouvons l’utiliser avec n’importe quel itérable, pas seulement les tableaux :\n\n```js\nlet [a, b, c] = \"abc\"; // [\"a\", \"b\", \"c\"]\nlet [one, two, three] = new Set([1, 2, 3]);\n```\nThat works, because internally a destructuring assignment works by iterating over the right value. It's a kind of syntax sugar for calling `for..of` over the value to the right of `=` and assigning the values.\n````\n\n````smart header=\"Attribuer n'importe quoi à la partie gauche\"\nNous pouvons utiliser n'importe quels \"assignables\" à gauche.\n\nPar exemple, une propriété d'objet :\n```js run\nlet user = {};\n[user.name, user.surname] = \"John Smith\".split(' ');\n\nalert(user.name); // John\nalert(user.surname); // Smith\n```\n\n````\n\n````smart header=\"Boucler avec .entries()\"\n\nDans le chapitre précédent, nous avons vu la méthode [Object.entries(obj)](mdn:js/Object/entries).\n\nNous pouvons l'utiliser avec la décomposition pour boucler sur les clés et valeurs d'un objet :\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30\n};\n\n// boucler sur les clés et les valeurs\n*!*\nfor (let [key, value] of Object.entries(user)) {\n*/!*\n  alert(`${key}:${value}`); // name:John, ensuite age:30\n}\n```\n\nLe code similaire pour un `Map` est plus simple, car il est itérable :\n\n```js run\nlet user = new Map();\nuser.set(\"name\", \"John\");\nuser.set(\"age\", \"30\");\n\n*!*\n// Map est itéré sous forme de paires [key, value], ce qui est très pratique pour la déstructuration\nfor (let [key, value] of user) {\n*/!*\n  alert(`${key}:${value}`); // name:John, ensuite age:30\n}\n```\n````\n\n````smart header=\"Astuce d'échange de variables\"\nIl existe une astuce bien connue pour permuter les valeurs de deux variables à l'aide d'une affectation de déstructuration :\n\n```js run\nlet guest = \"Jane\";\nlet admin = \"Pete\";\n\n// Permutons les valeurs : guest=Pete, admin=Jane\n*!*\n[guest, admin] = [admin, guest];\n*/!*\n\nalert(`${guest} ${admin}`); // Pete Jane (échangé avec succès !)\n```\n\nIci, nous créons un tableau temporaire de deux variables et le déstructurons immédiatement dans l'ordre permuté.\n\nNous pouvons échanger plus de deux variables de cette façon.\n\n````\n\n### Le rest '...'\n\nHabituellement, si le tableau est plus long que la liste à gauche, les éléments \"supplémentaires\" sont omis.\n\nPar exemple, ici, seuls deux éléments sont pris et le reste est simplement ignoré :\n\n```js run\nlet [name1, name2] = [\"Julius\", \"Caesar\", \"Consul\", \"of the Roman Republic\"];\n\nalert(name1); // Julius\nalert(name2); // Caesar\n// Les autres éléments ne sont affectés nulle part\n```\n\nSi nous souhaitons également rassembler tout ce qui suit, nous pouvons ajouter un paramètre supplémentaire qui permet d'obtenir \"le reste\" à l'aide de trois points `\"...\"` :\n\n```js run\nlet [name1, name2, *!*...rest*/!*] = [\"Julius\", \"Caesar\", *!*\"Consul\", \"of the Roman Republic\"*/!*];\n\n*!*\n// reste est un tableau d'éléments, à partir du 3ème\nalert(rest[0]); // Consul\nalert(rest[1]); // of the Roman Republic\nalert(rest.length); // 2\n*/!*\n```\n\nLa valeur de `rest` est le tableau des éléments du tableau restants.\n\nNous pouvons utiliser n’importe quel autre nom de variable à la place de `rest`, assurez-vous simplement qu’il a trois points devant lui et soit placé en dernier dans l’affectation par décomposition.\n\n```js run\nlet [name1, name2, *!*...titles*/!*] = [\"Julius\", \"Caesar\", \"Consul\", \"of the Roman Republic\"];\n// maintenant titles = [\"Consul\", \"of the Roman Republic\"]\n```\n\n### Les valeurs par défaut\n\nSi le tableau est plus court que la liste des variables à gauche, il n'y aura aucune erreur. Les valeurs absentes sont considérées comme non définies :\n\n```js run\n*!*\nlet [firstName, surname] = [];\n*/!*\n\nalert(firstName); // undefined\nalert(surname); // undefined\n```\n\nSi nous voulons qu'une valeur \"par défaut\" remplace la valeur manquante, nous pouvons la fournir en utilisant `=` :\n\n```js run\n*!*\n// les valeurs par défaut\nlet [name = \"Guest\", surname = \"Anonymous\"] = [\"Julius\"];\n*/!*\n\nalert(name);    // Julius (depuis le tableau)\nalert(surname); // Anonymous (valeur par défaut)\n```\n\nLes valeurs par défaut peuvent être des expressions plus complexes ou même des appels de fonction. Ils ne sont évalués que si la valeur n'est pas fournie.\n\nPar exemple, nous utilisons ici la fonction `prompt` pour deux valeurs par défaut :\n\n```js run\n// ne demande que le nom de famille\nlet [name = prompt('name?'), surname = prompt('surname?')] = [\"Julius\"];\n\nalert(name);    // Julius (depuis le tableau)\nalert(surname); // saisi par l'utilisateur\n```\n\nNotez que le `prompt` ne s'exécutera que pour la valeur manquante (`surname`).\n\n## Décomposition d'object\n\nL'affectation par décomposition fonctionne également avec les objets.\n\nLa syntaxe de base est la suivante :\n\n```js\nlet {var1, var2} = {var1:…, var2:…}\n```\n\nNous devrions avoir un objet existant sur le côté droit, que nous voulons diviser en variables. La partie gauche contient un \"modèle\" de type objet pour les propriétés correspondantes. Dans le cas le plus simple, c'est une liste de noms de variables dans `{...}`.\n\nPar exemple :\n\n```js run\nlet options = {\n  title: \"Menu\",\n  width: 100,\n  height: 200\n};\n\n*!*\nlet {title, width, height} = options;\n*/!*\n\nalert(title);  // Menu\nalert(width);  // 100\nalert(height); // 200\n```\n\nLes propriétés `options.title`, `options.width` et `options.height` sont affectées aux variables correspondantes.\n\nL'ordre n'a pas d'importance. Cela fonctionne aussi :\n\n```js\n// changé l'ordre dans let {...}\nlet {height, width, title} = { title: \"Menu\", height: 200, width: 100 }\n```\n\nLe pattern à gauche peut être plus complexe et spécifier le mapping entre propriétés et variables.\n\nSi nous voulons affecter une propriété à une variable portant un autre nom, par exemple, `options.width` pour aller dans la variable nommée `w`, alors nous pouvons la définir en utilisant deux points :\n\n```js run\nlet options = {\n  title: \"Menu\",\n  width: 100,\n  height: 200\n};\n\n*!*\n// { sourceProperty: targetVariable }\nlet {width: w, height: h, title} = options;\n*/!*\n\n// width -> w\n// height -> h\n// title -> title\n\nalert(title);  // Menu\nalert(w);      // 100\nalert(h);      // 200\n```\n\nLes deux points montrent \"quoi: va où\". Dans l'exemple ci-dessus, la propriété `width` est définie sur `w`, la propriété `height` est définie sur `h` et le `title` est attribué au même nom.\n\nPour les propriétés potentiellement manquantes, nous pouvons définir les valeurs par défaut à l'aide de `\"=\"`, comme ceci :\n\n```js run\nlet options = {\n  title: \"Menu\"\n};\n\n*!*\nlet {width = 100, height = 200, title} = options;\n*/!*\n\nalert(title);  // Menu\nalert(width);  // 100\nalert(height); // 200\n```\n\nComme pour les tableaux ou les paramètres de fonction, les valeurs par défaut peuvent être des expressions ou même des appels de fonction. Elles seront évaluées si la valeur n'est pas fournie.\n\nLe code `prompt` ci-dessous demande la `width`, mais pas le `title`.\n\n```js run\nlet options = {\n  title: \"Menu\"\n};\n\n*!*\nlet {width = prompt(\"width?\"), title = prompt(\"title?\")} = options;\n*/!*\n\nalert(title);  // Menu\nalert(width);  // saisi par l'utilisateur\n```\n\nNous pouvons également combiner les deux points et l'égalité :\n\n```js run\nlet options = {\n  title: \"Menu\"\n};\n\n*!*\nlet {width: w = 100, height: h = 200, title} = options;\n*/!*\n\nalert(title);  // Menu\nalert(w);      // 100\nalert(h);      // 200\n```\n\nSi nous avons un objet complexe avec de nombreuses propriétés, nous pouvons extraire que ce dont nous avons besoin :\n\n```js run\nlet options = {\n  title: \"Menu\",\n  width: 100,\n  height: 200\n};\n\n// extraire uniquement le titre en tant que variable\nlet { title } = options;\n\nalert(title); // Menu\n```\n\n### Le pattern rest \"...\"\n\nEt si l'objet a plus de propriétés que de variables ? Peut-on en prendre puis assigner le \"rest\" quelque part ?\n\nNous pouvons utiliser le modèle rest, comme nous l'avons fait avec les tableaux. Il n'est pas pris en charge par certains navigateurs plus anciens (IE, utilisez Babel pour le polyfiller), mais fonctionne avec les modernes.\n\nCela ressemble à ceci :\n\n```js run\nlet options = {\n  title: \"Menu\",\n  height: 200,\n  width: 100\n};\n\n*!*\n// title = propriété nommée title\n// rest = objet avec le reste des propriétés\nlet {title, ...rest} = options;\n*/!*\n\n// maintenant title=\"Menu\", rest={height: 200, width: 100}\nalert(rest.height);  // 200\nalert(rest.width);   // 100\n```\n\n````smart header=\"Attention, sans `let`, ça coince\"\nDans les exemples ci-dessus, les variables ont été déclarées juste avant l'affectation : `let {…} = {…}`. Bien sûr, nous pourrions aussi utiliser des variables existantes. Mais il y a un problème.\n\nCela ne fonctionnera pas :\n\n```js run\nlet title, width, height;\n\n// erreur dans cette ligne\n{title, width, height} = {title: \"Menu\", width: 200, height: 100};\n```\n\nLe problème est que JavaScript traite `{...}` dans le flux de code principal (pas dans une autre expression) en tant que bloc de code. De tels blocs de code peuvent être utilisés pour regrouper des instructions, comme ceci :\n\n```js run\n{\n  // un bloc de code\n  let message = \"Hello\";\n  // ...\n  alert( message );\n}\n```\n\nDonc ici, JavaScript suppose que nous avons un bloc de code, c'est pourquoi il y a une erreur. Nous voulons plutôt la déstructuration.\n\nPour montrer à JavaScript qu'il ne s'agit pas d'un bloc de code, nous pouvons envelopper l'expression entre parenthèses `(...)` :\n\n```js run\nlet title, width, height;\n\n// maintenant c'est bon\n*!*(*/!*{title, width, height} = {title: \"Menu\", width: 200, height: 100}*!*)*/!*;\n\nalert( title ); // Menu\n```\n````\n\n## Décomposition imbriquée\n\nSi un objet ou un tableau contient d'autres objets et tableaux imbriqués, nous pouvons utiliser des modèles à gauche plus complexes pour extraire des parties plus profondes.\n\nDans le code ci-dessous, `options` a un autre objet dans la propriété `size` et un tableau dans la propriété `items`. Le modèle à gauche de l'affectation a la même structure pour en extraire des valeurs :\n\n```js run\nlet options = {\n  size: {\n    width: 100,\n    height: 200\n  },\n  items: [\"Cake\", \"Donut\"],\n  extra: true\n};\n\n// affectation par décomposition divisée sur plusieurs lignes pour la clarté\nlet {\n  size: { // mettre la taille ici\n    width,\n    height\n  },\n  items: [item1, item2], // attribuer des éléments ici\n  title = \"Menu\" // non présent dans l'objet (la valeur par défaut est utilisée)\n} = options;\n\nalert(title);  // Menu\nalert(width);  // 100\nalert(height); // 200\nalert(item1);  // Cake\nalert(item2);  // Donut\n```\n\nToutes les propriétés de l'objet `options`, à l'exception de `extra` qui est absente dans la partie gauche, sont affectés aux variables correspondantes :\n\n![](destructuring-complex.svg)\n\nFinnalement nous avons `width`, `height`, `item1`, `item2` et `title` à partir de la valeur par défaut.\n\nNotez qu'il n'y a pas de variables pour `size` et `items`, car nous prenons leur contenu à la place.\n\n## Paramètres de fonction intelligente\n\nIl peut arriver qu'une fonction ait plusieurs paramètres, dont la plupart sont facultatifs. C’est particulièrement vrai pour les interfaces utilisateur. Imaginez une fonction qui crée un menu. Il peut avoir une largeur, une hauteur, un titre, une liste d’articles, etc.\n\nVoici une mauvaise façon d’écrire ce genre de fonction :\n\n```js\nfunction showMenu(title = \"Untitled\", width = 200, height = 100, items = []) {\n  // ...\n}\n```\n\nDans la vraie vie, le problème est de savoir comment retenir l'ordre des arguments. Habituellement, les IDE essaient de nous aider, surtout si le code est bien documenté, mais quand même… Un autre problème est de savoir comment appeler une fonction lorsque la plupart des paramètres sont corrects par défaut.\n\nComme ceci ?\n\n```js\n// undefined où les valeurs par défaut sont adéquates\nshowMenu(\"My Menu\", undefined, undefined, [\"Item1\", \"Item2\"])\n```\n\nC’est moche. Et devient illisible lorsque nous traitons plus de paramètres.\n\nLa décomposition vient à la rescousse !\n\nNous pouvons passer des paramètres sous forme d'objet, et la fonction les décomposent immédiatement en variables :\n\n```js run\n// on passe un objet à fonction\nlet options = {\n  title: \"My menu\",\n  items: [\"Item1\", \"Item2\"]\n};\n\n// ...et il est immédiatement étendu aux variables\nfunction showMenu(*!*{title = \"Untitled\", width = 200, height = 100, items = []}*/!*) {\n  // title, items – pris des options,\n  // width, height – défauts utilisés\n  alert( `${title} ${width} ${height}` ); // My Menu 200 100\n  alert( items ); // Item1, Item2\n}\n\nshowMenu(options);\n```\n\nNous pouvons également utiliser une décomposition plus complexe avec des objets imbriqués et des mappings de deux points :\n\n```js run\nlet options = {\n  title: \"My menu\",\n  items: [\"Item1\", \"Item2\"]\n};\n\n*!*\nfunction showMenu({\n  title = \"Untitled\",\n  width: w = 100,  // width va à w\n  height: h = 200, // height va à h\n  items: [item1, item2] // items premier élément va à item1, deuxième à item2\n}) {\n*/!*\n  alert( `${title} ${w} ${h}` ); // My Menu 100 200\n  alert( item1 ); // Item1\n  alert( item2 ); // Item2\n}\n\nshowMenu(options);\n```\n\nLa syntaxe complète est la même que pour une affectation par décomposition :\n```js\nfunction({\n  incomingProperty: varName = defaultValue\n  ...\n})\n```\n\nEnsuite, pour un objet de paramètres, il y aura une variable `varName` pour la propriété `incomingProperty`, avec `defaultValue` par défaut.\n\nVeuillez noter qu'une telle déstructuration suppose que `showMenu()` a un argument. Si nous voulons toutes les valeurs par défaut, alors nous devrions spécifier un objet vide :\n\n```js\nshowMenu({}); // ok, toutes les valeurs par défaut\n\nshowMenu(); // cela donnerait une erreur\n```\n\nNous pouvons résoudre ce problème en faisant de `{}` la valeur par défaut pour l'objet entier de paramètres :\n\n```js run\nfunction showMenu({ title = \"Menu\", width = 100, height = 200 }*!* = {}*/!*) {\n  alert( `${title} ${width} ${height}` );\n}\n\nshowMenu(); // Menu 100 200\n```\n\nDans le code ci-dessus, la totalité des arguments objet est `{}` par défaut, il y a donc toujours quelque chose à décomposer.\n\n## Résumé\n\n- L'affectation par décomposition permet de mapper instantanément un objet ou un tableau sur de nombreuses variables.\n- La syntaxe complète de l'objet :\n    ```js\n    let {prop : varName = default, ...rest} = object\n    ```\n\n    Cela signifie que la propriété `prop` doit aller dans la variable `varName` et que, si aucune propriété de ce type n'existe, la valeur `default` doit être utilisée.\n\n    Les propriétés d'objet sans mappage sont copiées dans l'objet `rest`.\n\n- La syntaxe complète du tableau :\n\n    ```js\n    let [item1 = default, item2, ...rest] = array\n    ```\n\n    Le premier item va à `item1`; le second passe à `item2`, tout le reste du tableau correspond au `rest`.\n\n- Il est possible d'extraire des données de tableaux / objets imbriqués, pour cela le côté gauche doit avoir la même structure que le droit.\n"
  },
  {
    "path": "1-js/05-data-types/11-date/1-new-date/solution.md",
    "content": "The `new Date` constructor uses the local time zone. So the only important thing to remember is that months start from zero.\n\nSo February has number 1.\n\nHere's an example with numbers as date components:\n\n```js run\n//new Date(year, month, date, hour, minute, second, millisecond)\nlet d1 = new Date(2012, 1, 20, 3, 12);\nalert( d1 );\n```\nWe could also create a date from a string, like this:\n\n```js run\n//new Date(datastring)\nlet d2 = new Date(\"2012-02-20T03:12\");\nalert( d2 );\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/1-new-date/task.md",
    "content": "importance: 5\n\n---\n\n# Créer une date\n\nCréez un objet `Date` pour la date: 20 février 2012, 3h12. Le fuseau horaire est local.\n\nMontrez-le en utilisant `alert`.\n"
  },
  {
    "path": "1-js/05-data-types/11-date/2-get-week-day/_js.view/solution.js",
    "content": "function getWeekDay(date) {\n  let days = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];\n\n  return days[date.getDay()];\n}\n"
  },
  {
    "path": "1-js/05-data-types/11-date/2-get-week-day/_js.view/test.js",
    "content": "describe(\"getWeekDay\", function() {\n  it(\"3 January 2014 - friday\", function() {\n    assert.equal(getWeekDay(new Date(2014, 0, 3)), 'FR');\n  });\n\n  it(\"4 January 2014 - saturday\", function() {\n    assert.equal(getWeekDay(new Date(2014, 0, 4)), 'SA');\n  });\n\n  it(\"5 January 2014 - sunday\", function() {\n    assert.equal(getWeekDay(new Date(2014, 0, 5)), 'SU');\n  });\n\n  it(\"6 January 2014 - monday\", function() {\n    assert.equal(getWeekDay(new Date(2014, 0, 6)), 'MO');\n  });\n\n  it(\"7 January 2014 - tuesday\", function() {\n    assert.equal(getWeekDay(new Date(2014, 0, 7)), 'TU');\n  });\n\n  it(\"8 January 2014 - wednesday\", function() {\n    assert.equal(getWeekDay(new Date(2014, 0, 8)), 'WE');\n  });\n\n  it(\"9 January 2014 - thursday\", function() {\n    assert.equal(getWeekDay(new Date(2014, 0, 9)), 'TH');\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/11-date/2-get-week-day/solution.md",
    "content": "La méthode `date.getDay()` renvoie le numéro du jour de la semaine à partir du dimanche.\n\nFaisons un tableau des jours de la semaine afin d’obtenir le nom du jour par son numéro:\n\n```js run demo\nfunction getWeekDay(date) {\n  let days = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];\n\n  return days[date.getDay()];\n}\n\nlet date = new Date(2014, 0, 3); // 3 Jan 2014\nalert( getWeekDay(date) ); // FR\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/2-get-week-day/task.md",
    "content": "importance: 5\n\n---\n\n# Montrer un jour de la semaine\n\nEcrivez une fonction `getWeekDay(date)` pour afficher le jour de la semaine sous forme abrégée: 'MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU'.\n\nPar exemple:\n\n```js no-beautify\nlet date = new Date(2012, 0, 3);  // 3 Janvier 2012\nalert( getWeekDay(date) );        // devrait afficher \"TU\"\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/3-weekday/_js.view/solution.js",
    "content": "function getLocalDay(date) {\n\n  let day = date.getDay();\n\n  if (day == 0) { // semaine 0 (dimanche) est 7 en européen\n    day = 7;\n  }\n\n  return day;\n}\n"
  },
  {
    "path": "1-js/05-data-types/11-date/3-weekday/_js.view/test.js",
    "content": "describe(\"getLocalDay returns the \\\"european\\\" weekday\", function() {\n  it(\"3 January 2014 - friday\", function() {\n    assert.equal(getLocalDay(new Date(2014, 0, 3)), 5);\n  });\n\n  it(\"4 January 2014 - saturday\", function() {\n    assert.equal(getLocalDay(new Date(2014, 0, 4)), 6);\n  });\n\n  it(\"5 January 2014 - sunday\", function() {\n    assert.equal(getLocalDay(new Date(2014, 0, 5)), 7);\n  });\n\n  it(\"6 January 2014 - monday\", function() {\n    assert.equal(getLocalDay(new Date(2014, 0, 6)), 1);\n  });\n\n  it(\"7 January 2014 - tuesday\", function() {\n    assert.equal(getLocalDay(new Date(2014, 0, 7)), 2);\n  });\n\n  it(\"8 January 2014 - wednesday\", function() {\n    assert.equal(getLocalDay(new Date(2014, 0, 8)), 3);\n  });\n\n  it(\"9 January 2014 - thursday\", function() {\n    assert.equal(getLocalDay(new Date(2014, 0, 9)), 4);\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/11-date/3-weekday/solution.md",
    "content": ""
  },
  {
    "path": "1-js/05-data-types/11-date/3-weekday/task.md",
    "content": "importance: 5\n\n---\n\n# Jour de la semaine européenne\n\nLes pays européens ont des jours de la semaine commençant par lundi (numéro 1), puis mardi (numéro 2) et jusqu'au dimanche (numéro 7). Ecrivez une fonction `getLocalDay(date)` qui renvoie le jour de la semaine \"européen\" pour `date`.\n\n```js no-beautify\nlet date = new Date(2012, 0, 3);  // 3 Janvier 2012\nalert( getLocalDay(date) );       // mardi, devrait afficher 2\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/4-get-date-ago/_js.view/solution.js",
    "content": "function getDateAgo(date, days) {\n  let dateCopy = new Date(date);\n\n  dateCopy.setDate(date.getDate() - days);\n  return dateCopy.getDate();\n}\n"
  },
  {
    "path": "1-js/05-data-types/11-date/4-get-date-ago/_js.view/test.js",
    "content": "describe(\"getDateAgo\", function() {\n\n  it(\"1 day before 02.01.2015 -> day 1\", function() {\n    assert.equal(getDateAgo(new Date(2015, 0, 2), 1), 1);\n  });\n\n\n  it(\"2 days before 02.01.2015 -> day 31\", function() {\n    assert.equal(getDateAgo(new Date(2015, 0, 2), 2), 31);\n  });\n\n  it(\"100 days before 02.01.2015 -> day 24\", function() {\n    assert.equal(getDateAgo(new Date(2015, 0, 2), 100), 24);\n  });\n\n  it(\"365 days before 02.01.2015 -> day 2\", function() {\n    assert.equal(getDateAgo(new Date(2015, 0, 2), 365), 2);\n  });\n\n  it(\"does not modify the given date\", function() {\n    let date = new Date(2015, 0, 2);\n    let dateCopy = new Date(date);\n    getDateAgo(dateCopy, 100);\n    assert.equal(date.getTime(), dateCopy.getTime());\n  });\n\n});\n"
  },
  {
    "path": "1-js/05-data-types/11-date/4-get-date-ago/solution.md",
    "content": "L'idée est simple: soustraire un nombre donné de jours à partir de la `date`:\n\n```js\nfunction getDateAgo(date, days) {\n  date.setDate(date.getDate() - days);\n  return date.getDate();\n}\n```\n\n...Mais la fonction ne doit pas changer la `date`. C'est une chose importante, car le code externe qui nous donne la date ne s'attend pas à ce qu'il change.\n\nPour le mettre en oeuvre, clonons la date, comme ceci:\n\n```js run demo\nfunction getDateAgo(date, days) {\n  let dateCopy = new Date(date);\n\n  dateCopy.setDate(date.getDate() - days);\n  return dateCopy.getDate();\n}\n\nlet date = new Date(2015, 0, 2);\n\nalert( getDateAgo(date, 1) ); // 1, (1 Jan 2015)\nalert( getDateAgo(date, 2) ); // 31, (31 Dec 2014)\nalert( getDateAgo(date, 365) ); // 2, (2 Jan 2014)\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/4-get-date-ago/task.md",
    "content": "importance: 4\n\n---\n\n# Quel jour du mois était il y a plusieurs jours ?\n\nCréez une fonction `getDateAgo(date, days)` pour renvoyer le `days` précédent la date `date`.\n\nPar exemple, si aujourd'hui on est le 20, alors `getDateAgo(new Date(), 1)` doit être le 19 et `getDateAgo(new Date(), 2)` doit être le 18.\n\nelle doit fonctionner de manière fiable sur plus de 365 jours.\n\n```js\nlet date = new Date(2015, 0, 2);\n\nalert( getDateAgo(date, 1) ); // 1, (1 Jan 2015)\nalert( getDateAgo(date, 2) ); // 31, (31 Dec 2014)\nalert( getDateAgo(date, 365) ); // 2, (2 Jan 2014)\n```\n\nP.S. La fonction ne doit pas modifier la `date` donnée.\n"
  },
  {
    "path": "1-js/05-data-types/11-date/5-last-day-of-month/_js.view/solution.js",
    "content": "function getLastDayOfMonth(year, month) {\n  let date = new Date(year, month + 1, 0);\n  return date.getDate();\n}\n"
  },
  {
    "path": "1-js/05-data-types/11-date/5-last-day-of-month/_js.view/test.js",
    "content": "describe(\"getLastDayOfMonth\", function() {\n  it(\"last day of 01.01.2012 - 31\", function() {\n    assert.equal(getLastDayOfMonth(2012, 0), 31);\n  });\n\n  it(\"last day of 01.02.2012 - 29 (leap year)\", function() {\n    assert.equal(getLastDayOfMonth(2012, 1), 29);\n  });\n\n  it(\"last day of 01.02.2013 - 28\", function() {\n    assert.equal(getLastDayOfMonth(2013, 1), 28);\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/11-date/5-last-day-of-month/solution.md",
    "content": "Créons une date en utilisant le mois suivant, mais passons zéro comme jour:\n```js run demo\nfunction getLastDayOfMonth(year, month) {\n  let date = new Date(year, month + 1, 0);\n  return date.getDate();\n}\n\nalert( getLastDayOfMonth(2012, 0) ); // 31\nalert( getLastDayOfMonth(2012, 1) ); // 29\nalert( getLastDayOfMonth(2013, 1) ); // 28\n```\n\nNormalement, les dates commencent à 1, mais techniquement, nous pouvons passer n'importe quel nombre, la date s'ajustera automatiquement. Ainsi, lorsque nous passons 0, cela signifie \"un jour avant le 1er jour du mois\", autrement dit: \"le dernier jour du mois précédent\".\n"
  },
  {
    "path": "1-js/05-data-types/11-date/5-last-day-of-month/task.md",
    "content": "importance: 5\n\n---\n\n# Dernier jour du mois ?\n\nEcrivez une fonction `getLastDayOfMonth(year, month)` qui renvoie le dernier jour du mois. Parfois, c'est 30, 31 ou même 28/29 février.\n\nParamètres:\n\n- `year` -- année à quatre chiffres, par exemple 2012.\n- `month` -- mois, de 0 à 11.\n\nPar exemple, `getLastDayOfMonth(2012, 1) = 29` (année bissextile, février).\n"
  },
  {
    "path": "1-js/05-data-types/11-date/6-get-seconds-today/solution.md",
    "content": "Pour obtenir le nombre de secondes, nous pouvons générer une date à l'aide du jour et de l'heure en cours 00:00:00, puis la soustraire de \"maintenant\".\n\nLa différence est le nombre de millisecondes à partir du début de la journée, qu'il faut diviser par 1000 pour obtenir les secondes:\n\n```js run\nfunction getSecondsToday() {\n  let now = new Date();\n\n  // crée un objet en utilisant le jour / mois / année en cours\n  let today = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n\n  let diff = now - today; // ms difference\n  return Math.round(diff / 1000); // arrondir en secondes\n}\n\nalert( getSecondsToday() );\n```\n\nUne autre solution serait d’obtenir les heures / minutes / secondes et de les convertir en secondes:\n\n```js run\nfunction getSecondsToday() {\n  let d = new Date();\n  return d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds();\n}\n\nalert( getSecondsToday() );\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/6-get-seconds-today/task.md",
    "content": "importance: 5\n\n---\n\n# Combien de secondes se sont écoulées aujourd'hui ?\n\nEcrivez une fonction `getSecondsToday()` qui renvoie le nombre de secondes depuis le début de la journée.\n\nPar exemple, s'il est maintenant `10:00 am`, et qu'il n'y a pas de décalage de l'heure d'été, alors :\n\n```js\ngetSecondsToday() == 36000 // (3600 * 10)\n```\n\nLa fonction devrait fonctionner dans n'importe quel jour. Autrement dit, il ne devrait pas avoir de valeur \"aujourd'hui\" codée en dur.\n"
  },
  {
    "path": "1-js/05-data-types/11-date/7-get-seconds-to-tomorrow/solution.md",
    "content": "Pour obtenir le nombre de millisecondes jusqu'à demain, nous pouvons, à partir de \"demain 00:00:00\", soustraire la date actuelle.\n\nTout d'abord, nous générons ce \"demain\", puis nous le faisons:\n\n```js run\nfunction getSecondsToTomorrow() {\n  let now = new Date();\n\n  // date de demain\n  let tomorrow = new Date(now.getFullYear(), now.getMonth(), *!*now.getDate()+1*/!*);\n\n  let diff = tomorrow - now; // difference in ms\n  return Math.round(diff / 1000); // convert to seconds\n}\n```\n\nsolution alternative:\n\n```js run\nfunction getSecondsToTomorrow() {\n  let now = new Date();\n  let hour = now.getHours();\n  let minutes = now.getMinutes();\n  let seconds = now.getSeconds();\n  let totalSecondsToday = (hour * 60 + minutes) * 60 + seconds;\n  let totalSecondsInADay = 86400;\n\n  return totalSecondsInADay - totalSecondsToday;\n}\n```\n\nVeuillez noter que de nombreux pays ont l'heure d'été (DST), il peut donc y avoir des jours avec 23 ou 25 heures. Nous voudrons peut-être traiter ces jours séparément.\n"
  },
  {
    "path": "1-js/05-data-types/11-date/7-get-seconds-to-tomorrow/task.md",
    "content": "importance: 5\n\n---\n\n# Combien de secondes jusqu'à demain ?\n\nCréez une focntion `getSecondsToTomorrow()` qui renvoie le nombre de secondes jusqu'à demain.\n\nPar exemple, s'il est maintenant `23:00`, alors:\n\n```js\ngetSecondsToTomorrow() == 3600\n```\n\nP.S. La fonction devrait fonctionner à tout moment, le «aujourd'hui» n'est pas codé en dur.\n"
  },
  {
    "path": "1-js/05-data-types/11-date/8-format-date-relative/_js.view/solution.js",
    "content": "\nfunction formatDate(date) {\n  let diff = new Date() - date; // the difference in milliseconds\n\n  if (diff < 1000) { // less than 1 second\n    return 'right now';\n  }\n\n  let sec = Math.floor(diff / 1000); // convert diff to seconds\n\n  if (sec < 60) {\n    return sec + ' sec. ago';\n  }\n\n  let min = Math.floor(diff / 60000); // convert diff to minutes\n  if (min < 60) {\n    return min + ' min. ago';\n  }\n\n  // format the date\n  // add leading zeroes to single-digit day/month/hours/minutes\n  let d = date;\n  d = [\n    '0' + d.getDate(),\n    '0' + (d.getMonth() + 1),\n    '' + d.getFullYear(),\n    '0' + d.getHours(),\n    '0' + d.getMinutes()\n  ].map(component => component.slice(-2)); // take last 2 digits of every component\n\n  // join the components into date\n  return d.slice(0, 3).join('.') + ' ' + d.slice(3).join(':');\n}\n"
  },
  {
    "path": "1-js/05-data-types/11-date/8-format-date-relative/_js.view/test.js",
    "content": "describe(\"formatDate\", function() {\n  it(\"shows 1ms ago as \\\"right now\\\"\", function() {\n    assert.equal(formatDate(new Date(new Date - 1)), 'right now');\n  });\n\n  it('\"30 seconds ago\"', function() {\n    assert.equal(formatDate(new Date(new Date - 30 * 1000)), \"30 sec. ago\");\n  });\n\n  it('\"5 minutes ago\"', function() {\n    assert.equal(formatDate(new Date(new Date - 5 * 60 * 1000)), \"5 min. ago\");\n  });\n\n  it(\"older dates as DD.MM.YY HH:mm\", function() {\n    assert.equal(formatDate(new Date(2014, 2, 1, 11, 22, 33)), \"01.03.14 11:22\");\n  });\n\n});\n"
  },
  {
    "path": "1-js/05-data-types/11-date/8-format-date-relative/solution.md",
    "content": "Pour obtenir l'heure à partir de la `date` jusqu'à maintenant -- allons soutraire les dates.\n\n```js run demo\nfunction formatDate(date) {\n  let diff = new Date() - date; // la différence en millisecondes\n\n  if (diff < 1000) { // moins d'une seconde\n    return 'right now';\n  }\n\n  let sec = Math.floor(diff / 1000); // convertir la différence en secondes\n\n  if (sec < 60) {\n    return sec + ' sec. ago';\n  }\n\n  let min = Math.floor(diff / 60000); // convertir la différence en minutes\n  if (min < 60) {\n    return min + ' min. ago';\n  }\n\n  // formater la date\n  // ajoute des zéros au premier jour / mois / heure / minutes\n  let d = date;\n  d = [\n    '0' + d.getDate(),\n    '0' + (d.getMonth() + 1),\n    '' + d.getFullYear(),\n    '0' + d.getHours(),\n    '0' + d.getMinutes()\n  ].map(component => component.slice(-2)); // prend les 2 derniers chiffres de chaque composant\n\n  // joindre les composants en date\n  return d.slice(0, 3).join('.') + ' ' + d.slice(3).join(':');\n}\n\nalert( formatDate(new Date(new Date - 1)) ); // \"right now\"\n\nalert( formatDate(new Date(new Date - 30 * 1000)) ); // \"30 sec. ago\"\n\nalert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // \"5 min. ago\"\n\n// date d'hier comme ceci 31.12.2016 20:00\nalert( formatDate(new Date(new Date - 86400 * 1000)) );\n```\n\nsolution alternative:\n\n```js run\nfunction formatDate(date) {\n  let dayOfMonth = date.getDate();\n  let month = date.getMonth() + 1;\n  let year = date.getFullYear();\n  let hour = date.getHours();\n  let minutes = date.getMinutes();\n  let diffMs = new Date() - date;\n  let diffSec = Math.round(diffMs / 1000);\n  let diffMin = diffSec / 60;\n  let diffHour = diffMin / 60;\n\n  // formatage\n  year = year.toString().slice(-2);\n  month = month < 10 ? '0' + month : month;\n  dayOfMonth = dayOfMonth < 10 ? '0' + dayOfMonth : dayOfMonth;\n  hour = hour < 10 ? '0' + hour : hour;\n  minutes = minutes < 10 ? '0' + minutes : minutes;\n\n  if (diffSec < 1) {\n    return 'right now';  \n  } else if (diffMin < 1) {\n    return `${diffSec} sec. ago`\n  } else if (diffHour < 1) {\n    return `${diffMin} min. ago`\n  } else {\n    return `${dayOfMonth}.${month}.${year} ${hour}:${minutes}`\n  }\n}\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/8-format-date-relative/task.md",
    "content": "importance: 4\n\n---\n\n# Formater la date relative\n\nCréez une fonction `formatDate(date)` qui devrait formater la `date` comme ceci:\n\n- Si depuis la `date` il s'est passé moins de 1 seconde, alors `\"right now\"`.\n- Sinon, si il s'est passé moins d'une minute, alors `\"n sec. ago\"`.\n- Sinon, si c'est moins d'une heure, alors `\"m min. ago\"`.\n- Sinon, la date complète au format `\"DD.MM.YY HH:mm\"`. C'est à dire: `\"day.month.year hours:minutes\"`, le tout au format 2 chiffres, par exemple. `31.12.16 10:00`.\n\nPar exemple:\n\n```js\nalert( formatDate(new Date(new Date - 1)) ); // \"right now\"\n\nalert( formatDate(new Date(new Date - 30 * 1000)) ); // \"30 sec. ago\"\n\nalert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // \"5 min. ago\"\n\n// date d'hier comme ceci 31.12.16 20:00\nalert( formatDate(new Date(new Date - 86400 * 1000)) );\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/article.md",
    "content": "# Date et Temps\n\nFaisons connaissance avec un nouvel objet intégré : [Date](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date). Il stocke la date, l'heure et fournit des méthodes pour la gestion de la date et de l'heure.\n\nPar exemple, nous pouvons l'utiliser pour enregistrer les heures de création / modification, pour mesurer l'heure ou simplement pour imprimer la date du jour.\n\n## Creation\n\nPour créer un nouvel objet `Date`, appelez `new Date()` avec l'un des arguments suivants :\n\n`new Date()`\n: Sans arguments - crée un objet `Date` pour la date et l'heure actuelles.\n\n    ```js run\n    let now = new Date();\n    alert( now ); // affiche la date/heure actuelle\n    ```\n\n`new Date(millisecondes)`\n: Crée un objet `Date` avec l'heure correspondant au nombre de millisecondes (1/1000 de seconde) écoulée après le 1er janvier 1970 UTC.\n\n    ```js run\n    // 0 signifie 01.01.1970 UTC+0\n    let Jan01_1970 = new Date(0);\n    alert( Jan01_1970 );\n\n    // maintenant, ajoutez 24 heures, cela devient 02.01.1970 UTC+0\n    let Jan02_1970 = new Date(24 * 3600 * 1000);\n    alert( Jan02_1970 );\n    ```\n\n    Un nombre entier représentant le nombre de millisecondes écoulées depuis le début de 1970 est appelé un *timestamp* (horodatage).\n\n    C’est une représentation numérique d’une date. Nous pouvons toujours créer une date à partir d'un *horodatage* à l'aide de `new Date(*horodatage*)` et convertir l'objet `Date` existant en un *horodatage* à l'aide de la méthode `date.getTime()` (voir ci-dessous).\n\n    Les dates antérieures au 01.01.1970 ont des horodatages négatifs, par exemple :\n    ```js run\n    // 31 Dec 1969\n    let Dec31_1969 = new Date(-24 * 3600 * 1000);\n    alert( Dec31_1969 );\n    ```\n\n`new Date(datestring)`\n: S'il existe un seul argument, et qu'il s'agit d'une chaîne, il est automatiquement analysé. L'algorithme est le même que celui utilisé par `Date.parse`, nous le couvrirons plus tard.\n\n    ```js run\n    let date = new Date(\"2017-01-26\");\n    alert(date);\n    // La partie heure de la date est supposée être minuit GMT et\n    // est ajusté en fonction du fuseau horaire dans lequel le code est exécuté\n    // Donc, le résultat pourrait être\n    // jeu. 26 janv. 2017 11:00:00 GMT + 1100 (heure avancée de l'Est)\n    // ou\n    // mer. 25 janv. 2017 16:00:00 GMT-0800 (Heure standard du Pacifique)\n    ```\n\n`new Date(année, mois, date, heures, minutes, secondes, ms)`\n: Crée la date avec les composants donnés dans le fuseau horaire local. Seul le premier argument est obligatoire.\n\n    Note :\n\n    - L'année `year` doit avoir 4 chiffres. Pour des raisons de compatibilité, 2 chiffres sont également acceptés et considérés comme `19xx`, par ex. `98` est identique à `1998` ici, mais il est fortement recommandé d'utiliser toujours 4 chiffres.\n    - Le décompte des mois `month` commence par `0` (Janvier) jusqu'à `11` (Décembre).\n    - Le paramètre `date` est en fait le jour du mois, s'il est absent alors `1` est supposé.\n    - Si `heures`/`minutes`/`seconds`/`ms` est absent, elles sont supposées égales à `0`.\n\n    Par exemple :\n\n    ```js\n    new Date(2011, 0, 1, 0, 0, 0, 0); // // 1 Jan 2011, 00:00:00\n    new Date(2011, 0, 1); // la même chose car les heures etc sont égales à 0 par défaut\n    ```\n\n    La précision maximale est de 1 ms (1/1000 sec) :\n\n    ```js run\n    let date = new Date(2011, 0, 1, 2, 3, 4, 567);\n    alert( date ); // 1.01.2011, 02:03:04.567\n    ```\n\n## Composants de date d'accès\n\nIl existe de nombreuses méthodes pour accéder à l'année, au mois, etc. à partir de l'objet Date.\n\n[getFullYear()](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/getFullYear)\n: Obtenir l'année (4 chiffres)\n\n[getMonth()](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/getMonth)\n: Obtenir le mois, **de 0 à 11**.\n\n[getDate()](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/getDate)\n: Obtenir le jour du mois, de 1 à 31.\n\n[getHours()](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/getHours), [getMinutes()](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/getMinutes), [getSeconds()](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/getSeconds), [getMilliseconds()](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/getMilliseconds)\n: Obtenir l'heures / les minutes / les secondes / les millisecondes.\n\n```warn header=\"Pas `getYear()`, mais `getFullYear()`\"\nDe nombreux moteurs JavaScript implémentent une méthode non standard `getYear()`. Cette méthode est obsolète. Elle retourne parfois l'année à 2 chiffres. S'il vous plaît ne l'utilisez jamais. Il y a `getFullYear()` pour l'année.\n```\n\nDe plus, nous pouvons obtenir un jour de la semaine :\n\n[getDay()](mdn:js/Date/getDay)\n: Obtenir le jour de la semaine, de `0` (dimanche) à `6` (samedi). Le premier jour est toujours le dimanche, dans certains pays ce n’est pas le cas, mais ça ne peut pas être changé.\n\n**Toutes les méthodes ci-dessus renvoient les composants par rapport au fuseau horaire local.**\n\nIl existe également leurs homologues UTC, qui renvoient jour, mois, année, etc. pour le fuseau horaire UTC + 0 : [getUTCFullYear()](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/getUTCFullYear), [getUTCMonth()](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/getUTCMonth), [getUTCDay()](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/getUTCDay). Il suffit d'insérer le `UTC` juste après `get`.\n\nSi votre fuseau horaire local est décalé par rapport à UTC, le code ci-dessous indique différentes heures :\n\n```js run\n// date actuel\nlet date = new Date();\n\n// l'heure dans votre fuseau horaire actuel\nalert( date.getHours() );\n\n// l'heure dans le fuseau horaire UTC + 0 (heure de Londres sans heure avancée)\nalert( date.getUTCHours() );\n```\n\nOutre les méthodes indiquées, il existe deux méthodes spéciales qui ne possèdent pas de variante UTC :\n\n[getTime()](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/getTime)\n: Renvoie l'horodatage de la date - nombre de millisecondes écoulées à partir du 1er janvier 1970 UTC + 0.\n\n[getTimezoneOffset()](mdn:js/Date/getTimezoneOffset)\n: Renvoie la différence entre le fuseau horaire local et l'heure UTC, en minutes :\n\n    ```js run\n    // si vous êtes dans le fuseau horaire UTC-1, génère 60\n    // si vous êtes dans le fuseau horaire UTC + 3, les sorties -180\n    alert( new Date().getTimezoneOffset() );\n\n    ```\n\n## Réglage des composants de date\n\nLes méthodes suivantes permettent de définir des composants date / heure :\n\n- [`setFullYear(year, [month], [date])`](mdn:js/Date/setFullYear)\n- [`setMonth(month, [date])`](mdn:js/Date/setMonth)\n- [`setDate(date)`](mdn:js/Date/setDate)\n- [`setHours(hour, [min], [sec], [ms])`](mdn:js/Date/setHours)\n- [`setMinutes(min, [sec], [ms])`](mdn:js/Date/setMinutes)\n- [`setSeconds(sec, [ms])`](mdn:js/Date/setSeconds)\n- [`setMilliseconds(ms)`](mdn:js/Date/setMilliseconds)\n- [`setTime(milliseconds)`](mdn:js/Date/setTime) (définit la date entière en millisecondes depuis 01.01.1970 UTC)\n\nComme nous pouvons le constater, certaines méthodes peuvent définir plusieurs composants à la fois, par exemple `setHours`. Les composants non mentionnés ne sont pas modifiés.\n\nPar exemple :\n\n```js run\nlet today = new Date();\n\ntoday.setHours(0);\nalert(today); // encore aujourd'hui, mais l'heure est changée à 0\n\ntoday.setHours(0, 0, 0, 0);\nalert(today); // toujours aujourd'hui, maintenant 00:00:00 pile.\n```\n\n## Auto-correction\n\nL'*auto-correction* est une fonctionnalité très pratique des objets Date. Nous pouvons définir des valeurs hors limites et le système s'ajustera automatiquement.\n\nPar exemple :\n\n```js run\nlet date = new Date(2013, 0, *!*32*/!*); // 32 Jan 2013 ?!?\nalert(date); // ...c'est le 1st Feb 2013!\n```\n\nLes composants de date hors limites sont traités automatiquement.\n\nSupposons que nous devions augmenter la date «28 février 2016» de 2 jours. Ce peut être «2 mars» ou «1 mars» dans le cas d'une année bissextile. Nous n’avons pas besoin d’y penser. Il suffit d'ajouter 2 jours. L'objet `Date` fera le reste :\n\n```js run\nlet date = new Date(2016, 1, 28);\n*!*\ndate.setDate(date.getDate() + 2);\n*/!*\n\nalert( date ); // 1 Mar 2016\n```\n\nCette fonctionnalité est souvent utilisée pour obtenir la date après la période donnée. Par exemple, obtenons la date «70 secondes après maintenant» :\n\n```js run\nlet date = new Date();\ndate.setSeconds(date.getSeconds() + 70);\n\nalert( date ); // montre la date correcte\n```\n\nNous pouvons également définir zéro ou même des valeurs négatives. Par exemple :\n\n```js run\nlet date = new Date(2016, 0, 2); // 2 Jan 2016\n\ndate.setDate(1); // met le jour 1 du mois\nalert( date );\n\ndate.setDate(0); // la date minimum est le 1, le dernier jour du mois précédent devient alors la date\nalert( date ); // 31 Dec 2015\n```\n\n## De Date à numéro, différence de date\n\nLorsqu'un objet `Date` est converti en nombre, il devient l'horodatage identique à `date.getTime()` :\n\n```js run\nlet date = new Date();\nalert(+date); // le nombre de millisecondes, identique à date.getTime()\n```\n\nL'effet secondaire important : les dates peuvent être soustraites, le résultat est leur différence en ms.\n\nCela peut être utilisé pour les mesures de temps :\n\n```js run\nlet start = new Date(); // démarre le compteur\n\n// fait le travail\nfor (let i = 0; i < 100000; i++) {\n  let doSomething = i * i * i;\n}\n\nlet end = new Date(); // fin\n\nalert( `The loop took ${end - start} ms` );\n```\n\n## Date.now()\n\nSi nous voulons seulement mesurer la différence, nous n’avons pas besoin de l’objet Date.\n\nIl existe une méthode spéciale `Date.now()` qui renvoie l’horodatage actuel.\n\nIl est sémantiquement équivalent à `new Date().getTime()`, mais il ne crée pas d’objet `Date` intermédiaire. Donc, c’est plus rapide et cela n’exerce aucune pression sur le ramasse-miettes.\n\nIl est principalement utilisé pour des raisons de commodité ou lorsque les performances sont importantes, comme dans les jeux en JavaScript ou dans d'autres applications spécialisées.\n\nDonc c'est probablement mieux :\n\n```js run\n*!*\nlet start = Date.now(); // compteur en millisecondes depuis le 1 Jan 1970\n*/!*\n\n// fait le travail\nfor (let i = 0; i < 100000; i++) {\n  let doSomething = i * i * i;\n}\n\n*!*\nlet end = Date.now(); // fin\n*/!*\n\nalert( `The loop took ${end - start} ms` ); // soustrait des nombres, pas des dates\n```\n\n## Benchmarking\n\nSi nous voulons une référence fiable en matière de fonction gourmande en ressources processeur, nous devons être prudents.\n\nPar exemple, mesurons deux fonctions qui calculent la différence entre deux dates : laquelle est la plus rapide ?\n\nCes mesures de performance sont souvent appelées \"benchmarks\".\n\n```js\n// nous avons date1 et date2, quelle fonction retourne plus rapidement leur différence en ms ?\nfunction diffSubtract(date1, date2) {\n  return date2 - date1;\n}\n\n// ou\nfunction diffGetTime(date1, date2) {\n  return date2.getTime() - date1.getTime();\n}\n```\n\nCes deux font exactement la même chose, mais l’un d’eux utilise un `date.getTime()` explicite pour obtenir la date en ms, et l’autre repose sur une transformation date à nombre. Leur résultat est toujours le même.\n\nAlors, lequel est le plus rapide ?\n\nLa première idée peut être de les exécuter plusieurs fois de suite et de mesurer le décalage horaire. Pour notre cas, les fonctions sont très simples, nous devons donc le faire environ 100 000 fois.\n\nMesurons :\n\n```js run\nfunction diffSubtract(date1, date2) {\n  return date2 - date1;\n}\n\nfunction diffGetTime(date1, date2) {\n  return date2.getTime() - date1.getTime();\n}\n\nfunction bench(f) {\n  let date1 = new Date(0);\n  let date2 = new Date();\n\n  let start = Date.now();\n  for (let i = 0; i < 100000; i++) f(date1, date2);\n  return Date.now() - start;\n}\n\nalert( 'Time of diffSubtract: ' + bench(diffSubtract) + 'ms' );\nalert( 'Time of diffGetTime: ' + bench(diffGetTime) + 'ms' );\n```\n\nWow! Utiliser `getTime()` est beaucoup plus rapide ! C’est parce qu’il n’y a pas de conversion de type, il est beaucoup plus facile pour JavaScript de faire le calcul.\n\nOk, nous avons quelque chose. Mais ce n’est pas encore une bonne référence.\n\nImaginons qu’au moment de l’exécution du processeur `bench(diffSubtract)`, on faisait quelque chose en parallèle et que cela prenait des ressources. Et au moment de l'exécution du `bench(diffGetTime)`, le travail est terminé.\n\n\nUn scénario assez réel pour un système d'exploitation moderne multi-processus.\n\nEn conséquence, le premier test aura moins de ressources de processeur que le second. Cela peut conduire à des résultats erronés.\n\n**Pour un benchmarking plus fiable, l'ensemble des tests doit être réexécuté plusieurs fois.**\n\nPar exemple, comme ceci :\n\n```js run\nfunction diffSubtract(date1, date2) {\n  return date2 - date1;\n}\n\nfunction diffGetTime(date1, date2) {\n  return date2.getTime() - date1.getTime();\n}\n\nfunction bench(f) {\n  let date1 = new Date(0);\n  let date2 = new Date();\n\n  let start = Date.now();\n  for (let i = 0; i < 100000; i++) f(date1, date2);\n  return Date.now() - start;\n}\n\nlet time1 = 0;\nlet time2 = 0;\n\n*!*\n// exécute bench(diffSubtract) et bench(diffGetTime) chacune 10 fois en alternance\nfor (let i = 0; i < 10; i++) {\n  time1 += bench(diffSubtract);\n  time2 += bench(diffGetTime);\n}\n*/!*\n\nalert( 'Total time for diffSubtract: ' + time1 );\nalert( 'Total time for diffGetTime: ' + time2 );\n```\n\nLes moteurs JavaScript modernes commencent à appliquer des optimisations avancées uniquement au «code dynamique» qui s'exécute plusieurs fois (inutile d'optimiser les tâches rarement exécutées). Ainsi, dans l'exemple ci-dessus, les premières exécutions ne sont pas bien optimisées. Nous voudrons peut-être ajouter un test pour s'échauffer :\n\n```js\n// ajouté pour \"s'échauffer\" avant la boucle principale\nbench(diffSubtract);\nbench(diffGetTime);\n\n// maintenant comparons\nfor (let i = 0; i < 10; i++) {\n  time1 += bench(diffSubtract);\n  time2 += bench(diffGetTime);\n}\n```\n\n```warn header=\"Faites attention au micro-benchmarking\"\nLes moteurs JavaScript modernes effectuent de nombreuses optimisations. Ils peuvent modifier les résultats des «tests artificiels» par rapport à «l'utilisation normale», en particulier lorsque nous comparons quelque chose de très petit. Donc, si vous voulez sérieusement comprendre les performances, alors étudiez le fonctionnement du moteur JavaScript. Et puis vous n’aurez probablement pas besoin de micro-points de repère.\n\nUn bon paquet d'article a propos de V8 se trouve ici <https://mrale.ph>.\n```\n\n## Date.parse d'une chaîne de caractère\n\nLa methode [Date.parse(str)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Date/parse) peut lire une date provenant d'une chaîne de caractères.\n\nLe format de la chaîne de caractères doit être : `YYYY-MM-DDTHH:mm:ss.sssZ`, où :\n\n- `YYYY-MM-DD` -- est la date : année-mois-jour.\n- Le caractère `\"T\"` est utilisé comme délimiteur.\n- `HH:mm:ss.sss` - correspond à l'heure : heures, minutes, secondes et millisecondes.\n- La partie optionnelle `Z` indique le fuseau horaire au format `+-hh:mm`. Une seule lettre `Z` qui signifierait UTC + 0.\n\nDes variantes plus courtes sont également possibles, telles que `YYYY-MM-DD` ou `YYYY-MM` ou même `YYYY`.\n\nL'appel à `Date.parse(str)` analyse la chaîne au format indiqué et renvoie l'horodatage (nombre de millisecondes à compter du 1er janvier 1970 UTC + 0). Si le format n'est pas valide, renvoie `NaN`.\n\nPar exemple :\n\n```js run\nlet ms = Date.parse('2012-01-26T13:51:50.417-07:00');\n\nalert(ms); // 1327611110417  (horodatage)\n```\n\nNous pouvons créer instantanément un nouvel objet `Date` à partir de l'horodatage :\n\n```js run\nlet date = new Date( Date.parse('2012-01-26T13:51:50.417-07:00') );\n\nalert(date);\n```\n\n## Résumé\n\n- La date et l'heure en JavaScript sont représentées avec l'objet `Date`. Nous ne pouvons pas créer une date «seule» ou une heure «seule» : les objets `Date` comportent toujours les deux.\n- Les mois sont comptés à partir de zéro (janvier est le mois zéro).\n- Les jours de la semaine dans `getDay()` sont également comptés à partir de zéro (c’est le dimanche).\n- L'objet `Date` se corrige automatiquement lorsque des composants hors plage sont définis. C'est pratique pour ajouter / soustraire des jours / mois / heures.\n- Les dates peuvent être soustraites, ce qui donne leur différence en millisecondes. En effet, une date devient un horodatage lorsqu'elle est convertie en nombre.\n- Utilisez `Date.now()` pour obtenir l’horodatage actuel rapidement.\n\nNotez que contrairement à de nombreux autres systèmes, les horodatages JavaScript sont exprimés en millisecondes et non en secondes.\n\nDe plus, nous avons parfois besoin de mesures de temps plus précises. JavaScript lui-même ne permet pas de mesurer le temps en microsecondes (un millionième de seconde), mais la plupart des environnements le fournissent. Par exemple, le navigateur a [performance.now()](https://developer.mozilla.org/fr/docs/Web/API/Performance/now) qui donne le nombre de millisecondes à partir du début du chargement de la page avec une précision de l'ordre de la microseconde (3 chiffres après le point) :\n\n```js run\nalert(`Loading started ${performance.now()}ms ago`);\n// Quelque chose comme : \"Le chargement a commencé il y a 34731.26000000001ms\"\n// .26 indique les microsecondes (260 microsecondes)\n// plus de 3 chiffres après le point décimal sont des erreurs de précision, seuls les 3 premiers sont corrects\n```\n\nNode.js a un module `microtime` et d'autres moyens. Techniquement, presque tous les appareils et environnements permettent d'obtenir plus de précision, ce n'est pas seulement dans `Date`.\n"
  },
  {
    "path": "1-js/05-data-types/12-json/1-serialize-object/solution.md",
    "content": "\n\n```js\nlet user = {\n  name: \"John Smith\",\n  age: 35\n};\n\n*!*\nlet user2 = JSON.parse(JSON.stringify(user));\n*/!*\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/12-json/1-serialize-object/task.md",
    "content": "importance: 5\n\n---\n\n# Transforme l'objet en JSON et revenez en arrière\n\nTransformez `l’utilisateur` en JSON puis relisez-le dans une autre variable.\n\n```js\nlet user = {\n  name: \"John Smith\",\n  age: 35\n};\n```\n"
  },
  {
    "path": "1-js/05-data-types/12-json/2-serialize-event-circular/solution.md",
    "content": "\n```js run\nlet room = {\n  number: 23\n};\n\nlet meetup = {\n  title: \"Conference\",\n  occupiedBy: [{name: \"John\"}, {name: \"Alice\"}],\n  place: room\n};\n\nroom.occupiedBy = meetup;\nmeetup.self = meetup;\n\nalert( JSON.stringify(meetup, function replacer(key, value) {\n  return (key != \"\" && value == meetup) ? undefined : value;\n}));\n\n/* \n{\n  \"title\":\"Conference\",\n  \"occupiedBy\":[{\"name\":\"John\"},{\"name\":\"Alice\"}],\n  \"place\":{\"number\":23}\n}\n*/\n```\n\nIci, nous devons également tester la `key==\"\"` pour exclure le premier appel où il est normal que la `valeur` soit `meetup`.\n\n"
  },
  {
    "path": "1-js/05-data-types/12-json/2-serialize-event-circular/task.md",
    "content": "importance: 5\n\n---\n\n# Exclure les backreferences\n\nDans les cas simples de références circulaires, nous pouvons exclure une propriété incriminée de la sérialisation par son nom.\n\nMais parfois, nous ne pouvons pas simplement utiliser le nom, car il peut être utilisé à la fois dans les références circulaires et dans les propriétés normales. Ainsi, nous pouvons vérifier la propriété par sa valeur.\n\nÉcrivez la fonction de `remplacement` pour tout stringify, mais supprimez les propriétés qui font référence à `meetup`:\n\n```js run\nlet room = {\n  number: 23\n};\n\nlet meetup = {\n  title: \"Conference\",\n  occupiedBy: [{name: \"John\"}, {name: \"Alice\"}],\n  place: room\n};\n\n*!*\n// circular references\nroom.occupiedBy = meetup;\nmeetup.self = meetup;\n*/!*\n\nalert( JSON.stringify(meetup, function replacer(key, value) {\n  /* your code */\n}));\n\n/* result should be:\n{\n  \"title\":\"Conference\",\n  \"occupiedBy\":[{\"name\":\"John\"},{\"name\":\"Alice\"}],\n  \"place\":{\"number\":23}\n}\n*/\n```\n"
  },
  {
    "path": "1-js/05-data-types/12-json/article.md",
    "content": "# JSON methods, toJSON\n\nSupposons que nous avons un objet complexe et que nous aimerions le convertir en chaîne, l'envoyer par le réseau ou simplement le rendre (l'output) à des fins de journalisation.\n\nNaturellement, une telle chaîne devrait inclure toutes les propriétés importantes.\n\nNous pourrions implémenter la conversion comme ceci :\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30,\n\n*!*\n  toString() {\n    return `{name: \"${this.name}\", age: ${this.age}}`;\n  }\n*/!*\n};\n\nalert(user); // {name: \"John\", age: 30}\n```\n\n... Mais au cours du développement, de nouvelles propriétés sont ajoutées, les anciennes propriétés sont renommées et supprimées. Mettre à jour un tel `toString` à chaque fois peut devenir pénible. Nous pourrions essayer de passer en boucle sur les propriétés qu'il contient, mais que se passe-t-il si l'objet est complexe et qu'il contient des objets imbriqués ? Nous aurions également besoin de mettre en œuvre leur conversion.\n\nHeureusement, il n'est pas nécessaire d'écrire le code pour gérer tout cela. La tâche a déjà été résolue.\n\n## JSON.stringify\n\nLe [JSON](http://en.wikipedia.org/wiki/JSON) (JavaScript Object Notation) est un format général pour représenter les valeurs et les objets. Il est décrit comme tel dans le standard [RFC 4627](http://tools.ietf.org/html/rfc4627). Initialement, il était conçu pour JavaScript, mais de nombreux autres langages disposent également de bibliothèques pour le gérer. Il est donc facile d’utiliser JSON pour l’échange de données lorsque le client utilise JavaScript et que le serveur est écrit sur Ruby/PHP/Java et bien d'autres.\n\nJavaScript fournit des méthodes :\n\n- `JSON.stringify` pour convertir des objets en JSON.\n- `JSON.parse` pour reconvertir JSON en objet.\n\nPar exemple, nous allons `JSON.stringify` un student (étudiant) :\n\n```js run\nlet student = {\n  name: 'John',\n  age: 30,\n  isAdmin: false,\n  courses: ['html', 'css', 'js'],\n  spouse: null\n};\n\n*!*\nlet json = JSON.stringify(student);\n*/!*\n\nalert(typeof json); // nous avons une string !\n\nalert(json);\n*!*\n/* Objet encodé en JSON :\n{\n  \"name\": \"John\",\n  \"age\": 30,\n  \"isAdmin\": false,\n  \"courses\": [\"html\", \"css\", \"js\"],\n  \"spouse\": null\n}\n*/\n*/!*\n```\n\nLa méthode `JSON.stringify(student)` prend l'objet et le convertit en une chaîne.\n\nLa chaine `json` résultante est appelé un objet *JSON-encoded* ou *serialized* (sérialisé) ou *stringified* ou *marshalled*. Nous sommes prêts à l'envoyer par le câble ou à le placer dans un simple stockage de données.\n\nVeuillez noter qu'un objet JSON-encoded a plusieurs différences importantes par rapport au objet littéral :\n\n- Les chaînes utilisent des guillemets doubles. Il n'y a pas de guillemets simples ni de backticks en JSON. Donc `'John'` deviendra `\"John\"`.\n- Les propriété d'objet sont également en guillemets doubles. C'est obligatoire. Donc `age:30` deviendra `\"age\":30`.\n\n`JSON.stringify` peut aussi être appliqué aux primitives.\n\nJSON prend en charge les types de données suivants :\n\n- Objets `{ ... }`\n- Tableaux `[ ... ]`\n- Primitives :\n    - chaînes,\n    - nombres,\n    - valeurs booléennes `true`/`false`,\n    - `null`.\n\nPar exemple :\n\n```js run\n// un nombre en JSON est juste un nombre\nalert( JSON.stringify(1) ) // 1\n\n// une chaîne en JSON est toujours une chaîne, mais entre guillemets\nalert( JSON.stringify('test') ) // \"test\"\n\nalert( JSON.stringify(true) ); // true\n\nalert( JSON.stringify([1, 2, 3]) ); // [1,2,3]\n```\n\nJSON est une spécification indépendante du langage et ne contenant que des données. Par conséquent, certaines propriétés d'objet spécifiques à JavaScript sont ignorées par `JSON.stringify`.\n\nÀ savoir :\n\n- Propriétés de fonction (méthodes).\n- Clés et valeurs symboliques.\n- Propriétés qui stockent `undefined`.\n\n```js run\nlet user = {\n  sayHi() { // ignorée\n    alert(\"Hello\");\n  },\n  [Symbol(\"id\")]: 123, // ignorée\n  something: undefined // ignorée\n};\n\nalert( JSON.stringify(user) ); // {} (objet vide)\n```\n\nD'habitude ça va. Si ce n'est pas ce que nous souhaitons, nous verrons bientôt comment personnaliser le processus.\n\nLe grand avantage est que les objets imbriqués sont pris en charge et convertis automatiquement.\n\nPar exemple :\n\n```js run\nlet meetup = {\n  title: \"Conference\",\n*!*\n  room: {\n    number: 23,\n    participants: [\"john\", \"ann\"]\n  }\n*/!*\n};\n\nalert( JSON.stringify(meetup) );\n/* La structure entière est stringified :\n{\n  \"title\":\"Conference\",\n  \"room\":{\"number\":23,\"participants\":[\"john\",\"ann\"]},\n}\n*/\n```\n\nLa limitation importante est qu'il ne doit pas y avoir de références circulaires.\n\nPar exemple :\n\n```js run\nlet room = {\n  number: 23\n};\n\nlet meetup = {\n  title: \"Conference\",\n  participants: [\"john\", \"ann\"]\n};\n\nmeetup.place = room;      // meetup references room\nroom.occupiedBy = meetup; // room references meetup\n\n*!*\nJSON.stringify(meetup); // Erreur: Conversion d'une structure circulaire en JSON\n*/!*\n```\n\nIci, la conversion échoue à cause d'une référence circulaire : `room.occupiedBy` references `meetup`, et `meetup.place` references `room` :\n\n![](json-meetup.svg)\n\n## Exclure et transformer : replacer\n\nLa syntaxe complète de `JSON.stringify` est :\n\n```js\nlet json = JSON.stringify(value[, replacer, space])\n```\n\nValue\n: Une valeur à encoder.\n\nReplacer\n: Tableau de propriétés à encoder ou une fonction de mapping `function(key, value)`.\n\nSpace\n: Quantité d'espace à utiliser pour le formatage\n\nLa plupart du temps, `JSON.stringify` est utilisé avec le premier argument uniquement. Mais si nous devons affiner le processus de remplacement, préférez filtrer les références circulaires, nous pouvons utiliser le deuxième argument de `JSON.stringify`.\n\nSi nous lui passons un tableau de propriétés, seules ces propriétés seront encodées.\n\nPar exemple :\n\n```js run\nlet room = {\n  number: 23\n};\n\nlet meetup = {\n  title: \"Conference\",\n  participants: [{name: \"John\"}, {name: \"Alice\"}],\n  place: room // meetup references room\n};\n\nroom.occupiedBy = meetup; // room references meetup\n\nalert( JSON.stringify(meetup, *!*['title', 'participants']*/!*) );\n// {\"title\":\"Conference\",\"participants\":[{},{}]}\n```\n\nIci, nous sommes probablement trop strictes. La liste de propriétés est appliquée à la structure entière de l'objet. Donc, les objets dans `participants` sont vides, parce que `name` n'est pas dans la liste.\n\nIncluons dans la liste toutes les propriétés sauf `room.occupiedBy` qui provoquerait la référence circulaire :\n\n```js run\nlet room = {\n  number: 23\n};\n\nlet meetup = {\n  title: \"Conference\",\n  participants: [{name: \"John\"}, {name: \"Alice\"}],\n  place: room // meetup references room\n};\n\nroom.occupiedBy = meetup; // room references meetup\n\nalert( JSON.stringify(meetup, *!*['title', 'participants', 'place', 'name', 'number']*/!*) );\n/*\n{\n  \"title\":\"Conference\",\n  \"participants\":[{\"name\":\"John\"},{\"name\":\"Alice\"}],\n  \"place\":{\"number\":23}\n}\n*/\n```\n\nMaintenant tout sauf `occupiedBy` est serialisé. Mais la liste des propriétés est assez longue.\n\nHeureusement, nous pouvons utiliser une fonction au lieu d'un tableau comme `replacer`.\n\nLa fonction sera appelée pour chaque paire de `(key, value)` et devrait renvoyer la valeur \"remplacée\", qui sera utilisée à la place de celle d'origine. Ou `undefined` si la valeur doit être ignorée.\n\nDans notre cas, nous pouvons retourner une `value` \"en l'état\" pour tout sauf `occupiedBy`. Pour ignorer `occupiedBy`, le code ci-dessous retourne `undefined` :\n\n```js run\nlet room = {\n  number: 23\n};\n\nlet meetup = {\n  title: \"Conference\",\n  participants: [{name: \"John\"}, {name: \"Alice\"}],\n  place: room // meetup references room\n};\n\nroom.occupiedBy = meetup; // room references meetup\n\nalert( JSON.stringify(meetup, function replacer(key, value) {\n  alert(`${key}: ${value}`);\n  return (key == 'occupiedBy') ? undefined : value;\n}));\n\n/* key:value pairs that come to replacer:\n:             [object Object]\ntitle:        Conference\nparticipants: [object Object],[object Object]\n0:            [object Object]\nname:         John\n1:            [object Object]\nname:         Alice\nplace:        [object Object]\nnumber:       23\noccupiedBy:   [object Object]\n*/\n```\n\nVeuillez noter que la fonction `replacer` récupère chaque paire clé/valeur, y compris les objets imbriqués et les éléments de tableau. Il est appliqué de manière récursive. La valeur `this` dans `replacer` est l'objet qui contient la propriété actuelle.\n\nLe premier appel est spécial. Il est fabriqué en utilisant un \"objet wrapper\" spécial: `{\"\": meetup}`. En d'autres termes, la première paire `(key, value)` a une clé vide, et la valeur est l'objet cible dans son ensemble. C'est pourquoi la première ligne est `\":[object Object]\"` dans l'exemple ci-dessus.\n\nL’idée est de fournir autant de puissance pour `replacer` que possible : il a une chance d'analyser et de remplacer/ignorer même l'objet entier si nécessaire.\n\n## Formatage : space\n\nLe troisième argument de `JSON.stringify(value, replacer, space)` est le nombre d'espaces à utiliser pour un joli formatage.\n\nAuparavant, tous les objets stringifiés n’avaient pas d'indentation ni d’espace supplémentaire. C'est bien si nous voulons envoyer un objet sur un réseau. L'arguement `space` est utilisé exclusivement pour une belle sortie.\n\nIci `space = 2` indique à JavaScript d'afficher des objets imbriqués sur plusieurs lignes, avec l'indentation de 2 espaces à l'intérieur d'un objet :\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 25,\n  roles: {\n    isAdmin: false,\n    isEditor: true\n  }\n};\n\nalert(JSON.stringify(user, null, 2));\n/* indentation de 2 espaces :\n{\n  \"name\": \"John\",\n  \"age\": 25,\n  \"roles\": {\n    \"isAdmin\": false,\n    \"isEditor\": true\n  }\n}\n*/\n\n/* pour JSON.stringify(user, null, 4) le résultat serait plus indenté :\n{\n    \"name\": \"John\",\n    \"age\": 25,\n    \"roles\": {\n        \"isAdmin\": false,\n        \"isEditor\": true\n    }\n}\n*/\n```\n\nLe troisième argument peut également être une chaîne de caractères. Dans ce cas, la chaîne de caractères est utilisée pour l'indentation au lieu d'un certain nombre d'espaces.\n\nLe paramètre `space` est utilisé uniquement à des fins de journalisation et de sortie agréable.\n\n## \"toJSON\" personnalisé\n\nComme `toString` pour la conversion de chaîne, un objet peut fournir une méthode `toJSON` pour une conversion en JSON. `JSON.stringify` appelle automatiquement si  il est disponible.\n\nPar exemple :\n\n```js run\nlet room = {\n  number: 23\n};\n\nlet meetup = {\n  title: \"Conference\",\n  date: new Date(Date.UTC(2017, 0, 1)),\n  room\n};\n\nalert( JSON.stringify(meetup) );\n/*\n  {\n    \"title\":\"Conference\",\n*!*\n    \"date\":\"2017-01-01T00:00:00.000Z\",  // (1)\n*/!*\n    \"room\": {\"number\":23}               // (2)\n  }\n*/\n```\n\nIci on peut voir que `date` `(1)` est devenu une chaîne. C’est parce que toutes les dates ont une méthode `toJSON` intégrée qui retourne ce genre de chaîne.\n\nAjoutons maintenant un `toJSON` personnalisé pour notre objet `room` `(2)` :\n\n```js run\nlet room = {\n  number: 23,\n*!*\n  toJSON() {\n    return this.number;\n  }\n*/!*\n};\n\nlet meetup = {\n  title: \"Conference\",\n  room\n};\n\n*!*\nalert( JSON.stringify(room) ); // 23\n*/!*\n\nalert( JSON.stringify(meetup) );\n/*\n  {\n    \"title\":\"Conference\",\n*!*\n    \"room\": 23\n*/!*\n  }\n*/\n```\n\nComme on peut le voir, `toJSON` est utilisé à la fois pour l'appel direct `JSON.stringify(room)` et quand `room` est imbriqué dans un autre objet encodé.\n\n## JSON.parse\n\nPour décoder une chaîne JSON, nous avons besoin d'une autre méthode nommée [JSON.parse](mdn:js/JSON/parse).\n\nLa syntaxe :\n\n```js\nlet value = JSON.parse(str, [reviver]);\n```\n\nstr\n: La chaîne JSON à parse.\n\nreviver\n: Fonction optionnelle (clé, valeur) qui sera appelée pour chaque paire `(key, value)` et peut transformer la valeur.\n\nPar exemple :\n\n```js run\n// stringified array\nlet numbers = \"[0, 1, 2, 3]\";\n\nnumbers = JSON.parse(numbers);\n\nalert( numbers[1] ); // 1\n```\n\nOu pour les objets imbriqués :\n\n```js run\nlet userData = '{ \"name\": \"John\", \"age\": 35, \"isAdmin\": false, \"friends\": [0,1,2,3] }';\n\nlet user = JSON.parse(userData);\n\nalert( user.friends[1] ); // 1\n```\n\nLe JSON peut être aussi complexe que nécessaire, les objets et les tableaux peuvent inclure d'autres objets et tableaux. Mais ils doivent obéir au même format JSON.\n\nVoici des erreurs typiques dans JSON écrit à la main (nous devons parfois l'écrire à des fins de débogage) :\n\n```js\nlet json = `{\n  *!*name*/!*: \"John\",                     // Erreur: nom de propriété sans guillemets\n  \"surname\": *!*'Smith'*/!*,               // Erreur: guillemets simples en valeur (doit être double)\n  *!*'isAdmin'*/!*: false                  // Erreur: guillemets simples dans la clé (doit être double)\n  \"birthday\": *!*new Date(2000, 2, 3)*/!*, // Erreur: aucun \"nouveau\" n'est autorisé, seules les valeurs nues\n  \"friends\": [0,1,2,3]              // Ici tout va bien\n}`;\n```\n\nEn outre, JSON ne prend pas en charge les commentaires. L'ajout d'un commentaire à JSON le rend invalide.\n\nIl y a un autre format nommé [JSON5](https://json5.org/) qui autorise les clés non commentées, les commentaires, etc. Mais il s’agit d’une bibliothèque autonome, pas dans la spécification du langage.\n\nLe JSON standard est très strict, non pas parce que ses développeurs sont paresseux, mais pour permettre une implémentation facile, fiable et très rapide de l'algorithme de conversion.\n\n## Utiliser Reviver\n\nImaginez, nous avons un objet stringified `meetup` sur le serveur.\n\nCela ressemble à ça :\n\n```js\n// title: (meetup title), date: (meetup date)\nlet str = '{\"title\":\"Conference\",\"date\":\"2017-11-30T12:00:00.000Z\"}';\n```\n\n... Et maintenant, nous devons le *deserialize*, pour le retourner en objet JavaScript.\n\nFaisons-le en appelant `JSON.parse` :\n\n```js run\nlet str = '{\"title\":\"Conference\",\"date\":\"2017-11-30T12:00:00.000Z\"}';\n\nlet meetup = JSON.parse(str);\n\n*!*\nalert( meetup.date.getDate() ); // Error!\n*/!*\n```\n\nWhoops ! Une erreur !\n\nLa valeur de `meetup.date` est une chaîne et non un objet `Date`. Comment `JSON.parse` pourrait-il savoir qu'il devrait transformer cette chaîne en `Date` ?\n\nPassons à `JSON.parse` la fonction de réactivation en tant que second argument, qui renvoie toutes les valeurs \"en l'état\", mais `date` deviendra une `Date` :\n\n```js run\nlet str = '{\"title\":\"Conference\",\"date\":\"2017-11-30T12:00:00.000Z\"}';\n\n*!*\nlet meetup = JSON.parse(str, function(key, value) {\n  if (key == 'date') return new Date(value);\n  return value;\n});\n*/!*\n\nalert( meetup.date.getDate() ); // ça fonctionne maintenant !\n```\n\nÀ propos, cela fonctionne aussi pour les objets imbriqués :\n\n```js run\nlet schedule = `{\n  \"meetups\": [\n    {\"title\":\"Conference\",\"date\":\"2017-11-30T12:00:00.000Z\"},\n    {\"title\":\"Birthday\",\"date\":\"2017-04-18T12:00:00.000Z\"}\n  ]\n}`;\n\nschedule = JSON.parse(schedule, function(key, value) {\n  if (key == 'date') return new Date(value);\n  return value;\n});\n\n*!*\nalert( schedule.meetups[1].date.getDate() ); // ça fonctionne !\n*/!*\n```\n\n## Résumé\n\n- JSON est un format de données qui possède son propre standard indépendant et ses propres bibliothèques pour la plupart des langages de programmation.\n- JSON prend en charge les objets simples, les tableaux, les chaînes, les nombres, les booléens et `null`.\n- JavaScript fournit des méthodes [JSON.stringify](mdn:js/JSON/stringify) pour sérialiser en JSON et [JSON.parse](mdn:js/JSON/parse) pour lire depuis JSON.\n- Les deux méthodes prennent en charge les fonctions du transformateur pour une lecture/écriture intelligente.\n- Si un objet a `toJSON`, alors il est appelé par `JSON.stringify`.\n"
  },
  {
    "path": "1-js/05-data-types/index.md",
    "content": "# Types de données\n\nPlus de structures de données et une étude plus approfondie des types.\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md",
    "content": "La solution utilisant une boucle:\n\n```js run\nfunction sumTo(n) {\n  let sum = 0;\n  for (let i = 1; i <= n; i++) {\n    sum += i;\n  }\n  return sum;\n}\n\nalert( sumTo(100) );\n```\n\nLa solution utilisant la récursion:\n\n```js run\nfunction sumTo(n) {\n  if (n == 1) return 1;\n  return n + sumTo(n - 1);\n}\n\nalert( sumTo(100) );\n```\n\nLa solution utilisant la formule: `sumTo(n) = n*(n+1)/2`:\n\n```js run\nfunction sumTo(n) {\n  return n * (n + 1) / 2;\n}\n\nalert( sumTo(100) );\n```\n\nP.S. Naturellement, la formule est la solution la plus rapide. Elle n’utilise que 3 opérations pour n’importe quel nombre `n`. Le calcul aide!\n\nLa variante de boucle est la seconde en termes de vitesse. Dans la variante récursive et la variante de boucle, nous additionnons les mêmes nombres. Mais la récursion implique des appels imbriqués et la gestion de la pile d'exécution. Donc, cela prend des ressources, donc c'est plus lent.\n\nP.P.S. Certains moteurs prennent en charge l'optimisation \"tail call\" (dernier appel) : si un appel récursif est le tout dernier dans la fonction, sans autres calculs effectués, alors la fonction externe n'aura pas besoin de reprendre l'exécution, donc le moteur n'a pas besoin de se souvenir son contexte d'exécution. Cela supprime le fardeau de la mémoire. Mais si le moteur JavaScript ne prend pas en charge l'optimisation des appels de queue (la plupart d'entre eux ne le font pas), il y aura une erreur : taille maximale de la pile dépassée, car il y a généralement une limitation sur la taille totale de la pile.\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/01-sum-to/task.md",
    "content": "importance: 5\n\n---\n\n# Additionner tous les nombres jusqu'à celui donné\n\nÉcrire une fonction `sumTo(n)` qui calcule la somme des nombres `1 + 2 + ... + n`.\n\nPar exemple:\n\n```js no-beautify\nsumTo(1) = 1\nsumTo(2) = 2 + 1 = 3\nsumTo(3) = 3 + 2 + 1 = 6\nsumTo(4) = 4 + 3 + 2 + 1 = 10\n...\nsumTo(100) = 100 + 99 + ... + 2 + 1 = 5050\n```\n\nFaites 3 variantes de solution:\n\n1. Utiliser une boucle for.\n2. Utiliser une récursion, avec `sumTo(n) = n + sumTo(n-1)` pour `n > 1`.\n3. Utiliser la formule de [progression arithmétique](https://en.wikipedia.org/wiki/Arithmetic_progression).\n\nUn exemple de résultat:\n\n```js\nfunction sumTo(n) { /*... ton code ... */ }\n\nalert( sumTo(100) ); // 5050\n```\n\nP.S. Quelle solution est la plus rapide? La plus lente? Pourquoi?\n\nP.P.S. Peut-on utiliser la récursion pour compter `sumTo(100000)`? \n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/02-factorial/solution.md",
    "content": "Par définition, une factorielle est `n!` peut être écrit `n * (n-1)!`.\n\nEn d'autres termes, le résultat de `factorial(n)` peut être calculé comme `n` multiplié par le résultat de `factorial(n-1)`. Et l'appel de `n-1` peut récursivement descendre plus bas, et plus bas, jusqu'à `1`.\n\n```js run\nfunction factorial(n) {\n  return (n != 1) ? n * factorial(n - 1) : 1;\n}\n\nalert( factorial(5) ); // 120\n```\n\nLa base de la récursivité est la valeur `1`. Nous pouvons aussi faire de `0` la base ici, ça importe peu, mais donne une étape récursive supplémentaire:\n\n```js run\nfunction factorial(n) {\n  return n ? n * factorial(n - 1) : 1;\n}\n\nalert( factorial(5) ); // 120\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/02-factorial/task.md",
    "content": "importance: 4\n\n---\n\n# Calcule factoriel\n\nLe [factorielle](https://en.wikipedia.org/wiki/Factorial) d'un nombre naturel est multiplié par `\"nombre moins un\"`, ensuite par `\"nombre moins deux\"`, et ainsi de suite jusqu'à `1`. La factorielle de `n` est noté comme `n!`\n\nNous pouvons écrire une définition de factorielle comme ceci:\n\n```js\nn! = n * (n - 1) * (n - 2) * ...*1\n```\n\nValeurs des factorielles pour des `n` différents:\n\n```js\n1! = 1\n2! = 2 * 1 = 2\n3! = 3 * 2 * 1 = 6\n4! = 4 * 3 * 2 * 1 = 24\n5! = 5 * 4 * 3 * 2 * 1 = 120\n```\n\nLa tâche est d'écrire une fonction `factorial(n)` qui calcule `n!` en utilisant des appels récursifs.\n\n```js\nalert( factorial(5) ); // 120\n```\n\nP.S. Indice: `n!` peut être écrit `n * (n-1)!` Par exemple: `3! = 3*2! = 3*2*1! = 6`\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/solution.md",
    "content": "La première solution que nous pourrions essayer ici est la solution récursive.\n\nLes nombres de Fibonacci sont récursifs par définition:\n\n```js run\nfunction fib(n) {\n  return n <= 1 ? n : fib(n - 1) + fib(n - 2);\n}\n\nalert( fib(3) ); // 2\nalert( fib(7) ); // 13\n// fib(77); // Sera extrêmement lent!\n```\n\n...Mais pour les grandes valeurs de `n` c'est très lent. Par exemple, `fib(77)` peut bloquer le moteur pendant un certain temps en consommant toutes les ressources du processeur.\n\nC'est parce que la fonction crée trop de sous-appels. Les mêmes valeurs sont réévaluées encore et encore.\n\nPar exemple, voyons un calcul pour `fib(5)`:\n\n```js no-beautify\n...\nfib(5) = fib(4) + fib(3)\nfib(4) = fib(3) + fib(2)\n...\n```\n\nIci, nous pouvons voir que la valeur de `fib(3)` est nécessaire pour les deux `fib(5)` et `fib(4)`. Alors `fib(3)` sera appelé et évalué deux fois de manière totalement indépendante.\n\nVoici l'arbre de récursion complet:\n\n![fibonacci recursion tree](fibonacci-recursion-tree.svg)\n\nNous pouvons clairement remarquer que `fib(3)` est évalué deux fois et `fib(2)` est évalué trois fois. La quantité totale de calculs augmente beaucoup plus vite que `n`, le rendant énorme même pour `n=77`.\n\nNous pouvons optimiser cela en nous rappelant les valeurs déjà évaluées: si une valeur de `fib(3)` est calculé une fois, alors nous pouvons simplement le réutiliser dans les calculs futurs.\n\nUne autre variante consisterait à abandonner la récursion et à utiliser un algorithme totalement différent basé sur des boucles.\n\nAu lieu de partir de `n` jusqu'à des valeurs plus basses, nous pouvons faire une boucle qui commence à partir de `1` et `2`, puis obtient `fib(3)` comme leur somme, ensuite `fib(4)` comme la somme de deux valeurs précédentes, ensuite `fib(5)` et monte, jusqu'à ce qu'il atteigne la valeur nécessaire. À chaque étape, il suffit de rappeler deux valeurs précédentes.\n\nVoici les étapes du nouvel algorithme en détails.\n\nLe début:\n\n```js\n// a = fib(1), b = fib(2), ces valeurs sont par définition 1\nlet a = 1, b = 1;\n\n// obtien c = fib(3) comme leur somme\nlet c = a + b;\n\n/* nous avons maintenant fib(1), fib(2), fib(3)\na  b  c\n1, 1, 2\n*/\n```\n\nMaintenant, nous voulons obtenir `fib(4) = fib(2) + fib(3)`.\n\nPassons aux variables: `a,b` aura `fib(2),fib(3)`, et `c` obtiendra leur somme:\n\n```js no-beautify\na = b; // maintenant a = fib(2)\nb = c; // maintenant b = fib(3)\nc = a + b; // c = fib(4)\n\n/* maintenant nous avons la séquence:\n   a  b  c\n1, 1, 2, 3\n*/\n```\n\nL'étape suivante donne un autre numéro de séquence:\n\n```js no-beautify\na = b; // maintenant a = fib(3)\nb = c; // maintenant b = fib(4)\nc = a + b; // c = fib(5)\n\n/* maintenant la séquence est (encore un numéro):\n      a  b  c\n1, 1, 2, 3, 5\n*/\n```\n\n...Et ainsi de suite jusqu'à l'obtention de la valeur nécessaire. C'est beaucoup plus rapide que la récursion et n'implique aucun calcul en double.\n\nLe code complet:\n\n```js run\nfunction fib(n) {\n  let a = 1;\n  let b = 1;\n  for (let i = 3; i <= n; i++) {\n    let c = a + b;\n    a = b;\n    b = c;\n  }\n  return b;\n}\n\nalert( fib(3) ); // 2\nalert( fib(7) ); // 13\nalert( fib(77) ); // 5527939700884757\n```\n\nLa boucle commence par `i=3`, parce que les première et deuxième valeurs de séquence sont codées en dur dans des variables `a=1`, `b=1`.\n\nCette approche s'appelle la [programmation dynamique de bas en haut](https://fr.wikipedia.org/wiki/Programmation_dynamique).\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/task.md",
    "content": "importance: 5\n\n---\n\n# Numéros de Fibonacci\n\nLa séquence des [Numéros de Fibonacci](https://fr.wikipedia.org/wiki/Nombre_de_Fibonacci) a la formule <code>F<sub>n</sub> = F<sub>n-1</sub> + F<sub>n-2</sub></code>. En d'autres termes, le nombre suivant est la somme des deux précédents.\n\nLes deux premiers chiffres sont `1`, puis `2(1+1)`, ensuite `3(1+2)`, `5(2+3)` etc: `1, 1, 2, 3, 5, 8, 13, 21...`.\n\nLes nombres de Fibonacci sont liés au [nombre d'or](https://fr.wikipedia.org/wiki/Nombre_d%27or) et de nombreux phénomènes naturels autour de nous.\n\nÉcrire une fonction `fib(n)` qui retourne le Numéro de Fibonacci `n-th`.\n\nUn exemple de travail:\n\n```js\nfunction fib(n) { /* votre code */ }\n\nalert(fib(3)); // 2\nalert(fib(7)); // 13\nalert(fib(77)); // 5527939700884757\n```\n\nP.S. La fonction devrait être rapide. L'appel de `fib(77)` devrait prendre pas plus d'une fraction de seconde.\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/solution.md",
    "content": "# Solution basée sur la boucle\n\nLa variante de la solution basée sur la boucle:\n\n```js run\nlet list = {\n  value: 1,\n  next: {\n    value: 2,\n    next: {\n      value: 3,\n      next: {\n        value: 4,\n        next: null\n      }\n    }\n  }\n};\n\nfunction printList(list) {\n  let tmp = list;\n\n  while (tmp) {\n    alert(tmp.value);\n    tmp = tmp.next;\n  }\n\n}\n\nprintList(list);\n```\n\nVeuillez noter que nous utilisons une variable temporaire `tmp` pour parcourir la liste. Techniquement, nous pourrions utiliser un paramètre de fonction `list` à la place:\n\n```js\nfunction printList(list) {\n\n  while(*!*list*/!*) {\n    alert(list.value);\n    list = list.next;\n  }\n\n}\n```\n\n...Mais ce ne serait pas sage. Dans le futur, nous allons peut-être devoir étendre une fonction, faire autre chose avec la liste. Si nous changeons `list`, alors nous perdons cette capacité.\n\nParlant des bons noms de variables, `list` est la liste elle-même. Le premier élément de celui-ci. Et ça devrait rester comme ça. C'est clair et fiable.\n\nDe l’autre côté, le rôle de `tmp` est exclusivement une liste de traversée, comme `i` dans la boucle `for`.\n\n# Solution récursive\n\nLa variante récursive de `printList(list)` suit une logique simple: pour afficher une liste, il faut afficher l'élément courant `list`, puis faire de même pour `list.next`:\n\n```js run\nlet list = {\n  value: 1,\n  next: {\n    value: 2,\n    next: {\n      value: 3,\n      next: {\n        value: 4,\n        next: null\n      }\n    }\n  }\n};\n\nfunction printList(list) {\n\n  alert(list.value); // affiche l'élément en cours\n\n  if (list.next) {\n    printList(list.next); // fait la même chose pour le reste de la liste\n  }\n\n}\n\nprintList(list);\n```\n\nMaintenant qu'est-ce qui est le mieux?\n\nTechniquement, la boucle est plus efficace. Ces deux variantes font la même chose, mais la boucle ne dépense pas de ressources pour les appels de fonction imbriqués.\n\nDe l'autre côté, la variante récursive est plus courte et parfois plus facile à comprendre.\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/task.md",
    "content": "importance: 5\n\n---\n\n# Produire une liste de simple lien\n\nDisons que nous avons une liste de simple lien (comme décrit dans le chapitre <info:recursion>):\n\n```js\nlet list = {\n  value: 1,\n  next: {\n    value: 2,\n    next: {\n      value: 3,\n      next: {\n        value: 4,\n        next: null\n      }\n    }\n  }\n};\n```\n\nÉcrire une fonction `printList(list)` qui sort les éléments de la liste un par un.\n\nFaites deux variantes de la solution: en utilisant une boucle et en utilisant la récursion.\n\nQu'est-ce qui est le mieux: Avec ou sans récursion ?\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/solution.md",
    "content": "# Utiliser une récursion\n\nLa logique récursive est un peu délicate ici.\n\nNous devons d'abord afficher le reste de la liste et *ensuite* afficher l'actuel:\n\n```js run\nlet list = {\n  value: 1,\n  next: {\n    value: 2,\n    next: {\n      value: 3,\n      next: {\n        value: 4,\n        next: null\n      }\n    }\n  }\n};\n\nfunction printReverseList(list) {\n\n  if (list.next) {\n    printReverseList(list.next);\n  }\n\n  alert(list.value);\n}\n\nprintReverseList(list);\n```\n\n# En utilisant une boucle\n\nLa variante de boucle est aussi un peu plus compliquée que la sortie directe.\n\nIl n'y a aucun moyen d'obtenir la dernière valeur de notre `list`. Nous ne pouvons pas non plus \"revenir en arrière\".\n\nNous pouvons donc commencer par parcourir les éléments dans l'ordre direct et les mémoriser dans un tableau, puis afficher ce que nous nous sommes rappelés dans l'ordre inverse:\n\n```js run\nlet list = {\n  value: 1,\n  next: {\n    value: 2,\n    next: {\n      value: 3,\n      next: {\n        value: 4,\n        next: null\n      }\n    }\n  }\n};\n\nfunction printReverseList(list) {\n  let arr = [];\n  let tmp = list;\n\n  while (tmp) {\n    arr.push(tmp.value);\n    tmp = tmp.next;\n  }\n\n  for (let i = arr.length - 1; i >= 0; i--) {\n    alert( arr[i] );\n  }\n}\n\nprintReverseList(list);\n```\n\nVeuillez noter que la solution récursive fait exactement la même chose: elle suit la liste, mémorise les éléments de la chaîne d'appels imbriqués (dans la pile de contexte d'exécution), puis les affiches.\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/task.md",
    "content": "importance: 5\n\n---\n\n# Afficher une liste à lien unique dans l'ordre inverse\n\nAfficher une liste à lien unique de la tâche précédente <info:task/output-single-linked-list> dans l'ordre inverse.\n\nFaites deux solutions: en utilisant une boucle et en utilisant une récursion.\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/article.md",
    "content": "# Récursion et pile\n\nRevenons aux fonctions et étudions-les plus en profondeur.\n\nNotre premier sujet sera la *recursion*.\n\nSi vous n’êtes pas novice en programmation, cela vous est probablement familier et vous pouvez sauter ce chapitre.\n\nLa récursion est un modèle de programmation utile dans les situations où une tâche peut être naturellement divisée en plusieurs tâches du même type, mais plus simple. Ou lorsqu'une tâche peut être simplifiée en une action facile plus une variante plus simple de la même tâche. Ou, comme nous le verrons bientôt, pour traiter certaines structures de données.\n\nLorsqu'une fonction résout une tâche, elle peut appeler de nombreuses autres fonctions. Cela se produit partiellement lorsqu'une fonction s'appelle *elle-même*. Cela s'appelle la *récursion*.\n\n## Deux façons de penser\n\nPrenons quelque chose de simple pour commencer -- écrivons une fonction `pow(x, n)` qui élève `x` à une puissance naturel de `n`. En d'autres termes, multiplie `x` par lui-même `n` fois.\n\n```js\npow(2, 2) = 4\npow(2, 3) = 8\npow(2, 4) = 16\n```\n\nIl y a deux façons de le mettre en œuvre.\n\n1. La pensée itérative: la boucle `for`:\n\n    ```js run\n    function pow(x, n) {\n      let result = 1;\n\n      // multiplier le résultat par x n fois dans la boucle\n      for (let i = 0; i < n; i++) {\n        result *= x;\n      }\n\n      return result;\n    }\n\n    alert( pow(2, 3) ); // 8\n    ```\n\n2. La pensée récursive: simplifie la tâche et s'appele elle-même:\n\n    ```js run\n    function pow(x, n) {\n      if (n == 1) {\n        return x;\n      } else {\n        return x * pow(x, n - 1);\n      }\n    }\n\n    alert( pow(2, 3) ); // 8\n    ```\n\nVeuillez noter en quoi la variante récursive est fondamentalement différente.\n\nQuand `pow(x, n)` est appelé,  l'exécution se scinde en deux branches:\n\n```js\n              if n==1  = x\n             /\npow(x, n) =\n             \\\n              else     = x * pow(x, n - 1)\n```\n\n1. Si `n == 1`, alors tout est trivial. On l'appelle *la base* de la récursion, car elle produit immédiatement le résultat évident: `pow(x, 1)` équivaut à `x`.\n2. Sinon, nous pouvons représenter `pow(x, n)` comme `x * pow(x, n - 1)`. En maths, on écrirait <code>x<sup>n</sup> = x * x<sup>n-1</sup></code>. Ceci s'appelle *une étape récursive*: nous transformons la tâche en une action plus simple (multiplication par `x`) et un appel plus simple de la même tâche (`pow` avec le petit `n`). Les prochaines étapes le simplifient de plus en plus jusqu’à ce que `n` atteigne `1`.\n\nOn peut aussi dire que `pow` *s'appelle récursivement* jusqu'à ce que `n == 1`.\n\n![diagramme récursif de puissance](recursion-pow.svg)\n\n\nPar exemple, pour calculer `pow(2, 4)` la variante récursive effectue ces étapes:\n\n1. `pow(2, 4) = 2 * pow(2, 3)`\n2. `pow(2, 3) = 2 * pow(2, 2)`\n3. `pow(2, 2) = 2 * pow(2, 1)`\n4. `pow(2, 1) = 2`\n\nAinsi, la récursion réduit un appel de fonction à un processus plus simple, puis -- à un processus encore plus simple, etc. jusqu'à ce que le résultat devienne évident.\n\n````smart header=\"La récursion est généralement plus courte\"\nUne solution récursive est généralement plus courte qu'une solution itérative.\n\nIci, nous pouvons réécrire la même chose en utilisant l'opérateur conditionnel `?` Au lieu de `if` pour rendre `pow (x, n)` plus concis et toujours très lisible:\n\n```js run\nfunction pow(x, n) {\n  return (n == 1) ? x : (x * pow(x, n - 1));\n}\n```\n````\n\nLe nombre maximal d'appels imbriqués (y compris le premier) est appelé la *profondeur de récursivité*. Dans notre cas, ce sera exactement `n`.\n\nLa profondeur maximale de récursion est limitée par le moteur JavaScript. Nous sommes sur qu'il va jusqu'à 10000, certains moteurs en autorisent plus, mais 100000 est probablement hors limite pour la majorité d'entre eux. Il existe des optimisations automatiques qui aident à atténuer ce problème (\"optimisation des appels de queue\"), mais elles ne sont pas encore prises en charge partout et ne fonctionnent que dans des cas simples.\n\nCela limite l'application de la récursion, mais cela reste très large. Il y a beaucoup de tâches pour lesquelles la pensée récursive donne un code plus simple et plus facile à gérer.\n\n## Le contexte d'exécution et la pile\n\nVoyons maintenant comment fonctionnent les appels récursifs. Pour cela, nous allons regarder sous le capot des fonctions.\n\nLes informations sur le processus d'exécution d'une fonction en cours d'exécution sont stockées dans son *contexte d'exécution*.\n\nLe [contexte d'exécution](https://tc39.github.io/ecma262/#sec-execution-contexts) est une structure de données interne contenant des détails sur l'exécution d'une fonction: où le flux de contrôle est maintenant, les variables actuelles, la valeur de `this` (nous ne l'utilisons pas ici) et quelques autres détails internes.\n\nUn appel de fonction est associé à exactement un contexte d'exécution.\n\nLorsqu'une fonction effectue un appel imbriqué, les événements suivants se produisent:\n\n- La fonction en cours est suspendue.\n- Le contexte d’exécution qui lui est associé est mémorisé dans une structure de données spéciale appelée *pile de contexte d’exécution*.\n- L'appel imbriqué s'exécute.\n- Une fois terminé, l'ancien contexte d'exécution est extrait de la pile et la fonction externe reprend à partir de son point d'arrêt.\n\nVoyons ce qui se passe pendant l'appel de `pow(2, 3)`.\n\n### pow(2, 3)\n\nAu début de l'appel de `pow(2, 3)` le contexte d'exécution stockera des variables: `x = 2, n = 3`, le flux d'exécution est à la ligne `1` de la fonction.\n\nNous pouvons l'esquisser comme:\n\n<ul class=\"function-execution-context-list\">\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 3, at line 1 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 3)</span>\n  </li>\n</ul>\n\nC'est à ce moment que la fonction commence à s'exécuter. La condition`n == 1` est faux, donc le flux continue dans la deuxième branche de `if`:\n\n```js run\nfunction pow(x, n) {\n  if (n == 1) {\n    return x;\n  } else {\n*!*\n    return x * pow(x, n - 1);\n*/!*\n  }\n}\n\nalert( pow(2, 3) );\n```\n\n\nLes variables sont les mêmes, mais la ligne change, le contexte est donc le suivant:\n\n<ul class=\"function-execution-context-list\">\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 3, at line 5 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 3)</span>\n  </li>\n</ul>\n\nPour calculer `x * pow(x, n - 1)`, nous devons faire un sous-appel de `pow` avec de nouveaux arguments `pow(2, 2)`.\n\n### pow(2, 2)\n\nPour effectuer un appel imbriqué, JavaScript se souvient du contexte d'exécution actuel dans le *contexte d'exécution de la pile*.\n\nIci, nous appelons la même fonction `pow`, mais cela n’a absolument aucune importance. Le processus est le même pour toutes les fonctions:\n\n1. Le contexte actuel est \"mémorisé\" en haut de la pile.\n2. Le nouveau contexte est créé pour le sous-appel.\n3. Quand le sous-appel est fini -- le contexte précédent est extrait de la pile et son exécution se poursuit.\n\nVoici la pile de contexte lorsque nous sommes entrés dans le sous-appel `pow(2, 2)`:\n\n<ul class=\"function-execution-context-list\">\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 2, at line 1 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 2)</span>\n  </li>\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 3, at line 5 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 3)</span>\n  </li>\n</ul>\n\nLe nouveau contexte d'exécution actuel est en haut (et en gras) et les contextes précédemment mémorisés sont en dessous.\n\nQuand on termine le sous-appel -- il est facile de reprendre le contexte précédent, car il conserve les deux variables et l'emplacement exact du code où il s'est arrêté.\n\n```smart\nIci, dans l'image, nous utilisons le mot \"line\", comme dans notre exemple, il n'y a qu'un seul sous-appel en ligne, mais généralement une seule ligne de code peut contenir plusieurs sous-appels, comme `pow(…) + pow(…) + somethingElse(…)`.\n\nIl serait donc plus précis de dire que l'exécution reprend \"immédiatement après le sous-appel\".\n```\n\n### pow(2, 1)\n\nLe processus se répète: un nouveau sous-appel est fait à la ligne `5`, maintenant avec des arguments `x=2`, `n=1`.\n\nUn nouveau contexte d'exécution est créé, le précédent est placé en haut de la pile:\n\n<ul class=\"function-execution-context-list\">\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 1, at line 1 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 1)</span>\n  </li>\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 2, at line 5 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 2)</span>\n  </li>\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 3, at line 5 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 3)</span>\n  </li>\n</ul>\n\nIl y a 2 anciens contextes et 1 en cours d'exécution pour `pow(2, 1)`.\n\n### La sortie\n\nPendant l'exécution de `pow(2, 1)`, contrairement à avant, la condition `n == 1` est la vérité, donc la première branche de `if` fonctionne:\n\n```js\nfunction pow(x, n) {\n  if (n == 1) {\n*!*\n    return x;\n*/!*\n  } else {\n    return x * pow(x, n - 1);\n  }\n}\n```\n\nIl n'y a plus d'appels imbriqués, donc la fonction se termine en renvoyant`2`.\n\nLorsque la fonction se termine, son contexte d'exécution n'est plus nécessaire, il est donc supprimé de la mémoire. La précédente est restaurée en haut de la pile:\n\n\n<ul class=\"function-execution-context-list\">\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 2, at line 5 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 2)</span>\n  </li>\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 3, at line 5 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 3)</span>\n  </li>\n</ul>\n\nL'exécution de `pow(2, 2)` est repris. Il a le résultat du sous-appel `pow(2, 1)`, de sorte qu'il peut également terminer l'évaluation de `x * pow(x, n - 1)`, retournant `4`.\n\nEnsuite, le contexte précédent est restauré:\n\n<ul class=\"function-execution-context-list\">\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 3, at line 5 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 3)</span>\n  </li>\n</ul>\n\nQuand il se termine, nous avons un résultat de `pow(2, 3) = 8`.\n\nLa profondeur de récursion dans ce cas était: **3**.\n\nComme nous pouvons le voir dans les illustrations ci-dessus, la profondeur de récursion est égale au nombre maximal de contextes dans la pile.\n\nNotez les besoins en mémoire. Les contextes prennent de la mémoire. Dans notre cas, augmenter à la puissance de `n` nécessite en réalité de la mémoire pour les contextes `n`, pour toutes les valeurs inférieures de `n`.\n\nUn algorithme basé sur des boucles est plus économe en mémoire:\n\n```js\nfunction pow(x, n) {\n  let result = 1;\n\n  for (let i = 0; i < n; i++) {\n    result *= x;\n  }\n\n  return result;\n}\n```\n\nLe `pow` itératif utilise un contexte unique qui change les processus `i` et `result` dans le processus. Ses besoins en mémoire sont faibles, fixes et ne dépendent pas de `n`.\n\n**Toute récursion peut être réécrite sous forme de boucle. La variante de boucle peut généralement être rendue plus efficace.**\n\n..Parfois, la réécriture n’est pas triviale, en particulier lorsque la fonction utilise différents sous-appels récursifs en fonction des conditions et fusionne leurs résultats ou lorsque la création de branche est plus complexe. Et l'optimisation risque de ne pas être nécessaire et de ne pas valoir la peine.\n\nLa récursion peut donner un code plus court, plus facile à comprendre et à supporter. Les optimisations ne sont pas nécessaires à chaque endroit, nous avons surtout besoin d'un bon code, c'est pourquoi il est utilisé.\n\n## Traversées récursives\n\nUne autre grande application de la récursion est une traversée récursive.\n\nImaginez, nous avons une entreprise. La structure du personnel peut être présentée comme un objet:\n\n```js\nlet company = {\n  sales: [{\n    name: 'John',\n    salary: 1000\n  }, {\n    name: 'Alice',\n    salary: 1600\n  }],\n\n  development: {\n    sites: [{\n      name: 'Peter',\n      salary: 2000\n    }, {\n      name: 'Alex',\n      salary: 1800\n    }],\n\n    internals: [{\n      name: 'Jack',\n      salary: 1300\n    }]\n  }\n};\n```\n\nEn d'autres termes, une entreprise a des départements.\n\n- Un département peut avoir un tableau de personnel. Par exemple, le département des ventes compte 2 employés: John et Alice.\n- Ou bien un département peut être divisé en sous-départements, comme `development` a deux branches: `sites` et `internes`. Chacun d'entre eux a son propre personnel.\n- Il est également possible que lorsqu'un sous-département s'agrandisse, il se divise en sous-départements (ou équipes).\n\n    Par exemple, le département `sites` peut être divisé en équipes pour les sites `siteA` et `siteB`. Et, potentiellement, ils peuvent être diviser encore plus. Ce n'est pas sur la photo, c'est juste quelque chose qu'ont pourrait immaginer.\n\nMaintenant, disons que nous voulons une fonction pour obtenir la somme de tous les salaires. Comment peut-on faire ça?\n\nUne approche itérative n’est pas facile, car la structure n’est pas simple. La première idée peut être de créer une boucle `for` sur `company` avec une sous-boucle imbriqué sur les départements de premier niveau. Mais ensuite, nous avons besoin de plus de sous-boucles imbriquées pour parcourir le personnel des départements de second niveau, tels que les `sites`... Et puis une autre sous-boucle dans ceux des départements de 3ème niveau qui pourraient apparaître dans le futur ? Si nous mettons 3-4 sous-boucles imbriquées dans le code pour traverser un seul objet, cela devient plutôt moche.\n\nEssayons la récursion.\n\nComme nous pouvons le constater, lorsque notre fonction demande à un département de faire la somme, il existe deux cas possibles:\n\n1. S’il s’agit d’un \"simple\" département avec un *tableau* de personnes, nous pouvons alors additionner les salaires en une simple boucle.\n2. Ou bien *c'est un objet* avec `N` sous-départements -- alors nous pouvons faire des appels `N` récursifs pour obtenir la somme de chaque sous-étape et combiner les résultats.\n\nLe premier cas est la base de la récursivité, le cas trivial, lorsque nous obtenons un tableau.\n\nLe 2ème cas où nous obtenons un objet est l'étape récursive. Une tâche complexe est divisée en sous-tâches pour les plus petits départements. Ils peuvent à leur tour se séparer à nouveau, mais tôt ou tard, la scission se terminera à (1).\n\nL'algorithme est probablement encore plus facile à lire à partir du code:\n\n\n```js run\nlet company = { // le même objet, compressé pour la brièveté\n  sales: [{name: 'John', salary: 1000}, {name: 'Alice', salary: 1600 }],\n  development: {\n    sites: [{name: 'Peter', salary: 2000}, {name: 'Alex', salary: 1800 }],\n    internals: [{name: 'Jack', salary: 1300}]\n  }\n};\n\n// La fonction pour faire le travail\n*!*\nfunction sumSalaries(department) {\n  if (Array.isArray(department)) { // case (1)\n    return department.reduce((prev, current) => prev + current.salary, 0); // additionne le tableau\n  } else { // case (2)\n    let sum = 0;\n    for (let subdep of Object.values(department)) {\n      sum += sumSalaries(subdep); // appel récursivement pour les sous-départements, additionnez les résultats\n    }\n    return sum;\n  }\n}\n*/!*\n\nalert(sumSalaries(company)); // 7700\n```\n\nLe code est court et facile à comprendre (tout va bien?). C'est le pouvoir de la récursion. Cela fonctionne également pour tous les niveaux d'imbrication de sous-départements.\n\nVoici le schéma des appels:\n\n![salaires récursifs](recursive-salaries.svg)\n\nOn peut facilement voir le principe: pour un objet `{...}` les sous-appels sont faits, alors que les tableaux `[...]` sont les \"feuilles\" de l'arbre de récurrence, elles donnent un résultat immédiat.\n\nNotez que le code utilise des fonctionnalités intelligentes que nous avons déjà abordées:\n\n- La méthode `arr.reduce` a été expliquée dans le chapitre <info:array-methods> pour obtenir la somme du tableau.\n- La boucle `for(val of Object.values(obj))` itérer sur les valeurs d'objet: `Object.values` retourne un tableau d'eux-mêmes.\n\n\n## Structures récursives\n\nUne structure de données récursive (définie de manière récursive) est une structure qui se réplique par parties.\n\nNous venons de le voir dans l'exemple d'une structure d'entreprise ci-dessus.\n\nUn *département* d'entreprise est:\n- Soit un éventail de personnes.\n- Ou un objet avec des *départements*.\n\nPour les développeurs Web, il existe des exemples bien mieux connus: les documents HTML et XML.\n\nDans le document HTML, une balise *HTML* peut contenir une liste de:\n- Morceaux de texte.\n- Commentaires HTML.\n- Autres *balises HTML* (pouvant à leur tour contenir des morceaux de texte/commentaires ou d’autres balises, etc.).\n\nC'est encore une définition récursive.\n\nPour une meilleure compréhension, nous allons couvrir une autre structure récursive nommée \"Liste chaînée\" qui pourrait être une meilleure alternative aux tableaux dans certains cas.\n\n### Liste chaînée\n\nImaginez, nous voulons stocker une liste ordonnée d'objets.\n\nLe choix naturel serait un tableau:\n\n```js\nlet arr = [obj1, obj2, obj3];\n```\n\n... Mais il y a un problème avec les tableaux. Les opérations \"delete element\" et \"insert element\" sont coûteuses. Par exemple, l'opération `arr.unshift(obj)` doit renuméroter tous les éléments pour faire de la place pour un nouvel `obj`, et si le tableau est grand, cela prend du temps. Même chose avec `arr.shift()`.\n\nLes seules modifications structurelles ne nécessitant pas de renumérotation en masse sont celles qui fonctionnent avec la fin du tableau: `arr.push/pop`. Ainsi, un tableau peut être assez lent pour les grandes files d'attente, lorsque nous devons travailler avec sont début.\n\nAlternativement, si nous avons vraiment besoin d'une insertion/suppression rapide, nous pouvons choisir une autre structure de données appelée la [Liste chaînée](https://fr.wikipedia.org/wiki/Liste_cha%C3%AEn%C3%A9e).\n\n*L'élémentde la liste liée* est défini de manière récursive en tant qu'objet avec:\n- `value`.\n- `next` propriété référençant le prochain *élément de liste liée* ou `null` si c'est la fin.\n\nPar exemple:\n\n```js\nlet list = {\n  value: 1,\n  next: {\n    value: 2,\n    next: {\n      value: 3,\n      next: {\n        value: 4,\n        next: null\n      }\n    }\n  }\n};\n```\n\nReprésentation graphique de la liste:\n\n![linked list](linked-list.svg)\n\nAn alternative code for creation:\n\n```js no-beautify\nlet list = { value: 1 };\nlist.next = { value: 2 };\nlist.next.next = { value: 3 };\nlist.next.next.next = { value: 4 };\nlist.next.next.next.next = null;\n```\n\nIci, nous pouvons voir encore plus clairement qu'il y a plusieurs objets, chacun ayant les valeurs `value` et `next` pointant vers le voisin. La variable `list` est le premier objet de la chaîne. Par conséquent, en suivant les pointeurs `next`, nous pouvons atteindre n'importe quel élément.\n\nLa liste peut être facilement divisée en plusieurs parties et ultérieurement réunie:\n\n```js\nlet secondList = list.next.next;\nlist.next.next = null;\n```\n\n![linked list split](linked-list-split.svg)\n\nPour joindre:\n\n```js\nlist.next.next = secondList;\n```\n\nEt nous pouvons sûrement insérer ou retirer des objets n’importe où.\n\nPar exemple, pour ajouter une nouvelle valeur, nous devons mettre à jour la tête de la liste:\n\n```js\nlet list = { value: 1 };\nlist.next = { value: 2 };\nlist.next.next = { value: 3 };\nlist.next.next.next = { value: 4 };\n\n*!*\n// ajoute la nouvelle valeur à la liste\nlist = { value: \"new item\", next: list };\n*/!*\n```\n\n![linked list](linked-list-0.svg)\n\nPour supprimer une valeur du milieu, changez le `next` de la précédente:\n\n```js\nlist.next = list.next.next;\n```\n\n![linked list](linked-list-remove-1.svg)\n\n`List.next` a sauté `1` à la valeur `2`. La valeur `1` est maintenant exclue de la chaîne. Si elle n'est pas stocké ailleurs, elle sera automatiquement supprimé de la mémoire.\n\nContrairement aux tableaux, il n'y a pas de renumérotation en masse, nous pouvons facilement réorganiser les éléments.\n\nNaturellement, les listes ne sont pas toujours meilleures que les tableaux. Sinon, tout le monde n'utiliserait que des listes.\n\nLe principal inconvénient est que nous ne pouvons pas facilement accéder à un élément par son numéro. Dans un tableau simple: `arr [n]` est une référence directe. Mais dans la liste, nous devons commencer à partir du premier élément et aller `next``N` fois pour obtenir le Nième élément.\n\n...Mais nous n’avons pas toujours besoin de telles opérations. Par exemple, quand on a besoin d’une file d’attente ou même d’un [deque](https://en.wikipedia.org/wiki/Double-ended_queue) -- la structure ordonnée qui doit permettre l'ajout/suppression très rapide d'éléments des deux extrémités, mais l'accès au milieu n'est pas nécessaire.\n\n\nLes listes peuvent être améliorées:\n- Nous pouvons ajouter la propriété `prev` en plus de `next` pour référencer l'élément précédent, pour revenir facilement.\n- Nous pouvons également ajouter une variable nommée `tail` faisant référence au dernier élément de la liste (et la mettre à jour lors de l'ajout/suppression d'éléments de la fin).\n- ... La structure de données peut varier en fonction de nos besoins.\n\n## Résumé\n\nTerms:\n- *Recursion*  est un terme de programmation qui signifie q'une fonction s'appelle elle-même. Les fonctions récursives peuvent être utilisées pour résoudre des tâches de manière élégante.\n\nLorsqu'une fonction s'appelle elle-même, cela s'appelle une *étape de récursion*. La *base* de la récursion est constituée par les arguments de la fonction qui rendent la tâche si simple que la fonction ne fait plus d'appels.\n\n- Une structure de données de [Type récursif](https://fr.wikipedia.org/wiki/Type_r%C3%A9cursif) est une structure de données qui peut être définie à l'aide de elle-même.\n\n    Par exemple, la liste chaînée peut être définie comme une structure de données consistant en un objet référençant une liste (ou null).\n\n    ```js\n    list = { value, next -> list }\n    ```\n\n    Les arbres tels que l’arbre des éléments HTML ou l’arbre des départements de ce chapitre sont également naturellement récursifs: ils ont des branches et chaque branche peut avoir d’autres branches.\n\n    Des fonctions récursives peuvent être utilisées pour les parcourir, comme nous l'avons vu dans l'exemple `sumSalary`.\n\nToute fonction récursive peut être réécrite en une fonction itérative. Et c'est parfois nécessaire pour optimiser les choses. Mais pour de nombreuses tâches, une solution récursive est assez rapide et plus facile à écrire et à supporter.\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/head.html",
    "content": "<style>\n.function-execution-context-list {\n  margin: 0;\n  padding: 0;\n  overflow: auto;\n}\n\n.function-execution-context {\n  border: 1px solid black;\n  font-family: 'DejaVu Sans Mono', 'Lucida Console', 'Menlo', 'Monaco', monospace;\n  padding: 4px 6px;\n  margin: 0 4px;\n}\n\n.function-execution-context-call {\n  color: gray;\n}\n\n.function-execution-context-call::before {\n  content: ' call: ';\n}\n\n.function-execution-context-list li:first-child {\n  font-weight: bold;\n}\n</style>\n"
  },
  {
    "path": "1-js/06-advanced-functions/02-rest-parameters-spread/article.md",
    "content": "# Les paramètres Rest et la syntaxe spread\n\nDe nombreuses fonctions intégrées à JavaScript prennent en charge un nombre arbitraire d'arguments.\n\nPar exemple :\n\n- `Math.max(arg1, arg2, ..., argN)` -- renvoie le plus grand des arguments.\n- `Object.assign(dest, src1, ..., srcN)` -- copie les propriétés de `src1..N` dans `dest`.\n- ... etc.\n\nDans ce chapitre, nous apprendrons à faire de même. Et aussi, comment passer des tableaux en paramètre à de telles fonctions.\n\n## Les paramètres Rest `...`\n\nUne fonction peut être appelée avec un nombre quelconque d'arguments, peu importe comment elle a été définie.\n\nComme ici :\n\n```js run\nfunction sum(a, b) {\n  return a + b;\n}\n\nalert( sum(1, 2, 3, 4, 5) );\n```\n\nIl n'y aura pas d'erreur à cause d'arguments \"excessifs\". Mais bien sûr, dans le résultat, seuls les deux premiers seront comptés, donc le résultat dans le code ci-dessus est `3`.\n\nLe reste des paramètres peut être inclus dans la définition de la fonction en utilisant trois points `...` suivis du nom du tableau qui les contiendra. Les points signifient littéralement \"rassemblez les paramètres restants dans un tableau\".\n\nPar exemple, pour rassembler tous les arguments dans un tableau `args` :\n\n```js run\nfunction sumAll(...args) { // args est le nom du tableau\n  let sum = 0;\n\n  for (let arg of args) sum += arg;\n\n  return sum;\n}\n\nalert( sumAll(1) ); // 1\nalert( sumAll(1, 2) ); // 3\nalert( sumAll(1, 2, 3) ); // 6\n```\n\nNous pouvons choisir d’obtenir les premiers paramètres sous forme de variables et de ne rassembler que le reste.\n\nIci, les deux premiers arguments vont dans les variables et le reste dans le tableau `titles` :\n\n```js run\nfunction showName(firstName, lastName, ...titles) {\n  alert( firstName + ' ' + lastName ); // Julius Caesar\n\n  // le reste va dans le tableau titles\n  // i.e. titles = [\"Consul\", \"Imperator\"]\n  alert( titles[0] ); // Consul\n  alert( titles[1] ); // Imperator\n  alert( titles.length ); // 2\n}\n\nshowName(\"Julius\", \"Caesar\", \"Consul\", \"Imperator\");\n```\n\n````warn header=\"Les paramètres rest doivent être à la fin\"\nLes paramètres rest regroupent tous les arguments restants. Par conséquent, ce qui suit n'a pas de sens et génère une erreur :\n\n```js\nfunction f(arg1, ...rest, arg2) { // arg2 après ...rest ?!\n  // error\n}\n```\n\nLe `...rest` doit toujours être le dernier.\n````\n\n## La variable \"arguments\"\n\nIl existe également un objet spécial array-like nommé `arguments` qui contient tous les arguments en fonction de leur index.\n\nPar exemple :\n\n```js run\nfunction showName() {\n  alert( arguments.length );\n  alert( arguments[0] );\n  alert( arguments[1] );\n\n  // c'est iterable\n  // for(let arg of arguments) alert(arg);\n}\n\n// affiche : 2, Julius, Caesar\nshowName(\"Julius\", \"Caesar\");\n\n// affiche : 1, Ilya, undefined (pas de second argument)\nshowName(\"Ilya\");\n```\n\nAutrefois, les paramètres rest n'existaient pas dans le langage, et utiliser `arguments` était le seul moyen d'obtenir tous les arguments de la fonction. Et cela fonctionne toujours, on peut le trouver dans l'ancien code.\n\nMais l’inconvénient est que, bien que `arguments` ressemblent à un tableau et qu’ils soient itératifs, ce n’est pas un tableau. Il ne supporte pas les méthodes de tableau, nous ne pouvons donc pas appeler `arguments.map(...)` par exemple.\n\nDe plus, il contient toujours tous les arguments. Nous ne pouvons pas les capturer partiellement, comme nous l’avons fait avec les paramètres rest.\n\nAinsi, lorsque nous avons besoin de ces fonctionnalités, les paramètres rest sont préférés.\n\n````smart header=\"Les fonctions fléchées n'ont pas d'`\\\"arguments\\\"`\"\nSi nous accédons à l'objet `arguments` à partir d'une fonction fléchée, il le prend à la fonction externe \"normale\".\n\nVoici un exemple :\n\n```js run\nfunction f() {\n  let showArg = () => alert(arguments[0]);\n  showArg();\n}\n\nf(1); // 1\n```\n\nComme nous nous en souvenons, les fonctions fléchées n’ont pas leur propre `this`. Nous savons maintenant qu’ils n’ont pas non plus l’objet spécial `arguments`.\n````\n\n## Spread syntax [#spread-syntax]\n\nNous venons de voir comment obtenir un tableau à partir de la liste de paramètres.\n\nMais parfois, nous devons faire exactement l'inverse.\n\nPar exemple, il existe une fonction intégrée [Math.max](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Math/max) qui renvoie le plus grand nombre d'une liste :\n\n```js run\nalert( Math.max(3, 5, 1) ); // 5\n```\n\nMaintenant, disons que nous avons un tableau `[3, 5, 1]`. Comment appelons-nous `Math.max` avec ?\n\nLe passer \"tel quel\" ne fonctionnera pas, car `Math.max` attend une liste d’arguments numériques et non un tableau :\n\n```js run\nlet arr = [3, 5, 1];\n\n*!*\nalert( Math.max(arr) ); // NaN\n*/!*\n```\n\nEt nous ne pouvons sûrement pas lister manuellement les éléments dans le code `Math.max(arr[0], arr[1], arr[2])`, parce que nous pouvons ne pas savoir combien il y en a. Au fur et à mesure que notre script s'exécute, il peut y en avoir beaucoup ou pas du tout. Et ça deviendrait moche.\n\n*La sytaxe Spread* à la rescousse ! Il ressemble aux paramètres rest, en utilisant également `...`, mais fait tout le contraire.\n\nQuand `...arr` est utilisé dans l'appel de fonction, il \"développe\" un objet itérable `arr` dans la liste des arguments.\n\nPour `Math.max` :\n\n```js run\nlet arr = [3, 5, 1];\n\nalert( Math.max(...arr) ); // 5 (spread transforme un tableau en une liste d'arguments)\n```\n\nNous pouvons aussi passer plusieurs iterables de cette façon :\n\n```js run\nlet arr1 = [1, -2, 3, 4];\nlet arr2 = [8, 3, -8, 1];\n\nalert( Math.max(...arr1, ...arr2) ); // 8\n```\n\nOn peut même combiner la sytaxe spread avec des valeurs normales :\n\n\n```js run\nlet arr1 = [1, -2, 3, 4];\nlet arr2 = [8, 3, -8, 1];\n\nalert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25\n```\n\nDe plus, la sytaxe spread peut être utilisée pour fusionner des tableaux :\n\n```js run\nlet arr = [3, 5, 1];\nlet arr2 = [8, 9, 15];\n\n*!*\nlet merged = [0, ...arr, 2, ...arr2];\n*/!*\n\nalert(merged); // 0,3,5,1,2,8,9,15 (0, ensuite arr, ensuite 2, ensuite arr2)\n```\n\nDans les exemples ci-dessus, nous avons utilisé un tableau pour illustrer la syntaxe spread, mais tout itérable fera l'affaire.\n\nPar exemple, nous utilisons ici la syntaxe spread pour transformer le string en tableau de caractères :\n\n```js run\nlet str = \"Hello\";\n\nalert( [...str] ); // H,e,l,l,o\n```\n\nLa syntaxe spread utilise en interne des itérateurs pour rassembler les éléments, de la même manière que `for..of`.\n\nDonc, pour une chaine de caractères, `for..of` retourne des caractères et `...str` devient `\"H\",\"e\",\"l\",\"l\",\"o\"`. La liste de caractères est transmise à l'initialiseur de tableau `[...str]`.\n\nPour cette tâche particulière, nous pourrions également utiliser `Array.from`, car il convertit un itérable (comme une chaîne de caractères) en un tableau :\n\n```js run\nlet str = \"Hello\";\n\n// Array.from convertit un itérable en tableau\nalert( Array.from(str) ); // H,e,l,l,o\n```\n\nLe résultat est le même que `[...str]`.\n\nMais il existe une différence subtile entre `Array.from(obj)` et `[...obj]` :\n\n- `Array.from` fonctionne à la fois sur les tableaux et les iterables.\n- La syntaxe spread ne fonctionne que sur des iterables.\n\nDonc, pour transformer quelque chose en tableau, `Array.from` tend à être plus universel.\n\n## Copier un tableau/objet\n\nVous souvenez-vous quand nous avons parlé de `Object.assign()` [par le passé](info:object-copy#cloning-and-merging-object-assign) ?\n\nIl est possible de faire la même chose avec la syntaxe spread !\n\n```js run\nlet arr = [1, 2, 3];\n\n*!*\nlet arrCopy = [...arr]; // répartir le tableau dans une liste de paramètres\n                        // puis mettre le résultat dans un nouveau tableau\n*/!*\n\n// les tableaux ont-ils le même contenu ?\nalert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true\n\n// les tableaux sont-ils égaux ?\nalert(arr === arrCopy); // false (pas la même référence)\n\n// la modification de notre tableau initial ne modifie pas la copie :\narr.push(4);\nalert(arr); // 1, 2, 3, 4\nalert(arrCopy); // 1, 2, 3\n```\n\nNotez qu'il est possible de faire la même chose pour faire une copie d'un objet :\n\n```js run\nlet obj = { a: 1, b: 2, c: 3 };\n\n*!*\nlet objCopy = { ...obj }; // répartir l'objet dans une liste de paramètres\n                          // puis retourne le résultat dans un nouvel objet\n*/!*\n\n// les objets ont-ils le même contenu ?\nalert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true\n\n// les objets sont-ils égaux ?\nalert(obj === objCopy); // false (pas la même référence)\n\n// la modification de notre objet initial ne modifie pas la copie :\nobj.d = 4;\nalert(JSON.stringify(obj)); // {\"a\":1,\"b\":2,\"c\":3,\"d\":4}\nalert(JSON.stringify(objCopy)); // {\"a\":1,\"b\":2,\"c\":3}\n```\n\nCette façon de copier un objet est beaucoup plus courte que `let objCopy = Object.assign({}, obj)` ou pour un tableau `let arrCopy = Object.assign([], arr);` nous préférons donc l'utiliser chaque fois que nous le pouvons.\n\n\n## Résumé\n\nQuand on voit `\"...\"` dans le code, il s’agit soit des paramètres rest ou de la syntaxe spread.\n\nIl existe un moyen facile de les distinguer :\n\n- Lorsque `...` se trouve à la fin des paramètres de fonction, il s'agit des \"paramètres rest\" et rassemble le reste de la liste des arguments dans un tableau.\n- Lorsque `...` est présent dans un appel de fonction ou similaire, on l'appelle \"la syntaxe spread\" (syntaxe de propagation) et étend un tableau en une liste.\n\nModèles d'utilisation :\n\n- Les paramètres rest permettent de créer des fonctions acceptant un nombre quelconque d'arguments.\n- La syntaxe spread est utilisée pour passer un tableau à des fonctions nécessitant normalement une liste d'arguments.\n\nEnsemble, ils permettent de voyager facilement entre une liste et un tableau de paramètres.\n\nTous les arguments d'un appel de fonction sont également disponibles dans la vriable `arguments` \"à l'ancienne\" : objet itérable array-like.\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md",
    "content": "La réponse est : **Pete**.\n\nUne fonction obtient des variables externes telles qu'elles sont maintenant, elle utilise les valeurs les plus récentes.\n\nLes anciennes valeurs de variable ne sont enregistrées nulle part. Lorsqu'une fonction veut une variable, elle prend la valeur actuelle de son propre environnement lexical ou de l'environnement externe.\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md",
    "content": "importance: 5\n\n---\n\n# Une fonction récupère-t-elle les dernières modifications ?\n\nLa fonction sayHi utilise un nom de variable externe. Lorsque la fonction s'exécute, quelle valeur va-t-elle utiliser ?\n\n```js\nlet name = \"John\";\n\nfunction sayHi() {\n  alert(\"Hi, \" + name);\n}\n\nname = \"Pete\";\n\nsayHi(); // qu'affichera-t-elle : \"John\" ou \"Pete\" ?\n```\n\nDe telles situations sont courantes à la fois dans le développement côté navigateur et côté serveur. Une fonction peut être programmée pour s'exécuter plus tard qu'elle n'est créée, par exemple après une action de l'utilisateur ou une demande réseau.\n\nDonc, la question est : reprend-elle les derniers changements ?\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/10-make-army/_js.view/solution.js",
    "content": "function makeArmy() {\n\n  let shooters = [];\n\n  for(let i = 0; i < 10; i++) {\n    let shooter = function() { // shooter function\n      alert( i ); // should show its number\n    };\n    shooters.push(shooter);\n  }\n\n  return shooters;\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/10-make-army/_js.view/source.js",
    "content": "function makeArmy() {\n  let shooters = [];\n\n  let i = 0;\n  while (i < 10) {\n    let shooter = function() { // shooter function\n      alert( i ); // should show its number\n    };\n    shooters.push(shooter);\n    i++;\n  }\n\n  return shooters;\n}\n\n/*\nlet army = makeArmy();\n\narmy[0](); // the shooter number 0 shows 10\narmy[5](); // and number 5 also outputs 10...\n// ... all shooters show 10 instead of their 0, 1, 2, 3...\n*/\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/10-make-army/_js.view/test.js",
    "content": "describe(\"army\", function() {\n\n  let army;\n  \n  before(function() {\n    army = makeArmy();\n    window.alert = sinon.stub(window, \"alert\");\n  });\n\n  it(\"army[0] shows 0\", function() {\n    army[0]();\n    assert(alert.calledWith(0));\n  });\n\n\n  it(\"army[5] shows 5\", function() {\n    army[5]();\n    assert(alert.calledWith(5));\n  });\n\n  after(function() {\n    window.alert.restore();\n  });\n\n});\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/10-make-army/solution.md",
    "content": "\nExaminons exactement ce qui se fait à l'intérieur de `makeArmy`, et la solution deviendra évidente.\n\n1. Elle crée un tableau vide `shooters`:\n\n    ```js\n    let shooters = [];\n    ```\n2. Le remplit avec des fonctions dans la boucle via `shooters.push(function)`.\n\n    Chaque élément est une fonction, le tableau résultant ressemble à ceci :\n\n    ```js no-beautify\n    shooters = [\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); }\n    ];\n    ```\n\n3. Le tableau est renvoyé par la fonction.\n    \n    Puis, plus tard, l'appel à n'importe quel membre, par ex. `Army[5]()` récupérera l'élément `army[5]` du tableau (qui est une fonction) et l'appellera.\n    \n    Maintenant, pourquoi toutes ces fonctions affichent-elles la même valeur, `10` ?\n    \n    C'est parce qu'il n'y a pas de variable locale `i` dans les fonctions de `shooter`. Lorsqu'une telle fonction est appelée, elle prend `i` de son environnement lexical externe.\n    \n    Alors, quelle sera la valeur de `i` ?\n    \n    Si nous regardons la source :\n    \n    ```js\n    function makeArmy() {\n      ...\n      let i = 0;\n      while (i < 10) {\n        let shooter = function() { // fonction shooter \n          alert( i ); // should show its number\n        };\n        shooters.push(shooter); // ajoute une fonction au tableau\n        i++;\n      }\n      ...\n    }\n    ```\n    \n    Nous pouvons voir que toutes les fonctions `shooter` sont créées dans l'environnement lexical de la fonction `makeArmy()`. Mais quand `army[5]()` est appelé, `makeArmy` a déjà terminé son travail, et la valeur finale de `i` est `10` (`while` s'arrête à `i=10`).\n    \n    En conséquence, toutes les fonctions `shooter` obtiennent la même valeur de l'environnement lexical externe et c'est-à-dire la dernière valeur, `i=10`.\n    \n    ![](lexenv-makearmy-empty.svg)\n    \n    Comme vous pouvez le voir ci-dessus, à chaque itération d'un bloc `while {...}`, un nouvel environnement lexical est créé. Donc, pour résoudre ce problème, nous pouvons copier la valeur de `i` dans une variable dans le bloc `while {...}`, comme ceci :\n    \n    ```js run\n    function makeArmy() {\n      let shooters = [];\n    \n      let i = 0;\n      while (i < 10) {\n        *!*\n          let j = i;\n        */!*\n          let shooter = function() { // fonction shooter \n            alert( *!*j*/!* ); // devrait afficher son numéro\n          };\n        shooters.push(shooter);\n        i++;\n      }\n    \n      return shooters;\n    }\n    \n    let army = makeArmy();\n    \n    // Maintenant, le code fonctionne correctement\n    army[0](); // 0\n    army[5](); // 5\n    ```\n    \n    Ici, `let j = i` déclare une variable \"itération-locale\" `j` et y copie `i`. Les primitives sont copiées \"par valeur\", donc nous obtenons en fait une copie indépendante de `i`, appartenant à l'itération de boucle courante.\n    \n    Les shooters fonctionnent correctement, car la valeur de `i` vit maintenant un peu plus près. Pas dans l'environnement lexical `makeArmy()`, mais dans l'environnement lexical qui correspond à l'itération de la boucle actuelle :\n    \n    ![](lexenv-makearmy-while-fixed.svg)\n    \n    Ce genre de problème pourrait également être évité si nous utilisions `for` au début, comme ceci :\n    \n    ```js run demo\n    function makeArmy() {\n    \n      let shooters = [];\n    \n    *!*\n      for(let i = 0; i < 10; i++) {\n    */!*\n        let shooter = function() { // fonction shooter \n          alert( i ); // devrait afficher son numéro\n        };\n        shooters.push(shooter);\n      }\n    \n      return shooters;\n    }\n    \n    let army = makeArmy();\n    \n    army[0](); // 0\n    army[5](); // 5\n    ```\n    \n    C'est essentiellement la même chose, car `for` génère un nouvel environnement lexical à chaque itération avec sa propre variable `i`. Ainsi, le `shooter` généré à chaque itération fait référence à son propre `i`, à partir de cette itération même.\n    \n    ![](lexenv-makearmy-for-fixed.svg)\n\n    Maintenant que vous avez déployé tant d'efforts pour lire ceci, et que la recette finale est si simple - utilisez simplement `for`, vous vous demandez peut-être - cela en valait-il la peine ?\n\n    Eh bien, si vous pouviez facilement répondre à la question, vous ne liriez pas la solution. Donc, j'espère que cette tâche doit vous avoir aidé à comprendre un peu mieux les choses.\n\nEn outre, il existe en effet des cas où l'on préfère `while` à `for`, et d'autres scénarios où de tels problèmes sont réels.\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/10-make-army/task.md",
    "content": "importance: 5\n\n---\n\n# Armée de fonctions\n\nLe code suivant crée un tableau de `shooters`.\n\nChaque fonction est censée sortir son numéro. Mais quelque chose ne va pas …\n\n```js run\nfunction makeArmy() {\n  let shooters = [];\n\n  let i = 0;\n  while (i < 10) {\n    let shooter = function() { // créer une fonction shooter\n      alert( i ); // qui devrait afficher son numéro\n    };\n    shooters.push(shooter); // and add it to the array\n    i++;\n  }\n\n  // ...and return the array of shooters\n  return shooters;\n}\n\nlet army = makeArmy();\n\n*!*\n// tous les shooters affichent 10 au lieu de leurs numéros 0, 1, 2, 3 ...\narmy[0](); // 10 du shooter numéro 0\narmy[1](); // 10 du shooter numéro 1\narmy[2](); // 10 ...etc.\n*/!*\n```\n\nPourquoi tous les shooters affichent-ils la même valeur ?\n\nCorrigez le code pour qu'il fonctionne comme prévu.\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md",
    "content": "La réponse est : **Pete**.\n\nLa fonction `work()` dans le code ci-dessous obtient `name` du lieu de son origine via la référence d'environnement lexical externe :\n\n![](lexenv-nested-work.svg)\n\nDonc, le résultat est `\"Pete\"` ici.\n\nMais s'il n'y avait pas de `let name` dans `makeWorker()`, alors la recherche irait à l'extérieur et prendrait la variable globale comme nous pouvons le voir dans la chaîne ci-dessus. Dans ce cas, le résultat serait `\"John\"`.\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md",
    "content": "importance: 5\n\n---\n\n# Quelles variables sont disponibles ?\n\nLa fonction `makeWorker` ci-dessous crée une autre fonction et la renvoie. Cette nouvelle fonction peut être appelée ailleurs.\n\nAura-t-elle accès aux variables externes depuis son lieu de création, ou depuis le lieu d'invocation, ou les deux ?\n\n```js\nfunction makeWorker() {\n  let name = \"Pete\";\n\n  return function() {\n    alert(name);\n  };\n}\n\nlet name = \"John\";\n\n// créons une fonction\nlet work = makeWorker();\n\n// appelons-la\nwork(); // que va-t-elle afficher ?\n```\n\nQuelle valeur va-t-elle afficher ? \"Pete\" ou \"John\" ?\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md",
    "content": "La réponse : **0,1.**\n\nLes fonctions `counter` et `counter2` sont créées par différentes invocations de `makeCounter`.\n\nElles ont donc des environnements lexicaux externes indépendants, chacun ayant son propre `count`.\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/3-counter-independent/task.md",
    "content": "importance: 5\n\n---\n\n# Les compteurs sont-ils indépendants ?\n\nIci, nous faisons deux compteurs : `counter` et `counter2` en utilisant la même fonction `makeCounter`.\n\nSont-ils indépendants ? Que va montrer le deuxième compteur ? `0,1` ou `2,3` ou autre chose ?\n\n```js\nfunction makeCounter() {\n  let count = 0;\n\n  return function() {\n    return count++;\n  };\n}\n\nlet counter = makeCounter();\nlet counter2 = makeCounter();\n\nalert( counter() ); // 0\nalert( counter() ); // 1\n\n*!*\nalert( counter2() ); // ?\nalert( counter2() ); // ?\n*/!*\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md",
    "content": "\nCela fonctionnera sûrement très bien.\n\nLes deux fonctions imbriquées sont créées dans le même environnement Lexical externe. Elles partagent donc l'accès à la même variable `count` :\n\n```js run\nfunction Counter() {\n  let count = 0;\n\n  this.up = function() {\n    return ++count;\n  };\n  \n  this.down = function() {\n    return --count;\n  };\n}\n\nlet counter = new Counter();\n\nalert( counter.up() ); // 1\nalert( counter.up() ); // 2\nalert( counter.down() ); // 1\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md",
    "content": "importance: 5\n\n---\n\n# Objet compteur\n\nIci, un objet compteur est créé à l'aide de la fonction constructeur.\n\nEst-ce que cela fonctionnera ? Que va-t-elle afficher ?\n\n```js\nfunction Counter() {\n  let count = 0;\n\n  this.up = function() {\n    return ++count;\n  };\n  this.down = function() {\n    return --count;\n  };\n}\n\nlet counter = new Counter();\n\nalert( counter.up() ); // ?\nalert( counter.up() ); // ?\nalert( counter.down() ); // ?\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md",
    "content": "Le résultat est **une erreur**.\n\nLa fonction `sayHi` est déclarée à l'intérieur du `if`, elle ne vit donc qu'à l'intérieur. Il n'y a pas de `sayHi` dehors.\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/5-function-in-if/task.md",
    "content": "importance: 5\n\n---\n# Fonction dans if\n\nRegardez ce code. Quel sera le résultat de l'appel à la dernière ligne ?\n\n```js run\nlet phrase = \"Hello\";\n\nif (true) {\n  let user = \"John\";\n\n  function sayHi() {\n    alert(`${phrase}, ${user}`);\n  }\n}\n\n*!*\nsayHi();\n*/!*\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md",
    "content": "Pour que les secondes parenthèses fonctionnent, les premières doivent renvoyer une fonction.\n\nComme ceci :\n\n```js run\nfunction sum(a) {\n\n  return function(b) {\n    return a + b; // prend \"a\" de l'environnement lexical externe\n  };\n\n}\n\nalert( sum(1)(2) ); // 3\nalert( sum(5)(-1) ); // 4\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/6-closure-sum/task.md",
    "content": "importance: 4\n\n---\n\n# Somme avec des closures\n\nÉcrivez une fonction `sum` qui fonctionne comme ceci :` sum(a)(b) = a + b`.\n\nOui, exactement de cette façon, en utilisant des doubles parenthèses (ce n'est pas une faute de frappe).\n\nPar exemple :\n\n```js\nsum(1)(2) = 3\nsum(5)(-1) = 4\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/7-let-scope/solution.md",
    "content": "Le résultat est : **error**.\n\nEssayez de l'exécuter :\n\n```js run\nlet x = 1;\n\nfunction func() {\n*!*\n  console.log(x); // ReferenceError: Cannot access 'x' before initialization\n*/!*\n  let x = 2;\n}\n\nfunc();\n```\n\nDans cet exemple, nous pouvons observer la différence particulière entre une variable \"non existante\" et une variable \"non initialisée\".\n\nComme vous l'avez peut-être lu dans l'article [](info:closure), une variable démarre à l'état \"non initialisée\" à partir du moment où l'exécution entre dans un bloc de code (ou une fonction). Et elle reste non initialisée jusqu'à la déclaration `let` correspondante.\n\nEn d'autres termes, une variable existe techniquement, mais ne peut pas être utilisée avant `let`.\n\nLe code ci-dessus le démontre.\n\n```js\nfunction func() {\n*!*\n  // la variable locale x est connue du moteur depuis le début de la fonction,\n  // mais \"non initialisée\" (inutilisable) jusqu'à let (\"zone morte\")\n  // d'où l'erreur\n*/!*\n\n  console.log(x); // ReferenceError: Cannot access 'x' before initialization\n\n  let x = 2;\n}\n```\n\nCette zone d'inutilisabilité temporaire d'une variable (du début du bloc de code jusqu'à `let`) est parfois appelée \"dead zone\" (zone morte).\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/7-let-scope/task.md",
    "content": "importance: 4\n\n---\n\n# La variable est-elle visible ?\n\nQuel sera le résultat de ce code ?\n\n```js\nlet x = 1;\n\nfunction func() {\n  console.log(x); // ?\n\n  let x = 2;\n}\n\nfunc();\n```\n\nP.S. Il y a un piège dans cette tâche. La solution n'est pas évidente.\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/solution.js",
    "content": "\nfunction inArray(arr) {\n  return x => arr.includes(x);\n}\n\nfunction inBetween(a, b) {\n  return x => (x >= a && x <= b);\n}"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/source.js",
    "content": "\nlet arr = [1, 2, 3, 4, 5, 6, 7];\n\nfunction inBetween(a, b) {\n  // ...your code...\n}\n\nfunction inArray(arr) {\n  // ...your code...\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/test.js",
    "content": "\ndescribe(\"inArray\", function() {\n  let arr = [1, 2, 3, 4, 5, 6, 7];\n\n  it(\"returns the filter for values in array\", function() {\n\n    let filter = inArray(arr);\n    assert.isTrue(filter(5));\n    assert.isFalse(filter(0));\n  });\n});\n\n\ndescribe(\"inBetween\", function() {\n\n  it(\"returns the filter for values between\", function() {\n    let filter = inBetween(3, 6);\n    assert.isTrue(filter(5));\n    assert.isFalse(filter(0));\n  });\n});\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/8-filter-through-function/solution.md",
    "content": "\n# Filter inBetween\n\n```js run\nfunction inBetween(a, b) {\n  return function(x) {\n    return x >= a && x <= b;\n  };\n}\n\nlet arr = [1, 2, 3, 4, 5, 6, 7];\nalert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6\n```\n\n# Filter inArray\n\n```js run demo\nfunction inArray(arr) {\n  return function(x) {\n    return arr.includes(x);\n  };\n}\n\nlet arr = [1, 2, 3, 4, 5, 6, 7];\nalert( arr.filter(inArray([1, 2, 10])) ); // 1,2\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md",
    "content": "importance: 5\n\n---\n\n# Filtrer par fonction\n\nNous avons une méthode intégrée `arr.filter(f)` pour les tableaux. Elle filtre tous les éléments à travers la fonction `f`. S'elle renvoie `true`, cet élément est renvoyé dans le tableau résultant.\n\nCréez un ensemble de filtres \"prêts à l'emploi\":\n\n- `inBetween(a, b)` -- entre `a` et `b` ou égal à eux (inclusivement).\n- `inArray([...])` -- dans le tableau donné.\n\nL'usage doit être comme ceci :\n\n- `arr.filter(inBetween(3,6))` -- sélectionne uniquement les valeurs entre 3 et 6.\n- `arr.filter(inArray([1,2,3]))` -- sélectionne uniquement les éléments correspondant à l'un des membres de `[1,2,3]`.\n\nPar exemple :\n\n```js\n/* .. votre code pour inBetween et inArray */\nlet arr = [1, 2, 3, 4, 5, 6, 7];\n\nalert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6\n\nalert( arr.filter(inArray([1, 2, 10])) ); // 1,2\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/solution.js",
    "content": "function byField(fieldName){\n  return (a, b) => a[fieldName] > b[fieldName] ? 1 : -1;\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/source.js",
    "content": "function byField(fieldName){\n\n  // Your code goes here.\n\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/test.js",
    "content": "describe(\"byField\", function(){\n\n  let users = [\n    { name: \"John\", age: 20, surname: \"Johnson\" },\n    { name: \"Pete\", age: 18, surname: \"Peterson\" },\n    { name: \"Ann\", age: 19, surname: \"Hathaway\" },\n  ];\n\n  it(\"sorts users by name\", function(){\n    let nameSortedKey = [\n      { name: \"Ann\", age: 19, surname: \"Hathaway\" },\n      { name: \"John\", age: 20, surname: \"Johnson\"},\n      { name: \"Pete\", age: 18, surname: \"Peterson\" },\n    ];\n    let nameSortedAnswer = users.sort(byField(\"name\"));\n    assert.deepEqual(nameSortedKey, nameSortedAnswer);\n  });\n\n  it(\"sorts users by age\", function(){\n    let ageSortedKey = [\n      { name: \"Pete\", age: 18, surname: \"Peterson\" },\n      { name: \"Ann\", age: 19, surname: \"Hathaway\" },\n      { name: \"John\", age: 20, surname: \"Johnson\"},\n    ];\n    let ageSortedAnswer = users.sort(byField(\"age\"));\n    assert.deepEqual(ageSortedKey, ageSortedAnswer);\n  });\n\n  it(\"sorts users by surname\", function(){\n    let surnameSortedKey = [\n      { name: \"Ann\", age: 19, surname: \"Hathaway\" },\n      { name: \"John\", age: 20, surname: \"Johnson\"},\n      { name: \"Pete\", age: 18, surname: \"Peterson\" },\n    ];\n    let surnameSortedAnswer = users.sort(byField(\"surname\"));\n    assert.deepEqual(surnameSortedAnswer, surnameSortedKey);\n  });\n\n});\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/9-sort-by-field/solution.md",
    "content": "\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md",
    "content": "importance: 5\n\n---\n\n# Trier par champ\n\nNous avons un tableau d'objets à trier :\n\n```js\nlet users = [\n  { name: \"John\", age: 20, surname: \"Johnson\" },\n  { name: \"Pete\", age: 18, surname: \"Peterson\" },\n  { name: \"Ann\", age: 19, surname: \"Hathaway\" }\n];\n```\n\nLa manière habituelle de le faire serait :\n\n```js\n// par nom (Ann, John, Pete)\nusers.sort((a, b) => a.name > b.name ? 1 : -1);\n\n// par age (Pete, Ann, John)\nusers.sort((a, b) => a.age > b.age ? 1 : -1);\n```\n\nPeut-on le rendre encore moins verbeux, comme ceci ?\n\n```js\nusers.sort(byField('name'));\nusers.sort(byField('age'));\n```\n\nDonc, au lieu d’écrire une fonction, il suffit de mettre `byField(fieldName)`.\n\nEcrivez la fonction `byField` qui peut être utilisée pour cela.\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/article.md",
    "content": "\n# Variable scope, closure\n\nJavaScript est un langage orienté vers le fonctionnel. Cela nous donne beaucoup de liberté. Une fonction peut être créée dynamiquement, passée en argument à une autre fonction et appelée ultérieurement à partir d'un code totalement différent.\n\nNous savons déjà qu'une fonction peut accéder à des variables en dehors de celle-ci (variables externes).\n\nMais que se passe-t-il si les variables externes changent depuis la création d'une fonction ? La fonction obtiendra-t-elle des valeurs plus récentes ou les anciennes ?\n\nEt si une fonction est transmise en tant que paramètre et appelée depuis un autre endroit du code, aura-t-elle accès aux variables externes au nouvel endroit ?\n\nDéveloppons maintenant nos connaissances pour inclure des scénarios plus complexes.\n\n```smart header=\"Nous parlerons ici des variables `let`/`const`\"\nEn JavaScript, il y a 3 façons de déclarer une variable : `let`, `const` (les modernes) et `var` (le vestige du passé).\n\n- Dans cet article, nous utiliserons des variables `let` dans les exemples.\n- Les variables, déclarées avec `const`, se comportent de la même manière, donc cet article concerne également `const`.\n- L'ancien `var` a quelques différences notables, elles seront traitées dans l'article <info:var>.\n```\n\n## Code blocks\n\nSi une variable est déclarée à l'intérieur d'un bloc de code `{...}`, elle n'est visible qu'à l'intérieur de ce bloc.\n\nPar exemple :\n\n```js run\n{\n  // faire un travail avec des variables locales qui ne devraient pas être vues à l'extérieur\n\n  let message = \"Hello\"; // visible uniquement dans ce bloc\n\n  alert(message); // Hello\n}\n\nalert(message); // Error: message is not defined\n```\n\nNous pouvons l'utiliser pour isoler un morceau de code qui fait sa propre tâche, avec des variables qui lui appartiennent uniquement :\n\n```js run\n{\n  // show message\n  let message = \"Hello\";\n  alert(message);\n}\n\n{\n  // show another message\n  let message = \"Goodbye\";\n  alert(message);\n}\n```\n\n````smart header=\"Il y aurait une erreur sans blocs\"\nVeuillez noter que sans blocs séparés, il y aurait une erreur, si nous utilisons `let` avec le nom de variable existant :\n\n```js run\n// show message\nlet message = \"Hello\";\nalert(message);\n\n// show another message\n*!*\nlet message = \"Goodbye\"; // Error: variable already declared\n*/!*\nalert(message);\n```\n````\n\nPour `if`, `for`, `while` et ainsi de suite, les variables déclarées dans `{...}` ne sont également visibles qu'à l'intérieur :\n\n```js run\nif (true) {\n  let phrase = \"Hello!\";\n\n  alert(phrase); // Hello!\n}\n\nalert(phrase); // Error, no such variable!\n```\n\nIci, après la fin du `if`, l'`alert` ci-dessous ne verra pas `phrase`, d'où l'erreur.\n\nC'est super, car cela nous permet de créer des variables locales, spécifiques à une branche `if`.\n\nLa même chose vaut pour les boucles `for` et `while` :\n\n```js run\nfor (let i = 0; i < 3; i++) {\n  // la variable i n'est visible que dans ce for\n  alert(i); // 0, ensuite 1, ensuite 2\n}\n\nalert(i); // Error, no such variable\n```\n\nVisuellement, `let i` est à l'extérieur de `{...}`. Mais la construction de `for` est spéciale ici : la variable déclarée à l'intérieur est considérée comme faisant partie du bloc.\n\n## Fonctions imbriquées\n\nUne fonction est appelée \"imbriquée\" lorsqu'elle est créée dans une autre fonction.\n\nIl est possible de faire cela facilement avec JavaScript.\n\nNous pouvons l'utiliser pour organiser notre code, comme ceci :\n\n```js\nfunction sayHiBye(firstName, lastName) {\n\n  // helper nested function to use below\n  function getFullName() {\n    return firstName + \" \" + lastName;\n  }\n\n  alert( \"Hello, \" + getFullName() );\n  alert( \"Bye, \" + getFullName() );\n\n}\n```\n\nIci, la fonction *imbriquée* `getFullName()` est faite pour plus de commodité. Elle peut accéder aux variables externes et peut donc renvoyer le nom complet. Les fonctions imbriquées sont assez courantes dans JavaScript.\n\nCe qui est beaucoup plus intéressant, une fonction imbriquée peut être retournée : soit en tant que propriété d'un nouvel objet, soit en tant que résultat par elle-même. Elle peut ensuite être utilisée ailleurs. Peu importe où, elle a toujours accès aux mêmes variables externes.\n\nCi-dessous, `makeCounter` crée la fonction \"counter\" qui renvoie le nombre suivant à chaque appel :\n\n```js run\nfunction makeCounter() {\n  let count = 0;\n\n  return function() {\n    return count++;\n  };\n}\n\nlet counter = makeCounter();\n\nalert( counter() ); // 0\nalert( counter() ); // 1\nalert( counter() ); // 2\n```\n\nBien que simples, des variantes légèrement modifiées de ce code ont des utilisations pratiques, par exemple un [générateur de nombres aléatoires](https://fr.wikipedia.org/wiki/G%C3%A9n%C3%A9rateur_de_nombres_pseudo-al%C3%A9atoires) pour générer des valeurs aléatoires pour des tests automatisés.\n\nComment cela marche-t-il ? Si nous créons plusieurs compteurs, seront-ils indépendants ? Que se passe-t-il avec les variables ici ?\n\nLa compréhension de ce genre de choses est excellente pour la connaissance globale de JavaScript et bénéfique pour les scénarios plus complexes. Allons donc un peu en profondeur.\n\n## Lexical Environment\n\n```warn header=\"Voilà des dragons !\"\nL'explication technique approfondie reste à venir.\n\nBien que je souhaiterai éviter les détails de bas niveau du langage, toute compréhension sans eux serait manquante et incomplète, alors préparez-vous.\n```\n\nPour plus de clarté, l'explication est divisée en plusieurs étapes.\n\n### Étape 1. Variables\n\nEn JavaScript, chaque fonction en cours d'exécution, bloc de code `{...}` et le script dans son ensemble ont un objet associé interne (caché) connu sous le nom de *Environnement Lexical*.\n\nL'objet environnement lexical se compose de deux parties :\n\n1. *Environment Record* -- un objet qui stocke toutes les variables locales comme ses propriétés (et quelques autres informations comme la valeur de `this`).\n2. Une référence à *l'environnement lexical externe*, celui associé au code externe.\n\n**Une \"variable\" est juste une propriété de l'objet interne spécial `Environment Record`. \"Pour obtenir ou modifier une variable\" signifie \"pour obtenir ou modifier une propriété de cet objet\".**\n\nDans ce code simple sans fonctions, il n'y a qu'un seul environnement lexical :\n\n![lexical environment](lexical-environment-global.svg)\n\nIl s'agit de l'environnement Lexical dit *global*, associé à l'ensemble du script.\n\nSur l'image ci-dessus, le rectangle signifie Environment Record (stockage de variable) et la flèche signifie la référence externe. L'environnement lexical global n'a pas de référence externe, c'est pourquoi la flèche pointe vers `null`.\n\nÀ mesure que le code commence à s'exécuter et se poursuit, l'environnement lexical change.\n\nVoici un peu plus de code :\n\n![lexical environment](closure-variable-phrase.svg)\n\nLes rectangles sur le côté droit montrent comment l'environnement lexical global change pendant l'exécution :\n\n1. Lorsque le script démarre, l'environnement lexical est prérempli avec toutes les variables déclarées.\n    - Initialement, elles sont à l'état \"non initialisé\". C'est un état interne spécial, cela signifie que le moteur connaît la variable, mais elle ne peut pas être référencée tant qu'elle n'a pas été déclarée avec `let`. C'est presque la même chose que si la variable n'existait pas.\n2. Ensuite, la définition de `let phrase` apparaît. Il n'y a pas encore d'affectation, donc sa valeur est `undefined`. Nous pouvons utiliser la variable depuis ce moment.\n3. `phrase` se voit attribuer une valeur.\n4. `phrase` change de valeur.\n\nTout semble simple pour l'instant, non ?\n\n- Une variable est la propriété d'un objet interne spécial, associé au bloc/fonction/script en cours d'exécution.\n- Travailler avec des variables, c'est travailler avec les propriétés de cet objet.\n\n```smart header=\"L'environnement lexical est un objet de spécification\"\n\"L'environnement lexical\" est un objet de spécification : il n'existe que \"théoriquement\" dans la [spécification du langage](https://tc39.es/ecma262/#sec-lexical-environments) pour décrire comment les choses fonctionnent. nous ne pouvons pas obtenir cet objet dans notre code et le manipuler directement.\n\nLes moteurs JavaScript peuvent également l'optimiser, supprimer les variables inutilisées pour économiser de la mémoire et effectuer d'autres opérations internes, tant que le comportement visible reste conforme à la description.\n\n```\n\n### Step 2. Fonctions Declarations\n\nUne fonction est également une valeur, comme une variable.\n\n**La différence est qu'une fonction déclaration est instantanément et complètement initialisée.**\n\nLorsqu'un environnement lexical est créé, une fonction déclaration devient immédiatement une fonction prête à l'emploi (contrairement à `let`, qui est inutilisable jusqu'à la déclaration).\n\nC'est pourquoi nous pouvons utiliser une fonction, déclarée comme fonction déclaration, avant même la déclaration elle-même.\n\nPar exemple, voici l'état initial de l'environnement lexical global lorsque nous ajoutons une fonction :\n\n![](closure-function-declaration.svg)\n\nNaturellement, ce comportement ne s'applique qu'aux fonctions déclarations, pas aux fonctions expressions où nous attribuons une fonction à une variable, telle que `let say = function(name)...`.\n\n### Step 3. Environnement lexical intérieur et extérieur\n\nLorsqu'une fonction s'exécute, au début de l'appel, un nouvel environnement lexical est créé automatiquement pour stocker les variables locales et les paramètres de l'appel.\n\nPar exemple, pour `say(\"John\")`, cela ressemble à ceci (l'exécution est à la ligne, marquée d'une flèche) :\n\n<!--\n    ```js\n    let phrase = \"Hello\";\n\n    function say(name) {\n     alert( `${phrase}, ${name}` );\n    }\n\n    say(\"John\"); // Hello, John\n    ```-->\n\n![](lexical-environment-simple.svg)\n\nPendant l'appel de la fonction, nous avons deux environnements lexicaux : l'intérieur (pour l'appel de la fonction) et l'extérieur (global) :\n\n- L'environnement lexical interne correspond à l'exécution actuelle de `say`. Il a une seule propriété : `name`, l'argument de la fonction. Nous avons appelé `say(\"John\")`, donc la valeur de `name` est `\"John\"`.\n- L'environnement lexical externe est l'environnement lexical global. Il a la variable `phrase` et la fonction elle-même.\n\nL'environnement lexical intérieur a une référence à l'environnement `outer` (extérieur).\n\n**Lorsque le code veut accéder à une variable - l'environnement lexical interne est recherché en premier, puis celui externe, puis le plus externe et ainsi de suite jusqu'à celui global.**\n\nSi une variable n'est trouvée nulle part, c'est une erreur en mode strict (sans `use strict` une affectation à une variable non existante crée une nouvelle variable globale, pour la compatibilité avec l'ancien code).\n\nDans cet exemple, la recherche se déroule comme ceci :\n\n- Pour la variable `name`, l'`alert` à l'intérieur de `say` la trouve immédiatement dans l'environnement lexical interne.\n- Lorsqu'elle veut accéder à `phrase`, il n'y a pas de `phrase` localement, elle suit donc la référence à l'environnement lexical externe et la trouve là.\n\n![lexical environment lookup](lexical-environment-simple-lookup.svg)\n\n### Step 4. Retourner une fonction\n\nRevenons à l'exemple `makeCounter`.\n\n```js\nfunction makeCounter() {\n  let count = 0;\n\n  return function() {\n    return count++;\n  };\n}\n\nlet counter = makeCounter();\n```\n\nAu début de chaque appel `makeCounter()`, un nouvel objet environnement lexical est créé, pour stocker les variables pour cette exécution `makeCounter`.\n\nNous avons donc deux environnements lexicaux imbriqués, comme dans l'exemple ci-dessus :\n\n![](closure-makecounter.svg)\n\nCe qui est différent, c'est que, pendant l'exécution de `makeCounter()`, une minuscule fonction imbriquée est créée à partir d'une seule ligne : `return count++`. Nous ne l'exécutons pas encore, nous créons seulement.\n\nToutes les fonctions se souviennent de l'environnement lexical dans lequel elles ont été créées. Techniquement, il n'y a pas de magie ici : toutes les fonctions ont la propriété cachée nommée `[[Environment]]`, qui garde la référence à l'environnement lexical où la fonction a été créée :\n\n![](closure-makecounter-environment.svg)\n\nAinsi, `counter.[[Environment]]` a la référence à l'environnement lexical `{count: 0}`. C'est ainsi que la fonction se souvient de l'endroit où elle a été créée, quel que soit son nom. La référence `[[Environnement]]` est définie une fois pour toutes au moment de la création de la fonction.\n\nPlus tard, lorsque `counter()` est appelé, un nouvel environnement lexical est créé pour l'appel, et sa référence externe à l'environnement lexical est tirée de `counter.[[Environnement]]` :\n\n![](closure-makecounter-nested-call.svg)\n\nMaintenant, lorsque le code à l'intérieur de `counter()` recherche la variable `count`, il recherche d'abord son propre environnement lexical (vide, car il n'y a pas de variables locales), puis l'environnement lexical de l'appel externe `makeCounter()`, où il la trouve et la change.\n\n**Une variable est mise à jour dans l'environnement lexical où elle se trouve.**\n\nVoici l'état après l'exécution :\n\n![](closure-makecounter-nested-call-2.svg)\n\nSi nous appelons `counter()` plusieurs fois, la variable `count` sera augmentée à `2`, `3` et ainsi de suite, au même endroit.\n\n```smart header=\"Closure\"\nIl existe un terme général de programmation \"closure\", que les développeurs devraient généralement connaître.\n\nUne [closure](https://fr.wikipedia.org/wiki/Fermeture_(informatique)) est une fonction qui se souvient de ses variables externes et peut y accéder. Dans certains langages, ce n'est pas possible, ou une fonction doit être écrite d'une manière spécifique pour y arriver. Mais comme expliqué ci-dessus, en JavaScript, toutes les fonctions sont naturellement des fermetures (il n'y a qu'une seule exception, à couvrir dans <info:new-function>).\n\nC'est-à-dire : elles se souviennent automatiquement de l'endroit où elles ont été créées en utilisant une propriété cachée `[[Environnement]]`, puis leur code peut accéder aux variables externes.\n\nLors d'un entretien d'embauche, un développeur frontend reçoit assez souvent une question du genre \"qu'est-ce qu'une closure ?\". Une réponse valide serait une définition de la closure ainsi qu'une explication sur le fait que toutes les fonctions en JavaScript sont des closures, et peut-être quelques mots de plus sur les détails techniques : la propriété `[[Environment]]` et comment fonctionnent les environnements lexicaux.\n```\n\n## Garbage collection\n\nHabituellement, un environnement lexical est supprimé de la mémoire avec toutes les variables une fois l'appel de fonction terminé. C'est parce qu'il n'y a plus aucune référence à cela. Comme tout objet JavaScript, il n'est conservé en mémoire que lorsqu'il est accessible.\n\nCependant, s'il y a une fonction imbriquée qui est toujours accessible après la fin d'une fonction, alors elle a la propriété `[[Environment]]` qui fait référence à l'environnement lexical.\n\nDans ce cas, l'environnement lexical est toujours accessible même après la fin de la fonction, il reste donc en vie.\n\nPar exemple :\n\n```js\nfunction f() {\n  let value = 123;\n\n  return function() {\n    alert(value);\n  }\n}\n\nlet g = f(); // g.[[Environment]] stocke une référence à l'environnement lexical\n// de l'appel f() correspondant\n```\n\nVeuillez noter que si `f()` est appelé plusieurs fois et que les fonctions résultantes sont sauvegardées, tous les objets correspondants de l'environnement lexical seront également conservés en mémoire. Dans le code ci-dessous, ils sont tous les trois :\n\n```js\nfunction f() {\n  let value = math.random();\n\n  return function() { alert(value); };\n}\n\n// 3 fonctions dans un tableau, chacune d'entre elles étant liée à l'environnement lexical\n// à partir de l'exécution de f() correspondante\nlet arr = [f(), f(), f()];\n```\n\nUn objet environnement lexical meurt lorsqu'il devient inaccessible (comme tout autre objet). En d'autres termes, il n'existe que s'il existe au moins une fonction imbriquée qui le référence.\n\nDans le code ci-dessous, une fois que la fonction imbriquée est supprimée, son environnement lexical englobant (et donc la `value`) est nettoyé de la mémoire :\n\n```js\nfunction f() {\n  let value = 123;\n\n  return function() {\n    alert(value);\n  }\n}\n\nlet g = f(); // tant que la fonction g existe, la valeur reste en mémoire\n\ng = null; // … et maintenant la mémoire est nettoyée\n```\n\n### Optimisations réelles\n\nComme nous l'avons vu, en théorie, lorsqu'une fonction est vivante, toutes les variables externes sont également conservées.\n\nMais dans la pratique, les moteurs JavaScript tentent d'optimiser cela. Ils analysent l'utilisation des variables et s'il est évident d'après le code qu'une variable externe n'est pas utilisée -- elle est supprimée.\n\n**Un effet secondaire important dans V8 (Chrome, Edge, Opera) est qu’une telle variable ne sera plus disponible lors du débogage.**\n\nEssayez d'exécuter l'exemple ci-dessous sous Chrome avec les outils de développement ouverts.\n\nQuand il se met en pause, dans la console, tapez `alert(value)`.\n\n```js run\nfunction f() {\n  let value = Math.random();\n\n  function g() {\n    debugger; // dans la console : tapez alert(value); No such variable!\n  }\n\n  return g;\n}\n\nlet g = f();\ng();\n```\n\nComme vous avez pu le constater, cette variable n'existe pas ! En théorie, elle devrait être accessible, mais le moteur l'a optimisée.\n\nCela peut conduire à des problèmes de débogage amusants (voire fastidieux). L'un d'eux -- nous pouvons voir une variable externe portant le même nom au lieu de celle attendue :\n\n```js run global\nlet value = \"Surprise!\";\n\nfunction f() {\n  let value = \"the closest value\";\n\n  function g() {\n    debugger; // dans la console : tapez alert(value); Surprise!\n  }\n\n  return g;\n}\n\nlet g = f();\ng();\n```\n\nCette fonctionnalité du V8 est bonne à savoir. Si vous déboguez avec Chrome/Edge/Opera, tôt ou tard vous la rencontrerez.\n\nCe n'est pas un bogue dans le débogueur, mais plutôt une caractéristique spéciale de V8. Peut-être que cela sera changé un jour. Vous pouvez toujours le vérifier en exécutant les exemples sur cette page.\n"
  },
  {
    "path": "1-js/06-advanced-functions/04-var/article.md",
    "content": "\n# L'ancien \"var\"\n\n```smart header=\"Cet article est pour comprendre les anciens scripts\"\nLes informations contenues dans cet article sont utiles pour comprendre les anciens scripts.\n\nCe n'est pas ainsi que nous écrivons du nouveau code.\n```\n\nDans le tout premier chapitre qui parle des [variables](info:variables), nous avons mentionné trois façons pour déclarer une variable :\n\n1. `let`\n2. `const`\n3. `var`\n\nLa déclaration `var` est similaire à `let`. La plupart du temps, nous pouvons remplacer `let` par `var` ou vice-versa et nous attendre à ce que les choses fonctionnent :\n\n```js run\nvar message = \"Hi\";\nalert(message); // Hi\n```\n\nMais en interne, `var` est une bête très différente, originaire de très vieux temps. Il n'est généralement pas utilisé dans les scripts modernes, mais se cache toujours dans les anciens.\n\nSi vous ne prévoyez pas de rencontrer de tels scripts, vous pouvez même sauter ce chapitre ou le reporter.\n\nD'un autre côté, il est important de comprendre les différences lors de la migration d'anciens scripts de `var` vers `let`, pour éviter des erreurs étranges.\n\n## \"var\" n'a pas de portée limitée aux blocs\n\nLes variables, déclarées avec `var`, ont une portée fonction ou globale. Ils sont visibles à travers des blocs.\n\nPar exemple :\n\n```js run\nif (true) {\n  var test = true; // utilise \"var\" au lieu \"let\"\n}\n\n*!*\nalert(test); // vrai, la variable existe après if\n*/!*\n```\n\nComme `var` ignore les blocs de code, nous avons une variable globale `test`.\n\nSi nous aurions utilisé `let test` au lieu de `var test`, la variable aurait seulement été visible à l'intérieur de `if` :\n\n```js run\nif (true) {\n  let test = true; // utilise \"let\"\n}\n\n*!*\nalert(test); // ReferenceError: test is not defined\n*/!*\n```\n\nMême principe pour les boucles : `var` ne peut pas être locale pour les blocs ni les boucles :\n\n```js run\nfor (var i = 0; i < 10; i++) {\n  var one = 1;\n  // ...\n}\n\n*!*\nalert(i); // 10, \"i\" est visible après la boucle, c'est une variable globale\nalert(one); // 1, \"one\" est visible après la boucle, c'est une variable globale\n*/!*\n```\n\nSi un bloc de code est à l'intérieur d'une fonction, `var` devient une variable à l'échelle de la fonction :\n\n```js run\nfunction sayHi() {\n  if (true) {\n    var phrase = \"Hello\";\n  }\n\n  alert(phrase); // fonctionne\n}\n\nsayHi();\nalert(phrase); // ReferenceError: phrase is not defined\n```\n\nComme nous pouvons le constater, `var` pénètre à travers `if`, `for` ou les autres blocs de code. C'est parce que, il y a longtemps, les blocs de JavaScript n'avaient pas d'environnements lexicaux, et `var` est un vestige de ce dernier.\n\n## \"var\" tolère les redéclarations\n\nSi nous déclarons la même variable avec `let` deux fois dans la même portée, c'est une erreur :\n\n```js run\nlet user;\nlet user; // SyntaxError: 'user' has already been declared\n```\n\nAvec `var`, nous pouvons redéclarer une variable autant de fois que nécessaire. Si nous utilisons `var` avec une variable déjà déclarée, elle est simplement ignorée :\n\n```js run\nvar user = \"Pete\";\n\nvar user = \"John\"; // ce \"var\" ne fait rien (déjà déclaré)\n// ...ça ne déclenche pas d'erreur\n\nalert(user); // John\n```\n\n## \"var\" les variables peuvent être déclarées sous leur utilisation\n\nLes déclarations `var` sont traitées quand la fonction commence (ou quand le script commence pour le cas global).\n\nEn d'autres mots, les variables `var` sont définies au début de la fonction, peu importe où la définition se retrouve (présumant que la définition n'est pas dans une fonction imbriquée).\n\nAlors ce code :\n\n```js run\nfunction sayHi() {\n  phrase = \"Hello\";\n\n  alert(phrase);\n\n*!*\n  var phrase;\n*/!*\n}\nsayHi();\n```\n\n...est techniquement identique à ceci (nous avons simplement bougé `var phrase` du code juste avant) :\n\n```js run\nfunction sayHi() {\n*!*\n  var phrase;\n*/!*\n\n  phrase = \"Hello\";\n\n  alert(phrase);\n}\nsayHi();\n```\n\n...ou même ceci (souvenez-vous, les blocs de code sont ignorés) :\n\n```js run\nfunction sayHi() {\n  phrase = \"Hello\"; // (*)\n\n  *!*\n  if (false) {\n    var phrase;\n  }\n  */!*\n\n  alert(phrase);\n}\nsayHi();\n```\n\nCertains nomment ce comportement \"hoisting\" (hisser) parce que toutes les `var` sont \"hoisted\" (hissées) jusqu'en haut de la fonction.\n\nAinsi, dans l'exemple ci-dessus, la branche `if (false)` ne s'exécute jamais, mais cela n'a pas d'importance. La `var` qu'elle contient est traitée au début de la fonction, donc au moment de `(*)` la variable existe.\n\n**Les déclarations sont hissées, mais les affectations ne le sont pas.**\n\nCela est mieux démontré avec un exemple :\n\n```js run\nfunction sayHi() {\n  alert(phrase);\n\n*!*\n  var phrase = \"Hello\";\n*/!*\n}\n\nsayHi();\n```\n\nLa ligne `var phrase = \"Hello\"` contient deux actions :\n\n1. Déclaration de la variable `var`\n2. Affectation de la variable `=`.\n\nLa déclaration est traitée au début de l'exécution de la fonction (\"hoisted\"), mais l'affectation fonctionne toujours à l'endroit où elle apparaît. Essentiellement, le code fonctionne comme ceci :\n\n```js run\nfunction sayHi() {\n*!*\n  var phrase; // déclaration fonctionne au début...\n*/!*\n\n  alert(phrase); // undefined\n\n*!*\n  phrase = \"Hello\"; // ...affectation - quand l'exécution y parvient.\n*/!*\n}\n\nsayHi();\n```\n\nParce que toutes les déclarations `var` sont traitées au début de la fonction, nous pouvons y faire référence n'importe où. Mais les variables sont indéfinies jusqu'aux affectations.\n\nDans les deux exemples au dessus, `alert` fonctionne sans erreur parce que la variable `phrase` existe. Mais sa valeur n'est pas encore affectée, alors cela donne `undefined`.\n\n## IIFE\n\nComme par le passé, il n'y avait que `var`, et qu'il n'a pas de visibilité au niveau du bloc, les programmeurs ont inventé un moyen de l'imiter. Ce qu'ils ont fait a été appelé \"expressions de fonction immédiatement invoquées\" (en abrégé IIFE).\n\nCe n'est pas quelque chose que nous devrions utiliser de nos jours, mais vous pouvez les trouver dans d'anciens scripts.\n\nUn IIFE ressemble à ceci :\n\n```js run\n(function() {\n\n  var message = \"Hello\";\n\n  alert(message); // Hello\n\n})();\n```\n\nIci, une fonction expression est créée et immédiatement appelée. Ainsi, le code s'exécute immédiatement et possède ses propres variables privées.\n\nLa fonction expression est entourée de parenthèses `(fonction {...})`, car lorsque JavaScript rencontre `\"function\"` dans le flux de code principal, il le comprend comme le début d'une fonction déclaration. Mais une fonction déclaration doit avoir un nom, donc ce type de code donnera une erreur :\n\n```js run\n// Essayons de déclarer et d'appeler immédiatement une fonction\nfunction() { // <-- SyntaxError: Les instructions de fonction nécessitent un nom de fonction\n\n  var message = \"Hello\";\n\n  alert(message); // Hello\n\n}();\n```\n\nMême si nous disons : \"d'accord, ajoutons un nom\", cela ne fonctionnera toujours pas, parce que JavaScript ne permet pas d'appeler immédiatement les fonctions déclarations :\n\n```js run\n// erreur de syntaxe à cause des parenthèses ci-dessous\nfunction go() {\n\n}(); // <-- ne peut pas appeler la fonction déclaration immédiatement\n```\n\nAinsi, les parenthèses autour de la fonction sont une astuce pour montrer à JavaScript que la fonction est créée dans le contexte d'une autre expression, et donc c'est une fonction expression : elle n'a pas besoin de nom et peut être appelée immédiatement.\n\nIl existe d'autres façons que les parenthèses pour dire à JavaScript que nous souhaitons une fonction expression :\n\n```js run\n// Façons de créer une IIFE\n\n*!*(*/!*function() {\n  alert(\"Parentheses around the function\");\n}*!*)*/!*();\n\n*!*(*/!*function() {\n  alert(\"Parentheses around the whole thing\");\n}()*!*)*/!*;\n\n*!*!*/!*function() {\n  alert(\"Bitwise NOT operator starts the expression\");\n}();\n\n*!*+*/!*function() {\n  alert(\"Unary plus starts the expression\");\n}();\n```\n\nDans tous les cas ci-dessus, nous déclarons une fonction expression et l'exécutons immédiatement. Notons encore : de nos jours il n'y a aucune raison d'écrire un tel code.\n\n## Résumé\n\nIl y a deux différences majeures entre `var` et `let`/`const`:\n\n1. Les variables `var` n'ont pas de portée de bloc, leur visibilité est étendue à la fonction actuelle, ou globale, si elle est déclarée hors fonction.\n2. Les déclarations `var` sont traitées au début de la fonction (ou au début du script pour le cas global).\n\nIl y a une autre différence mineure associée à l'objet global, mais nous traiterons ce point dans le prochain chapitre.\n\nCes différences rendent `var` pire que `let` dans la plupart des cas. Les variables au niveau des blocs sont extraordinaires. C'est pourquoi `let` a été introduit au standard il y a longtemps et c'est maintenant un moyen majeur (avec `const`) pour déclarer une variable.\n"
  },
  {
    "path": "1-js/06-advanced-functions/05-global-object/article.md",
    "content": "\n# L'objet global\n\nL'objet global fournit des variables et des fonctions qui sont disponibles partout. Par défaut, celles qui sont intégrées au langage ou à l'environnement.\n\nDans un navigateur, c'est appelé `window`, pour Node.js c'est `global`, et pour les autres environnements, il peut porter un autre nom.\n\nRécemment, `globalThis` a été ajouté au langage comme un nom standardisé pour l'objet global et devrait être supporté à travers tous les environnements. Il est pris en charge dans tous les principaux navigateurs.\n\nNous allons utiliser `window` ici, en supposant que notre environnement est un navigateur. Si votre script peut s'exécuter dans d'autres environnements, il est préférable d'utiliser `globalThis` à la place.\n\nToutes les propriétés de l'objet global sont directement accessibles :\n\n```js run\nalert(\"Hello\");\n// is the same as\nwindow.alert(\"Hello\");\n```\n\nDans un navigateur, les fonctions globales et les variables déclarées avec `var` (pas `let`/`const` !) deviennent la propriété de l'objet global :\n\n```js run untrusted refresh\nvar gVar = 5;\n\nalert(window.gVar); // 5 (var est devenue une propriété de l'objet global)\n```\n\nLes fonctions déclarations ont le même effet (instructions avec le mot clé `function` dans le flux de code principal, pas les fonctions expressions).\n\nNe comptez pas là-dessus ! Ce comportement existe pour des raisons de compatibilité. Les scripts modernes utilisent les [modules JavaScript](info:modules) où une telle chose ne se produit pas.\n\nSi nous utilisions `let` la place, une telle chose ne se produirait pas :\n\n```js run untrusted refresh\nlet gLet = 5;\n\nalert(window.gLet); // undefined (ne devient pas une propriété de l'objet global)\n```\n\nSi une valeur est si importante que vous voulez qu'elle soit disponible de façon global, écrivez la directement comme une propriété :\n\n```js run\n*!*\n// rendre l'information de l'utilisateur actuel globale pour permettre à tous les scripts de l'accéder.\nwindow.currentUser = {\n  name: \"John\"\n};\n*/!*\n\n// ailleurs dans le code\nalert(currentUser.name);  // John\n\n// ou, si nous avons une variable locale avec le nom \"currentUser\"\n// obtenez la de window explicitement (c'est sécuritaire !)\nalert(window.currentUser.name); // John\n```\n\nCela dit, l'utilisation de variables globales est généralement déconseillée. Il devrait y avoir le moins de variables globales que possible. La conception du code où une fonction reçoit des variables de saisies (input) et produit certains résultats est plus claire, moins susceptible aux erreurs et plus facile à tester que si elle utilise des variables externes ou globales.\n\n## Utilisation avec les polyfills\n\nNous utilisons l'objet global pour tester le support des fonctionnalités du langage moderne.\n\nPar exemple, nous pouvons tester si l'objet natif `Promise` existe (il n'existe pas dans les navigateurs très anciens) :\n\n```js run\nif (!window.Promise) {\n  alert(\"Your browser is really old!\");\n}\n```\n\nS'il n'y en a pas (disons que nous sommes dans un navigateur ancien), nous pouvons créer des \"polyfills\". Les \"polyfills\" ajoutent des fonctions qui ne sont pas supportés par l'environnement, mais qui existent dans le standard moderne.\n\n```js run\nif (!window.Promise) {\n  window.Promise = ... // implémentation personnalisée de la fonctionnalité du langage moderne\n}\n```\n\n## Résumé\n\n- L'objet global contient des variables qui devraient être disponibles partout.\n\n    Ceci inclut les objets natifs de JavaScript, tels que `Array` et des valeurs spécifiques à l'environnement, comme `window.innerHeight` -- l'hauteur de la fenêtre dans le navigateur.\n- L'objet global porte un nom universel `globalThis`.\n\n    ...Mais il est plus souvent appelé par des noms spécifiques à l'environnement de la vieille école, comme `window` (navigateur) et `global` (Node.js).\n- Nous devons seulement stocker des valeurs dans l'objet global si elles sont réellement globales pour notre projet. Et gardez la quantité de ces valeurs à un minimum.\n- Dans les navigateurs, à moins que nous utilisons des [modules](info:modules), les fonctions et variables globales déclarées avec `var` deviennent une propriété de l'objet global.\n- Pour que notre code soit à l'épreuve du temps et plus facile à comprendre, nous devons accéder les propriétés de l'objet global directement, en utilisant `window.x`.\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/_js.view/solution.js",
    "content": "function makeCounter() {\n  let count = 0;\n\n  function counter() {\n    return count++;\n  }\n\n  counter.set = value => count = value;\n\n  counter.decrease = () => count--;\n\n  return counter;\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/_js.view/source.js",
    "content": "function makeCounter() {\n  let count = 0;\n\n  // ... votre code ...\n}\n\nlet counter = makeCounter();\n\nalert( counter() ); // 0\nalert( counter() ); // 1\n\ncounter.set(10); // définir le nouveau \"count\"\n\nalert( counter() ); // 10\n\ncounter.decrease(); // diminuer de 1 le \"count\"\n\nalert( counter() ); // 10 (au lieu de 11)\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/_js.view/test.js",
    "content": "describe(\"counter\", function() {\n\n  it(\"increases from call to call\", function() {\n\n    let counter = makeCounter();\n\n    assert.equal( counter(), 0 ); \n    assert.equal( counter(), 1 ); \n    assert.equal( counter(), 2 ); \n  });\n\n  \n  describe(\"counter.set\", function() {\n    it(\"sets the count\", function() {\n\n      let counter = makeCounter();\n\n      counter.set(10);\n\n      assert.equal( counter(), 10 ); \n      assert.equal( counter(), 11 ); \n    });\n  });\n  \n  describe(\"counter.decrease\", function() {\n    it(\"decreases the count\", function() {\n\n      let counter = makeCounter();\n\n      counter.set(10);\n\n      assert.equal( counter(), 10 ); \n\n      counter.decrease();\n\n      assert.equal( counter(), 10 ); \n\n    });\n  });\n\n});"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/solution.md",
    "content": "\nLa solution utilise `count` dans la variable locale, mais les méthodes d'addition sont écrites directement dans le `compteur`. Ils partagent le même environnement lexical extérieur et peuvent également accéder au `count` actuel.\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/task.md",
    "content": "importance: 5\n\n---\n\n# Un mutateur et diminution pour counter\n\nModifiez le code de `makeCounter()` afin que le compteur puisse également diminuer et définir le nombre:\n\n- `counter()` devrait retourner le nombre suivant (comme avant).\n- `counter.set(value)` devrait définir le compteur à `value`.\n- `counter.decrease()` devrait décrémenter le compteur de 1.\n\nVoir le code sandbox pour un exemple d'utilisation complet.\n\nP.S. Vous pouvez utiliser une fermeture ou la propriété de fonction pour maintenir le nombre actuel. Ou écrivez les deux variantes.\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/solution.js",
    "content": "function sum(a) {\n\n  let currentSum = a;\n\n  function f(b) {\n    currentSum += b;\n    return f;\n  }\n\n  f.toString = function() {\n    return currentSum;\n  };\n\n  return f;\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/source.js",
    "content": "function sum(a){\n  // Your code goes here.\n\n}\n\n/*\nsum(1)(2) == 3; // 1 + 2\nsum(1)(2)(3) == 6; // 1 + 2 + 3\nsum(5)(-1)(2) == 6\nsum(6)(-1)(-2)(-3) == 0\nsum(0)(1)(2)(3)(4)(5) == 15\n*/\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/test.js",
    "content": "describe(\"sum\", function(){\n  \n  it(\"sum(1)(2) == 3\", function(){\n    assert.equal(3, sum(1)(2));\n  });\n\n  it(\"sum(5)(-1)(2) == 6\", function(){\n    assert.equal(6, sum(5)(-1)(2));\n  });\n  \n  it(\"sum(6)(-1)(-2)(-3) == 0\", function(){\n    assert.equal(0, sum(6)(-1)(-2)(-3));\n  });\n\n  it(\"sum(0)(1)(2)(3)(4)(5) == 15\", function(){\n    assert.equal(15, sum(0)(1)(2)(3)(4)(5));\n  });\n});\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/solution.md",
    "content": "\n1. Pour que tout fonctionne * de toute façon *, le résultat de `sum` doit être fonction.\n2. Cette fonction doit garder en mémoire la valeur actuelle entre les appels.\n3. Selon la tâche, la fonction doit devenir le numéro lorsqu'elle est utilisée dans `==`. Les fonctions étant des objets, la conversion s'effectue comme décrit dans le chapitre <info:object-toprimitive>, et nous pouvons fournir notre propre méthode qui renvoie le nombre.\n\nMaintenant le code:\n\n```js demo run\nfunction sum(a) {\n\n  let currentSum = a;\n\n  function f(b) {\n    currentSum += b;\n    return f;\n  }\n\n  f.toString = function() {\n    return currentSum;\n  };\n\n  return f;\n}\n\nalert( sum(1)(2) ); // 3\nalert( sum(5)(-1)(2) ); // 6\nalert( sum(6)(-1)(-2)(-3) ); // 0\nalert( sum(0)(1)(2)(3)(4)(5) ); // 15\n```\n\nVeuillez noter que la fonction `sum` ne fonctionne réellement qu'une fois. Il renvoie la fonction `f`.\n\nEnsuite, à chaque appel suivant, `f` ajoute son paramètre à la somme `currentSum`, et se renvoie lui-même.\n\n**Il n'y a pas de récursion dans la dernière ligne de `f`.**\n\nVoici à quoi ressemble la récursion:\n\n```js\nfunction f(b) {\n  currentSum += b;\n  return f(); // <-- appel récursif\n}\n```\n\nEt dans notre cas, nous renvoyons simplement la fonction, sans l'appeler:\n\n```js\nfunction f(b) {\n  currentSum += b;\n  return f; // <-- ne s'appelle pas, se renvoie\n}\n```\n\nCe `f` sera utilisé lors du prochain appel et se renvera lui-même autant de fois que nécessaire. Ensuite, lorsqu'il est utilisé sous forme de nombre ou de chaîne de caractères, le `toString` renvoie le `currentSum`. Nous pourrions aussi utiliser `Symbol.toPrimitive` ou `valueOf` ici pour la conversion.\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/task.md",
    "content": "importance: 2\n\n---\n\n# La somme avec une quantité arbitraire de parenthèses\n\nÉcrivez la fonction `sum` qui fonctionnerait comme ceci:\n\n```js\nsum(1)(2) == 3; // 1 + 2\nsum(1)(2)(3) == 6; // 1 + 2 + 3\nsum(5)(-1)(2) == 6\nsum(6)(-1)(-2)(-3) == 0\nsum(0)(1)(2)(3)(4)(5) == 15\n```\n\nP.S. Indice: vous devrez peut-être configurer une conversion d'objet à primitive personnalisé pour votre fonction."
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/article.md",
    "content": "\n# L'objet Function, NFE\n\nComme nous le savons déjà, une fonction en JavaScript est une valeur.\n\nChaque valeur en JavaScript a un type. De quel type est une fonction ?\n\nPour JavaScript, les fonctions sont des objets.\n\nUn bon moyen d’imaginer des fonctions est en tant que des \"objets d’action\" qu'on peut appeler. Nous pouvons non seulement les appeler, mais aussi les traiter comme des objets : ajouter/supprimer des propriétés, passer par référence, etc.\n\n## La propriété \"name\"\n\nLes objets Fonction contiennent des propriétés utilisables.\n\nPar exemple, le nom d'une fonction est accessible en tant que propriété \"name\" :\n\n```js run\nfunction sayHi() {\n  alert(\"Hi\");\n}\n\nalert(sayHi.name); // sayHi\n```\n\nCe qui est drôle, c'est que la logique d'attribution de noms est intelligente. Elle attribue également le nom correct à une fonction même si elle est créée sans, puis immédiatement attribué :\n\n```js run\nlet sayHi = function() {\n  alert(\"Hi\");\n};\n\nalert(sayHi.name); // sayHi (il y a un nom !)\n```\n\nCela fonctionne aussi si l’affectation est faite avec une valeur par défaut :\n\n```js run\nfunction f(sayHi = function() {}) {\n  alert(sayHi.name); // sayHi (ça marche !)\n}\n\nf();\n```\n\nDans la spécification, cette fonctionnalité est appelée \"contextual name\". Si la fonction n'en fournit pas, elle est déterminée à partir du contexte lors de l'affectation.\n\nLes méthodes d'objet ont aussi des noms :\n\n```js run\nlet user = {\n\n  sayHi() {\n    // ...\n  },\n\n  sayBye: function() {\n    // ...\n  }\n\n}\n\nalert(user.sayHi.name); // sayHi\nalert(user.sayBye.name); // sayBye\n```\n\nCependant c'est pas magique. Il y a des cas où il n'y a aucun moyen de trouver le bon nom. Dans ce cas, la propriété name est vide, comme ci-dessous :\n\n```js\n// fonction créée dans un tableau\nlet arr = [function() {}];\n\nalert( arr[0].name ); // <chaîne de caractères vide>\n// le moteur n'a aucun moyen de définir le bon nom. Donc, il n'y en a pas\n```\n\nPar contre, en pratique la plupart des fonctions ont un nom.\n\n## La propriété \"length\"\n\nIl existe une autre propriété native, \"length\", qui renvoie le nombre de paramètres de la fonction, par exemple :\n\n```js run\nfunction f1(a) {}\nfunction f2(a, b) {}\nfunction many(a, b, ...more) {}\n\nalert(f1.length); // 1\nalert(f2.length); // 2\nalert(many.length); // 2\n```\n\nNous pouvons voir que les paramètres du reste ne sont pas comptés.\n\nLa propriété `length` est parfois utilisée pour la [réfléxion (introspection en anglais)](https://fr.wikipedia.org/wiki/R%C3%A9flexion_(informatique)) dans des fonctions qui opèrent sur d'autres fonctions.\n\nPar exemple, dans le code ci-dessous, la fonction `ask` accepte une `question` à poser et un nombre arbitraire de fonctions `handler` (gestionnaires) à appeler.\n\nUne fois qu'un utilisateur a fourni sa réponse, la fonction appelle les gestionnaires. Nous pouvons transmettre deux types de gestionnaires :\n\n- Une fonction sans argument, qui n'est appelée que lorsque l'utilisateur donne une réponse positive.\n- Une fonction avec des arguments, appelée dans les deux cas et renvoyant une réponse.\n\nPour appeler `handler` correctement, nous examinons la propriété `handler.length`.\n\nL'idée est que nous avons une syntaxe de gestionnaire simple, sans argument, pour les cas positifs (variante la plus fréquente), mais que nous pouvons également prendre en charge les gestionnaires universels :\n\n```js run\nfunction ask(question, ...handlers) {\n  let isYes = confirm(question);\n\n  for(let handler of handlers) {\n    if (handler.length == 0) {\n      if (isYes) handler();\n    } else {\n      handler(isYes);\n    }\n  }\n\n}\n\n// pour une réponse positive, les deux gestionnaires sont appelés\n// pour une réponse négative, seulement le second\nask(\"Question?\", () => alert('You said yes'), result => alert(result));\n```\n\nCeci est un cas particulier de ce qu'on appelle le [polymorphism](https://en.wikipedia.org/wiki/Polymorphism_(computer_science)) -- le traitement des arguments différemment selon leur type ou, dans notre cas, en fonction de la `length`. Cette approche est utilisée dans les bibliothèques JavaScript.\n\n## Propriétés personnalisées\n\nNous pouvons également ajouter nos propres propriétés.\n\nNous ajoutons ici la propriété `counter` pour suivre le nombre total d'appels :\n\n```js run\nfunction sayHi() {\n  alert(\"Hi\");\n\n  *!*\n  // comptons combien de fois nous executons\n  sayHi.counter++;\n  */!*\n}\nsayHi.counter = 0; // valeur initiale\n\nsayHi(); // Hi\nsayHi(); // Hi\n\nalert( `Called ${sayHi.counter} times` ); // Appelée 2 fois\n```\n\n```warn header=\"Une propriété n'est pas une variable\"\nUne propriété affectée à une fonction comme `sayHi.counter = 0` *ne définit pas* une variable locale `counter` à l'intérieur de celle-ci. En d'autres termes, une propriété `counter` et une variable `let counter` sont deux choses indépendantes.\n\nOn peut traiter une fonction comme un objet, y stocker des propriétés, mais cela n’a aucun effet sur son exécution. Les variables ne sont pas des propriétés de fonction et inversement. Ce sont des mondes parallèles.\n```\n\nLes propriétés de fonction peuvent parfois remplacer les fermetures. Par exemple, nous pouvons réécrire l’exemple de fonction de compteur du chapitre <info:closure> pour utiliser une propriété de fonction :\n\n```js run\nfunction makeCounter() {\n  // au lieu de :\n  // let count = 0\n\n  function counter() {\n    return counter.count++;\n  };\n\n  counter.count = 0;\n\n  return counter;\n}\n\nlet counter = makeCounter();\nalert( counter() ); // 0\nalert( counter() ); // 1\n```\n\nLe `count` est maintenant stocké dans la fonction directement, pas dans son environnement lexical externe.\n\nEst-ce meilleur ou pire que d'utiliser une fermeture ?\n\nLa principale différence est que si la valeur de `count` réside dans une variable externe, le code externe ne peut pas y accéder. Seules les fonctions imbriquées peuvent le modifier. Et si c'est lié à une fonction, une telle chose est possible :\n\n```js run\nfunction makeCounter() {\n\n  function counter() {\n    return counter.count++;\n  };\n\n  counter.count = 0;\n\n  return counter;\n}\n\nlet counter = makeCounter();\n\n*!*\ncounter.count = 10;\nalert( counter() ); // 10\n*/!*\n```\n\nLe choix dépend donc de nos objectifs.\n\n## Fonction Expression Nommée (NFE)\n\nFonction Expression Nommée, ou NFE (\"Named Function Expression\" en anglais), est un terme pour les fonctions expressions qui ont un nom.\n\nPar exemple, prenons une fonction expression ordinaire :\n\n```js\nlet sayHi = function(who) {\n  alert(`Hello, ${who}`);\n};\n```\n\nEt ajoutons un nom à cela :\n\n```js\nlet sayHi = function *!*func*/!*(who) {\n  alert(`Hello, ${who}`);\n};\n```\n\nAvons-nous réalisé quelque chose ici ? Quel est le but de ce nom supplémentaire `\"func\"` ?\n\nNotons d'abord que nous avons toujours une expression de fonction. L'ajout du nom `\"func\"` après `function` n'en a pas fait une déclaration de fonction, car il est toujours créé dans le cadre d'une expression d'affectation.\n\nL'ajout d'un tel nom n'a également rien cassé.\n\nLa fonction est toujours disponible sous la forme `sayHi()` :\n\n```js run\nlet sayHi = function *!*func*/!*(who) {\n  alert(`Hello, ${who}`);\n};\n\nsayHi(\"John\"); // Hello, John\n```\n\nIl y a deux particularités à propos du nom `func`, voici les raisons :\n\n1. Il permet à la fonction de se référencer en interne.\n2. Il n'est pas visible en dehors de la fonction.\n\nPar exemple, la fonction `sayHi` ci-dessous s’appelle à nouveau avec `\"Guest\"` si aucun `who` est fourni :\n\n```js run\nlet sayHi = function *!*func*/!*(who) {\n  if (who) {\n    alert(`Hello, ${who}`);\n  } else {\n*!*\n    func(\"Guest\"); // utilise func pour se rappeler\n*/!*\n  }\n};\n\nsayHi(); // Hello, Guest\n\n// Mais ceci ne marchera pas :\nfunc(); // Error, func is not defined (pas visible à l'extérieur de la fonction)\n```\n\nPourquoi utilisons-nous `func` ? Peut-être juste utiliser `sayHi` pour l'appel imbriqué ?\n\nEn fait, dans la plupart des cas, nous pouvons :\n\n```js\nlet sayHi = function(who) {\n  if (who) {\n    alert(`Hello, ${who}`);\n  } else {\n*!*\n    sayHi(\"Guest\");\n*/!*\n  }\n};\n```\n\nLe problème avec ce code est que `sayHi` peut changer dans le code externe. Si la fonction est assignée à une autre variable, le code commencera à donner des erreurs :\n\n```js run\nlet sayHi = function(who) {\n  if (who) {\n    alert(`Hello, ${who}`);\n  } else {\n*!*\n    sayHi(\"Guest\"); // Error: sayHi is not a function\n*/!*\n  }\n};\n\nlet welcome = sayHi;\nsayHi = null;\n\nwelcome(); // Error, l'appel sayHi imbriqué ne fonctionne plus !\n```\n\nCela se produit parce que la fonction tire `sayHi` de son environnement lexical externe. Il n'y a pas de `sayHi` local, donc la variable externe est utilisée. Et au moment de l'appel, ce `sayHi` extérieur est `null`.\n\nLe nom optionnel que nous pouvons mettre dans l’expression de fonction est destiné à résoudre exactement ce type de problèmes.\n\nUtilisons-le pour corriger notre code :\n\n```js run\nlet sayHi = function *!*func*/!*(who) {\n  if (who) {\n    alert(`Hello, ${who}`);\n  } else {\n*!*\n    func(\"Guest\"); // Maintenant tout va bien\n*/!*\n  }\n};\n\nlet welcome = sayHi;\nsayHi = null;\n\nwelcome(); // Hello, Guest (l'appel imbriqué fonctionne)\n```\n\nMaintenant cela fonctionne, car le nom `'func'` est local à la fonction. Il n'est pas pris de l'extérieur (et non visible là-bas). La spécification garantit qu'elle fera toujours référence à la fonction actuelle.\n\nLe code externe a toujours sa variable `sayHi` ou `welcome`. Et `func` est un \"nom de fonction interne\", la façon dont la fonction peut s'appeler de manière fiable.\n\n```smart header=\"Il n'y a rien de tel pour la déclaration de fonction\"\nLa fonctionnalité \"nom interne\" décrite ici n'est disponible que pour les expressions de fonction, pas pour les déclarations de fonction. Pour les déclarations de fonctions, il n’y a aucune possibilité de syntaxe d’ajouter un nom \"interne\" supplémentaire.\n\nParfois, lorsque nous avons besoin d’un nom interne fiable, c’est la raison pour laquelle nous réécrivons une déclaration de fonction en tant qu'expression de fonction nommée.\n```\n\n## Résumé\n\nLes fonctions sont des objets.\n\nIci nous avons couvert leurs propriétés :\n\n- `name` - le nom de la fonction. Habituellement tiré de la définition de la fonction, mais s’il n’en existe pas, JavaScript essaie de le deviner à partir du contexte (par exemple, une affectation).\n- `length` - le nombre d'arguments dans la définition de la fonction. Les paramètres du reste ne sont pas comptés.\n\nSi la fonction est déclarée en tant qu'expression de fonction (et non dans le flux du code principal) et qu'elle porte un nom, elle est appelée expression de fonction nommée. Le nom peut être utilisé à l'intérieur pour se référencer, pour des appels récursifs ou autres.\n\nLes fonctions peuvent également comporter des propriétés supplémentaires. De nombreuses bibliothèques JavaScript bien connues font un grand usage de cette fonctionnalité.\n\nElles créent une fonction \"principale\" et y attachent de nombreuses autres fonctions \"d'assistance\". Par exemple, la bibliothèque [jQuery](https://jquery.com) crée une fonction nommée `$`. La bibliothèque [lodash](https://lodash.com) crée une fonction `_` et ajoute ensuite `_.clone`, `_.keyBy` et d'autres propriétés (voir la [doc](https://lodash.com/docs) lorsque vous souhaitez en savoir plus à leur sujet). En fait, elles le font pour réduire leur pollution de l'espace global, de sorte qu'une seule bibliothèque ne donne qu'une seule variable globale. Cela réduit la possibilité de conflits de noms.\n\nAinsi, une fonction peut faire un travail utile par elle-même et aussi porter un tas d’autres fonctionnalités dans les propriétés.\n"
  },
  {
    "path": "1-js/06-advanced-functions/07-new-function/article.md",
    "content": "\n# La syntaxe \"new Function\"\n\nIl existe encore un autre moyen de créer une fonction. C'est rarement utilisé, mais parfois il n'y a pas d'alternative.\n\n## Syntaxe\n\nLa syntaxe pour créer une fonction :\n\n```js\nlet func = new Function ([arg1, arg2, ...argN], functionBody);\n```\n\nLa fonction est créée avec les arguments `arg1...argN` et le `functionBody` donné.\n\nC'est plus facile à comprendre en regardant un exemple. Voici une fonction avec deux arguments :\n\n```js run\nlet sum = new Function('a', 'b', 'return a + b');\n\nalert( sum(1, 2) ); // 3\n```\n\nEt voici une fonction sans arguments, seulement le corps de la fonction :\n\n```js run\nlet sayHi = new Function('alert(\"Hello\")');\n\nsayHi(); // Hello\n```\n\nLa différence majeure par rapport aux autres méthodes que nous avons déjà vu est que la fonction est créée littéralement à partir d'une chaîne de caractères passée au moment de l'exécution.\n\nToutes les déclarations précédentes nous demandait, nous les programmeurs, d'écrire le code de la fonction dans le script.\n\nMais `new Function` nous permet de convertir n'importe qu'elle chaine de caractères en fonction. Par exemple, nous pouvons recevoir une nouvelle fonction d’un serveur puis l’exécuter :\n\n```js\nlet str = ... receive the code from a server dynamically ...\n\nlet func = new Function(str);\nfunc();\n```\n\nC'est utilisé dans des cas très spécifiques, comme lorsque nous recevons du code d'un serveur ou pour compiler dynamiquement une fonction à partir d'un modèle dans des applications web complexes.\n\n## Fermeture\n\nNormalement, une fonction se souvient du lieu de sa naissance dans la propriété spéciale `[[Environment]]`. Elle fait référence à l'environnement lexical à partir duquel la fonction a été créée (nous avons couvert cela dans le chapitre <info:closure>).\n\nMais quand une fonction est créée en utilisant `new Function`, son `[[Environment]]` est configuré pour faire référence non pas à l'environnement lexical actuel, mais à l'environnement global.\n\nDonc, une telle fonction n'a pas accès aux variables externes, mais uniquement aux variables globales.\n\n```js run\nfunction getFunc() {\n  let value = \"test\";\n\n*!*\n  let func = new Function('alert(value)');\n*/!*\n\n  return func;\n}\n\ngetFunc()(); // error: value is not defined\n```\n\nComparez-le avec le comportement habituel :\n\n```js run\nfunction getFunc() {\n  let value = \"test\";\n\n*!*\n  let func = function() { alert(value); };\n*/!*\n\n  return func;\n}\n\ngetFunc()(); // *!*\"test\"*/!*, provenant de l'Environnement Lexical de getFunc\n```\n\nCette particularité de `new Function` paraît étrange, mais semble très utile dans la pratique.\n\nImaginons que nous devions créer une fonction à partir d'une chaîne de caractères. Le code de cette fonction n'est pas connu au moment de l'écriture du script (c'est pourquoi nous n'utilisons pas de fonctions standard), mais sera connu dans le processus d'exécution. Nous pouvons le recevoir du serveur ou d'une autre source.\n\nNotre nouvelle fonction doit interagir avec le script principal.\n\nEt si il pouvait accéder aux variables externes ?\n\nLe problème est qu'avant la publication de JavaScript en production, JavaScript est compressé à l'aide d'un *minifier*, un programme spécial qui rétrécit le code en supprimant les commentaires, espaces supplémentaires et, plus particulièrement, renomme les variables locales pour quelles soient plus courtes.\n\nPar exemple, si une fonction a `let userName`, le minifier la remplace par `let a` (ou une autre lettre si celle-ci est déjà prise), et le fait partout. C’est généralement une chose sûre à faire, car la variable est locale, rien en dehors de la fonction ne peut y accéder. Et à l'intérieur de la fonction, le minifier remplace chaque mention. Les minifiers sont intelligents, ils analysent la structure du code pour ne rien casser. Ils ne sont pas juste une simple fonction de recherche et remplacement.\n\nDonc, si `new Function` avait accès aux variables externes, il serait impossible de trouver le nom d'utilisateur `userName` renommé.\n\n**Si `new Function` avait accès aux variables externes, elle aurait des problèmes avec les minifiers.**\n\nEn outre, ce code serait mauvais sur le plan architectural et sujet aux erreurs.\n\nPour passer quelque chose à une fonction créée par `new Function`, nous devons utiliser ses arguments.\n\n## Résumé\n\nLa syntaxe :\n\n```js\nlet func = new Function ([arg1, arg2, ...argN], functionBody);\n```\n\nPour des raisons historiques, les arguments peuvent également être fournis sous forme de liste séparée par des virgules.\n\nCes trois déclarations signifient la même chose :\n\n```js\nnew Function('a', 'b', 'return a + b'); // syntax de base\nnew Function('a,b', 'return a + b'); // séparés par des virgules\nnew Function('a , b', 'return a + b'); // séparés par des virgules et espacés\n```\n\nLes fonctions créées avec `new Function` ont leur `[[Environment]]` qui fait référence à l’environnement lexical global, et non à l’environnement externe. Par conséquent, elles ne peuvent pas utiliser de variables externes. Mais c’est bien, parce que cela nous protège des erreurs. Passer explicitement des paramètres est une méthode bien meilleure sur le plan architectural et ne pose aucun problème avec les minifiers.\n"
  },
  {
    "path": "1-js/06-advanced-functions/08-settimeout-setinterval/1-output-numbers-100ms/solution.md",
    "content": "\nAvec `setInterval`:\n\n```js run\nfunction printNumbers(from, to) {\n  let current = from;\n\n  let timerId = setInterval(function() {\n    alert(current);\n    if (current == to) {\n      clearInterval(timerId);\n    }\n    current++;\n  }, 1000);\n}\n\n// usage:\nprintNumbers(5, 10);\n```\n\nUtilisation de `setTimeout` imbriqué :\n\n\n```js run\nfunction printNumbers(from, to) {\n  let current = from;\n\n  setTimeout(function go() {\n    alert(current);\n    if (current < to) {\n      setTimeout(go, 1000);\n    }\n    current++;\n  }, 1000);\n}\n\n// utilisation :\nprintNumbers(5, 10);\n```\n\nNotons que, dans les deux solutions, il y a un délai initial avant le premier résultat. En effet, la fonction est appelée pour la première fois au bout de `1000ms`.\n\nAfin d'exécuter la fonction immédiatement, on peut ajouter un autre appel avant `setInterval`.\n\n```js run\nfunction printNumbers(from, to) {\n  let current = from;\n\n  function go() {\n    alert(current);\n    if (current == to) {\n      clearInterval(timerId);\n    }\n    current++;\n  }\n\n*!*\n  go();\n*/!*\n  let timerId = setInterval(go, 1000);\n}\n\nprintNumbers(5, 10);\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/08-settimeout-setinterval/1-output-numbers-100ms/task.md",
    "content": "importance: 5\n\n---\n\n# Un résultat par seconde\n\nÉcrire une fonction `printNumbers(from, to)` qui affiche un nombre par seconde, en partant de `from` jusqu'à `to`.\n\nFaites deux variantes de la solution :\n\n1. utilisant `setInterval`,\n2. Utilisation de `setTimeout` imbriqué.\n"
  },
  {
    "path": "1-js/06-advanced-functions/08-settimeout-setinterval/3-rewrite-settimeout/solution.md",
    "content": ""
  },
  {
    "path": "1-js/06-advanced-functions/08-settimeout-setinterval/3-rewrite-settimeout/task.md",
    "content": "importance: 4\n\n---\n\n# Réécrire setTimeout avec setInterval\n\nVoici une fonction qui utilise un `setTimeout` imbriqué pour découper une tâche en petit bouts.\n\nRéécrire le bloc suivant en utilisant `setInterval`:\n\n```js run\nlet i = 0;\n\nlet start = Date.now();\n\nfunction count() {\n\n  if (i == 1000000000) {\n    alert(\"Done in \" + (Date.now() - start) + 'ms');\n  } else {\n    setTimeout(count);\n  }\n\n  // un morceau d'une très grosse tâche\n  for(let j = 0; j < 1000000; j++) {\n    i++;\n  }\n\n}\n\ncount();\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/08-settimeout-setinterval/4-settimeout-result/solution.md",
    "content": "\n`setTimeout` ne peut s'exécuter qu'une fois le bloc de code courant terminé.\n\nLe `i` sera donc le dernier : `100000000`.\n\n```js run\nlet i = 0;\n\nsetTimeout(() => alert(i), 100); // 100000000\n\n// on considère que cette fonction met plus de 100ms à s'exécuter\nfor(let j = 0; j < 100000000; j++) {\n  i++; \n}\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/08-settimeout-setinterval/4-settimeout-result/task.md",
    "content": "importance: 5\n\n---\n\n# Que va afficher setTimeout ?\n\nDans le code ci-dessous il y a une exécution planifié par `setTimeout`, suivie par un calcul conséquent qui prend plus de 100ms à tourner.\n\nQuand la fonction planifiée va-t-elle s'exécuter ?\n\n1. Après la boucle.\n2. Avant la boucle.\n3. Au début de la boucle.\n\nQu'est-ce que `alert` va afficher ?\n\n```js\nlet i = 0;\n\nsetTimeout(() => alert(i), 100); // ?\n\n// on considère que cette fonction met plus de 100ms à s'exécuter\nfor(let j = 0; j < 100000000; j++) {\n  i++; \n}\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/08-settimeout-setinterval/article.md",
    "content": "# L'ordonnancement avec setTimeout et setInterval\n\nPeut-être que nous ne voulons pas exécuter une fonction tout de suite, mais à un certain moment dans le futur. Cela s'appelle \"ordonnancer (ou planifier) un appel de fonction\".\n\nIl existe deux méthodes pour cela :\n\n- `setTimeout` permet d'exécuter une fonction une unique fois après un certain laps de temps.\n- `setInterval` nous permet d'exécuter une fonction de manière répétée, en commençant après l'intervalle de temps, puis en répétant continuellement à cet intervalle.\n\nCes méthodes ne font pas partie de la spécification JavaScript. Mais la plupart des environnements ont un planificateur interne et fournissent ces méthodes. En particulier, elles sont supportées par tous les navigateurs et Node.js.\n\n## setTimeout\n\nLa syntaxe :\n\n```js\nlet timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)\n```\n\nLes paramètres :\n\n`func|code`\n: Fonction ou chaîne de caractères représentant du code à exécuter.\nEn général, c'est une fonction. Pour des raisons historiques, une chaîne de caractères représentant du code peut être donnée en argument, mais ce n'est pas recommandé.\n\n`delay`\n: La durée d'attente avant l'exécution, en millisecondes (1000ms = 1 seconde), par défaut 0.\n\n`arg1`, `arg2`...\n: Arguments pour la fonction\n\nPar exemple, le code ci-dessous appelle la fonction `sayHi()` une unique fois au bout de 1 seconde :\n\n```js run\nfunction sayHi() {\n  alert('Hello');\n}\n\n*!*\nsetTimeout(sayHi, 1000);\n*/!*\n```\n\nDans le cas où fonction `sayHi()` requiert des arguments :\n\n```js run\nfunction sayHi(phrase, who) {\n  alert( phrase + ', ' + who );\n}\n\n*!*\nsetTimeout(sayHi, 1000, \"Bonjour\", \"Jean\"); // Bonjour, Jean\n*/!*\n```\n\nSi le premier argument est une chaîne de caractères, JavaScript crée alors une fonction à partir de celle-ci.\n\nCe qui fait que le code ci-dessous fonctionne aussi :\n\n```js run no-beautify\nsetTimeout(\"alert('Bonjour')\", 1000);\n```\n\nCependant, utiliser des chaînes de caractères n'est pas recommandé, il est préférable d'utiliser des fonctions fléchées à la place, comme ceci :\n\n```js run no-beautify\nsetTimeout(() => alert('Bonjour'), 1000);\n```\n\n````smart header=\"Passer une fonction, mais sans l'exécuter\"\nLes développeurs novices font parfois l'erreur d'ajouter des parenthèses `()` après la fonction :\n\n```js\n// Faux!\nsetTimeout(sayHi(), 1000);\n```\nCela ne fonctionne pas car `setTimeout` attend une référence à une fonciton. Ici `sayHi()` appelle la fonction et le *résultat de cette exécution* est passé à `setTimeout`. Dans notre cas, le résultat de `sayHi()` est `undefined` (la fonction ne renvoie rien), du coup, rien n'est planifié.\n````\n\n### Annuler une tâche avec clearTimeout\n\nUn appel à `setTimeout` renvoie un \"identifiant de timer\" `timerId` que l'on peut utiliser pour annuler l'exécution de la fonction.\n\nLa syntaxe pour annuler une tâche planifiée est la suivante :\n\n```js\nlet timerId = setTimeout(...);\nclearTimeout(timerId);\n```\n\nDans le code ci-dessous, nous planifions l'appel à la fonction avant de l'annuler, au final rien ne s'est passé :\n\n```js run no-beautify\nlet timerId = setTimeout(() => alert(\"Je n'arriverai jamais\"), 1000);\nalert(timerId); // Identifiant du timer\n\nclearTimeout(timerId);\nalert(timerId); // Le même identifiant (ne devient pas null après l'annulation)\n```\n\nComme on peut le voir dans les résultats des `alert`, dans notre navigateur, l'identifiant du timer est un nombre. Selon l'environnement, il peut être d'un autre type. Par exemple, Node.js renvoie un objet timer équipé d'autres méthodes.\n\nEncore une fois, il n'y a pas de spécification universelle pour ces méthodes, donc ce n'est pas gênant.\n\nPour les navigateurs, les timers sont décrits dans la [section des timers](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers) de HTML Living Standard.\n\n## setInterval\n\nLa méthode `setInterval` a la même syntaxe que `setTimeout`:\n\n```js\nlet timerId = setInterval(func|code, [delay], [arg1], [arg2], ...)\n```\n\nTous ses arguments ont la même signfication que précédemment, mais contrairement à `setTimeout`, `setInterval` appelle la fonction non pas une fois, mais périodiquement après un interval de temps donné.\n\nAfin d'annuler les appels futurs à la fonction, il est nécessaire d'appeler `clearInterval(timerId)`.\n\nL'exemple suivant affiche le message toutes les 2 secondes, puis arrête la tâche au bout de 5 secondes :\n\n```js run\n// Se répète toutes les 2 secondes\nlet timerId = setInterval(() => alert('tick'), 2000);\n\n// S'arrête après 5 secondes\nsetTimeout(() => { clearInterval(timerId); alert('stop'); }, 5000);\n```\n\n```smart header=\"Le temps continue de s'écouler pendant que `alert` est affiché\"\nDans la majorité des navigateurs, dont Chrome et Firefox, le timer interne continue à s'incrémenter pendant qu'un message est affiché (via `alert`, `confirm` ou `prompt`).\n\nDonc, si vous exécutez le code ci-dessus et que vous ne fermez pas la fenêtre `alert` pendant un certain temps, la prochaine `alert` sera affichée immédiatement lorsque vous le faites. L'intervalle réel entre les alertes sera inférieur à 2 secondes.\n```\n\n## setTimeout imbriqué\n\nIl y a deux façon d'ordonnancer l'exécution périodique d'une tâche.\n\nL'un est `setInterval`. L'autre est un `setTimeout` imbriqué, comme ceci :\n\n```js\n/** Au lieu de :\nlet timerId = setInterval(() => alert('tick'), 2000);\n*/\n\nlet timerId = setTimeout(function tick() {\n  alert('tick');\n*!*\n  timerId = setTimeout(tick, 2000); // (*)\n*/!*\n}, 2000);\n```\n\nLe `setTimeout` ci-dessus planifie le prochain appel de la fonction à la fin de l'appel en cours `(*)`.\n\nLe `setTimeout` imbriqué est une méthode plus flexible que `setInterval`. Ainsi, le prochain appel peut être programmé différemment, en fonction des résultats de l'appel en cours.\n\nPar exemple, on peut avoir besoin d'implémenter un service qui envoie une requête à un serveur toutes les 5 secondes pour récupérer de la donnée, mais dans le cas où le serveur est surchargé, on doit augmenter le délai à 10 secondes, puis 20 secondes, 40 secondes...\n\nVoici le pseudo-code correspondant :\n```js\nlet delay = 5000;\n\nlet timerId = setTimeout(function request() {\n  ...send request...\n\n  if (request failed due to server overload) {\n    // Augmente l'intervalle avant le prochain appel\n    delay *= 2;\n  }\n\n  timerId = setTimeout(request, delay);\n\n}, delay);\n```\n\nOu par exemple, si les fonction qu'on souhaite planifier demandent beaucoup de ressources CPU, on peut alors mesurer leur temps d'exécution et planifier le prochain appel en fonction.\n\nEt si les fonctions que nous planifions sont gourmandes en ressources processeur, nous pouvons mesurer le temps pris par l'exécution et planifier le prochain appel tôt ou tard.\n\n**Un `setTimeout` imbriqué permet de définir le délai entre les exécutions plus précisément que `setInterval`.**\n\nComparons deux blocs de codes, le premier utilise `setInterval` :\n\n```js\nlet i = 1;\nsetInterval(function() {\n  func(i++);\n}, 100);\n```\n\nLe second utilise un `setTimeout` imbriqué :\n\n```js\nlet i = 1;\nsetTimeout(function run() {\n  func(i++);\n  setTimeout(run, 100);\n}, 100);\n```\n\nDans le cas du `setInterval` l'ordonnanceur interne va appeler `func(i++)` toutes les 100ms :\n\n![](setinterval-interval.svg)\n\nRien d'étrange ?\n\n**Le vrai délai entre deux appels à `func` est plus court que dans le code.**\n\nC'est normal car le temps d'exécution de `func` \"consomme\" une partie de ce délai.\n\nIl est donc possible que le temps d'exécution de `func` soit plus long que prévu et prenne plus de 100ms.\n\nDans ce cas le moteur interne attend que l'exécution de `func` soit terminée, puis consulte l'ordonnanceur et si le délai est déjà \"consommé\", il réexécute la fonction *immédiatement*.\n\nDans ce cas extrême, si la fonction qui s'exécute met toujours plus de temps que `delay` ms, alors les appels successifs vont s'effectuer sans aucun temps de pause.\n\nEt voici l'image pour le `setTimeout` imbriqué :\n\n![](settimeout-interval.svg)\n\n**Le `setTimeout` imbriqué garantit le délai fixé (ici 100 ms).**\n\nDans ce cas, c'est parce que le nouvel appel est planifié à la fin du précédent.\n\n````smart header=\"Le ramasse-miettes et le callback setInterval/setTimeout\"\nQuand une fonction est passée à `setInterval`/`setTimeout`, une référence interne à cette fonction est créée et conservée dans l'ordonnanceur. Cela empêche que la fonction soit détruite par le ramasse-miettes, même si il n'y a pas d'autres références à cette dernière.\n\n```js\n// La fonction reste en mémoire jusqu'à ce que l'ordonnanceur l'exécute\nsetTimeout(function() {...}, 100);\n```\n\nPour `setInterval`, la fonction reste en mémoire jusqu'à ce qu'on appelle `clearInterval`.\n\nMais il y a un effet de bord, une fonction référence l'environement lexical extérieur, donc tant qu'elle existe, les variables extérieures existent aussi. Ces variables peuvent occuper autant d'espace mémoire que la fonction elle-même. De ce fait quand on n'a plus besoin d'une fonction planifiée, il est préférable de l'annuler, même si elle est courte.\n````\n\n## setTimeout sans délai\n\nIl y a un cas d'usage particulier : `setTimeout(func, 0)` ou plus simplement `setTimeout(func)`.\n\nCeci programme l'exécution de `func` dès que possible. Mais le planificateur ne l'invoquera qu'une fois le script en cours d'exécution terminé.\n\nLa fonction est donc programmée pour s'exécuter \"juste après\" le script en cours.\n\nPar exemple, le code ci dessous affiche \"Hello\", et immédiatement après, \"World\" :\n\n```js run\nsetTimeout(() => alert(\"World\"));\n\nalert(\"Hello\");\n```\n\nLa première ligne \"met l'appel dans le calendrier après 0 ms\". Mais le planificateur \"vérifiera le calendrier\" uniquement une fois le script en cours terminé. `\"Hello\"` est donc le premier, et `\"World\"` -- après.\n\nIl y a aussi d'autres cas d'usage avancés d'ordonnancement à délai nul, spécifique au cas des navigateurs web, dont nous parlerons dans le chapitre <info:event-loop>.\n\n````smart header=\"Un délai nul n'est pas vraiment nul (pour un navigateur)\"\nDans le navigateur, la fréquence d'exécution des timers imbriqués est limitée. Le [HTML Living Standard](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers) indique : \"après cinq timers imbriqués, l'intervalle est forcé d'être d'au moins 4 millisecondes.\".\n\nNous allons illustrer ce que cela veut dire dans l'exemple ci-dessous. L'appel à `setTimeout` s'y ré-ordonnance lui-même avec un délai nul. Chaque appel se souvient de l'heure de l'appel précédent grâce au tableau `times`. Cela va nous permettre de mesurer les délais réels entre les exécutions :\n\n```js run\nlet start = Date.now();\nlet times = [];\n\nsetTimeout(function run() {\n  times.push(Date.now() - start); // on garde en mémoire le délai depuis l'appel précédent\n\n  if (start + 100 < Date.now()) alert(times); // on affiche les délais si plus de 100ms se sont écoulées\n  else setTimeout(run); // sinon on planifie un nouvel appel\n});\n\n// voici un exemple de résultat :\n// 1,1,1,1,9,15,20,24,30,35,40,45,50,55,59,64,70,75,80,85,90,95,100\n```\n\nLes 4 premiers timers s'exécutent immédiatemment (comme indiqué dans la spécification), ensuite on peut voir `9, 15, 20, 24...`. Le délai minimum de 4ms entre appel entre alors en jeu.\n\nCette même limitation s'applique si on utilise `setInterval` au lieu de `setTimeout` : `setInterfal(f)` appelle `f` un certain nombre de fois avec un délai nul avant d'observer un délai d'au moins 4ms.\n\nCette limitation est l'héritage d'un lointain passé et beaucoup de scripts se basent dessus, d'où la nécessité de cette limitation pour des raisons historiques.\n\nPour le JavaScript côté serveur, cette limitation n'existe pas, et il existe d'autres façon de planifier immédiatement des tâches asynchrones, notamment [setImmediate](https://nodejs.org/api/timers.html) pour Node.js. Il faut donc garder à l'esprit que ce nota bene est spécifique aux navigateurs web.\n````\n\n## Résumé\n\n- Les méthodes `setInterval(func, delay, ...args)` et `setTimeout(func, delay, ...args)` permettent d'exécuter `func` respectivement une seul fois/périodiquement après `delay` millisecondes.\n- Pour annuler l'exécution, nous devons appeler `clearInterval/clearTimeout` avec la valeur renvoyée par `setInterval/setTimeout`.\n- Les appels de `setTimeout` imbriqués sont une alternative plus flexible à `setInterval`, ils permettent de configurer le temps *entre* les exécution plus précisément.\n- L'ordonnancement à délai nul avec `setTimeout(func, 0)` (le même que `setTimeout(func)`) permet de planifier l'exécution \"dès que possible, mais seulement une fois que le bloc de code courant a été exécuté\".\n- Le navigateur limite le délai minimal pour cinq appels imbriqués ou plus de `setTimeout` ou pour `setInterval` (après le 5ème appel) à 4 ms. C'est pour des raisons historiques.\n\nVeuillez noter que toutes les méthodes de planification ne garantissent pas le délai exact.\n\nPar exemple, le timer interne au navigateur peut être ralenti pour de nombreuses raisons :\n\n- Le CPU est surchargé.\n- L'onglet du navigateur est en tâche de fond.\n- L'ordinateur est en mode économie d'énergie.\n\nTout ceci peut augmenter la résolution de l'horloge (le délai minimum) jusqu'à 300ms voire 1000ms en fonction du navigateur et des paramètres de performance au niveau du système d'exploitation.\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/solution.js",
    "content": "function spy(func) {\n\n  function wrapper(...args) {\n    // using ...args instead of arguments to store \"real\" array in wrapper.calls\n    wrapper.calls.push(args);\n    return func.apply(this, args);\n  }\n\n  wrapper.calls = [];\n\n  return wrapper;\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/source.js",
    "content": "function spy(func) {\n  // votre code\n}\n\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/test.js",
    "content": "describe(\"spy\", function() {\n  it(\"records calls into its property\", function() {\n    function work() {}\n\n    work = spy(work);\n    assert.deepEqual(work.calls, []);\n\n    work(1, 2);\n    assert.deepEqual(work.calls, [\n      [1, 2]\n    ]);\n\n    work(3, 4);\n    assert.deepEqual(work.calls, [\n      [1, 2],\n      [3, 4]\n    ]);\n  });\n\n  it(\"transparently wraps functions\", function() {\n\n    let sum = sinon.spy((a, b) => a + b);\n\n    let wrappedSum = spy(sum);\n\n    assert.equal(wrappedSum(1, 2), 3);\n    assert(sum.calledWith(1, 2));\n  });\n\n\n  it(\"transparently wraps methods\", function() {\n\n    let calc = {\n      sum: sinon.spy((a, b) => a + b)\n    };\n\n    calc.wrappedSum = spy(calc.sum);\n\n    assert.equal(calc.wrappedSum(1, 2), 3);\n    assert(calc.sum.calledWith(1, 2));\n    assert(calc.sum.calledOn(calc));\n  });\n\n});\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/solution.md",
    "content": "Le wrapper renvoyé par `spy(f)` doit stocker tous les arguments, puis utiliser `f.apply` pour transférer l'appel.\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/task.md",
    "content": "importance: 5\n\n---\n\n# Décorateur spy\n\nCréez un décorateur `spy(func)` qui devrait renvoyer un wrapper qui enregistre tous les appels à la fonction dans sa propriété `calls`.\n\nChaque appel est enregistré sous la forme d'un tableau d'arguments.\n\nPar exemple:\n\n```js\nfunction work(a, b) {\n  alert( a + b ); // work est une fonction ou une méthode arbitraire\n}\n\n*!*\nwork = spy(work);\n*/!*\n\nwork(1, 2); // 3\nwork(4, 5); // 9\n\nfor (let args of work.calls) {\n  alert( 'call:' + args.join() ); // \"call:1,2\", \"call:4,5\"\n}\n```\n\nP.S. Ce décorateur est parfois utile pour les tests unitaires. Sa forme avancée est `sinon.spy` dans la bibliothèque [Sinon.JS](http://sinonjs.org/).\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/02-delay/_js.view/solution.js",
    "content": "function delay(f, ms) {\n\n  return function() {\n    setTimeout(() => f.apply(this, arguments), ms);\n  };\n\n};"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/02-delay/_js.view/test.js",
    "content": "describe(\"delay\", function() {\n  before(function() {\n    this.clock = sinon.useFakeTimers();\n  });\n\n  after(function() {\n    this.clock.restore();\n  });\n\n  it(\"calls the function after the specified timeout\", function() {\n    let start = Date.now();\n\n    function f(x) {\n      assert.equal(Date.now() - start, 1000);\n    }\n    f = sinon.spy(f);\n\n    let f1000 = delay(f, 1000);\n    f1000(\"test\");\n    this.clock.tick(2000);\n    assert(f.calledOnce, 'calledOnce check fails');\n  });\n\n  it(\"passes arguments and this\", function() {\n    let start = Date.now();\n    let user = {\n      sayHi: function(phrase, who) {\n        assert.equal(this, user);\n        assert.equal(phrase, \"Hello\");\n        assert.equal(who, \"John\");\n        assert.equal(Date.now() - start, 1500);\n      }\n    };\n\n    user.sayHi = sinon.spy(user.sayHi);\n\n    let spy = user.sayHi;\n    user.sayHi = delay(user.sayHi, 1500);\n\n    user.sayHi(\"Hello\", \"John\");\n\n    this.clock.tick(2000);\n\n    assert(spy.calledOnce, 'calledOnce check failed');\n  });\n});\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/02-delay/solution.md",
    "content": "La solution:\n\n```js run demo\nfunction delay(f, ms) {\n\n  return function() {\n    setTimeout(() => f.apply(this, arguments), ms);\n  };\n\n}\n\nlet f1000 = delay(alert, 1000);\n\nf1000(\"test\"); // montre \"test\" après 1000ms\n```\n\nVeuillez noter comment une fonction fléchée est utilisée ici. Comme nous le savons, les fonctions fléchées ne possèdent pas leurs propres `this` et `arguments`, aussi `f.apply(this, arguments)` prend `this` et `arguments` du wrapper.\n\nSi nous passons une fonction régulière, `setTimeout` l'appellera sans arguments et `this = window` (en supposant que nous sommes dans le navigateur).\n\nNous pouvons toujours passer le bon `this` en utilisant une variable intermédiaire, mais c'est un peu plus lourd:\n\n```js\nfunction delay(f, ms) {\n\n  return function(...args) {\n    let savedThis = this; // stocker \"this\" dans une variable intermédiaire\n    setTimeout(function() {\n      f.apply(savedThis, args); // utilisez-le ici\n    }, ms);\n  };\n\n}\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/02-delay/task.md",
    "content": "importance: 5\n\n---\n\n# Décorateur de retardement \n\nCréez un décorateur `delay(f, ms)` qui retarde chaque appel de `f` de `ms` millisecondes.\n\nPar exemple:\n\n```js\nfunction f(x) {\n  alert(x);\n}\n\n// créer des wrappers\nlet f1000 = delay(f, 1000);\nlet f1500 = delay(f, 1500);\n\nf1000(\"test\"); // montre \"test\" après 1000ms\nf1500(\"test\"); // montre \"test\" après 1500ms\n```\n\nEn d'autres termes, `delay(f, ms)` renvoie une variante \"retardée de `ms`\" de `f`.\n\nDans le code ci-dessus, `f` est une fonction d'un seul argument, mais votre solution doit transmettre tous les arguments et le contexte `this`."
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/_js.view/solution.js",
    "content": "function debounce(func, ms) {\n  let timeout;\n  return function() {\n    clearTimeout(timeout);\n    timeout = setTimeout(() => func.apply(this, arguments), ms);\n  };\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/_js.view/test.js",
    "content": "describe('debounce', function () {\n  before(function () {\n    this.clock = sinon.useFakeTimers();\n  });\n\n  after(function () {\n    this.clock.restore();\n  });\n\n  it('for one call - runs it after given ms', function () {\n    const f = sinon.spy();\n    const debounced = debounce(f, 1000);\n\n    debounced('test');\n    assert(f.notCalled, 'not called immediately');\n    this.clock.tick(1000);\n    assert(f.calledOnceWith('test'), 'called after 1000ms');\n  });\n\n  it('for 3 calls - runs the last one after given ms', function () {\n    const f = sinon.spy();\n    const debounced = debounce(f, 1000);\n\n    debounced('a');\n    setTimeout(() => debounced('b'), 200); // ignored (too early)\n    setTimeout(() => debounced('c'), 500); // runs (1000 ms passed)\n    this.clock.tick(1000);\n\n    assert(f.notCalled, 'not called after 1000ms');\n\n    this.clock.tick(500);\n\n    assert(f.calledOnceWith('c'), 'called after 1500ms');\n  });\n\n  it('keeps the context of the call', function () {\n    let obj = {\n      f() {\n        assert.equal(this, obj);\n      },\n    };\n\n    obj.f = debounce(obj.f, 1000);\n    obj.f('test');\n    this.clock.tick(5000);\n  });\n  \n});\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/debounce.view/index.html",
    "content": "<!doctype html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js\"></script>\n\nFunction <code>handler</code> is called on this input:\n<br>\n<input id=\"input1\" placeholder=\"type here\">\n\n<p>\n\nDebounced function <code>debounce(handler, 1000)</code> is called on this input:\n<br>\n<input id=\"input2\" placeholder=\"type here\">\n\n<p>\n<button id=\"result\">The <code>handler</code> puts the result here</button>\n\n<script>\n  function handler(event) {\n    result.innerHTML = event.target.value;\n  }\n\n  input1.oninput = handler;\n  input2.oninput = _.debounce(handler, 1000);\n</script>"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md",
    "content": "```js demo\nfunction debounce(func, ms) {\n  let timeout;\n  return function() {\n    clearTimeout(timeout);\n    timeout = setTimeout(() => func.apply(this, arguments), ms);\n  };\n}\n\n```\n\nUn appel à `debounce` renvoie un wrapper. Lorsqu'il est appelé, il planifie l'appel de la fonction d'origine après `ms` donné et annule le délai d'expiration précédent.\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/task.md",
    "content": "importance: 5\n\n---\n\n# Décorateur debounce\n\nLe résultat du décorateur `debounce(f, ms)` est un wrapper qui suspend les appels à `f` jusqu'à ce qu'il y ait `ms` millisecondes d'inactivité (pas d'appels, \"période de cooldown\"), puis invoque `f` une fois avec les derniers arguments.\n\nEn d'autres termes, `debounce` est comme une secrétaire qui accepte les \"appels téléphoniques\", et attend jusqu'à ce qu'il y ait des millisecondes de silence. Et alors seulement, elle transfère les dernières informations d'appel au \"boss\" (appellez le \"f\" réel).\n\nPar exemple, nous avons eu une fonction `f` et l'avons remplacée par `f = debounce(f, 1000)`.\n\nEnsuite, si la fonction encapsulée est appelée à 0ms, 200ms et 500ms, et qu'il n'y a aucun appel, alors le `f` actuel ne sera appelé qu'une seule fois, à 1500 ms. Autrement dit: après la période de temps de recharge de 1000 ms à partir du dernier appel.\n\n![](debounce.svg)\n\n... Et il récupérera les arguments du tout dernier appel, les autres appels sont ignorés.\n\nVoici le code pour cela (utilise le décorateur debounce de la [librairie Lodash](https://lodash.com/docs/4.17.15#debounce)):\n\n```js\nlet f = _.debounce(alert, 1000);\n\nf(\"a\");\nsetTimeout( () => f(\"b\"), 200);\nsetTimeout( () => f(\"c\"), 500); \n// la fonction debounce attend 1000ms après le dernier appel puis exécute : alert(\"c\")\n```\n\n\nMaintenant, un exemple pratique. Disons que l'utilisateur tape quelque chose et que nous aimerions envoyer une requête au serveur lorsque l'entrée est terminée.\n\nIl ne sert à rien d'envoyer la requête pour chaque caractère saisi. Au lieu de cela, nous aimerions attendre, puis traiter l'ensemble du résultat.\n\nDans un navigateur Web, nous pouvons configurer un gestionnaire d'événements - une fonction qui est appelée à chaque modification d'un champ de saisie. Normalement, un gestionnaire d'événements est appelé très souvent, pour chaque touche tapée. Mais si on le `debounce` de 1000ms, il ne sera appelé qu'une seule fois, après 1000ms après la dernière entrée.\n\n```online\n\nDans cet exemple en live, le gestionnaire place le résultat dans une case ci-dessous, essayez-le :\n\n[iframe border=1 src=\"debounce\" height=200]\n\nVous voyez ? La deuxième entrée appelle la fonction \"debounced\", donc son contenu est traité après 1000ms à partir de la dernière entrée.\n```\n\nDonc, `debounce` est un excellent moyen de traiter une séquence d'événements: que ce soit une séquence de touches, de mouvements de souris ou autre.\n\n\nIl attend le temps donné après le dernier appel, puis exécute sa fonction, qui peut traiter le résultat.\n\nLa tâche est d'implémenter le décorateur `debounce`.\n\nIndice : ce ne sont que quelques lignes si vous y réfléchissez :)\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/solution.js",
    "content": "function throttle(func, ms) {\n\n  let isThrottled = false,\n    savedArgs,\n    savedThis;\n\n  function wrapper() {\n\n    if (isThrottled) {\n      // mémo derniers arguments à appeler après le temps de recharge\n      savedArgs = arguments;\n      savedThis = this;\n      return;\n    }\n\n    // sinon aller en état de recharge\n    func.apply(this, arguments);\n\n    isThrottled = true;\n\n    // prévoir de réinitialiser isThrottled après le délai\n    setTimeout(function() {\n      isThrottled = false;\n      if (savedArgs) {\n        // s'il y a eu des appels, savedThis/savedArgs auront le dernier\n        // appel récursif exécute la fonction et redéfinit le temps de recharge\n        wrapper.apply(savedThis, savedArgs);\n        savedArgs = savedThis = null;\n      }\n    }, ms);\n  }\n\n  return wrapper;\n}"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/test.js",
    "content": "describe(\"throttle(f, 1000)\", function() {\n  let f1000;\n  let log = \"\";\n\n  function f(a) {\n    log += a;\n  }\n\n  before(function() {\n    this.clock = sinon.useFakeTimers();\n    f1000 = throttle(f, 1000);\n  });\n\n  it(\"the first call runs now\", function() {\n    f1000(1); // runs now\n    assert.equal(log, \"1\");\n  });\n\n  it(\"then calls are ignored till 1000ms when the last call works\", function() {\n    f1000(2); // (throttling - less than 1000ms since the last run)\n    f1000(3); // (throttling - less than 1000ms since the last run)\n    // after 1000 ms f(3) call is scheduled\n\n    assert.equal(log, \"1\"); // right now only the 1st call done\n\n    this.clock.tick(1000); // after 1000ms...\n    assert.equal(log, \"13\"); // log==13, the call to f1000(3) is made\n  });\n\n  it(\"the third call waits 1000ms after the second call\", function() {\n    this.clock.tick(100);\n    f1000(4); // (throttling - less than 1000ms since the last run)\n    this.clock.tick(100);\n    f1000(5); // (throttling - less than 1000ms since the last run)\n    this.clock.tick(700);\n    f1000(6); // (throttling - less than 1000ms since the last run)\n\n    this.clock.tick(100); // now 100 + 100 + 700 + 100 = 1000ms passed\n\n    assert.equal(log, \"136\"); // the last call was f(6)\n  });\n\n  after(function() {\n    this.clock.restore();\n  });\n\n});\n\ndescribe('throttle', () => {\n\n  it('runs a forwarded call once', done => {\n    let log = '';\n    const f = str => log += str;\n    const f10 = throttle(f, 10);\n    f10('once');\n\n    setTimeout(() => {\n      assert.equal(log, 'once');\n      done();\n    }, 20);\n  });\n\n});\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md",
    "content": "```js demo\nfunction throttle(func, ms) {\n\n  let isThrottled = false,\n    savedArgs,\n    savedThis;\n\n  function wrapper() {\n\n    if (isThrottled) { // (2)\n      savedArgs = arguments;\n      savedThis = this;\n      return;\n    }\n    isThrottled = true;\n\n    func.apply(this, arguments); // (1)\n\n    setTimeout(function() {\n      isThrottled = false; // (3)\n      if (savedArgs) {\n        wrapper.apply(savedThis, savedArgs);\n        savedArgs = savedThis = null;\n      }\n    }, ms);\n  }\n\n  return wrapper;\n}\n```\n\nUn appel à `throttle(func, ms)` retourne `wrapper`.\n\n1. Lors du premier appel, le `wrapper` exécute simplement `func` et définit l'état de charge (`isThrottled = true`).\n2. Dans cet état, tous les appels sont mémorisés dans `savedArgs/savedThis`. Veuillez noter que le contexte et les arguments sont d'égale importance et doivent être mémorisés. Nous avons besoin d'eux simultanément pour reproduire l'appel.\n3. Après `ms` millisecondes, `setTimeout` se déclenche. L'état de charge est supprimé (`isThrottled = false`), et si nous avions ignoré les appels, alors `wrapper` est exécuté avec les derniers arguments et contextes mémorisés.\n\nLa 3ème étape n’exécute pas `func`, mais `wrapper`, car nous devons non seulement exécuter `func`, mais encore une fois entrer dans l’état de charge et configurer le délai d’expiration pour le réinitialiser.\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md",
    "content": "importance: 5\n\n---\n\n# Décorateur d'accélération\n\nCréez un décorateur \"d'accélération\" `throttle(f, ms)` -- qui retourne un wrapper.\n\nLorsqu'il est appelé plusieurs fois, il passe l'appel à `f` au maximum une fois par `ms` millisecondes.\n\nLa différence avec debounce est que c'est un décorateur complètement différent :\n- `debounce` exécute la fonction une fois après la période de \"cooldown\". Bon pour traiter le résultat final.\n- `throttle` ne l'exécute pas plus souvent que le temps donné en `ms`. Bon pour les mises à jour régulières qui ne devraient pas être très fréquentes.\n\nEn d'autres termes, `throttle` est comme une secrétaire qui accepte les appels téléphoniques, mais qui dérange le patron (appellez le `f` réel) pas plus d'une fois par `ms` millisecondes.\n\nExaminons l'application réelle pour mieux comprendre cette exigence et voir d'où elle vient.\n\n**Par exemple, nous voulons suivre les mouvements de la souris.**\n\nDans le navigateur, nous pouvons configurer une fonction à exécuter à chaque mouvement de la souris et obtenir l’emplacement du pointeur à mesure qu’il se déplace. Pendant une utilisation active de la souris, cette fonction est généralement utilisée très souvent et peut atteindre 100 fois par seconde (toutes les 10 ms).\n**Nous aimerions mettre à jour certaines informations sur la page Web lorsque le pointeur se déplace.**\n\n... Mais la mise à jour de la fonction `update()` est trop lourde pour tous les micro-mouvements. Il est également inutile de mettre à jour plus d'une fois toutes les 100 ms.\n\nNous allons donc l'envelopper dans le décorateur : utilisez `throttle(update, 100)` comme fonction à exécuter à chaque déplacement de souris à la place de `update()` d'origine. Le décorateur sera appelé souvent, mais `update()` sera appelé au maximum une fois toutes les 100 ms.\n\nVisuellement, cela ressemblera à ceci:\n\n1. Pour le premier mouvement de souris, la variante décorée passe l'appel à `update`. Cela est important, l'utilisateur voit notre réaction à leur mouvement immédiatement.\n2. Puis, alors que la souris continue d'avancer, il ne se passe plus rien jusqu'à `100ms`. La variante décorée ignore les appels.\n3. À la fin de `100ms` - une autre `update` se produit avec les dernières coordonnées.\n4. Enfin, la souris s’arrête quelque part. La variante décorée attend que `100ms` expire, puis lance `update` avec les dernières coordonnées. Donc, peut-être le plus important, les coordonnées finales de la souris sont traitées.\n\nUn exemple de code:\n\n```js\nfunction f(a) {\n  console.log(a);\n}\n\n// f1000 passe les appels à f au maximum une fois toutes les 1000 ms\nlet f1000 = throttle(f, 1000);\n\nf1000(1); // montre 1\nf1000(2); // (étranglement, 1000ms pas encore écoulée)\nf1000(3); // (étranglement, 1000ms pas encore écoulée)\n\n// quand 1000ms expirent...\n// ...sort 3, la valeur intermédiaire 2 a été ignorée\n```\n\nP.S. Les arguments et le contexte `this` transmis à `f1000` doivent être transmis à `f` d'origine.\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/article.md",
    "content": "# Décorateurs et transferts, call/apply\n\nJavaScript offre une flexibilité exceptionnelle dans le traitement des fonctions. Elles peuvent être échangées, utilisés comme objets, et maintenant nous allons voir comment *transférer* les appels entre eux et *les décorer*.\n\n## Cache transparent\n\nDisons que nous avons une fonction `slow(x)` qui nécessite beaucoup de ressources processeur, mais ses résultats sont stables. En d'autres termes, pour le même `x`, le résultat est toujours le même.\n\nSi la fonction est appelée souvent, nous voudrons peut-être mettre en mémoire cache (mémoriser) les résultats pour éviter de passer plus de temps sur les re-calculs.\n\nMais au lieu d’ajouter cette fonctionnalité à `slow()`, nous allons créer une fonction wrapper qui ajoute la mise en cache. Comme nous le verrons, cela présente de nombreux avantages.\n\nVoici le code, et les explications suivent :\n\n```js run\nfunction slow(x) {\n  // il peut y avoir un travail gourmand en ressources processeur ici\n  alert(`Called with ${x}`);\n  return x;\n}\n\nfunction cachingDecorator(func) {\n  let cache = new Map();\n\n  return function(x) {\n    if (cache.has(x)) {    // s'il y a une telle clé dans le cache\n      return cache.get(x); // lire le résultat\n    }\n\n    let result = func(x);  // sinon appeler func\n\n    cache.set(x, result);  // et mettre le résultat en cache\n    return result;\n  };\n}\n\nslow = cachingDecorator(slow);\n\nalert( slow(1) ); // slow(1) est mis en cache et le résultat est renvoyé\nalert( \"Again: \" + slow(1) ); // le résultat slow(1) est retourné à partir du cache\n\nalert( slow(2) ); // slow(2) est mis en cache et le résultat est renvoyé\nalert( \"Again: \" + slow(2) ); // le résultat slow(2) est retourné à partir du cache\n```\n\nDans le code ci-dessus, `cachingDecorator` est un *décorateur* : une fonction spéciale qui prend une autre fonction et modifie son comportement.\n\nL'idée est que nous pouvons appeler `cachingDecorator` pour n'importe quelle fonction, ce qui renverra le wrapper de mise en cache. C'est formidable, car nous pouvons avoir de nombreuses fonctions qui pourraient utiliser une telle fonctionnalité, et tout ce que nous avons à faire est de leur appliquer `cachingDecorator`.\n\nEn séparant la mise en cache du code de la fonction principale, nous simplifions également le code principal.\n\nLe résultat de `cachingDecorator(func)` est un \"wrapper\" : `function(x)` qui \"encapsule\" l'appel de `func(x)` dans la logique de mise en cache :\n\n![](decorator-makecaching-wrapper.svg)\n\nDepuis un code extérieur, la fonction encapsulée `slow` fait toujours la même chose. Un comportement de mise en cache vient d’être ajouté à son comportement.\n\nPour résumer, il y a plusieurs avantages à utiliser un `cachingDecorator` distinct au lieu de modifier le code de `slow` lui-même :\n\n- Le `cachingDecorator` est réutilisable. Nous pouvons l'appliquer à une autre fonction.\n- La logique de mise en cache est séparée, elle n’a pas augmenté la complexité de `slow` lui-même (s’il en existait).\n- Nous pouvons combiner plusieurs décorateurs si nécessaire (d'autres décorateurs suivront).\n\n## Utilisation de \"func.call\" pour le contexte\n\nLe décorateur de mise en cache mentionné ci-dessus n'est pas adapté pour travailler avec des méthodes d'objet.\n\nPar exemple, dans le code ci-dessous `worker.slow()` cesse de fonctionner après la décoration :\n\n```js run\n// on ajoutera une fonctionalité de cache à worker.slow\nlet worker = {\n  someMethod() {\n    return 1;\n  },\n\n  slow(x) {\n    // tâche lourde et effrayante pour le CPU ici\n    alert(\"Called with \" + x);\n    return x * this.someMethod(); // (*)\n  }\n};\n\n// même code que précédemment\nfunction cachingDecorator(func) {\n  let cache = new Map();\n  return function(x) {\n    if (cache.has(x)) {\n      return cache.get(x);\n    }\n*!*\n    let result = func(x); // (**)\n*/!*\n    cache.set(x, result);\n    return result;\n  };\n}\n\nalert( worker.slow(1) ); // la méthode originale fonctionne\n\nworker.slow = cachingDecorator(worker.slow); // ajoute la mise en cache\n\n*!*\nalert( worker.slow(2) ); // Whoops! Error: Cannot read property 'someMethod' of undefined\n*/!*\n```\n\nL'erreur se produit dans la ligne `(*)` qui tente d'accéder à `this.someMethod` et échoue. Pouvez-vous voir pourquoi ?\n\nLa raison en est que le wrapper appelle la fonction d'origine sous la forme `func(x)` dans la ligne `(**)`. Et, lorsqu'elle est appelée comme ça, la fonction obtient `this = undefined`.\n\nNous observerions un symptôme similaire si nous essayions d'executer :\n\n```js\nlet func = worker.slow;\nfunc(2);\n```\n\nAinsi, le wrapper passe l'appel à la méthode d'origine, mais sans le contexte `this`. D'où l'erreur.\n\nRéparons-le.\n\nIl existe une méthode de fonction intégrée spéciale [func.call(context, ...args)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Function/call) qui permet d'appeler explicitement une fonction en définissant `this`.\n\nLa syntaxe est la suivante :\n\n```js\nfunc.call(context, arg1, arg2, ...)\n```\n\nIl exécute `func` en fournissant `this` comme le premier argument et les suivants en tant qu'arguments.\n\nPour le dire simplement, ces deux appels font presque la même chose :\n\n```js\nfunc(1, 2, 3);\nfunc.call(obj, 1, 2, 3)\n```\n\nIls appellent tous les deux `func` avec les arguments `1`, `2` et `3`. La seule différence est que `func.call` définit également `this` sur `obj`.\n\nPar exemple, dans le code ci-dessous, nous appelons `sayHi` dans le contexte de différents objets : `sayHi.call(user)` exécute `sayHi` fournissant `this = user`, et la ligne suivante définit `this = admin` :\n\n```js run\nfunction sayHi() {\n  alert(this.name);\n}\n\nlet user = { name: \"John\" };\nlet admin = { name: \"Admin\" };\n\n// utilisons call pour passer différents objets en tant que \"this\"\nsayHi.call( user ); // John\nsayHi.call( admin ); // Admin\n```\n\nEt ici, nous utilisons `call` pour appeler `say` avec le contexte et la phrase donnés :\n\n```js run\nfunction say(phrase) {\n  alert(this.name + ': ' + phrase);\n}\n\nlet user = { name: \"John\" };\n\n// user devient this, et \"Hello\" devient le premier argument\nsay.call( user, \"Hello\" ); // John: Hello\n```\n\nDans notre cas, nous pouvons utiliser `call` dans le wrapper pour passer le contexte à la fonction d'origine :\n\n```js run\nlet worker = {\n  someMethod() {\n    return 1;\n  },\n\n  slow(x) {\n    alert(\"Called with \" + x);\n    return x * this.someMethod(); // (*)\n  }\n};\n\nfunction cachingDecorator(func) {\n  let cache = new Map();\n  return function(x) {\n    if (cache.has(x)) {\n      return cache.get(x);\n    }\n*!*\n    let result = func.call(this, x); // \"this\" est passé correctement maintenant\n*/!*\n    cache.set(x, result);\n    return result;\n  };\n}\n\nworker.slow = cachingDecorator(worker.slow); // ajoute la mise en cache\n\nalert( worker.slow(2) ); // ça fonctione\nalert( worker.slow(2) ); // ça fonctionne, n'appelle pas l'original (mis en cache)\n```\n\nMaintenant, tout va bien.\n\nPour que tout soit clair, voyons plus en détail comment `this` est passé :\n\n1. Après la décoration, `worker.slow` est désormais le wrapper `function(x) {...}`.\n2. Ainsi, lorsque `worker.slow(2)` est exécuté, le wrapper obtient `2` en argument et `this = worker` (c'est l'objet avant le point).\n3. Dans le wrapper, en supposant que le résultat ne soit pas encore mis en cache, `func.call(this, x)` passe le `this` (`= worker`) actuel et l'argument actuel (`= 2`) à la méthode d'origine.\n\n## Passer plusieurs arguments\n\nRendons maintenant `cachingDecorator` encore plus universel. Jusqu'à présent, il ne travaillait qu'avec des fonctions à un seul argument.\n\nMaintenant, comment mettre en cache la méthode multi-argument `worker.slow` ?\n\n```js\nlet worker = {\n  slow(min, max) {\n    return min + max; // la tâche est supposée lourde\n  }\n};\n\n// devrait se rappeler des appels au mêmes arguments\nworker.slow = cachingDecorator(worker.slow);\n```\n\nAuparavant, pour un seul argument, `x`, nous pouvions simplement `cache.set(x, result)` pour enregistrer le résultat et `cache.get(x)` pour le récupérer. Mais maintenant, nous devons nous rappeler le résultat pour une *combinaison d'arguments* `(min, max)`. Le `Map` natif prend une valeur unique en tant que clé.\n\nIl y a beaucoup de solutions possibles :\n\n1. Mettre en œuvre une nouvelle structure de données similaire à `Map` (ou utiliser une par une tierce partie) plus polyvalent et permetant l'utilisation de plusieurs clés.\n2. Utilisez des maps imbriquées : `cache.set(min)` sera un `Map` qui stocke la paire `(max, result)`. Donc, nous pouvons obtenir `result` avec `cache.get (min).get(max)`.\n3. Joignez deux valeurs en une. Dans notre cas particulier, nous pouvons simplement utiliser la chaîne `\"min, max\"` comme clé pour `Map`. Pour plus de flexibilité, nous pouvons permettre de fournir une *fonction de hachage* au décorateur, qui sait créer une valeur parmi plusieurs.\n\nPour de nombreuses applications pratiques, la 3ème variante est suffisante, nous allons donc nous y tenir.\n\nNous devons également transmettre non seulement `x`, mais tous les arguments dans `func.call`. Rappelons que dans une `function()` on peut obtenir un pseudo-tableau de ses arguments comme `arguments`, donc `func.call(this, x)` doit être remplacé par `func.call(this, ...arguments)`.\n\nVoici un `cachingDecorator` plus puissant :\n\n```js run\nlet worker = {\n  slow(min, max) {\n    alert(`Called with ${min},${max}`);\n    return min + max;\n  }\n};\n\nfunction cachingDecorator(func, hash) {\n  let cache = new Map();\n  return function() {\n*!*\n    let key = hash(arguments); // (*)\n*/!*\n    if (cache.has(key)) {\n      return cache.get(key);\n    }\n\n*!*\n    let result = func.call(this, ...arguments); // (**)\n*/!*\n\n    cache.set(key, result);\n    return result;\n  };\n}\n\nfunction hash(args) {\n  return args[0] + ',' + args[1];\n}\n\nworker.slow = cachingDecorator(worker.slow, hash);\n\nalert( worker.slow(3, 5) ); // ça marche\nalert( \"Again \" + worker.slow(3, 5) ); // pareil (mis en cache)\n```\n\nMaintenant, cela fonctionne avec n'importe quel nombre d'arguments (bien que la fonction de hachage doive également être ajustée pour permettre n'importe quel nombre d'arguments. Une façon intéressante de gérer cela sera traitée ci-dessous).\n\nIl y a deux changements :\n\n- Dans la ligne `(*)`, il appelle `hash` pour créer une clé unique à partir de `arguments`. Ici, nous utilisons une simple fonction \"d'assemblage\" qui transforme les arguments `(3, 5)` en la clé `\"3,5\"`. Les cas plus complexes peuvent nécessiter d'autres fonctions de hachage.\n- Ensuite `(**)` utilise `func.call(this, ...arguments)` pour transmettre le contexte et tous les arguments obtenus par le wrapper (pas seulement le premier) à la fonction d'origine.\n\n## func.apply\n\nInstead of `func.call(this, ...arguments)` we could use `func.apply(this, arguments)`.\n\nThe syntax of built-in method [func.apply](mdn:js/Function/apply) is:\n\n```js\nfunc.apply(context, args)\n```\n\nIt runs the `func` setting `this=context` and using an array-like object `args` as the list of arguments.\n\nThe only syntax difference between `call` and `apply` is that `call` expects a list of arguments, while `apply` takes an array-like object with them.\n\nSo these two calls are almost equivalent:\n\n```js\nfunc.call(context, ...args);\nfunc.apply(context, args);\n```\n\nThey perform the same call of `func` with given context and arguments.\n\nThere's only a subtle difference regarding `args`:\n\n- The spread syntax `...` allows to pass *iterable* `args` as the list to `call`.\n- The `apply` accepts only *array-like* `args`.\n\n...And for objects that are both iterable and array-like, such as a real array, we can use any of them, but `apply` will probably be faster, because most JavaScript engines internally optimize it better.\n\nPassing all arguments along with the context to another function is called *call forwarding*.\n\nThat's the simplest form of it:\n\n```js\nlet wrapper = function() {\n  return func.apply(this, arguments);\n};\n```\n\nWhen an external code calls such `wrapper`, it is indistinguishable from the call of the original function `func`.\n\n## Emprunter une méthode [#method-borrowing]\n\nMaintenant, apportons une autre amélioration mineure à la fonction de hachage :\n\n```js\nfunction hash(args) {\n  return args[0] + ',' + args[1];\n}\n```\n\nPour l'instant, cela ne fonctionne que sur deux arguments. Ce serait mieux s'il pouvait coller un nombre quelconque de `args`.\n\nLa solution naturelle serait d'utiliser la méthode [arr.join](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/join) :\n\n```js\nfunction hash(args) {\n  return args.join();\n}\n```\n\n... Malheureusement, ça ne marchera pas. Parce que nous appelons `hash(arguments)` et l’objet `arguments` est à la fois itérable et semblable à un tableau, mais pas un vrai tableau.\n\nDonc, appeler `join` échouerait, comme on peut le voir ci-dessous :\n\n```js run\nfunction hash() {\n*!*\n  alert( arguments.join() ); // Error: arguments.join is not a function\n*/!*\n}\n\nhash(1, 2);\n```\n\nNéanmoins, il existe un moyen simple d’utiliser `join` :\n\n```js run\nfunction hash() {\n*!*\n  alert( [].join.call(arguments) ); // 1,2\n*/!*\n}\n\nhash(1, 2);\n```\n\nL'astuce s'appelle *method borrowing* (empruntage de méthode).\n\nNous prenons (empruntons) la méthode `join` d'un tableau régulier (`[].join`) et utilisons `[].join.call` pour l'exécuter dans le contexte des `arguments`.\n\nPourquoi ça marche ?\n\nC'est parce que l'algorithme interne de la méthode native `arr.join(glue)` est très simple.\n\nTiré de la spécification presque \"tel quel\" :\n\n1. Soit `glue` le premier argument ou, s’il n’ya pas d’argument, une virgule `\",\"`.\n2. Soit `result` une chaîne de caractères vide.\n3. Ajoutez `this[0]` à `result`.\n4. Ajoutez `glue` et `this[1]`.\n5. Ajoutez `glue` et `this[2]`.\n6. ... Faites-le jusqu'à ce que `this.length` éléments soient collés.\n7. Retournez `result`.\n\nDonc, techniquement, cela prend `this` et associe `this[0]`, `this[1]`... etc. Il est intentionnellement écrit de manière à permettre à tout type de tableau `this` (ce n'est pas une coïncidence, de nombreuses méthodes suivent cette pratique). C'est pourquoi cela fonctionne aussi avec `this = arguments`.\n\n## Décorateurs et propriétés fonctionnelles\n\nIl est généralement prudent de remplacer une fonction ou une méthode par une fonction décorée, à une exception près. Si la fonction d'origine comportait des propriétés, telles que `func.calledCount` ou autre, la fonction décorée ne les fournira pas. Parce que c'est un wrapper. Il faut donc faire attention si on les utilise.\n\nDans l'exemple ci-dessus, si la fonction `slow` avait des propriétés, alors `cachingDecorator(slow)` est un wrapper sans elles.\n\nCertains décorateurs peuvent fournir leurs propres propriétés. Par exemple un décorateur peut compter le nombre de fois où une fonction a été appelée et combien de temps cela a pris, et exposer ces informations via les propriétés du wrapper.\n\nIl existe un moyen de créer des décorateurs qui conservent l'accès aux propriétés de la fonction, mais cela nécessite l'utilisation d'un objet `Proxy` spécial pour envelopper une fonction. Nous en discuterons plus tard dans l'article <info:proxy#proxy-apply>.\n\n## Résumé\n\n*Decorator* est un wrapper autour d'une fonction qui modifie son comportement. Le travail principal est toujours effectué par la fonction.\n\nLes décorateurs peuvent être considérés comme des \"caractéristiques\" ou des \"aspects\" pouvant être ajoutés à une fonction. Nous pouvons en ajouter un ou en ajouter plusieurs. Et tout ça sans changer son code !\n\nPour implémenter `cachingDecorator`, nous avons étudié les méthodes :\n\n- [func.call(context, arg1, arg2...)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Function/call) -- appelle `func` avec un contexte et des arguments donnés.\n- [func.apply(context, args)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Function/apply) -- appelle `func` en passant `context` comme `this` et `args` sous forme de tableau dans une liste d'arguments.\n\nLe renvoi d'appel, *call forwarding*, est généralement effectué avec `apply` :\n\n```js\nlet wrapper = function() {\n  return original.apply(this, arguments);\n};\n```\n\nNous avons également vu un exemple d'empruntage de méthode, *method borrowing*, lorsque nous prenons une méthode à partir d'un objet et que nous l'appelons dans le contexte d'un autre objet. Il est assez courant de prendre des méthodes de tableau et de les appliquer à `arguments`. L'alternative consiste à utiliser l'objet de paramètres du reste qui est un vrai tableau.\n\nIl y a beaucoup de décorateurs dans la nature. Vérifiez si vous les avez bien obtenus en résolvant les tâches de ce chapitre.\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/2-write-to-object-after-bind/solution.md",
    "content": "The answer: `null`.\n\n\n```js run\nfunction f() {\n  alert( this ); // null\n}\n\nlet user = {\n  g: f.bind(null)\n};\n\nuser.g();\n```\n\nThe context of a bound function is hard-fixed. There's just no way to further change it.\n\nSo even while we run `user.g()`, the original function is called with `this=null`.\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/2-write-to-object-after-bind/task.md",
    "content": "importance: 5\n\n---\n\n# Bound function as a method\n\nWhat will be the output?\n\n```js\nfunction f() {\n  alert( this ); // ?\n}\n\nlet user = {\n  g: f.bind(null)\n};\n\nuser.g();\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/3-second-bind/solution.md",
    "content": "The answer: **John**.\n\n```js run no-beautify\nfunction f() {\n  alert(this.name);\n}\n\nf = f.bind( {name: \"John\"} ).bind( {name: \"Pete\"} );\n\nf(); // John\n```\n\nThe exotic [bound function](https://tc39.github.io/ecma262/#sec-bound-function-exotic-objects) object returned by `f.bind(...)` remembers the context (and arguments if provided) only at creation time. \n\nA function cannot be re-bound.\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/3-second-bind/task.md",
    "content": "importance: 5\n\n---\n\n# Second bind\n\nCan we change `this` by additional binding?\n\nWhat will be the output?\n\n```js no-beautify\nfunction f() {\n  alert(this.name);\n}\n\nf = f.bind( {name: \"John\"} ).bind( {name: \"Ann\" } );\n\nf();\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/4-function-property-after-bind/solution.md",
    "content": "The answer: `undefined`.\n\nThe result of `bind` is another object. It does not have the `test` property.\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/4-function-property-after-bind/task.md",
    "content": "importance: 5\n\n---\n\n# Function property after bind\n\nThere's a value in the property of a function. Will it change after `bind`? Why, or why not?\n\n```js run\nfunction sayHi() {\n  alert( this.name );\n}\nsayHi.test = 5;\n\n*!*\nlet bound = sayHi.bind({\n  name: \"John\"\n});\n\nalert( bound.test ); // what will be the output? why?\n*/!*\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md",
    "content": "\nThe error occurs because `ask` gets functions `loginOk/loginFail` without the object.\n\nWhen it calls them, they naturally assume `this=undefined`.\n\nLet's `bind` the context:\n\n```js run\nfunction askPassword(ok, fail) {\n  let password = prompt(\"Password?\", '');\n  if (password == \"rockstar\") ok();\n  else fail();\n}\n\nlet user = {\n  name: 'John',\n\n  loginOk() {\n    alert(`${this.name} logged in`);\n  },\n\n  loginFail() {\n    alert(`${this.name} failed to log in`);\n  },\n\n};\n\n*!*\naskPassword(user.loginOk.bind(user), user.loginFail.bind(user));\n*/!*\n```\n\nNow it works.\n\nAn alternative solution could be:\n```js\n//...\naskPassword(() => user.loginOk(), () => user.loginFail());\n```\n\nUsually that also works and looks good.\n\nIt's a bit less reliable though in more complex situations where `user` variable might change *after* `askPassword` is called, but *before* the visitor answers and calls `() => user.loginOk()`. \n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/5-question-use-bind/task.md",
    "content": "importance: 5\n\n---\n\n# Fix a function that loses \"this\"\n\nThe call to `askPassword()` in the code below should check the password and then call `user.loginOk/loginFail` depending on the answer.\n\nBut it leads to an error. Why?\n\nFix the highlighted line for everything to start working right (other lines are not to be changed).\n\n```js run\nfunction askPassword(ok, fail) {\n  let password = prompt(\"Password?\", '');\n  if (password == \"rockstar\") ok();\n  else fail();\n}\n\nlet user = {\n  name: 'John',\n\n  loginOk() {\n    alert(`${this.name} logged in`);\n  },\n\n  loginFail() {\n    alert(`${this.name} failed to log in`);\n  },\n\n};\n\n*!*\naskPassword(user.loginOk, user.loginFail);\n*/!*\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/6-ask-partial/solution.md",
    "content": "\n\n1. Soit utiliser une fonction wrapper, une fléchée pour être concis:\n\n    ```js \n    askPassword(() => user.login(true), () => user.login(false)); \n    ```\n\n    Maintenant, elle obtient `user` des variables externes et l'exécute normalement.\n\n2. Ou créez une fonction partielle à partir de `user.login` qui utilise `user` comme contexte et a le bon premier argument:\n\n\n    ```js \n    askPassword(user.login.bind(user, true), user.login.bind(user, false)); \n    ```\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/6-ask-partial/task.md",
    "content": "importance: 5\n\n---\n\n# Application de fonction partielle pour login\n\nLa tâche est une variante un peu plus complexe de <info:task/question-use-bind>. \n\nL'objet `user` a été modifié. Maintenant, au lieu de deux fonctions `loginOk/loginFail`, il a une seule fonction `user.login(true/false)`.\n\nQue faire passer à `askPassword` dans le code ci-dessous, de sorte qu'il appelle `user.login(true)` comme `ok` et `user.login(false)` comme `fail` ?\n\n```js\nfunction askPassword(ok, fail) {\n  let password = prompt(\"Password?\", '');\n  if (password == \"rockstar\") ok();\n  else fail();\n}\n\nlet user = {\n  name: 'John',\n\n  login(result) {\n    alert( this.name + (result ? ' logged in' : ' failed to log in') );\n  }\n};\n\n*!*\naskPassword(?, ?); // ?\n*/!*\n```\n\nVos modifications doivent uniquement modifier le fragment surligné.\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/article.md",
    "content": "libs:\n  - lodash\n\n---\n\n# Le \"bind\" de fonction\n\nLorsque l'on transmet des méthodes objets en tant que callbacks, par exemple à `setTimeout`, il y a un problème connu : \"la perte du `this`\".\n\nDans ce chapitre nous verrons les façons de régler ça.\n\n## La perte du \"this\"\n\nNous avons déjà vu des exemples de la perte du `this`. Une fois qu'une méthode est passée quelque part séparement de l'objet -- `this` est perdu.\n\nVoici comment cela pourrait arriver avec `setTimeout` :\n\n```js run\nlet user = {\n  firstName: \"John\",\n  sayHi() {\n    alert(`Hello, ${this.firstName}!`);\n  }\n};\n\n*!*\nsetTimeout(user.sayHi, 1000); // Hello, undefined!\n*/!*\n```\n\nComme nous pouvons le voir, la sortie n'affiche pas \"John\" pour `this.firstName`, mais `undefined` !\n\nC'est car `setTimeout` a eu la fonction `user.sayHi`, séparement de l'objet. La dernière ligne pourrait être réécrite comme :\n\n```js\nlet f = user.sayHi;\nsetTimeout(f, 1000); // Perte du contexte d'user\n```\n\nLa méthode `setTimeout` dans le navigateur est un peu spéciale : elle définit `this=window` pour l'appel à la fonction (pour Node.js, `this` devient un objet \"timer\", mais ça n'a pas d'importance ici). Donc pour `this.firstName` il essaye de récuperer `window.firstName`, qui n'existe pas. Dans d'autres cas similaires, `this` devient généralement `undefined`.\n\nCette tâche est plutôt commune -- on veut transmettre une méthode objet quelque part ailleurs (ici -- au scheduler) où elle sera appelée.\nComment s'assurer qu'elle sera appelée dans le bon contexte ?\n\n## Solution 1 : Un wrapper\n\nLa solution la plus simple est d'utiliser une fonction enveloppée :\n\n```js run\nlet user = {\n  firstName: \"John\",\n  sayHi() {\n    alert(`Hello, ${this.firstName}!`);\n  }\n};\n\n*!*\nsetTimeout(function() {\n  user.sayHi(); // Hello, John!\n}, 1000);\n*/!*\n```\n\nMaintenant ça fonctionne, car elle reçoit `user` depuis un environnement lexical extérieur, et donc les appels à la fonction se font normalement.\n\nLa même chose mais en plus court :\n\n```js\nsetTimeout(() => user.sayHi(), 1000); // Hello, John!\n```\n\nÇa à l'air bon, mais une légère vulnérabilité apparaît dans la structure de notre code.\n\nQue se passe t-il si avant le déclenchement de `setTimeout` (il y une seconde de délai) `user` changeait de valeur ? Alors, soudainement, ça appelera le mauvais objet !\n\n```js run\nlet user = {\n  firstName: \"John\",\n  sayHi() {\n    alert(`Hello, ${this.firstName}!`);\n  }\n};\n\nsetTimeout(() => user.sayHi(), 1000);\n\n// ...La valeur d'user dans 1 seconde\nuser = {\n  sayHi() { alert(\"Another user in setTimeout!\"); }\n};\n\n// Un autre user est dans le setTimeout !\n```\n\nLa prochaine solution garantit que ce genre de chose n'arrivera pas\n\n## Solution 2 : \"bind\"\n\nLes fonctions fournissent une méthode intégrée, [bind](mdn:js/Function/bind) qui permet de corriger `this`.\n\nLa syntaxe basique est :\n\n```js\n// Une syntaxe plus complexe arrivera bientot\nlet boundFunc = func.bind(context);\n```\n\nLe résultat de `func.bind(context)` est une \"objet exotique\" dans le style d'une fonction, qui est appellable comme une fonction et qui transmet l'appel à `func` en définissant `this=context` de façon transparente.\n\nEn d'autres termes, appeller `boundFunc` équivaut à `func` avec un `this` corrigé.\n\nPar exemple, ici `funcUser` passe l'appel à `this` tel que `this=user` :\n\n```js run  \nlet user = {\n  firstName: \"John\"\n};\n\nfunction func() {\n  alert(this.firstName);\n}\n\n*!*\nlet funcUser = func.bind(user);\nfuncUser(); // John  \n*/!*\n```\n\nIci `func.bind(user)` en tant \"variante liée\" de `func`, avec `this=user`.\n\nTous les arguments sont passés à l'originale `func` \"tels quels\", par exemple :\n\n```js run  \nlet user = {\n  firstName: \"John\"\n};\n\nfunction func(phrase) {\n  alert(phrase + ', ' + this.firstName);\n}\n\n// Lie this à user\nlet funcUser = func.bind(user);\n\n*!*\nfuncUser(\"Hello\"); // Hello, John (l'argument \"Hello\" est passé, et this=user)\n*/!*\n```\n\nMaintenant essayons avec une méthode objet :\n\n\n```js run\nlet user = {\n  firstName: \"John\",\n  sayHi() {\n    alert(`Hello, ${this.firstName}!`);\n  }\n};\n\n*!*\nlet sayHi = user.sayHi.bind(user); // (*)\n*/!*\n\n// Peut s'exécuter sans objet\nsayHi(); // Hello, John!\n\nsetTimeout(sayHi, 1000); // Hello, John!\n\n// Mème si la valeur de user change dans 1 seconde\n// sayHi utilise la valeur pré-liée, laquelle fait référence à l'ancien objet user\nuser = {\n  sayHi() { alert(\"Another user in setTimeout!\"); }\n};\n```\n\nSur la ligne `(*)` nous prenons la méthode `user.sayHi` en nous la lions à `user`. La méthode `sayHi` est une fonction \"liée\", qui peut être appelée seule ou être transmise à `setTimeout` -- ça n'a pas d'importance, le contexte sera le bon.\n\nIci, nous pouvons voir que les arguments passés \"tels quels\", seulement `this` est corrigé par `bind` :\n\n```js run\nlet user = {\n  firstName: \"John\",\n  say(phrase) {\n    alert(`${phrase}, ${this.firstName}!`);\n  }\n};\n\nlet say = user.say.bind(user);\n\nsay(\"Hello\"); // Hello, John! (l'argument \"Hello\" est passé à say)\nsay(\"Bye\"); // Bye, John! (l'argument \"Bye\" est passé à say)\n```\n\n````smart header=\"La méthode pratique : `bindAll`\"\nSi un objet a plusieurs méthodes et que nous prévoyons de le transmettre plusieurs fois, alors on pourrait toutes les lier dans une boucle :\n\n```js\nfor (let key in user) {\n  if (typeof user[key] == 'function') {\n    user[key] = user[key].bind(user);\n  }\n}\n```\n\nLes librairies JavaScript fournissent aussi des fonctions pratiques pour les liaisons de masse, e.g. [_.bindAll(object, methodNames)](https://lodash.com/docs#bindAll) avec lodash.\n````\n\n## Les fonctions partielles\n\nJusqu'à maintenant nous avons parlé uniquement de lier `this`. Allons plus loin.\n\nNous pouvons lier `this`, mais aussi des arguments. C'est rarement utilisé, mais ça peut être pratique.\n\nLa syntaxe complète de `bind` :\n\n```js\nlet bound = func.bind(context, [arg1], [arg2], ...);\n```\n\nElle permet de lier le contexte en tant que `this` et de démarrer les arguments de la fonction.\n\nPar exemple, nous avons une fonction de multiplication `mul(a, b)` :\n\n```js\nfunction mul(a, b) {\n  return a * b;\n}\n```\n\nUtilisons `bind` pour créer une fonction `double` sur cette base :\n\n```js run\nfunction mul(a, b) {\n  return a * b;\n}\n\n*!*\nlet double = mul.bind(null, 2);\n*/!*\n\nalert( double(3) ); // = mul(2, 3) = 6\nalert( double(4) ); // = mul(2, 4) = 8\nalert( double(5) ); // = mul(2, 5) = 10\n```\n\nL'appel à `mul.bind(null, 2)` créer une nouvelle fonction `double` qui transmet les appels à `mul`, corrigeant `null` dans le contexte et `2` comme premier argument. Les arguments sont passés \"tels quels\" plus loin.\n\nÇa s'appelle [l'application de fonction partielle](https://en.wikipedia.org/wiki/Partial_application) -- nous créeons une nouvelle fonction en corrigeant certains paramètres d'une fonction existante.\n\nVeuillez noter que nous n'utilisons actuellement pas `this` ici. Mais `bind` en a besoin, donc nous devrions mettre quelque chose dedans comme `null`.\n\nLa fonction `triple` dans le code ci-dessous triple la valeur :\n\n```js run\nfunction mul(a, b) {\n  return a * b;\n}\n\n*!*\nlet triple = mul.bind(null, 3);\n*/!*\n\nalert( triple(3) ); // = mul(3, 3) = 9\nalert( triple(4) ); // = mul(3, 4) = 12\nalert( triple(5) ); // = mul(3, 5) = 15\n```\n\nPourquoi faisons nous généralement une fonction partielle ?\n\nL'avantage de faire ça est que nous pouvons créer une fonction indépendante avec un nom lisible (`double`, `triple`). Nous pouvons les utiliser et ne pas fournir de premier argument à chaque fois puisque c'est corrigé par `bind`.\n\nDans d'autres cas, les fonctions partielles sont utiles quand nous avons des fonctions vraiment génériques et que nous voulons une variante moins universelle pour des raisons pratiques.\n\nPar exemple, nous avons une fonction `send(from, to, text)`. Alors, dans un objet `user` nous pourrions vouloir en utiliser une variante partielle : `sendTo(to, text)` qui envoie depuis l'utilisateur actuel.\n\n## Aller dans les partielles sans contexte\n\nQue se passerait t-il si nous voulions corriger certains arguments, mais pas le contexte `this` ?\nPar exemple, pour une méthode objet.\n\nLa fonction `bind` native ne permet pas ça. Nous ne pouvons pas juste omettre le contexte et aller directement aux arguments.\n\nHeureusement, une fonction `partial` pour lier seulement les arguments peut être facilement implémentée.\n\nComme ça :\n\n```js run\n*!*\nfunction partial(func, ...argsBound) {\n  return function(...args) { // (*)\n    return func.call(this, ...argsBound, ...args);\n  }\n}\n*/!*\n\n// Utilisation :\nlet user = {\n  firstName: \"John\",\n  say(time, phrase) {\n    alert(`[${time}] ${this.firstName}: ${phrase}!`);\n  }\n};\n\n// Ajoute une méthode partielle avec time corrigé\nuser.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes());\n\nuser.sayNow(\"Hello\");\n// Quelque chose du genre :\n// [10:00] John: Hello!\n```\n\nLe résultat de l'appel `partial(func[, arg1, arg2...])` est une enveloppe `(*)` qui appelle `func` avec :\n- Le même `this` qu'il récupère (pour `user.sayNow` l'appel est `user`)\n- Alors il donne `...argsBound` -- les arguments provenant de l'appel de `partial` (`\"10:00\"`)\n- Alors il donne `...args` -- les arguments donnés à l'enveloppe (`\"Hello\"`)\n\nAlors, c'est simple à faire avec la spread syntaxe, pas vrai ?\n\nAussi il y a une implémentation de [_.partial](https://lodash.com/docs#partial) prête à l'emploi dans les librairies lodash.\n\n## Résumé\n\nLa méthode `func.bind(context, ...args)` retourne une \"variante liée\" de la fonction `func` qui corrige le contexte de `this` et des premiers arguments s'ils sont donnés.\n\nNous appliquons généralement `bind` pour corriger `this` pour une méthode objet, comme ça nous pouvons la passer ailleurs. Par exemple, à `setTimeout`.\n\nQuand nous corrigeons certains arguments d'une fonction existante, la fonction (moins universelle) en résultant est dite *partiellement appliquée* ou *partielle*.\n\nLes fonctions partielles sont pratiques quand nous ne voulons pas répéter le même argument encore et encore. Comme si nous avions une fonction `send(from, to)`, et que `from` devait être toujours le même pour notre tâche, nous pourrions récupérer une partielle et continuer.\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/head.html",
    "content": "<script>\nfunction mul(a, b) {\n  return a * b;\n};\n\nfunction ask(question, answer, ok, fail) {\n  let result = prompt(question, '');\n  if (result.toLowerCase() == answer.toLowerCase()) ok();\n  else fail();\n}\n\nfunction bind(func, context /*, args*/) {\n  let bindArgs = [].slice.call(arguments, 2); // (1)\n  function wrapper() {                        // (2)\n    let args = [].slice.call(arguments);\n    let unshiftArgs = bindArgs.concat(args);  // (3)\n    return func.apply(context, unshiftArgs);  // (4)\n  }\n  return wrapper;\n}\n</script>"
  },
  {
    "path": "1-js/06-advanced-functions/12-arrow-functions/article.md",
    "content": "# Les fonctions fléchées revisitées\n\nRevisitons les fonctions fléchées.\n\nLes fonctions fléchées ne sont pas simplement un \"raccourci\" pour écrire de petites choses. Elles ont des fonctionnalités très spécifiques et utiles.\n\nJavaScript est plein de situations dans lesquelles nous devons écrire une petite fonction exécutée ailleurs.\n\nPar exemple :\n\n- `arr.forEach(func)` -- `func` est exécutée par `forEach` pour chaque élément du tableau.\n- `setTimeout(func)` -- `func` est exécuté par le planificateur intégré.\n- ... plus à suivre.\n\nC'est dans l'esprit même de JavaScript de créer une fonction et de la transmettre quelque part.\n\nEt dans de telles fonctions, nous ne voulons généralement pas quitter le contexte actuel. C'est là que les fonctions fléchées sont utiles.\n\n## Les fonctions fléchées n'ont pas de \"this\"\n\nComme nous nous en souvenons du chapitre <info:object-methods>, les fonctions fléchées n'ont pas de `this`. Si `this` est utilisé, il est pris de l'extérieur.\n\nPar exemple, nous pouvons l'utiliser pour itérer à l'intérieur d'une méthode d'objet :\n\n```js run\nlet group = {\n  title: \"Our Group\",\n  students: [\"John\", \"Pete\", \"Alice\"],\n\n  showList() {\n*!*\n    this.students.forEach(\n      student => alert(this.title + ': ' + student)\n    );\n*/!*\n  }\n};\n\ngroup.showList();\n```\n\nIci, dans `forEach`, la fonction fléchée est utilisée, donc `this.title` est exactement la même chose que dans la méthode externe `showList`. C'est-à-dire `group.title`.\n\nSi nous utilisions une fonction \"régulière\", il y aurait une erreur :\n\n```js run\nlet group = {\n  title: \"Our Group\",\n  students: [\"John\", \"Pete\", \"Alice\"],\n\n  showList() {\n*!*\n    this.students.forEach(function(student) {\n      // Error: Cannot read property 'title' of undefined\n      alert(this.title + ': ' + student);\n    });\n*/!*\n  }\n};\n\ngroup.showList();\n```\n\nL'erreur se produit parce que `forEach` exécute des fonctions avec `this = undefined` par défaut, ce qui entraîne une tentative d'accès à `undefined.title`.\n\nCela n’affecte pas les fonctions fléchées, car elles n’ont tout simplement pas de `this`.\n\n```warn header=\"Les fonctions fléchées ne peuvent pas fonctionner avec `new`\"\nNe pas avoir de `this` signifie naturellement une autre limitation : les fonctions fléchées ne peuvent pas être utilisées en tant que constructeurs. Elles ne peuvent pas être appelées avec `new`.\n```\n\n```smart header=\"Les fonctions fléchées VS bind\"\nIl y a une différence subtile entre une fonction fléchée `=>` et une fonction régulière appelée avec `.bind(this)` :\n\n- `.bind(this)` crée une \"version liée\" de la fonction.\n- La flèche `=>` ne crée aucune liaison. La fonction n'a tout simplement pas de `this`. La recherche de `this` est faite exactement de la même manière qu’une recherche de variable normale : dans l’environnement lexical externe.\n```\n\n## Les fonctions fléchées n'ont pas \"d'arguments\"\n\nLes fonctions fléchées n'ont pas non plus de variable `arguments`.\n\nC'est très bien pour les décorateurs, quand nous devons transférer un appel avec les `arguments` et `this` actuels.\n\nPar exemple, `defer(f, ms)` obtient une fonction et retourne un wrapper qui retarde l'appel de `ms` millisecondes :\n\n```js run\nfunction defer(f, ms) {\n  return function() {\n    setTimeout(() => f.apply(this, arguments), ms);\n  };\n}\n\nfunction sayHi(who) {\n  alert('Hello, ' + who);\n}\n\nlet sayHiDeferred = defer(sayHi, 2000);\nsayHiDeferred(\"John\"); // Hello, John après 2 secondes\n```\n\nLa même chose sans une fonction fléchée ressemblerait à ceci :\n\n```js\nfunction defer(f, ms) {\n  return function(...args) {\n    let ctx = this;\n    setTimeout(function() {\n      return f.apply(ctx, args);\n    }, ms);\n  };\n}\n```\n\nIci, nous avons dû créer des variables additionnelles `args` et `ctx` afin que la fonction à l'intérieur de `setTimeout` puisse les prendre.\n\n## Résumé\n\nLes fonctions fléchées :\n\n- N'ont pas de `this`\n- N'ont pas d'`arguments`\n- Ne peuvent pas être appelées avec `new`\n- Elles n'ont pas non plus de `super`, mais nous ne l'avons pas encore étudié. Nous le ferons dans le chapitre <info:class-inheritance>.\n\nC'est parce qu'elles sont destinées à de courts morceaux de code qui n'ont pas leur propre \"contexte\", mais qui fonctionnent dans le contexte actuel. Et elles brillent vraiment dans ce cas d'utilisation.\n"
  },
  {
    "path": "1-js/06-advanced-functions/index.md",
    "content": "# Travail avancé avec les fonctions\n"
  },
  {
    "path": "1-js/07-object-properties/01-property-descriptors/article.md",
    "content": "\n# Attributs et descripteurs de propriétés\n\nComme nous le savons, les objets peuvent stocker des propriétés.\n\nJusqu'à présent, une propriété était pour nous une simple paire \"clé-valeur\". Mais une propriété d'objet est en réalité une chose plus flexible et plus puissante.\n\nDans ce chapitre, nous étudierons des options de configuration supplémentaires et, dans le prochain, nous verrons comment les transformer de manière invisible en fonctions de accesseur / mutateur.\n\n## Attributs de propriétés\n\nLes propriétés des objets, outre que **`valeur`**, ont trois attributs spéciaux (appelés drapeaux, ou \"flags\" en anglais):\n\n- **`writable`** -- si `true`, la valeur peut être changée, sinon c'est en lecture seule.\n- **`enumerable`** -- si `true`, alors listé dans les boucles, sinon non listé.\n- **`configurable`** -- si `true`, la propriété peut être supprimée et ces attributs peuvent être modifiés, sinon non.\n\nNous ne les avons pas encore vues, car généralement elles ne se présentent pas. Lorsque nous créons une propriété \"de la manière habituelle\", ils sont tous `true`. Mais nous pouvons aussi les changer à tout moment.\n\nVoyons d’abord comment obtenir ces \"flags\".\n\nLa methode [Object.getOwnPropertyDescriptor](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/getOwnPropertyDescriptor) permet d'interroger les informations *complètes* à propos d'une propriété.\n\nLa syntaxe est la suivante :\n\n```js\nlet descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);\n```\n\n`obj`\n: L'objet à partir duquel obtenir des informations.\n\n`propertyName`\n: Le nom de la propriété.\n\nLa valeur renvoyée est un objet dit \"descripteur de propriété\" : il contient la valeur et tous les descripteurs.\n\nPar exemple :\n\n```js run\nlet user = {\n  name: \"John\"\n};\n\nlet descriptor = Object.getOwnPropertyDescriptor(user, 'name');\n\nalert( JSON.stringify(descriptor, null, 2 ) );\n/* property descriptor:\n{\n  \"value\": \"John\",\n  \"writable\": true,\n  \"enumerable\": true,\n  \"configurable\": true\n}\n*/\n```\n\nPour changer les attributs, on peut utiliser [Object.defineProperty](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/defineProperty).\n\nLa syntaxe est la suivante :\n\n```js\nObject.defineProperty(obj, propertyName, descriptor)\n```\n\n`obj`, `propertyName`\n: L'objet et sa propriété pour appliquer le descripteur.\n\n`descriptor`\n: Descripteur de propriété d'objet à appliquer.\n\nSi la propriété existe, `defineProperty` met à jour ses attributs. Sinon, il crée la propriété avec la valeur et les descripteurs donnés. Dans ce cas, si aucun drapeau n'est fourni, il est supposé `false`.\n\nPar exemple, ici, une propriété `name` est créée avec tous les attributs falsy :\n\n```js run\nlet user = {};\n\n*!*\nObject.defineProperty(user, \"name\", {\nvalue: \"John\"\n});\n*/!*\n\nlet descriptor = Object.getOwnPropertyDescriptor(user, 'name');\n\nalert( JSON.stringify(descriptor, null, 2 ) );\n/*\n{\n\"value\": \"John\",\n*!*\n\"writable\": false,\n\"enumerable\": false,\n\"configurable\": false\n*/!*\n}\n*/\n```\n\nComparez-le avec `user.name` \"normalement créé\" ci-dessus : maintenant tous les attributs sont falsy. Si ce n'est pas ce que nous voulons, nous ferions mieux de leur attribuer la valeur `true` dans `descriptor`.\n\nVoyons maintenant les effets des attributs par exemple.\n\n## Lecture seule\n\nRendons `user.name` en lecture seule (ne peut pas être réaffecté) en modifiant l'indicateur `writeable` :\n\n```js run\nlet user = {\nname: \"John\"\n};\n\nObject.defineProperty(user, \"name\", {\n*!*\nwritable: false\n*/!*\n});\n\n*!*\nuser.name = \"Pete\"; // Error: Cannot assign to read only property 'name'\n*/!*\n```\n\nMaintenant, personne ne peut changer le nom de notre utilisateur, à moins qu’ils appliquent leur propre `defineProperty` pour remplacer le nôtre.\n\n```smart header=\"Les erreurs apparaissent uniquement en mode strict\"\nEn mode non strict, aucune erreur ne se produit lors de l'écriture dans des propriétés non inscriptibles et autres. Mais l'opération ne réussira toujours pas. Les actions violant l'indicateur sont simplement ignorées en silence dans les non-stricts.\n```\n\nVoici le même exemple, mais la propriété est créée à partir de zéro :\n\n```js run\nlet user = { };\n\nObject.defineProperty(user, \"name\", {\n*!*\nvalue: \"John\",\n// pour les nouvelles propriétés, nous devons lister explicitement ce qui est vrai\nenumerable: true,\nconfigurable: true\n*/!*\n});\n\nalert(user.name); // John\nuser.name = \"Pete\"; // Error\n```\n\n## Non énumérable\n\nAjoutons maintenant un `toString` personnalisé à `user`.\n\nNormalement, un `toString` intégré pour les objets n'est pas énumérable, il n'apparaît pas dans `for..in`. Mais si nous ajoutons notre propre `toString`, alors, par défaut, il apparaît dans `for..in`, comme ceci :\n\n```js run\nlet user = {\nname: \"John\",\ntoString() {\n  return this.name;\n}\n};\n\n// Par défaut, nos deux propriétés sont répertoriées :\nfor (let key in user) alert(key); // name, toString\n```\n\nSi nous n'aimons pas cela, alors nous pouvons définir `enumerable: false`. Ensuite, il n'apparaîtra pas dans la boucle `for..in`, comme dans la boucle intégrée :\n\n```js run\nlet user = {\nname: \"John\",\ntoString() {\n  return this.name;\n}\n};\n\nObject.defineProperty(user, \"toString\", {\n*!*\nenumerable: false\n*/!*\n});\n\n*!*\n// Maintenant notre toString disparaît :\n*/!*\nfor (let key in user) alert(key); // name\n```\n\nLes propriétés non énumérables sont également exclues de `Object.keys` :\n\n```js\nalert(Object.keys(user)); // name\n```\n\n## Non configurable\n\nLe descripteur non configurable (`configurable: false`) est parfois prédéfini pour les objets et propriétés intégrés.\n\nUne propriété non configurable ne peut pas être supprimée, ses attributs ne peuvent pas être modifiés.\n\nPar exemple, `Math.PI` est en lecture seule, non énumérable et non configurable :\n\n```js run\nlet descriptor = Object.getOwnPropertyDescriptor(Math, 'PI');\n\nalert( JSON.stringify(descriptor, null, 2 ) );\n/*\n{\n\"value\": 3.141592653589793,\n\"writable\": false,\n\"enumerable\": false,\n\"configurable\": false\n}\n*/\n```\n\nAinsi, un programmeur est incapable de changer la valeur de `Math.PI` ou de le remplacer.\n\n```js run\nMath.PI = 3; // Error, because it has writable: false\n\n// supprimer Math.PI ne fonctionnera pas non plus\n```\n\nNous ne pouvons pas non plus changer `Math.PI` pour qu'il soit à nouveau `writable` (éditable) :\n\n```js run\n// Error, parce que configurable: false\nObject.defineProperty(Math, \"PI\", { writable: true });\n```\n\nIl n'y a absolument rien que nous puissions faire avec `Math.PI`.\n\nRendre une propriété non configurable est une voie à sens unique. Nous ne pouvons pas le modifier avec `defineProperty`.\n\n**Veuillez noter : `configurable: false` empêche les changements d'indicateurs de propriété et sa suppression, tout en permettant de changer sa valeur.**\n\nIci, `user.name` n'est pas configurable, mais nous pouvons toujours le changer (car il est accessible en écriture) :\n\n```js run\nlet user = {\n  name: \"John\"\n};\n\nObject.defineProperty(user, \"name\", {\n  configurable: false\n});\n\nuser.name = \"Pete\"; // ça fonctionne\ndelete user.name; // Error\n```\n\nIci, nous faisons de `user.name` une constante \"scellée à jamais\", tout comme `Math.PI` :\n\n```js run\nlet user = {\n  name: \"John\"\n};\n\nObject.defineProperty(user, \"name\", {\n  writable: false,\n  configurable: false\n});\n\n// ne pourra pas changer user.name ou ses indicateurs\n// tout cela ne fonctionnera pas :\nuser.name = \"Pete\";\ndelete user.name;\nObject.defineProperty(user, \"name\", { value: \"Pete\" });\n```\n\n```smart header=\"Le seul changement d'attribut possible : writable true -> false\"\nIl existe une exception mineure concernant la modification des indicateurs.\n\nNous pouvons changer `writable: true` en `false` pour une propriété non configurable, empêchant ainsi la modification de sa valeur (pour ajouter une autre couche de protection).\n```\n\n## Object.defineProperties\n\nIl y a une méthode [Object.defineProperties(obj, descriptors)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/defineProperties) qui permet de définir plusieurs propriétés à la fois.\n\nLa syntaxe est la suivante :\n\n```js\nObject.defineProperties(obj, {\nprop1: descriptor1,\nprop2: descriptor2\n// ...\n});\n```\n\nPar exemple :\n\n```js\nObject.defineProperties(user, {\nname: { value: \"John\", writable: false },\nsurname: { value: \"Smith\", writable: false },\n// ...\n});\n```\n\nNous pouvons donc définir plusieurs propriétés à la fois.\n\n## Object.getOwnPropertyDescriptors\n\nPour obtenir tous les descripteurs de propriété à la fois, nous pouvons utiliser la méthode [Object.getOwnPropertyDescriptors(obj)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/getOwnPropertyDescriptors).\n\nAvec `Object.defineProperties`, elle peut être utilisé comme moyen de cloner un objet en tenant compte des attributs :\n\n```js\nlet clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));\n```\n\nNormalement, lorsque nous clonons un objet, nous utilisons une affectation pour copier les propriétés, comme ceci :\n\n```js\nfor (let key in user) {\nclone[key] = user[key]\n}\n```\n\n...Mais cela ne copie pas les attributs. Donc, si nous voulons un \"meilleur\" clone, alors `Object.defineProperties` est préféré.\n\nUne autre différence est que `for..in` ignore les propriétés symboliques, mais que `Object.getOwnPropertyDescriptors` renvoie *tous* les descripteurs de propriété, y compris ceux symboliques et non énumérables.\n\n## Sceller un objet globalement\n\nLes descripteurs de propriété fonctionnent au niveau des propriétés individuelles.\n\nIl existe également des méthodes qui limitent l'accès à l'objet *entier* :\n\n[Object.preventExtensions(obj)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/preventExtensions)\n: Interdit l'ajout de nouvelles propriétés à l'objet.\n\n[Object.seal(obj)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/seal)\n: Interdit l'ajout/la suppression de propriétés. Définit `configurable: false` pour toutes les propriétés existantes.\n\n[Object.freeze(obj)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/freeze)\n: Interdit l'ajout/la suppression/la modification de propriétés. Définit `configurable: false, writeable: false` pour toutes les propriétés existantes.\n\nEt aussi il y a des tests pour eux :\n\n[Object.isExtensible(obj)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/isExtensible)\n: Retourne `false` si l'ajout de propriétés est interdit, sinon `true`.\n\n[Object.isSealed(obj)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/isSealed)\n: Renvoie `true` si l'ajout/la suppression de propriétés est interdite et que toutes les propriétés existantes ont `configurable: false`.\n\n[Object.isFrozen(obj)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/isFrozen)\n: Retourne `true` si l'ajout/la suppression/la modification de propriétés est interdite et si toutes les propriétés actuelles sont `configurable: false, writable: false`.\n\nCes méthodes sont rarement utilisées dans la pratique.\n"
  },
  {
    "path": "1-js/07-object-properties/02-property-accessors/article.md",
    "content": "\n# Getters et Setters de propriété\n\nIl y a deux sortes de proriétés d'objet.\n\nLe premier type est *les propriétés de données*. Nous savons déjà comment travaillez avec. Toutes les propriétés que nous avons utilisés jusqu'à maintenant étaient des propriétés de données.\n\nLe second type de propriété est quelque chose de nouveau. C'est un accesseur de propriété. Ce sont essentiellement des fonctions qui exécutent une récupération ou une déclaration de valeur, mais qui ressemblent à une propriété normale pour le code extérieur.\n\n## Getters et Setters\n\nLes accesseurs de propriétés sont représentés par des méthodes \"getter\" et \"setter\". Dans un objet littéral elles se demarquent par `get` et `set` :\n\n```js\nlet obj = {\n  *!*get propName()*/!* {\n    // Getter, le code va récupérer obj.propName\n  },\n\n  *!*set propName(value)*/!* {\n    // Setter, le code va définir obj.propName = value\n  }\n};\n```\n\nLe getter fonctionne quand `obj.propName` est lu, le setter -- quand il s'agit d'une assignation.\n\nPar exemple, nous avons un objet `user` avec `name` et `surname` :\n\n```js\nlet user = {\n  name: \"John\",\n  surname: \"Smith\"\n};\n```\n\nMaintenant nous voulons ajouter une propriété `fullName`, qui devrait être `\"John Smith\"`. Bien sûr, nous ne voulons pas copier-coller l'information existante, donc nous pouvons implémenter un accesseur :\n\n```js run\nlet user = {\n  name: \"John\",\n  surname: \"Smith\",\n\n*!*\n  get fullName() {\n    return `${this.name} ${this.surname}`;\n  }\n*/!*\n};\n\n*!*\nalert(user.fullName); // John Smith\n*/!*\n```\n\nDe l'extérieur, un accesseur de propriété ressemble à une propriété normale. C'est l'idée d'un accesseur. Nous n'*appellons* pas `user.fullName` comme une fonction, nous la *lisons* normalement : le getter agit en arrière plan.\n\nPour l'instant, `fullName` n'a qu'un getter. Si nous essayons d'assigner `user.fullName=`, il y aura une erreur :\n\n```js run\nlet user = {\n  get fullName() {\n    return `...`;\n  }\n};\n\n*!*\nuser.fullName = \"Test\"; // Erreur (la propriété n'a qu'un getter)\n*/!*\n```\n\nCorrigeons cela en ajoutant un setter pour `user.fullName` :\n\n\n```js run\nlet user = {\n  name: \"John\",\n  surname: \"Smith\",\n\n  get fullName() {\n    return `${this.name} ${this.surname}`;\n  },\n\n*!*\n  set fullName(value) {\n    [this.name, this.surname] = value.split(\" \");\n  }\n*/!*\n};\n\n// Le setter est exécuté avec la valeur donnée.\nuser.fullName = \"Alice Cooper\";\n\nalert(user.name); // Alice\nalert(user.surname); // Cooper\n```\n\nComme résultat, nous avons une propriété \"virtuelle\" `fullName`. Elle est lisible et ecrivable.\n\n## Descripteurs d'accesseur\n\nLes descripteurs d'accesseur de propriété sont différents de ceux pour les propriété de données.\n\nPour les accesseurs de propriétés, il n'y a pas de `value` ou `writable`, à la place il y a les fonctions `get` et `set`.\n\nUn descripteur d'accesseur peut avoir :\n\n- **`get`** -- une fonction sans arguments, pour la lecture de propriété,\n- **`set`** -- une fonction avec un argument, qui fonctionne lorsque la propriété change de valeur,\n- **`enumerable`** -- identique aux propriétés de données\n- **`configurable`** -- identique aux propriétés de données\n\nPar exemple, pour créer un accesseur `fullName` avec `defineProperty`, on peut passer un descripteur avec `get` et `set` :\n\n```js run\nlet user = {\n  name: \"John\",\n  surname: \"Smith\"\n};\n\n*!*\nObject.defineProperty(user, 'fullName', {\n  get() {\n    return `${this.name} ${this.surname}`;\n  },\n\n  set(value) {\n    [this.name, this.surname] = value.split(\" \");\n  }\n*/!*\n});\n\nalert(user.fullName); // John Smith\n\nfor(let key in user) alert(key); // name, surname\n```\n\nVeuillez notez qu'une propriété peut être soit un accesseur (qui a les méthodes `get/set`) ou une propriété de données (qui a `value`), pas les deux.\n\nSi nous essayons de fournir les deux `get` and `value` dans le même descripteur, il y aura une erreur :\n\n```js run\n*!*\n// Erreur : Descripteur de propriété invalide.\n*/!*\nObject.defineProperty({}, 'prop', {\n  get() {\n    return 1\n  },\n\n  value: 2\n});\n```\n\n## Des getters/setters plus intelligents \n\nLes Getters/setters peuvent être utilisés comme des enveloppes autour des \"réelles\" valeurs de propriété pour gagner plus de contrôles sur leurs opérations.\n\nPar exemple, si nous voulions interdire les noms trop court pour `user`, nous pourrions avoir un setter `name` et garder la valeur dans une propriété séparée `_name` :\n\n```js run\nlet user = {\n  get name() {\n    return this._name;\n  },\n\n  set name(value) {\n    if (value.length < 4) {\n      alert(\"Name is too short, need at least 4 characters\");\n      return;\n    }\n    this._name = value;\n  }\n};\n\nuser.name = \"Pete\";\nalert(user.name); // Pete\n\nuser.name = \"\"; // Le nom est trop court...\n```\n\nDonc, le nom est stocké dans la propriété `_name`, et l'accés est fait par le getter et le setter.\n\nTechniquement, le code extérieur est capable d'accéder directement à la propriété en utilisant `user._name`. Mais il y a une convention très connue, selon laquelle les propriétés commençant par un underscore `\"_\"` sont internes et ne devraient pas être touchées depuis l'extérieur des objets.\n\n## Utilisation pour la compatibilité\n\nUn des avantages de l'utilisation des accesseurs et qu'ils permettent de prendre le contrôle sur un propriété de données \"normale\" à tout moment, en la remplaçant par un getter et un setter et modifiant son comportement.\n\nImaginons que nous commencions des objets utilisateur en utilisant des propriétés de données `name` et `age` :\n\n```js\nfunction User(name, age) {\n  this.name = name;\n  this.age = age;\n}\n\nlet john = new User(\"John\", 25);\n\nalert( john.age ); // 25\n```\n\n...Mais tôt ou tard, les choses pourraient changer. Au lieu d'`age` on pourrait decider de stocker `birthday`, parce que c'est plus précis et plus pratique :\n\n```js\nfunction User(name, birthday) {\n  this.name = name;\n  this.birthday = birthday;\n}\n\nlet john = new User(\"John\", new Date(1992, 6, 1));\n```\n\nMaintenant que fait-on avec l'ancien code qui utilise toujours la propriété `age` ?\n\nOn peut essayer de trouver tous les endroits où on utilisent `age` et les modifier, mais ça prend du temps et ça peut être compliqué à faire si le code est utilisé par plusieurs personnes. En plus, `age` est une bonne chose à avoir dans `user`, n'est ce pas ?\n\nGardons-le.\n\nAjoutons un getter pour `age` et résolvons le problème :\n\n```js run no-beautify\nfunction User(name, birthday) {\n  this.name = name;\n  this.birthday = birthday;\n\n*!*\n  // age est calculé à partir de la date actuelle et de birthday\n  Object.defineProperty(this, \"age\", {\n    get() {\n      let todayYear = new Date().getFullYear();\n      return todayYear - this.birthday.getFullYear();\n    }\n  });\n*/!*\n}\n\nlet john = new User(\"John\", new Date(1992, 6, 1));\n\nalert( john.birthday ); // birthday est disponible\nalert( john.age );      // ...Ainsi que l'age \n```\n\nMaintenant l'ancien code fonctionne toujours et nous avons une propriété additionnelle."
  },
  {
    "path": "1-js/07-object-properties/index.md",
    "content": "# Configuration des propriétés d'objet\n\nDans cette section, nous revenons sur les objets et étudions leurs propriétés encore plus en profondeur.\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/solution.md",
    "content": "\n1. `true`, tiré de `rabbit`.\n2. `null`, tiré de `animal`.\n3. `undefined`, il n'y a plus une telle propriété.\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/task.md",
    "content": "importance: 5\n\n---\n\n# Travailler avec prototype\n\nVoici le code qui crée une paire d'objets, puis les modifie.\n\nQuelles sont les valeurs affichées dans le processus ?\n\n```js\nlet animal = {\n  jumps: null\n};\nlet rabbit = {\n  __proto__: animal,\n  jumps: true\n};\n\nalert( rabbit.jumps ); // ? (1)\n\ndelete rabbit.jumps;\n\nalert( rabbit.jumps ); // ? (2)\n\ndelete animal.jumps;\n\nalert( rabbit.jumps ); // ? (3)\n```\n\nIl devrait y avoir 3 réponses.\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/solution.md",
    "content": "\n1. Ajoutons `__proto__`:\n\n    ```js run\n    let head = {\n      glasses: 1\n    };\n\n    let table = {\n      pen: 3,\n      __proto__: head\n    };\n\n    let bed = {\n      sheet: 1,\n      pillow: 2,\n      __proto__: table\n    };\n\n    let pockets = {\n      money: 2000,\n      __proto__: bed\n    };\n\n    alert( pockets.pen ); // 3\n    alert( bed.glasses ); // 1\n    alert( table.money ); // undefined\n    ```\n\n2. Dans les moteurs modernes, en termes de performances, il n’ya pas de différence selon que l’on prend une propriété d’un objet ou de son prototype. Ils se souviennent du lieu où la propriété a été trouvée et le réutilisent à la demande suivante.\n\n    Par exemple, pour `pockets.glasses` ils se souviennent où ils ont trouvé `glasses` (dans `head`), et la prochaine fois rechercheront là. Ils sont également assez intelligents pour mettre à jour les caches internes en cas de changement, de sorte que l'optimisation est sécurisée.\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md",
    "content": "importance: 5\n\n---\n\n# Algorithme de recherche\n\nLa tâche comporte deux parties.\n\nÉtant donné les objets suivants :\n\n```js\nlet head = {\n  glasses: 1\n};\n\nlet table = {\n  pen: 3\n};\n\nlet bed = {\n  sheet: 1,\n  pillow: 2\n};\n\nlet pockets = {\n  money: 2000\n};\n```\n\n1. Utilisez `__proto__` pour attribuer des prototypes de manière à ce que toute recherche de propriété suive le chemin:`pockets` -> `bed` -> `table` -> `head`. Par exemple, `pocket.pen` devrait être `3` (trouvé dans `table`), et `bed.glasses` devrait être `1` (trouvé dans `head`).\n2. Répondez à la question: est-il plus rapide d’obtenir `glasses` en tant que `pockets.glasses` ou `head.glasses`? Analyse si nécessaire.\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/solution.md",
    "content": "**La réponse: `rabbit`.**\n\nC'est parce que `this` est un objet avant le point, donc `rabbit.eat()` modifie `rabbit`.\n\nLa recherche et l'exécution de propriétés sont deux choses différentes.\n\nLa méthode `rabbit.eat` est d'abord trouvée dans le prototype, puis exécutée avec `this=rabbit`."
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md",
    "content": "importance: 5\n\n---\n\n# Où est-ce écrit ?\n\nNous avons `rabbit` héritant de `animal`.\n\nSi nous appelons `rabbit.eat()`, quel objet reçoit la propriété `full`: `animal` ou `rabbit`?\n\n```js\nlet animal = {\n  eat() {\n    this.full = true;\n  }\n};\n\nlet rabbit = {\n  __proto__: animal\n};\n\nrabbit.eat();\n```\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md",
    "content": "Examinons attentivement ce qui se passe dans l'appel `speedy.eat(\"apple\")`.\n\n1. La méthode `speedy.eat` se trouve dans le prototype (`=hamster`), puis exécutée avec `this=speedy` (l'objet avant le point).\n\n2. Ensuite, `this.stomach.push()` doit trouver la propriété `stomach` et appeler `push` dessus. Il cherche `stomach` dans `this` (`=speedy`), mais rien n'est trouvé.\n\n3. Ensuite, il suit la chaîne de prototypes et trouve `stomach` dans `hamster`.\n\n4. Ensuite, il appelle `push` dessus, en ajoutant la nourriture dans *stomach du prototype*.\n\nTous les hamsters partagent donc un seul estomac!\n\nTant pour `lazy.stomach.push(...)` et `speedy.stomach.push()`, la propriété `stomach` se trouve dans le prototype (comme il est pas dans l'objet lui-même), alors les nouvelles données sont poussé dedans.\n\nVeuillez noter qu'une telle chose ne se produit pas dans le cas d'une simple affectation `this.stomach=`:\n\n```js run\nlet hamster = {\n  stomach: [],\n\n  eat(food) {\n*!*\n    // assigner à this.stomach au lieu de this.stomach.push\n    this.stomach = [food];\n*/!*\n  }\n};\n\nlet speedy = {\n   __proto__: hamster\n};\n\nlet lazy = {\n  __proto__: hamster\n};\n\n// Speedy a trouvé la nourriture\nspeedy.eat(\"apple\");\nalert( speedy.stomach ); // apple\n\n// L'estomac de Lazy est vide\nalert( lazy.stomach ); // <rien>\n```\n\nMaintenant, tout fonctionne bien, car `this.stomach=` n'effectue pas de recherche de `stomach`. La valeur est écrite directement dans l'objet `this`.\n\nNous pouvons également éviter le problème en nous assurant que chaque hamster a son propre stomach :\n\n```js run\nlet hamster = {\n  stomach: [],\n\n  eat(food) {\n    this.stomach.push(food);\n  }\n};\n\nlet speedy = {\n  __proto__: hamster,\n*!*\n  stomach: []\n*/!*\n};\n\nlet lazy = {\n  __proto__: hamster,\n*!*\n  stomach: []\n*/!*\n};\n\n// Speedy a trouvé la nourriture\nspeedy.eat(\"apple\");\nalert( speedy.stomach ); // apple\n\n// L'estomac de Lazy est vide\nalert( lazy.stomach ); // <rien>\n```\n\nEn tant que solution commune, toutes les propriétés qui décrivent l'état d'un objet particulier, comme `stomach` ci-dessus, devraient être écrits dans cet objet. Cela empêche de tels problèmes.\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md",
    "content": "importance: 5\n\n---\n\n# Pourquoi deux hamsters sont rassasiés ?\n\nNous avons deux hamsters: `speedy` et `lazy` héritant de l'objet général `hamster`.\n\nLorsque nous nourrissons l'un d'eux, l'autre est également rassasié. Pourquoi ? Comment y remédier ?\n\n```js run\nlet hamster = {\n  stomach: [],\n\n  eat(food) {\n    this.stomach.push(food);\n  }\n};\n\nlet speedy = {\n  __proto__: hamster\n};\n\nlet lazy = {\n  __proto__: hamster\n};\n\n// Celui-ci a trouvé la nourriture\nspeedy.eat(\"apple\");\nalert( speedy.stomach ); // apple\n\n// Celui-ci l'a aussi, pourquoi ? Merci de corriger cela.\nalert( lazy.stomach ); // apple\n```\n\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/article.md",
    "content": "# Héritage prototypal\n\nEn programmation, nous voulons souvent prendre quelque chose et l’étendre.\n\nPar exemple, nous avons un objet `user` avec ses propriétés et méthodes et souhaitons en faire des variantes `admin` et `guest` légèrement modifiées. Nous aimerions réutiliser ce que nous avons dans `user`, et non pas copier/réimplémenter ses méthodes, mais simplement créer un nouvel objet par-dessus.\n\n*L'héritage prototypal* est une fonctionnalité de langage qui aide à cela.\n\n## [[Prototype]]\n\nEn JavaScript, les objets ont une propriété cachée spéciale `[[Prototype]]` (comme indiqué dans la spécification), qui est soit `null` ou fait référence à un autre objet. Cet objet s'appelle \"un prototype\" :\n\n![prototype](object-prototype-empty.svg)\n\nLorsque nous lisons une propriété depuis `object`, et qu'elle est manquante, JavaScript la prend automatiquement du prototype. En programmation, une telle chose est appelée \"héritage prototypal\". Et bientôt, nous étudierons de nombreux exemples d'un tel héritage, ainsi que des fonctionnalités de langage plus cool qui en découlent.\n\nLa propriété `[[Prototype]]` est interne et cachée, mais il y a plusieurs façons de la définir.\n\nL'un d'eux est d'utiliser le nom spécial `__proto__`, comme ceci :\n\n```js run\nlet animal = {\n  eats: true\n};\nlet rabbit = {\n  jumps: true\n};\n\n*!*\nrabbit.__proto__ = animal; // sets rabbit.[[Prototype]] = animal\n*/!*\n```\n\nSi nous recherchons une propriété dans `rabbit`, et qu'elle en manque, JavaScript la prend automatiquement à partir de `animal`.\n\nPar exemple :\n\n```js\nlet animal = {\n  eats: true\n};\nlet rabbit = {\n  jumps: true\n};\n\n*!*\nrabbit.__proto__ = animal; // (*)\n*/!*\n\n// nous pouvons maintenant trouver les deux propriétés dans rabbit :\n*!*\nalert( rabbit.eats ); // true (**)\n*/!*\nalert( rabbit.jumps ); // true\n```\n\nIci, la ligne `(*)` définit `animal` comme le prototype de `lapin`.\n\nEnsuite, lorsque `alert` essaie de lire la propriété `rabbit.eats` `(**)`, ce n'est pas dans `rabbit`, donc JavaScript suit la référence `[[Prototype]]` et la trouve dans `animal` (regarde de bas en haut) :\n\n![](proto-animal-rabbit.svg)\n\nIci, nous pouvons dire que \"`animal` est le prototype de `rabbit`\" ou que \"`rabit` hérite de manière prototypal de `animal`\".\n\nDonc, si `animal` a beaucoup de propriétés et de méthodes utiles, elles deviennent automatiquement disponibles dans `rabbit`. De telles propriétés sont appelées \"héritées\".\n\nSi nous avons une méthode dans `animal`, elle peut être appelée sur `rabbit` :\n\n```js run\nlet animal = {\n  eats: true,\n*!*\n  walk() {\n    alert(\"Animal walk\");\n  }\n*/!*\n};\n\nlet rabbit = {\n  jumps: true,\n  __proto__: animal\n};\n\n// walk est prise à partir du prototype\n*!*\nrabbit.walk(); // Animal walk\n*/!*\n```\n\nLa méthode est automatiquement prise à partir du prototype, comme ceci :\n\n![](proto-animal-rabbit-walk.svg)\n\nLa chaîne de prototypes peut être plus longue :\n\n```js run\nlet animal = {\n  eats: true,\n  walk() {\n    alert(\"Animal walk\");\n  }\n};\n\nlet rabbit = {\n  jumps: true,\n*!*\n  __proto__: animal\n*/!*\n};\n\nlet longEar = {\n  earLength: 10,\n*!*\n  __proto__: rabbit\n*/!*\n};\n\n// walk est prise à partir de la chaîne de prototype\nlongEar.walk(); // Animal walk\nalert(longEar.jumps); // true (de rabbit)\n```\n\n![](proto-animal-rabbit-chain.svg)\n\nMaintenant, si nous lisons quelque chose de `longEar`, et qu'il est manquant, JavaScript le recherchera dans `rabbit`, puis dans `animal`.\n\nIl n'y a que deux limitations :\n\n1. Les références ne peuvent pas tourner en rond. JavaScript va générer une erreur si nous essayons d'assigner `__proto__` dans un cercle.\n2. La valeur de `__proto__` peut être un objet ou `null`. Les autres types sont ignorés.\n\nCela peut aussi être évident, mais quand même : il ne peut y avoir qu'un seul `[[Prototype]]`. Un objet ne peut pas hériter de deux autres.\n\n```smart header=\"`__proto__` est un getter/setter historique pour [[Prototype]]`\"\nC'est une erreur courante des développeurs novices de ne pas connaître la différence entre les deux.\n\nVeuillez noter que `__proto__` n'est *pas la même* que la propriété interne `[[Prototype]]`. C'est un getter/setter pour `[[Prototype]]`. Plus tard, nous verrons des situations où cela compte, pour l'instant gardons cela à l'esprit, alors que nous construisons notre compréhension du langage JavaScript.\n\nLa propriété `__proto__` est un peu obsolète. Elle existe pour des raisons historiques, le JavaScript moderne suggère que nous devrions utiliser les fonctions `Object.getPrototypeOf/Object.setPrototypeOf` à la place pour obtenir/définir le prototype. Nous aborderons également ces fonctions plus tard.\n\nSelon la spécification, `__proto__` ne doit être pris en charge que par les navigateurs. En fait cependant, tous les environnements, y compris côté serveur, prennent en charge `__proto__`, donc nous sommes assez sûrs de l'utiliser.\n\nComme la notation `__proto__` est un peu plus évidente, nous l'utilisons dans les exemples.\n```\n\n## L'écriture n'utilise pas de prototype\n\nLe prototype n'est utilisé que pour la lecture des propriétés.\n\nLes opérations d'écriture/suppression fonctionnent directement avec l'objet.\n\nDans l'exemple ci-dessous, nous affectons sa propre méthode `walk` à `rabbit` :\n\n```js run\nlet animal = {\n  eats: true,\n  walk() {\n    /* cette méthode ne sera pas utilisée par rabbit */\n  }\n};\n\nlet rabbit = {\n  __proto__: animal\n};\n\n*!*\nrabbit.walk = function() {\n  alert(\"Rabbit! Bounce-bounce!\");\n};\n*/!*\n\nrabbit.walk(); // Rabbit! Bounce-bounce!\n```\n\nA partir de maintenant, l'appel `rabbit.walk()` trouve la méthode immédiatement dans l'objet et l'exécute, sans utiliser le prototype :\n\n![](proto-animal-rabbit-walk-2.svg)\n\nLes propriétés d'accesseur constituent une exception, car l'affectation est gérée par une fonction mutateur. Donc, écrire dans une telle propriété revient en fait à appeler une fonction.\n\nPour cette raison, `admin.fullName` fonctionne correctement dans le code ci-dessous :\n\n```js run\nlet user = {\n  name: \"John\",\n  surname: \"Smith\",\n\n  set fullName(value) {\n    [this.name, this.surname] = value.split(\" \");\n  },\n\n  get fullName() {\n    return `${this.name} ${this.surname}`;\n  }\n};\n\nlet admin = {\n  __proto__: user,\n  isAdmin: true\n};\n\nalert(admin.fullName); // John Smith (*)\n\n// le mutateur se déclanche !\nadmin.fullName = \"Alice Cooper\"; // (**)\n\nalert(admin.fullName); // Alice Cooper, l'état de admin est modifié\nalert(user.fullName); // John Smith, l'état de user est protégé\n```\n\nIci dans la ligne `(*)` la propriété `admin.fullName` a un accesseur dans le prototype `user`, il est donc appelé. Et dans la ligne `(**)` la propriété a un mutateur dans le prototype, il est donc appelé.\n\n## La valeur de \"this\"\n\nUne question intéressante peut se poser dans l'exemple ci-dessus : quelle est la valeur de `this` dans `set fullName(value)` ? Où sont écrites les propriétés `this.name` et `this.surname` : dans `user` ou `admin` ?\n\nLa réponse est simple : `this` n'est pas du tout affecté par les prototypes.\n\n**Peu importe où la méthode est trouvée : dans un objet ou son prototype. Dans un appel de méthode, `this` est toujours l'objet avant le point.**\n\nAinsi, l'appel du groupe `admin.fullName=` utilise `admin` comme `this`, pas `user`.\n\nC'est en fait une chose très importante, car nous pouvons avoir un gros objet avec de nombreuses méthodes et en hériter. Ensuite, les objets hérités peuvent exécuter ces méthodes héritées, ils ne modifieront que leurs propres états, pas l'état du gros objet.\n\nPar exemple, ici `animal` représente un \"stockage de méthode\" et `rabbit` en fait usage.\n\nL'appel `rabbit.sleep()` définit `this.isSleeping` sur l'objet `rabbit` :\n\n```js run\n// animal a des méthodes\nlet animal = {\n  walk() {\n    if (!this.isSleeping) {\n      alert(`I walk`);\n    }\n  },\n  sleep() {\n    this.isSleeping = true;\n  }\n};\n\nlet rabbit = {\n  name: \"White Rabbit\",\n  __proto__: animal\n};\n\n// modifie rabbit.isSleeping\nrabbit.sleep();\n\nalert(rabbit.isSleeping); // true\nalert(animal.isSleeping); // undefined (aucune propriété de ce type dans le prototype)\n```\n\nL'image résultante :\n\n![](proto-animal-rabbit-walk-3.svg)\n\nSi nous avions d'autres objets tels que `bird`, `snake` etc. héritant de `animal`, ils auraient également accès aux méthodes de `animal`. Mais `this` dans chaque appel de méthode serait l'objet correspondant, évalué au moment de l'appel (avant le point), et non `animal`. Ainsi, lorsque nous écrivons des données dans `this`, elles sont stockées dans ces objets.\n\nEn conséquence, les méthodes sont partagées, mais pas l'état d'objet.\n\n## La boucle for..in\n\nLa boucle `for..in` itère aussi sur les propriétés héritées.\n\nPar exemple :\n\n```js run\nlet animal = {\n  eats: true\n};\n\nlet rabbit = {\n  jumps: true,\n  __proto__: animal\n};\n\n*!*\n// Object.keys ne renvoie que ses propres clés\nalert(Object.keys(rabbit)); // jumps\n*/!*\n\n*!*\n// for..in boucle sur les clés propres et héritées\nfor(let prop in rabbit) alert(prop); // jumps, puis eats\n*/!*\n```\n\nSi ce n'est pas ce que nous voulons et que nous aimerions exclure les propriétés héritées, il existe une méthode intégrée [obj.hasOwnProperty(key)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/hasOwnProperty) : elle renvoie `true` si `obj` a sa propre propriété (non héritée) nommée `key`.\n\nNous pouvons donc filtrer les propriétés héritées (ou faire autre chose avec elles) :\n\n```js run\nlet animal = {\n  eats: true\n};\n\nlet rabbit = {\n  jumps: true,\n  __proto__: animal\n};\n\nfor(let prop in rabbit) {\n  let isOwn = rabbit.hasOwnProperty(prop);\n\n  if (isOwn) {\n    alert(`Our: ${prop}`); // Our : jumps\n  } else {\n    alert(`Inherited: ${prop}`); // Inherited: eats\n  }\n}\n```\n\nNous avons ici la chaîne d'héritage suivante : `rabbit` hérite de `animal`, qui lui hérite de `Object.prototype` (car `animal` est un objet littéral `{...}`, donc c'est par défaut), puis `null` au-dessus :\n\n![](rabbit-animal-object.svg)\n\nRemarque, il y a une chose amusante. D'où vient la méthode `rabbit.hasOwnProperty` ? Nous ne l'avons pas défini. En regardant la chaîne, nous pouvons voir que la méthode est fournie par `Object.prototype.hasOwnProperty`. En d'autres termes, c'est hérité.\n\n...Mais pourquoi `hasOwnProperty` n'apparaît pas dans la boucle `for..in`, comme `eats` et `jumps`, s'il répertorie toutes les propriétés héritées.\n\nLa réponse est simple : ce n'est pas énumérable. Comme toutes les autres propriétés de `Object.prototype`, il possède l'attribut `enumerable: false`. C'est pourquoi ils ne sont pas répertoriés. Et `for..in` ne répertorie que les propriétés énumérables. C'est pourquoi elle et le reste des propriétés de `Object.prototype` ne sont pas listés.\n\n```smart header=\"Presque toutes les autres méthodes d'obtention de clé/valeur ignorent les propriétés héritées\"\nPresque toutes les autres méthodes d'obtention de clé/valeur, telles que `Object.keys`, `Object.values` et ainsi de suite ignorent les propriétés héritées.\n\nElles ne fonctionnent que sur l'objet lui-même. Les propriétés du prototype ne sont *pas* prises en compte.\n```\n\n## Résumé\n\n- En JavaScript, tous les objets ont une propriété masquée `[[Prototype]]` qui est soit un autre objet, soit `null`.\n- Nous pouvons utiliser `obj.__ proto__` pour y accéder (un accesseur/mutateur historique, il existe d'autres moyens, à couvrir bientôt).\n- L'objet référencé par `[[Prototype]]` s'appelle un \"prototype\".\n- Si nous voulons lire une propriété de `obj` ou appeler une méthode, et que celle-ci n'existe pas, alors JavaScript essaye de la trouver dans le prototype.\n- Les opérations d'écriture/suppression agissent directement sur l'objet, elles n'utilisent pas le prototype (en supposant qu'il s'agisse d'une propriété de données, et non d'un setter).\n- Si nous appelons `obj.method()`, et que la `méthode` est extraite du prototype, `this` fait toujours référence à `obj`. Les méthodes fonctionnent donc toujours avec l'objet actuel, même si elles sont héritées.\n- La boucle `for..in` parcourt les propriétés propres et héritées. Toutes les autres méthodes d'obtention de clé / valeur ne fonctionnent que sur l'objet lui-même.\n"
  },
  {
    "path": "1-js/08-prototypes/02-function-prototype/1-changing-prototype/solution.md",
    "content": "\nRéponses :\n\n1. `true`.\n\n     L'affectation à `Rabbit.prototype` configure `[[Prototype]]` pour les nouveaux objets, mais n'affecte pas les objets existants.\n\n2. `false`.\n\n     Les objets sont assignés par référence. L'objet de `Rabbit.prototype` n'est pas dupliqué, mais un objet unique est référencé à la fois par `Rabbit.prototype` et par le `[[Prototype]]` de `rabbit`.\n\n     Ainsi, lorsque nous modifions son contenu par l’une des références, il est visible par l’autre.\n\n3. `true`.\n\n     Toutes les opérations `delete` sont appliquées directement à l'objet. `delete rabbit.eats` tente ici de supprimer la propriété `eats` de `rabbit`, mais ne l’a pas. Donc l'opération n'aura aucun effet.\n\n4. `undefined`.\n\n     La propriété `eats` est supprimée du prototype, elle n’existe plus.\n"
  },
  {
    "path": "1-js/08-prototypes/02-function-prototype/1-changing-prototype/task.md",
    "content": "importance: 5\n\n---\n\n# Changement de \"prototype\"\n\nDans le code ci-dessous, nous créons `new Rabbit`, puis essayons de modifier son prototype.\n\nAu début, nous avons ce code :\n\n```js run\nfunction Rabbit() {}\nRabbit.prototype = {\n  eats: true\n};\n\nlet rabbit = new Rabbit();\n\nalert( rabbit.eats ); // true\n```\n\n1. Nous avons ajouté une chaîne de caractères supplémentaire (surlignée), qu'affiche `alert` maintenant ?\n\n    ```js\n    function Rabbit() {}\n    Rabbit.prototype = {\n      eats: true\n    };\n\n    let rabbit = new Rabbit();\n\n    *!*\n    Rabbit.prototype = {};\n    */!*\n\n    alert( rabbit.eats ); // ?\n    ```\n\n2. ...Et si le code est comme ça (une ligne remplacée) ?\n\n    ```js\n    function Rabbit() {}\n    Rabbit.prototype = {\n      eats: true\n    };\n\n    let rabbit = new Rabbit();\n\n    *!*\n    Rabbit.prototype.eats = false;\n    */!*\n\n    alert( rabbit.eats ); // ?\n    ```\n\n3. Et comme ceci (une ligne remplacée) ?\n\n    ```js\n    function Rabbit() {}\n    Rabbit.prototype = {\n      eats: true\n    };\n\n    let rabbit = new Rabbit();\n\n    *!*\n    delete rabbit.eats;\n    */!*\n\n    alert( rabbit.eats ); // ?\n    ```\n\n4. La dernière variante :\n\n    ```js\n    function Rabbit() {}\n    Rabbit.prototype = {\n      eats: true\n    };\n\n    let rabbit = new Rabbit();\n\n    *!*\n    delete Rabbit.prototype.eats;\n    */!*\n\n    alert( rabbit.eats ); // ?\n    ```\n"
  },
  {
    "path": "1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md",
    "content": "Nous pouvons utiliser cette approche si nous sommes sûrs que la propriété `\"constructeur\"` a la valeur correcte.\n\nPar exemple, si nous ne touchons pas le `\"prototype\"` par défaut, alors ce code fonctionne à coup sûr :\n\n```js run\nfunction User(name) {\n  this.name = name;\n}\n\nlet user = new User('John');\nlet user2 = new user.constructor('Pete');\n\nalert( user2.name ); // Pete (ça marche !)\n```\n\nCela a fonctionné, car `User.prototype.constructor == User`.\n\n..Mais si quelqu'un, pour ainsi dire, écrase `User.prototype` et oublie de recréer `constructor` pour faire référence à `User`, il échouera.\n\nPar exemple :\n\n```js run\nfunction User(name) {\n  this.name = name;\n}\n*!*\nUser.prototype = {}; // (*)\n*/!*\n\nlet user = new User('John');\nlet user2 = new user.constructor('Pete');\n\nalert( user2.name ); // undefined\n```\n\nPourquoi `user2.name` est `undefined` ?\n\nVoici comment `new user.constructor('Pete')` fonctionne :\n\n1. Tout d'abord, il cherche `constructor` dans `user`. Rien.\n2. Ensuite, il suit la chaîne de prototypes. Le prototype de `user` est `User.prototype`, et il n'a pas non plus de `constructor` (parce que nous avons \"oublié\" de le régler correctement !).\n3. En remontant la chaîne, `User.prototype` est un objet simple, son prototype est le `Object.prototype` intégré.\n4. Enfin, pour le `Object.prototype` intégré, il existe un `Object.prototype.constructor == Object` intégré. Il est donc utilisé.\n\nEn fin de compte, nous avons `let user2 = new Object('Pete')`.\n\nCe n'est probablement pas ce que nous voulons. Nous aimerions créer un `new user`, pas un `new Object`. C'est le résultat du `constructor` manquant.\n\n(Juste au cas où vous seriez curieux, l'appel `new Object(...)` convertit son argument en un objet. C'est une chose théorique, en pratique personne n'appelle `new Object` avec une valeur, et généralement nous n’utilisons pas `new Object` pour créer des objets).\n"
  },
  {
    "path": "1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/task.md",
    "content": "importance: 5\n\n---\n\n# Créer un objet avec le même constructeur\n\nImaginez nous avons un objet arbitraire `obj`, créé par une fonction constructeur - nous ne savons pas lequel, mais nous aimerions créer un nouvel objet à l'aide de celui-ci.\n\nPouvons-nous le faire comme ça ?\n\n```js\nlet obj2 = new obj.constructor();\n```\n\nDonne un exemple de fonction constructeur pour `obj` qui laisse ce code fonctionner correctement. Et un exemple qui fait que ça marche mal.\n"
  },
  {
    "path": "1-js/08-prototypes/02-function-prototype/article.md",
    "content": "# F.prototype\n\nRappelez-vous que de nouveaux objets peuvent être créés avec une fonction constructeur, comme `new F()`.\n\nSi `F.prototype` est un objet, alors l'opérateur `new` l'utilise pour définir `[[Prototype]]` pour le nouvel objet.\n\n```smart header=\"Veuillez noter\"\nJavaScript avait l'héritage prototypique depuis le début. C'était l'une des caractéristiques principales du langage.\n\nMais dans le passé, il n'y avait pas d'accès direct. La seule chose qui fonctionnait de manière fiable est une propriété `\"prototype\"` de la fonction constructeur décrite dans ce chapitre. Donc, il y a beaucoup de scripts qui l'utilisent encore.\n```\n\nVeuillez noter que `F.prototype` signifie ici une propriété régulière nommée `\"prototype\"` sur `F`. Cela ressemble quelque peu au terme \"prototype\", mais nous entendons ici une propriété régulière portant ce nom.\n\nVoici l'exemple :\n\n```js run\nlet animal = {\n  eats: true\n};\n\nfunction Rabbit(name) {\n  this.name = name;\n}\n\n*!*\nRabbit.prototype = animal;\n*/!*\n\nlet rabbit = new Rabbit(\"White Rabbit\"); //  rabbit.__proto__ == animal\n\nalert( rabbit.eats ); // true\n```\n\nDéfinir `Rabbit.prototype=animal` énonce littéralement ce qui suit : \"Lorsqu'un `new Rabbit` est créé, assigner son `[[Prototype]]` à `animal`\".\n\nVoici l'image résultante :\n\n![](proto-constructor-animal-rabbit.svg)\n\nSur l'image, `\"prototype\"` est une flèche horizontale, ce qui signifie une propriété normale, et `[[Prototype]]` est vertical, ce qui signifie l'héritage de `rabbit` de `animal`.\n\n```smart header=\"`F.prototype` utilisé uniquement pendant `new F`\"\nLa propriété `F.prototype` est utilisée uniquement lorsque `new F` est appelé, elle attribue `[[Prototype]]` du nouvel objet. Après cela, il n'y a plus de connexion entre `F.prototype` et le nouvel objet.\n\nSi, après la création, la propriété `F.prototype` change (`F.prototype = <un autre objet>`), les nouveaux objets créés par `new F` auront un autre objet comme `[[Prototype]]`, mais les objets déjà existants conservent l'ancien.\n```\n\n## F.prototype par défaut, propriété du constructeur\n\nChaque fonction a la propriété `\"prototype\"` même si nous ne la fournissons pas.\n\nLe `\"prototype\"` par défaut est un objet avec comme seule propriété `constructor` qui renvoie à la fonction elle-même.\n\nComme ça :\n\n```js\nfunction Rabbit() {}\n\n/* prototype par défaut\nRabbit.prototype = { constructor: Rabbit };\n*/\n```\n\n![](function-prototype-constructor.svg)\n\nNous pouvons le vérifier :\n\n```js run\nfunction Rabbit() {}\n// par défaut:\n// Rabbit.prototype = { constructor: Rabbit }\n\nalert( Rabbit.prototype.constructor == Rabbit ); // true\n```\n\nNaturellement, si nous ne faisons rien, la propriété `constructor` est disponible pour tous les \"rabbits\" via `[[Prototype]]` :\n\n```js run\nfunction Rabbit() {}\n// par défaut:\n// Rabbit.prototype = { constructor: Rabbit }\n\nlet rabbit = new Rabbit(); // hérite de {constructor: Rabbit}\n\nalert(rabbit.constructor == Rabbit); // true (de prototype)\n```\n\n![](rabbit-prototype-constructor.svg)\n\nNous pouvons utiliser la propriété `constructor` pour créer un nouvel objet en utilisant le même constructeur que l'existant.\n\nComme ici :\n\n```js run\nfunction Rabbit(name) {\n  this.name = name;\n  alert(name);\n}\n\nlet rabbit = new Rabbit(\"White Rabbit\");\n\n*!*\nlet rabbit2 = new rabbit.constructor(\"Black Rabbit\");\n*/!*\n```\n\nC'est pratique lorsque nous avons un objet, ne sachant pas quel constructeur a été utilisé pour cela (par exemple, il provient d'une bibliothèque externe), et nous devons en créer un autre du même type.\n\nMais probablement la chose la plus importante à propos de `\"constructor\"` est que...\n\n**...JavaScript lui-même n'assure pas la bonne valeur de `\"constructor\"`.**\n\nOui, il existe dans le `\"prototype\"` par défaut des fonctions, mais c'est tout. Ce qu'il en adviendra par la suite dépend entièrement de nous.\n\nEn particulier, si nous remplaçons le prototype par défaut dans son ensemble, il ne contiendra pas de \"constructor\".\n\nPar exemple :\n\n```js run\nfunction Rabbit() {}\nRabbit.prototype = {\n  jumps: true\n};\n\nlet rabbit = new Rabbit();\n*!*\nalert(rabbit.constructor === Rabbit); // false\n*/!*\n```\n\nDonc, pour garder le bon `\"constructor\"`, nous pouvons choisir d'ajouter/supprimer des propriétés au `\"prototype\"` par défaut au lieu de l'écraser dans son ensemble :\n\n```js\nfunction Rabbit() {}\n\n// Ne pas écraser Rabbit.prototype totalement\n// juste y ajouter\nRabbit.prototype.jumps = true\n// le Rabbit.prototype.constructor par défaut est conservé\n```\n\nOu bien, recréez manuellement la propriété `constructor` :\n\n```js\nRabbit.prototype = {\n  jumps: true,\n*!*\n  constructor: Rabbit\n*/!*\n};\n\n// maintenant le constructeur est également correct, car nous l'avons ajouté\n```\n\n## Résumé\n\nDans ce chapitre, nous avons brièvement décrit la manière de définir un `[[Prototype]]` pour les objets créés via une fonction constructeur. Plus tard, nous verrons des modèles de programmation plus avancés qui en dépendent.\n\nTout est assez simple, juste quelques précisions pour clarifier les choses :\n\n- La propriété `F.prototype` (ne pas confondre avec `[[Prototype]]`) définit `[[Prototype]]` sur les nouveaux objets lorsque `new F()` est appelée.\n- La valeur de `F.prototype` devrait soit être un objet ou `null` : les autres valeurs ne fonctionneront pas.\n- La propriété `\"prototype\"` n'a cet effet spécial que lorsqu'elle est définie dans une fonction constructeur et invoquée avec `new`.\n\nSur les objets ordinaires, le `prototype` n'a rien de spécial :\n\n```js\nlet user = {\n  name: \"John\",\n  prototype: \"Bla-bla\" // pas de magie\n};\n```\n\nPar défaut, toutes les fonctions ont `F.prototype={constructor:F}`, nous pouvons donc obtenir le constructeur d'un objet en accédant à sa propriété `\"constructor\"`.\n"
  },
  {
    "path": "1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md",
    "content": "```js run\nFunction.prototype.defer = function(ms) {\n  setTimeout(this, ms);\n};\n\nfunction f() {\n  alert(\"Hello!\");\n}\n\nf.defer(1000); // montre \"Hello!\" après 1 seconde\n```\n"
  },
  {
    "path": "1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md",
    "content": "importance: 5\n\n---\n\n# Ajouter la méthode \"f.defer(ms)\" aux fonctions\n\nAjoutez au prototype de toutes les fonctions la méthode `defer(ms)`, qui exécute la fonction après `ms` millisecondes.\n\nUne fois que vous le faites, ce code devrait fonctionner :\n\n```js\nfunction f() {\n  alert(\"Hello!\");\n}\n\nf.defer(1000); // montre \"Hello!\" après 1 seconde\n```\n"
  },
  {
    "path": "1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md",
    "content": "```js run\nFunction.prototype.defer = function(ms) {\n  let f = this;\n  return function(...args) {\n    setTimeout(() => f.apply(this, args), ms);\n  }\n};\n\n// vérification\nfunction f(a, b) {\n  alert( a + b );\n}\n\nf.defer(1000)(1, 2); // montre 3 après 1 seconde\n```\n\nNotez que nous utilisons `this` dans `f.apply` pour que notre décoration fonctionne pour les méthodes d'objets.\n\nAinsi, si la fonction wrapper est appelée en tant que méthode d'objet, alors `this` est passé à la méthode originale `f`.\n\n```js run\nFunction.prototype.defer = function(ms) {\n  let f = this;\n  return function(...args) {\n    setTimeout(() => f.apply(this, args), ms);\n  }\n};\n\nlet user = {\n  name: \"John\",\n  sayHi() {\n    alert(this.name);\n  }\n}\n\nuser.sayHi = user.sayHi.defer(1000);\n\nuser.sayHi();\n```\n"
  },
  {
    "path": "1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md",
    "content": "importance: 4\n\n---\n\n# Ajouter la décoration \"defer()\" aux fonctions\n\nAjoutez au prototype de toutes les fonctions la méthode `defer(ms)`, qui renvoie un wrapper, retardant l’appel de `ms` millisecondes.\n\nVoici un exemple de la façon dont cela devrait fonctionner :\n\n```js\nfunction f(a, b) {\n  alert( a + b );\n}\n\nf.defer(1000)(1, 2); // montre 3 après 1 seconde\n```\n\nVeuillez noter que les arguments doivent être passés à la fonction d'origine.\n"
  },
  {
    "path": "1-js/08-prototypes/03-native-prototypes/article.md",
    "content": "# Prototypes natifs\n\nLa propriété `\"prototype\"` est largement utilisée au centre de JavaScript lui-même. Toutes les fonctions constructeurs intégrées l'utilisent.\n\nNous verrons d’abord les détails, puis comment l’utiliser pour ajouter de nouvelles fonctionnalités aux objets intégrés.\n\n## Object.prototype\n\nDisons que nous produisons un objet vide :\n\n```js run\nlet obj = {};\nalert( obj ); // \"[object Object]\" ?\n```\n\nOù est le code qui génère la chaîne `\"[object Object]\"` ? C'est une méthode `toString` intégrée, mais où est-elle ? L'objet `obj` est vide !\n\n...Mais la notation abrégée `obj = {}` est identique à `obj = new Object()`, où `Object` est une fonction constructeur de l'objet intégrée, avec son propre `prototype` référençant un énorme objet avec `toString` et d'autres méthodes.\n\nVoici ce qui se passe :\n\n![](object-prototype.svg)\n\nLorsque `new Object()` est appelé (ou un objet littéral `{...}` est créé), le `[[Prototype]]` de celui-ci est défini sur `Object.prototype` conformément à la règle dont nous avons parlé dans le chapitre précédent :\n\n![](object-prototype-1.svg)\n\nAinsi, quand on appelle `obj.toString()`, la méthode est extraite de `Object.prototype`.\n\nNous pouvons le vérifier comme ceci :\n\n```js run\nlet obj = {};\n\nalert(obj.__proto__ === Object.prototype); // true\n\nalert(obj.toString === obj.__proto__.toString); //true\nalert(obj.toString === Object.prototype.toString); //true\n```\n\nVeuillez noter qu'il n'y a plus de `[[Prototype]]` dans la chaîne au dessus de `Object.prototype` :\n\n```js run\nalert(Object.prototype.__proto__); // null\n```\n\n## Autres prototypes intégrés\n\nD'autres objets intégrés, tels que `Array`, `Date`, `Function` et autres, conservent également des méthodes dans des prototypes.\n\nPar exemple, lorsque nous créons un tableau `[1, 2, 3]`, le constructeur `new Array()` par défaut est utilisé en interne. Donc `Array.prototype` devient son prototype et fournit des méthodes. C'est très efficace en mémoire.\n\nPar spécification, tous les prototypes intégrés ont `Object.prototype` en haut. C'est pourquoi certaines personnes disent que \"tout hérite d'objets\".\n\nVoici la vue d'ensemble :\n\n![](native-prototypes-classes.svg)\n\nVérifions les prototypes manuellement :\n\n```js run\nlet arr = [1, 2, 3];\n\n// il hérite de Array.prototype ?\nalert( arr.__proto__ === Array.prototype ); // true\n\n// puis de Object.prototype ?\nalert( arr.__proto__.__proto__ === Object.prototype ); // true\n\n// et null tout en haut.\nalert( arr.__proto__.__proto__.__proto__ ); // null\n```\n\nCertaines méthodes dans les prototypes peuvent se chevaucher, par exemple, `Array.prototype` a son propre `toString` qui répertorie les éléments délimités par des virgules :\n\n```js run\nlet arr = [1, 2, 3]\nalert(arr); // 1,2,3 <-- le résultat de Array.prototype.toString\n```\n\nComme nous l'avons vu précédemment, `Object.prototype` a aussi `toString`, mais `Array.prototype` est plus proche dans la chaîne, la variante de tableau est donc utilisée.\n\n![](native-prototypes-array-tostring.svg)\n\nLes outils intégrés au navigateur, tels que la console de développement Chrome, affichent également l'héritage (il faut éventuellement utiliser `console.dir` pour les objets intégrés) :\n\n![](console_dir_array.png)\n\nLes autres objets intégrés fonctionnent également de la même manière. Même les fonctions - ce sont des objets d'un constructeur intégré `Function`, et leurs méthodes (`call`/`apply` et autres) sont extraites de `Function.prototype`. Les fonctions ont aussi leur propre `toString`.\n\n```js run\nfunction f() {}\n\nalert(f.__proto__ == Function.prototype); // true\nalert(f.__proto__.__proto__ == Object.prototype); // true, hérite d'objets\n```\n\n## Primitives\n\nUne chose complexe se produit avec les chaînes, les nombres et les booléens.\n\nComme on s'en souvient, ce ne sont pas des objets. Mais si nous essayons d'accéder à leurs propriétés, des objets wrapper temporaires sont créés à l'aide des constructeurs intégrés `String`, `Number` et `Boolean`, ils fournissent les méthodes et disparaissent.\n\nCes objets sont créés de manière invisible pour nous et la plupart des moteurs les optimisent, mais la spécification le décrit exactement de cette façon. Les méthodes de ces objets résident également dans des prototypes, disponibles sous les noms `String.prototype`, `Number.prototype` et `Boolean.prototype`.\n\n```warn header=\"Les valeurs `null` et `undefined` n'ont pas de wrappers d'objet\"\nLes valeurs spéciales `null` et `undefined` se démarquent. Elles n'ont pas de wrapper d'objet, donc les méthodes et les propriétés ne sont pas disponibles pour eux. Et il n'y a pas non plus de prototypes correspondants.\n```\n\n## Modification des prototypes natifs [#native-prototype-change]\n\nLes prototypes natifs peuvent être modifiés. Par exemple, si nous ajoutons une méthode à `String.prototype`, elle devient disponible pour toutes les chaînes :\n\n```js run\nString.prototype.show = function() {\n  alert(this);\n};\n\n\"BOOM!\".show(); // BOOM!\n```\n\nAu cours du processus de développement, nous pouvons avoir des idées de nouvelles méthodes intégrées que nous aimerions avoir et nous pourrions être tentés de les ajouter à des prototypes natifs. Mais c'est généralement une mauvaise idée.\n\n```warn\nLes prototypes sont globaux, il est donc facile de créer un conflit. Si deux bibliothèques ajoutent une méthode `String.prototype.show`, l'une d'elles remplacera la méthode de l'autre.\n\nDonc, généralement, modifier un prototype natif est considéré comme une mauvaise idée.\n```\n\n**Dans la programmation moderne, il n'y a qu'un seul cas où la modification de prototypes natifs est approuvée. Le polyfilling.**\n\nPolyfilling est un terme utilisé pour remplacer une méthode existante dans la spécification JavaScript, mais qui n'est pas encore prise en charge par un moteur JavaScript particulier.\n\nEnsuite, nous pouvons l’implémenter manuellement et y ajouter le prototype intégré.\n\nPar exemple :\n\n```js run\nif (!String.prototype.repeat) { // s'il n'y a pas une telle méthode\n  // ajouter le au prototype\n\n  String.prototype.repeat = function(n) {\n    // répéter la chaîne n fois\n\n    // en fait, le code devrait être un peu plus complexe que cela\n    // (l'algorithme complet est dans la spécification)\n    // mais même un polyfill imparfait est souvent considéré comme suffisant\n    return new Array(n + 1).join(this);\n  };\n}\n\nalert( \"La\".repeat(3) ); // LaLaLa\n```\n\n## Emprunt de prototypes\n\nDans le chapitre <info:call-apply-decorators#method-borrowing> nous avons parlé de l'emprunt de méthode.\n\nC'est quand nous prenons une méthode d'un objet et le copions dans un autre.\n\nCertaines méthodes de prototypes natifs sont souvent empruntées.\n\nPar exemple, si nous créons un objet semblable à un tableau, nous voudrons peut-être y copier des méthodes `Array`.\n\nPar exemple :\n\n```js run\nlet obj = {\n  0: \"Hello\",\n  1: \"world!\",\n  length: 2,\n};\n\n*!*\nobj.join = Array.prototype.join;\n*/!*\n\nalert( obj.join(',') ); // Hello,world!\n```\n\nCela fonctionne car l'algorithme interne de la méthode `join` intégrée ne se préoccupe que des index corrects et de la propriété `length`. Il ne vérifie pas que l'objet est bien un tableau. Et beaucoup de méthodes intégrées sont comme ça.\n\nUne autre possibilité consiste à hériter en fixant `obj.__proto__` sur `Array.prototype`, afin que toutes les méthodes `Array` soient automatiquement disponibles dans `obj`.\n\nMais c'est impossible si `obj` hérite déjà d'un autre objet. N'oubliez pas que nous ne pouvons hériter que d'un objet à la fois.\n\nL'emprunt des méthodes est flexible, cela permet de mélanger les fonctionnalités provenants d'objets différents en cas de besoin.\n\n## Résumé\n\n- Tous les objets intégrés suivent le même schéma :\n    - Les méthodes sont stockées dans le prototype (`Array.prototype`, `Object.prototype`, `Date.prototype`, etc.).\n    - L'objet lui-même ne stocke que les données (éléments de tableau, propriétés de l'objet, date).\n- Les primitives stockent également des méthodes dans des prototypes d'objets wrapper : `Number.prototype`, `String.prototype`, `Boolean.prototype`. Seuls `undefined` et `null` n'ont pas d'objets wrapper.\n- Les prototypes intégrés peuvent être modifiés ou remplis avec de nouvelles méthodes. Mais il n'est pas recommandé de les changer. La seule cause possible est probablement l’ajout d’un nouveau standard, mais pas encore pris en charge par le moteur JavaScript.\n"
  },
  {
    "path": "1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md",
    "content": "\nLa méthode peut prendre toutes les clés énumérables en utilisant `Object.keys` et afficher leur liste.\n\nPour rendre `toString` non-énumérable, définissons-le à l'aide d'un descripteur de propriété. La syntaxe de `Object.create` nous permet de fournir un objet avec des descripteurs de propriété comme second argument.\n\n```js run\n*!*\nlet dictionary = Object.create(null, {\n  toString: { // définir la propriété toString\n    value() { // la valeur est une fonction\n      return Object.keys(this).join();\n    }\n  }\n});\n*/!*\n\ndictionary.apple = \"Apple\";\ndictionary.__proto__ = \"test\";\n\n// apple et __proto__ sont dans la boucle\nfor(let key in dictionary) {\n  alert(key); // \"apple\", puis \"__proto__\"\n}  \n\n// liste de propriétés séparées par des virgules par toString\nalert(dictionary); // \"apple,__proto__\"\n```\n\nLorsque nous créons une propriété à l'aide d'un descripteur, ses indicateurs sont `false` par défaut. Donc, dans le code ci-dessus, `dictionary.toString` est non énumérable.\n\nVoir le chapitre [](info:property-descriptors) pour revoir.\n"
  },
  {
    "path": "1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/task.md",
    "content": "importance: 5\n\n---\n\n# Ajouter toString au dictionnaire\n\nIl existe un objet `dictionary`, créé en tant que `Object.create(null)`, pour stocker toutes les paires `clé`/`valeur`.\n\nAjoutez la méthode `dictionary.toString()`, qui devrait renvoyer une liste de clés délimitée par des virgules. Votre `toString` ne devrait pas apparaître dans la boucle `for..in` sur l'objet.\n\nVoici comment cela devrait fonctionner :\n\n```js\nlet dictionary = Object.create(null);\n\n*!*\n// votre code pour ajouter la méthode dictionary.toString\n*/!*\n\n// add some data\ndictionary.apple = \"Apple\";\ndictionary.__proto__ = \"test\"; // __proto__ est une clé de propriété régulière ici\n\n// seulement apple et __proto__ sont dans la boucle\nfor(let key in dictionary) {\n  alert(key); // \"apple\", puis \"__proto__\"\n}\n\n// votre toString en action\nalert(dictionary); // \"apple,__proto__\"\n```\n"
  },
  {
    "path": "1-js/08-prototypes/04-prototype-methods/3-compare-calls/solution.md",
    "content": "\nLe premier appel a `this==rabbit`, les autres ont `this` égal à `Rabbit.prototype`, car il s'agit en fait de l'objet avant le point.\n\nAinsi, seul le premier appel indique `Rabbit`, les autres affichent `undefined` :\n\n```js run\nfunction Rabbit(name) {\n  this.name = name;\n}\nRabbit.prototype.sayHi = function() {\n  alert( this.name );\n}\n\nlet rabbit = new Rabbit(\"Rabbit\");\n\nrabbit.sayHi();                        // Rabbit\nRabbit.prototype.sayHi();              // undefined\nObject.getPrototypeOf(rabbit).sayHi(); // undefined\nrabbit.__proto__.sayHi();              // undefined\n```\n"
  },
  {
    "path": "1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md",
    "content": "importance: 5\n\n---\n\n# La différence entre les appels\n\nCréons un nouvel objet `rabbit` :\n\n```js\nfunction Rabbit(name) {\n  this.name = name;\n}\nRabbit.prototype.sayHi = function() {\n  alert(this.name);\n};\n\nlet rabbit = new Rabbit(\"Rabbit\");\n```\n\nCes appels font-ils la même chose ou non ?\n\n```js\nrabbit.sayHi();\nRabbit.prototype.sayHi();\nObject.getPrototypeOf(rabbit).sayHi();\nrabbit.__proto__.sayHi();\n```\n"
  },
  {
    "path": "1-js/08-prototypes/04-prototype-methods/article.md",
    "content": "\n# Méthodes de prototypes, objets sans __proto__\n\nDans le premier chapitre de cette section, nous avons indiqué qu'il existe des méthodes modernes pour configurer un prototype.\n\nLa définition ou la lecture du prototype avec `obj.__proto__` est considérée comme obsolète et dépréciée (déplacée dans la soi-disant \"annexe B\" de la norme JavaScript, destinée uniquement aux navigateurs).\n\nLes méthodes modernes pour obtenir/définir un prototype sont :\n\n- [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- retourn le `[[Prototype]]` de `obj`.\n- [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- configure le `[[Prototype]]` de `obj` à `proto`.\n\nLa seule utilisation de `__proto__`, qui n'est pas mal vue, est en tant que propriété lors de la création d'un nouvel objet : `{ __proto__: ... }`.\n\nBien qu'il existe également une méthode spéciale pour cela :\n\n- [Object.create(proto, [descriptors])](mdn:js/Object/create) -- crée un objet vide avec `proto` donné comme `[[Prototype]]` et des descripteurs de propriété facultatifs.\n\nPar exemple :\n\n```js run\nlet animal = {\n  eats: true\n};\n\n// créer un nouvel objet avec animal comme prototype\n*!*\nlet rabbit = Object.create(animal); // identique à {__proto__: animal}\n*/!*\n\nalert(rabbit.eats); // true\n\n*!*\nalert(Object.getPrototypeOf(rabbit) === animal); // true\n*/!*\n\n*!*\nObject.setPrototypeOf(rabbit, {}); // change le prototype de rabbit en {}\n*/!*\n```\n\nLa méthode `Object.create` est un peu plus puissante, car elle a un deuxième argument facultatif : les descripteurs de propriété.\n\nNous pouvons fournir des propriétés supplémentaires au nouvel objet, comme ceci :\n\n```js run\nlet animal = {\n  eats: true\n};\n\nlet rabbit = Object.create(animal, {\n  jumps: {\n    value: true\n  }\n});\n\nalert(rabbit.jumps); // true\n```\n\nLes descripteurs sont dans le même format que décrit dans le chapitre <info:property-descriptors>.\n\nNous pouvons utiliser `Object.create` pour effectuer un clonage d'objet plus puissant que la copie des propriétés dans la boucle `for..in` :\n\n```js\nlet clone = Object.create(\n  Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)\n);\n```\n\nCet appel crée une copie véritablement exacte de `obj`, y compris de toutes les propriétés : énumérable et non-énumérable, des propriétés de données et des accesseurs/mutateurs - tout, et avec le bon `[[Prototype]]`.\n\n## Bref historique\n\nIl y a tellement de façons de gérer `[[Prototype]]`. Comment est-ce arrivé ? Pourquoi ?\n\nC'est pour des raisons historiques.\n\nL'héritage prototypal était dans le langage depuis son aube, mais les façons de le gérer ont évolué au fil du temps.\n\n- La propriété `prototype` d'une fonction constructeur fonctionne depuis des temps très anciens. C'est la manière la plus ancienne de créer des objets avec un prototype donné.\n- Plus tard, en 2012, `Object.create` est apparu dans la norme. Il a donné la possibilité de créer des objets avec un prototype donné, mais n'a pas fourni la possibilité de l'obtenir/le définir. Certains navigateurs ont implémenté l'accesseur non standard `__proto__` qui permettait à l'utilisateur d'obtenir/définir un prototype à tout moment, pour donner plus de flexibilité aux développeurs.\n- Plus tard, en 2015, `Object.setPrototypeOf` et `Object.getPrototypeOf` ont été ajoutés à la norme, pour exécuter la même fonctionnalité que `__proto__`. Comme `__proto__` était de facto implémenté partout, il était en quelque sorte obsolète et a fait son chemin vers l'annexe B de la norme, c'est-à-dire facultatif pour les environnements sans navigateur.\n- Plus tard, en 2022, il a été officiellement autorisé d'utiliser `__proto__` dans les objets littéraux `{...}` (sortie de l'annexe B), mais pas en tant que getter/setter `obj.__proto__` (toujours dans l'annexe B).\n\nPourquoi `__proto__` a été remplacé par les fonctions `getPrototypeOf`/`setPrototypeOf` ?\n\nPourquoi `__proto__` a-t-il été partiellement réhabilité et son utilisation autorisée dans `{...}`, mais pas en tant que getter/setter ?\n\nC'est une question intéressante, qui nous oblige à comprendre pourquoi `__proto__` est mauvais.\n\nEt bientôt nous aurons la réponse.\n\n```warn header=\"Ne changez pas `[[Prototype]]` sur des objets existants si la vitesse est importante\"\nTechniquement, nous pouvons accéder/muter `[[Prototype]]` à tout moment. Mais en général, nous ne le définissons qu’une fois au moment de la création de l’objet, puis nous ne le modifions pas : `rabbit` hérite de `animal`, et cela ne changera pas.\n\nEt les moteurs JavaScript sont hautement optimisés pour cela. Changer un prototype \"à la volée\" avec `Object.setPrototypeOf` ou `obj.__ proto __=` est une opération très lente, elle rompt les optimisations internes pour des opérations d'accès aux propriétés d'objet. Alors évitez-la à moins que vous ne sachiez ce que vous faites, ou que la vitesse de JavaScript n'a pas d'importance pour vous.\n```\n\n## Objets \"très simples\" [#very-plain]\n\nComme nous le savons, les objets peuvent être utilisés en tant que tableaux associatifs pour stocker des paires clé/valeur.\n\n...Mais si nous essayons de stocker des clés *fournies par l'utilisateur* (par exemple, un dictionnaire saisi par l'utilisateur), nous verrons un petit problème intéressant : toutes les clés fonctionnent très bien, sauf `\"__proto __\"`.\n\nDécouvrez l'exemple :\n\n```js run\nlet obj = {};\n\nlet key = prompt(\"What's the key?\", \"__proto__\");\nobj[key] = \"some value\";\n\nalert(obj[key]); // [object Object], pas \"some value\" !\n```\n\nIci, si l'utilisateur tape `__proto__`, l'assignation à la ligne 4 est ignorée !\n\nCela pourrait sûrement être surprenant pour un non-développeur, mais assez compréhensible pour nous. La propriété `__proto__` est spéciale : elle doit être soit un objet, soit `null`. Une chaîne de caractères ne peut pas devenir un prototype. C'est pourquoi une affectation d'une chaîne à `__proto__` est ignorée.\n\nMais nous n'avions pas *l'intention* de mettre en œuvre un tel comportement, non ? Nous voulons stocker des paires clé/valeur, et la clé nommée `\"__proto__\"` n'a pas été correctement enregistrée. Donc c'est un bug !\n\nIci les conséquences ne sont pas terribles. Mais dans d'autres cas, nous pouvons stocker des objets au lieu de chaînes dans `obj`, puis le prototype sera effectivement modifié. En conséquence, l'exécution ira mal de manière totalement inattendue.\n\nCe qui est pire -- généralement les développeurs ne pensent pas du tout à cette possibilité. Cela rend ces bugs difficiles à remarquer et même à les transformer en vulnérabilités, en particulier lorsque JavaScript est utilisé côté serveur.\n\nDes choses inattendues peuvent également se produire lors de l'affectation à `obj.toString`, car il s'agit d'une méthode d'objet intégrée.\n\nComment pouvons-nous éviter ce problème ?\n\nTout d'abord, nous pouvons simplement passer à l'utilisation de `Map` pour le stockage au lieu d'objets simples, puis tout va bien.\n\n```js run\nlet map = new Map();\n\nlet key = prompt(\"What's the key?\", \"__proto__\");\nmap.set(key, \"some value\");\n\nalert(map.get(key)); // \"some value\" (comme prévu)\n```\n\n...Mais la syntaxe `Object` est souvent plus attrayante, car elle est plus concise.\n\nHeureusement, nous *pouvons* utiliser des objets, car les créateurs du langage ont réfléchi à ce problème il y a longtemps.\n\nComme nous le savons, `__proto__` n'est pas une propriété d'un objet, mais un accesseur sur la propriété `Object.prototype` :\n\n![](object-prototype-2.svg)\n\nAinsi, si `obj.__proto__` est lu ou muté, l'accésseur/mutateur correspondant est appelé à partir de son prototype et il accède/mute `[[Prototype]]`.\n\nComme il a été dit au début de cette section de tutoriel : `__proto__` est un moyen d'accéder `[[Prototype]]`, il n'est pas `[[Prototype]]` lui-même.\n\nMaintenant, si nous avons l'intention d'utiliser un objet comme tableau associatif et de ne pas avoir de tels problèmes, nous pouvons le faire avec une petite astuce :\n\n```js run\n*!*\nlet obj = Object.create(null);\n// ou : obj = { __proto__: null }\n*/!*\n\nlet key = prompt(\"What's the key?\", \"__proto__\");\nobj[key] = \"some value\";\n\nalert(obj[key]); // \"some value\"\n```\n\n`Object.create(null)` crée un objet vide sans prototype (`[[Prototype]]` est `null`) :\n\n![](object-prototype-null.svg)\n\nDonc, il n'y a pas d'accésseur/mutateur hérité pour `__proto__`. Maintenant, il est traité comme une propriété de données normale, ainsi l'exemple ci-dessus fonctionne correctement.\n\nNous pouvons appeler de tels objets des objets \"très simples\" ou \"dictionnaire pur\", car ils sont encore plus simples que les objets simples ordinaires `{...}`.\n\nL'inconvénient est que de tels objets ne possèdent aucune méthode d'objet intégrée, par exemple `toString` :\n\n```js run\n*!*\nlet obj = Object.create(null);\n*/!*\n\nalert(obj); // Error (pas de toString)\n```\n\n...Mais c'est généralement acceptable pour les tableaux associatifs.\n\nNotez que la plupart des méthodes liées aux objets sont `Object.quelquechose(...)`, comme `Object.keys(obj)` - elles ne sont pas dans le prototype, elles continueront donc à travailler sur de tels objets :\n\n```js run\nlet chineseDictionary = Object.create(null);\nchineseDictionary.hello = \"你好\";\nchineseDictionary.bye = \"再见\";\n\nalert(Object.keys(chineseDictionary)); // hello,bye\n```\n\n## Résumé\n\n- Pour créer un objet avec le prototype donné, utilisez :\n\n    - la syntaxe littérale : `{ __proto__: ... }`, permet de spécifier plusieurs propriétés\n    - ou [Object.create(proto, [descriptors])](mdn:js/Object/create), permet de spécifier des descripteurs de propriété.\n\n    Le `Object.create` fournit un moyen simple de copier superficiellement un objet avec tous les descripteurs :\n\n    ```js\n    let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));\n    ```\n\n- Les méthodes modernes pour obtenir/définir le prototype sont :\n\n    - [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- renvoie le `[[Prototype]]` de `obj` (identique au getter `__proto__`).\n    - [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- définit le `[[Prototype]]` de `obj` à `proto` (identique au setter `__proto__`).\n\n- Obtenir/définir le prototype en utilisant le getter/setter intégré. `__proto__` n'est pas recommandé, il est maintenant dans l'annexe B de la spécification.\n\n- Nous avons également couvert les objets sans prototype, créés avec `Object.create(null)` ou `{__proto__: null}`.\n\n    Ces objets sont utilisés comme dictionnaires, pour stocker toutes les clés (éventuellement générées par l'utilisateur).\n\n    Normalement, les objets héritent des méthodes intégrées et du getter/setter `__proto__` de `Object.prototype`, rendant les clés correspondantes \"occupées\" et provoquant potentiellement des effets secondaires. Avec le prototype \"null\", les objets sont vraiment vides.\n"
  },
  {
    "path": "1-js/08-prototypes/index.md",
    "content": "# Prototypes, héritage\n"
  },
  {
    "path": "1-js/09-classes/01-class/1-rewrite-to-class/_js.view/solution.js",
    "content": "class Clock {\n  constructor({ template }) {\n    this.template = template;\n  }\n\n  render() {\n    let date = new Date();\n\n    let hours = date.getHours();\n    if (hours < 10) hours = '0' + hours;\n\n    let mins = date.getMinutes();\n    if (mins < 10) mins = '0' + mins;\n\n    let secs = date.getSeconds();\n    if (secs < 10) secs = '0' + secs;\n\n    let output = this.template\n      .replace('h', hours)\n      .replace('m', mins)\n      .replace('s', secs);\n\n    console.log(output);\n  }\n\n  stop() {\n    clearInterval(this.timer);\n  }\n\n  start() {\n    this.render();\n    this.timer = setInterval(() => this.render(), 1000);\n  }\n}\n\n\nlet clock = new Clock({template: 'h:m:s'});\nclock.start();\n"
  },
  {
    "path": "1-js/09-classes/01-class/1-rewrite-to-class/_js.view/source.js",
    "content": "function Clock({ template }) {\n\n  let timer;\n\n  function render() {\n    let date = new Date();\n\n    let hours = date.getHours();\n    if (hours < 10) hours = '0' + hours;\n\n    let mins = date.getMinutes();\n    if (mins < 10) mins = '0' + mins;\n\n    let secs = date.getSeconds();\n    if (secs < 10) secs = '0' + secs;\n\n    let output = template\n      .replace('h', hours)\n      .replace('m', mins)\n      .replace('s', secs);\n\n    console.log(output);\n  }\n\n  this.stop = function() {\n    clearInterval(timer);\n  };\n\n  this.start = function() {\n    render();\n    timer = setInterval(render, 1000);\n  };\n\n}\n\nlet clock = new Clock({template: 'h:m:s'});\nclock.start();\n"
  },
  {
    "path": "1-js/09-classes/01-class/1-rewrite-to-class/solution.md",
    "content": ""
  },
  {
    "path": "1-js/09-classes/01-class/1-rewrite-to-class/task.md",
    "content": "importance: 5\n\n\n---\n\n# Réécrire en classe\n\nLa classe `Clock` (voir la sandbox) est écrite en style fonctionnelle. Réécrivez la en syntaxe de \"classe\".\n\nP.S. La montre doit tictaquer dans la console, ouvrez la pour la voir.\n"
  },
  {
    "path": "1-js/09-classes/01-class/article.md",
    "content": "\n# Syntaxe de base de la Classe\n\n```quote author=\"Wikipedia\"\nEn langage orienté objet, une *classe* est un modèle de code programme extensible servant à créer des objets. Elle fournit les valeurs initiales de l'état (les variables membres) et de l'implémentation du comportement (les fonctions ou méthodes membres).\n```\n\nEn pratique, nous avons souvent besoin de créer beaucoup d'objets de même type, tels que des utilisateurs, des biens ou toute autre chose.\n\nComme nous le savons dans le chapitre <info:constructor-new>, `new function` peut nous aider à faire cela.\n\nMais dans le JavaScript moderne, il y a une construction de la \"classe\" plus avancée qui introduit de nombreux nouveaux aspects utiles en langage orienté objet.\n\n## La syntaxe de \"classe\"\n\nLa syntaxe de base est :\n\n```js\nclass MyClass {\n  // Les méthodes de la classe\n  constructor() { ... }\n  method1() { ... }\n  method2() { ... }\n  method3() { ... }\n  ...\n}\n```\n\nVous pouvez ensuite utiliser `new MyClass()` pour créer un nouvel objet ayant toute la liste des méthodes.\n\nLa méthode `constructor()` est automatiquement appelée par `new`, donc nous pouvons initialiser l'objet à ce niveau.\n\nPar exemple :\n\n```js run\nclass User {\n\n  constructor(name) {\n    this.name = name;\n  }\n\n  sayHi() {\n    alert(this.name);\n  }\n\n}\n\n// Usage:\nlet user = new User(\"John\");\nuser.sayHi();\n```\n\nLorsque `new User(\"John\")` est appelé :\n\n1. Un nouvel objet est créé.\n2. Le `constructor` s'exécute avec les arguments qui lui sont passés et assigne `this.name` a l'objet.\n\n...ensuite nous pouvons appeler les méthodes de l'objet, tel que `user.sayHi()`.\n\n```warn header=\"Pas de virgule entre les méthodes de la classe\"\nUn piège fréquent des développeurs novices est de mettre une virgule entre les méthodes de la classe, entrainant ainsi une erreur syntaxique.\n\nLa notation ici ne doit pas être confondue avec les objets littéraux. A l'intérieure d'une classe, aucune virgule n'est requise.\n```\n\n## Qu'est-ce qu'une classe ?\n\nAlors, c'est quoi exactement une `class` ? Ce n'est pas totalement une nouvelle entité au niveau du langage, comme on pourrait le penser.\n\nDévoilons maintenant la magie et regardons ce qu'est réellement une classe. Cela va nous aider à comprendre plusieurs aspects complexes.\n\nEn JavaScript, une classe est une sorte de fonction.\n\nRegardons ici :\n\n```js run\nclass User {\n  constructor(name) { this.name = name; }\n  sayHi() { alert(this.name); }\n}\n\n// La preuve : User est une fonction\n*!*\nalert(typeof User); // function\n*/!*\n```\n\nCe que la construction de la classe `classe User {...}` fait réellement, c'est :\n\n1. Créer une fonction nommée `User`, qui devient le résultat de la déclaration de la classe. Le code de la fonction est tirée de la méthode `constructor` (considérée comme étant vide au cas ou cette méthode n'est pas écrite).\n2. Garde les méthodes de la classe, telle que `sayHi`, dans `User.prototype`.\n\nAprès la création de `new User`, lorsque nous appelons sa méthode, elle est extraite du prototype, comme décrit dans le chapitre <info:function-prototype>. Donc, l'objet a accès aux méthodes de classe.\n\nNous pouvons illustrer le résultat de la déclaration de `class User` ainsi :\n\n![](class-user.svg)\n\nVoici le code pour une introspection :\n\n```js run\nclass User {\n  constructor(name) { this.name = name; }\n  sayHi() { alert(this.name); }\n}\n\n// classe est une fonction\nalert(typeof User); // function\n\n// ...ou, plus précisément, le constructeur de la méthode\nalert(User === User.prototype.constructor); // true\n\n// Les méthodes sont dans User.prototype, par exemple :\nalert(User.prototype.sayHi); // le code de la méthode sayHi\n\n// Il y a exactement deux méthodes dans le prototype\nalert(Object.getOwnPropertyNames(User.prototype)); // constructeur, sayHi\n```\n\n## Pas simplement un sucre syntaxique\n\nParfois certaines personnes disent que la notion de `class` est un \"sucre syntaxique\" (une syntaxe qui est destinée à rendre la lecture plus facile, mais elle n'introduit rien de nouveau), parce qu'en réalité nous pouvons déclarer la même chose sans utiliser le mot clé `class` :\n\n```js run\n// Réécriture de class User en fonctions pures\n\n// 1. Créer la fonction constructeur\nfunction User(name) {\n  this.name = name;\n}\n// un prototype de fonction a une propriété constructeur par défaut,\n// nous n'avons donc pas besoin de le créer\n\n// 2. Ajouter la méthode au prototype\nUser.prototype.sayHi = function() {\n  alert(this.name);\n};\n\n// Usage:\nlet user = new User(\"John\");\nuser.sayHi();\n```\n\nLe résultat de cette définition est à peu près la même chose. Donc, il y a bien des raisons de vouloir considérer `class` comme pouvant être un sucre syntaxique pour définir un constructeur ensemble avec ses méthodes de prototype.\n\nCependant, il existe des différences importantes.\n\n1. Premièrement, une fonction créée par `class` est labellisée par une propriété interne spéciale `[[IsClassConstructor]]: true`. Ce n'est donc pas tout à fait la même chose que de le créer manuellement.\n\n    Le langage vérifie cette propriété à divers endroits. Par exemple, contrairement à une fonction régulière, elle doit être appelée avec `new` :\n\n    ```js run\n    class User {\n      constructor() {}\n    }\n\n    alert(typeof User); // fonction\n    User(); // Erreur: le constructeur Class User ne peut être invoque sans 'new'\n    ```\n\n    Aussi, la représentation en chaîne de caractères d'un constructeur de class dans la plupart des moteurs de JavaScript commence avec \"class...\" :\n\n    ```js run\n    class User {\n      constructor() {}\n    }\n\n    alert(User); // class User { ... }\n    ```\n\n    Il y a d'autres différences, nous les verrons bientôt.\n\n2. Les méthodes de Class sont non-énumérable.\n    Une définition de la classe attribue `false` à la propriété `enumerable` pour toutes les méthodes du `\"prototype\"`.\n\n    C'est bien, parce que si nous exécutons un `for..in` sur un Object, souvent nous ne voulons pas accéder aux méthodes de sa classe.\n\n3. Les Classes utilisent toujours `use strict`.\n    Tout code à l'intérieur de la construction de la classe est automatiquement en mode strict.\n\nEn outres, la syntaxe `classe` apporte beaucoup d'autres caractéristiques que nous allons explorer plus tard.\n\n## L'Expression Class\n\nTout comme les fonctions, les classes peuvent être définies a l'intérieur d'une autre expression, passées en paramètres, retournées, assignées etc.\n\nVoici un exemple d'expression d'une classe :\n\n```js\nlet User = class {\n  sayHi() {\n    alert(\"Hello\");\n  }\n};\n```\n\nSimilairement aux Fonction Expressions nommées, les expressions de classe peuvent avoir un nom.\n\nSi une expression de classe a un nom, il est visible à l'intérieur de la classe uniquement :\n\n```js run\n// \"Expression de Classe nommée\"\n// (Terme non existant dans la spécification, mais elle est similaire a une Expression de Fonction nommée)\nlet User = class *!*MyClass*/!* {\n  sayHi() {\n    alert(MyClass); // le nom MyClass est seulement visible dans la classe\n  }\n};\n\nnew User().sayHi(); // ça fonctionne, montre la définition de MyClass\n\nalert(MyClass); // erreur, le nom MyClass n'est pas visible en dehors de la classe\n```\n\nNous pouvons même créer les classes dynamiquement \"à la demande\", comme suit :\n\n```js run\nfunction makeClass(phrase) {\n  // déclare une classe et la retourne\n  return class {\n    sayHi() {\n      alert(phrase);\n    }\n  };\n}\n\n// Crée une nouvelle classe\nlet User = makeClass(\"Hello\");\n\nnew User().sayHi(); // Hello\n```\n\n## Accesseurs/Mutateurs\n\nTout comme les objets littéraux, les classes peuvent inclure des accesseurs/mutateurs, des propriétés évaluées etc.\n\nVoici un exemple pour `user.name` implémenté en utilisant les propriétés `get`/`set` :\n\n```js run\nclass User {\n\n  constructor(name) {\n    // invoque l'accesseur (the setter)\n    this.name = name;\n  }\n\n*!*\n  get name() {\n*/!*\n    return this._name;\n  }\n\n*!*\n  set name(value) {\n*/!*\n    if (value.length < 4) {\n      alert(\"Name is too short.\");\n      return;\n    }\n    this._name = value;\n  }\n\n}\n\nlet user = new User(\"John\");\nalert(user.name); // John\n\nuser = new User(\"\"); // le nom est trop court.\n```\n\nTechniquement, une telle déclaration de classe fonctionne en créant des getters et des setters dans `User.prototype`.\n\n## Computed names [...]\n\nVoici un exemple avec un nom de méthode calculé utilisant des crochets `[...]` :\n\n```js run\nclass User {\n\n*!*\n  ['say' + 'Hi']() {\n*/!*\n    alert(\"Hello\");\n  }\n\n}\n\nnew User().sayHi();\n```\n\nCes caractéristiques sont faciles à retenir, car elles ressemblent à celles d'objets littéraux.\n\n## Champs de classe\n\n```warn header=\"Les anciens navigateurs peuvent avoir besoin de polyfill\"\nLes propriétés de classe sont un ajout récent au langage.\n```\n\nAuparavant, nos classes n'avaient que des méthodes.\n\nLes \"Class fields\" (champs de classe) sont une syntaxe qui permet d'ajouter des propriétés.\n\nPar exemple, ajoutons la propriété `name` à `class User` :\n\n```js run\nclass User {\n*!*\n  name = \"John\";\n*/!*\n\n  sayHi() {\n    alert(`Hello, ${this.name}!`);\n  }\n}\n\nnew User().sayHi(); // Hello, John!\n```\n\nIl suffit donc d'écrire \"<property name> = <value>\" dans la déclaration, et c'est tout.\n\nLa différence importante des champs de classe est qu'ils sont définis sur des objets individuels, et non sur `User.prototype` :\n\n```js run\nclass User {\n*!*\n  name = \"John\";\n*/!*\n}\n\nlet user = new User();\nalert(user.name); // John\nalert(User.prototype.name); // undefined\n```\n\nNous pouvons également attribuer des valeurs à l'aide d'expressions et d'appels de fonctions plus complexes :\n\n```js run\nclass User {\n*!*\n  name = prompt(\"Name, please?\", \"John\");\n*/!*\n}\n\nlet user = new User();\nalert(user.name); // John\n```\n\n### Création de méthodes liées avec des champs de classe\n\nComme démontré dans le chapitre <info:bind> les fonctions en JavaScript ont un `this` dynamique. Cela dépend du contexte de l'appel.\n\nDonc, si une méthode objet est contournée et appelée dans un autre contexte, `this` ne sera plus une référence à son objet.\n\nPar exemple, ce code affichera `undefined` :\n\n```js run\nclass Button {\n  constructor(value) {\n    this.value = value;\n  }\n\n  click() {\n    alert(this.value);\n  }\n}\n\nlet button = new Button(\"hello\");\n\n*!*\nsetTimeout(button.click, 1000); // undefined\n*/!*\n```\n\nLe problème est appelé \"perdre le `this`\".\n\nIl existe deux approches pour le corriger, comme indiqué dans le chapitre <info:bind> :\n\n1. Passer une fonction wrapper, telle que `setTimeout(() => button.click(), 1000)`.\n2. Lier la méthode à l'objet, par exemple dans le constructeur.\n\nLes champs de classe fournissent une syntaxe plus élégante :\n\n```js run\nclass Button {\n  constructor(value) {\n    this.value = value;\n  }\n*!*\n  click = () => {\n    alert(this.value);\n  }\n*/!*\n}\n\nlet button = new Button(\"hello\");\n\nsetTimeout(button.click, 1000); // hello\n```\n\nLe champ de classe `click = () => {...}` est créé par objet, il y a une fonction distincte pour chaque objet `Button`, avec `this` à l'intérieur référençant cet objet. Nous pouvons passer `button.click` n'importe où, et la valeur de `this` sera toujours correcte.\n\nC'est particulièrement utile dans un environnement de navigateur, pour les écouteurs d'événements.\n\n## Résumé\n\nLa syntaxe de base d'une classe ressemble à ceci :\n\n```js\nclass MyClass {\n  prop = value; // propriété\n\n  constructor(...) { // constructeur\n    // ...\n  }\n\n  method(...) {} // méthode\n\n  get something(...) {} //  méthode définie avec un accesseur\n  set something(...) {} //  méthode définie avec un mutateur\n\n  [Symbol.iterator]() {} // méthode avec un nom évalué (symbole ici)\n  // ...\n}\n```\n\n`MyClass` est techniquement une fonction (celle que nous fournissons en tant que `constructor`), tandis que les méthodes, accesseurs et mutateurs sont écrits dans `MyClass.prototype`.\n\nDans les prochains chapitres nous apprendrons plus à propos des classes, y compris la notion d'héritage et les autres caractéristiques.\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md",
    "content": "C'est parce que le constructeur de l'enfant doit appeler `super()`.\n\nVoici le code corrigé:\n\n```js run\nclass Animal {\n\n  constructor(name) {\n    this.name = name;\n  }\n\n}\n\nclass Rabbit extends Animal {\n  constructor(name) {  \n    *!*\n    super(name);\n    */!*\n    this.created = Date.now();\n  }\n}\n\n*!*\nlet rabbit = new Rabbit(\"White Rabbit\"); // OK maintenant\n*/!*\nalert(rabbit.name); // White Rabbit\n```\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md",
    "content": "importance: 5\n\n---\n\n# Erreur lors de la création d'une instance\n\nVoici le code avec `Rabbit` étendant `Animal`.\n\nMalheureusement, des objets `Rabbit` ne peuvent pas être créés. Qu'est-ce qui ne va pas ? Répare-le.\n\n```js run\nclass Animal {\n\n  constructor(name) {\n    this.name = name;\n  }\n\n}\n\nclass Rabbit extends Animal {\n  constructor(name) {\n    this.name = name;\n    this.created = Date.now();\n  }\n}\n\n*!*\nlet rabbit = new Rabbit(\"White Rabbit\"); // Error: this is not defined\n*/!*\nalert(rabbit.name);\n```\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.md",
    "content": "[js src=\"solution.view/extended-clock.js\"]\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/clock.js",
    "content": "class Clock {\n  constructor({ template }) {\n    this.template = template;\n  }\n\n  render() {\n    let date = new Date();\n\n    let hours = date.getHours();\n    if (hours < 10) hours = '0' + hours;\n\n    let mins = date.getMinutes();\n    if (mins < 10) mins = '0' + mins;\n\n    let secs = date.getSeconds();\n    if (secs < 10) secs = '0' + secs;\n\n    let output = this.template\n      .replace('h', hours)\n      .replace('m', mins)\n      .replace('s', secs);\n\n    console.log(output);\n  }\n\n  stop() {\n    clearInterval(this.timer);\n  }\n\n  start() {\n    this.render();\n    this.timer = setInterval(() => this.render(), 1000);\n  }\n}\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js",
    "content": "class ExtendedClock extends Clock {\n  constructor(options) {\n    super(options);\n    let { precision = 1000 } = options;\n    this.precision = precision;\n  }\n\n  start() {\n    this.render();\n    this.timer = setInterval(() => this.render(), this.precision);\n  }\n};\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<script src=\"clock.js\"></script>\n<script src=\"extended-clock.js\"></script>\n\n<script>\n  let lowResolutionClock = new ExtendedClock({\n    template: 'h:m:s',\n    precision: 10000\n  });\n\n  lowResolutionClock.start();\n</script>\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/clock.js",
    "content": "class Clock {\n  constructor({ template }) {\n    this.template = template;\n  }\n\n  render() {\n    let date = new Date();\n\n    let hours = date.getHours();\n    if (hours < 10) hours = '0' + hours;\n\n    let mins = date.getMinutes();\n    if (mins < 10) mins = '0' + mins;\n\n    let secs = date.getSeconds();\n    if (secs < 10) secs = '0' + secs;\n\n    let output = this.template\n      .replace('h', hours)\n      .replace('m', mins)\n      .replace('s', secs);\n\n    console.log(output);\n  }\n\n  stop() {\n    clearInterval(this.timer);\n  }\n\n  start() {\n    this.render();\n    this.timer = setInterval(() => this.render(), 1000);\n  }\n}\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<script src=\"clock.js\"></script>\n<script>\n  let clock = new Clock({\n    template: 'h:m:s'\n  });\n  clock.start();\n\n\n  /* Votre classe devrait fonctionner comme ceci: */\n\n  /*\n\n    let lowResolutionClock = new ExtendedClock({\n      template: 'h:m:s',\n      precision: 10000\n    });\n\n    lowResolutionClock.start();\n  */\n</script>\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md",
    "content": "importance: 5\n\n---\n\n# Horloge étendue\n\nNous avons une classe `Clock`. À partir de maintenant, il affiche l'heure à chaque seconde.\n\n\n[js src=\"source.view/clock.js\"]\n\nCréez une nouvelle classe `ExtendedClock` qui hérite de `Clock` et ajoute le paramètre `precision` - le nombre de `ms` entre \"ticks\". Devrait être `1000` (1 seconde) par défaut.\n\n- Votre code devrait être dans le fichier `extended-clock.js`\n- Ne modifiez pas le fichier `clock.js` d'origine. Étendez-le.\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/article.md",
    "content": "\n# Héritage de classe\n\nL'héritage de classe est un moyen pour une classe d'étendre une autre classe.\n\nNous pouvons donc créer de nouvelles fonctionnalités à partir de celles qui existent déjà.\n\n## Le mot-clé \"extends\"\n\nSupposons que nous ayons la classe `Animal` :\n\n```js\nclass Animal {\n  constructor(name) {\n    this.speed = 0;\n    this.name = name;\n  }\n  run(speed) {\n    this.speed = speed;\n    alert(`${this.name} runs with speed ${this.speed}.`);\n  }\n  stop() {\n    this.speed = 0;\n    alert(`${this.name} stands still.`);\n  }\n}\n\nlet animal = new Animal(\"My animal\");\n```\n\nVoici comment nous pouvons représenter graphiquement l'objet `animal` et la classe `Animal` :\n\n![](rabbit-animal-independent-animal.svg)\n\n... Et nous aimerions créer une autre `class Rabbit`.\n\nComme les lapins sont des animaux, la classe `Rabbit` devrait être basé sur `Animal`, avoir accès à des méthodes animales, de sorte que les lapins puissent faire ce que les animaux \"génériques\" peuvent faire.\n\nLa syntaxe pour étendre une autre classe est la suivante : `class Child extends Parent`.\n\nCréons `class Rabbit` qui hérite de `Animal` :\n\n```js\n*!*\nclass Rabbit extends Animal {\n*/!*\n  hide() {\n    alert(`${this.name} hides!`);\n  }\n}\n\nlet rabbit = new Rabbit(\"White Rabbit\");\n\nrabbit.run(5); // White Rabbit court à la vitesse 5.\nrabbit.hide(); // White Rabbit se cache!\n```\n\nL’objet de la classe `Rabbit` a accès à la fois aux méthodes `Rabbit`, telles que `rabbit.hide()`, et aux méthodes `Animal`, telles que `rabbit.run()`.\n\nEn interne, le mot clé `extended` fonctionne en utilisant le bon vieux prototype. Il établit `Rabbit.prototype.[[Prototype]]` vers `Animal.prototype`. Donc, si une méthode n'est pas trouvée dans `Rabbit.prototype`, JavaScript le prend de `Animal.prototype`.\n\n![](animal-rabbit-extends.svg)\n\nPar exemple, pour trouver la méthode `rabbit.run`, le moteur vérifie (de bas en haut sur l'image) :\n\n1. L'objet `rabbit` (n'a pas de `run`).\n2. Son prototype, c'est-à-dire `Rabbit.prototype` (a `hide`, mais pas `run`).\n3. Son prototype, c'est-à-dire (en raison de `extends`) `Animal.prototype`, qui a finalement la méthode `run`.\n\nComme nous pouvons nous en rappeler dans le chapitre <info:native-prototypes>, JavaScript lui-même utilise l'héritage prototypale pour les objets intégrés. Exemple : `Date.prototype.[[Prototype]]` est `Object.prototype`. C'est pourquoi les dates ont accès aux méthodes d'objet génériques.\n\n````smart header=\"Toute expression est autorisée après `extends`\"\nLa syntaxe de classe permet de spécifier non seulement une classe, mais toute expression après `extends`.\n\nPar exemple, un appel de fonction qui génère la classe parent :\n\n```js run\nfunction f(phrase) {\n  return class {\n    sayHi() { alert(phrase); }\n  };\n}\n\n*!*\nclass User extends f(\"Hello\") {}\n*/!*\n\nnew User().sayHi(); // Hello\n```\nIci la classe `user` hérite du résultat de `f(\"Hello\")`.\n\nCela peut être utile pour les modèles de programmation avancés lorsque nous utilisons des fonctions pour générer des classes en fonction de nombreuses conditions et que nous pouvons en hériter.\n````\n\n## Remplacer une méthode\n\nMaintenant, avançons et substituons une méthode. Par défaut, toutes les méthodes qui ne sont pas spécifiées dans `class Rabbit` sont prises directement \"telles quelles\" dans `class Animal`.\n\nMais si nous spécifions notre propre méthode dans `Rabbit`, telle que `stop()`, elle sera utilisée à la place :\n\n```js\nclass Rabbit extends Animal {\n  stop() {\n    // ...maintenant ceci sera utilisé pour rabbit.stop()\n    // au lieu de stop() de la classe Animal\n  }\n}\n```\n\nMais en général, nous ne voulons pas remplacer totalement une méthode parente, mais plutôt construire dessus, modifier ou étendre ses fonctionnalités. Nous faisons quelque chose dans notre méthode, mais appelons la méthode parente avant / après ou dans le processus.\n\nLes classes fournissent le mot clé `\"super\"` pour cela.\n\n- `super.method(...)` pour appeler une méthode parente.\n- `super(...)` pour appeler un constructeur parent (dans notre constructeur uniquement).\n\nPar exemple, laissons rabbit se cacher automatiquement à l’arrêt :\n\n```js run\nclass Animal {\n\n  constructor(name) {\n    this.speed = 0;\n    this.name = name;\n  }\n\n  run(speed) {\n    this.speed = speed;\n    alert(`${this.name} runs with speed ${this.speed}.`);\n  }\n\n  stop() {\n    this.speed = 0;\n    alert(`${this.name} stands still.`);\n  }\n\n}\n\nclass Rabbit extends Animal {\n  hide() {\n    alert(`${this.name} hides!`);\n  }\n\n*!*\n  stop() {\n    super.stop(); // appeler stop du parent\n    this.hide(); // puis hide\n  }\n*/!*\n}\n\nlet rabbit = new Rabbit(\"White Rabbit\");\n\nrabbit.run(5); // White Rabbit court à la vitesse 5.\nrabbit.stop(); // White Rabbit reste immobile. White Rabbit se cache!\n```\n\nMaintenant, `Rabbit` a la méthode `stop` qui appelle le `super.stop()` du parent dans le processus.\n\n````smart header=\"Les fonctions fléchées n'ont pas de `super`\"\nComme mentionné dans le chapitre <info:arrow-functions>, les fonctions fléchées n'ont pas `super`.\n\nSi on y accède, c'est tiré de la fonction externe. Par exemple :\n\n```js\nclass Rabbit extends Animal {\n  stop() {\n    setTimeout(() => super.stop(), 1000); // appel stop du parent après 1sec\n  }\n}\n```\n\nLe `super` dans la fonction fléchée est le même que dans `stop()`, donc cela fonctionne comme prévu. Si nous spécifions ici une fonction \"régulière\", il y aurait une erreur :\n\n```js\n// Super inattendu\nsetTimeout(function() { super.stop() }, 1000);\n```\n````\n\n## Remplacement du constructeur\n\nAvec les constructeurs, cela devient un peu délicat.\n\nJusqu'à maintenant, `Rabbit` n'avait pas son propre `constructor`.\n\nSelon la [spécification](https://tc39.github.io/ecma262/#sec-runtime-semantics-classdefinitionevaluation), si une classe étend une autre classe et n'a pas de `constructor`, alors le `constructor` \"vide\" suivant est généré :\n\n```js\nclass Rabbit extends Animal {\n  // généré pour l'extension de classes sans constructeur propre\n*!*\n  constructor(...args) {\n    super(...args);\n  }\n*/!*\n}\n```\n\nComme nous pouvons le constater, il appelle essentiellement le `constructor` du parent en lui passant tous les arguments. Cela se produit si nous n'écrivons pas notre propre constructeur.\n\nAjoutons maintenant un constructeur personnalisé à `Rabbit`. Il spécifiera le `earLength` en plus de `name` :\n\n```js run\nclass Animal {\n  constructor(name) {\n    this.speed = 0;\n    this.name = name;\n  }\n  // ...\n}\n\nclass Rabbit extends Animal {\n\n*!*\n  constructor(name, earLength) {\n    this.speed = 0;\n    this.name = name;\n    this.earLength = earLength;\n  }\n*/!*\n\n  // ...\n}\n\n*!*\n// Ça ne marche pas!\nlet rabbit = new Rabbit(\"White Rabbit\", 10); // Error: this is not defined.\n*/!*\n```\n\nOups ! Nous avons une erreur. Maintenant, nous ne pouvons pas créer de lapins. Qu'est-ce qui s'est passé ?\n\nLa réponse courte est :\n\n- **les constructeurs dans les classes qui héritent doivent appeler `super(...)`, et (!) le faire avant d'utiliser `this`.**\n\n...Mais pourquoi ? Que se passe t-il ici ? En effet, l'exigence semble étrange.\n\nBien sûr, il y a une explication. Entrons dans les détails pour que vous compreniez vraiment ce qui se passe.\n\nEn JavaScript, il existe une distinction entre une fonction constructeur d'une classe héritante (appelée \"constructeur dérivé\") et d'autres fonctions. Un constructeur dérivé a une propriété interne spéciale `[[ConstructorKind]] : \"derived\"`. C'est un label interne spécial.\n\nCe label affecte son comportement avec `new`.\n\n- Lorsqu'une fonction normale est exécutée avec `new`, elle crée un objet vide et l'assigne à `this`.\n- Mais lorsqu'un constructeur dérivé s'exécute, il ne le fait pas. Il s'attend à ce que le constructeur parent fasse ce travail.\n\nAinsi, un constructeur dérivé doit appeler `super` pour exécuter son constructeur parent (de base), sinon l'objet pour `this` ne sera pas créé. Et nous aurons une erreur.\n\nPour que le constructeur `Rabbit` fonctionne, il doit appeler `super()` avant d'utiliser `this`, comme ici :\n\n```js run\nclass Animal {\n\n  constructor(name) {\n    this.speed = 0;\n    this.name = name;\n  }\n\n  // ...\n}\n\nclass Rabbit extends Animal {\n\n  constructor(name, earLength) {\n*!*\n    super(name);\n*/!*\n    this.earLength = earLength;\n  }\n\n  // ...\n}\n\n*!*\n// maintenant ça marche\nlet rabbit = new Rabbit(\"White Rabbit\", 10);\nalert(rabbit.name); // White Rabbit\nalert(rabbit.earLength); // 10\n*/!*\n```\n\n### Remplacer les champs de classe : une note délicate\n\n```warn header=\"Note avancée\"\nCette note suppose que vous avez une certaine expérience avec les classes, peut-être dans d'autres langages de programmation.\n\nCela donne un meilleur aperçu du langage et explique également le comportement qui pourrait être une source de bogues (mais pas très souvent).\n\nSi vous avez du mal à comprendre, continuez, poursuivez votre lecture et revenez-y un peu plus tard.\n```\n\nNous pouvons remplacer non seulement les méthodes, mais également les champs de classe.\n\nCependant, il existe un comportement délicat lorsque nous accédons à un champ surchargé dans le constructeur parent, assez différent de la plupart des autres langages de programmation.\n\nPrenons cet exemple :\n\n```js run\nclass Animal {\n  name = 'animal';\n\n  constructor() {\n    alert(this.name); // (*)\n  }\n}\n\nclass Rabbit extends Animal {\n  name = 'rabbit';\n}\n\nnew Animal(); // animal\n*!*\nnew Rabbit(); // animal\n*/!*\n```\n\nIci, la classe `Rabbit` étend `Animal` et remplace le champ `name` par sa propre valeur.\n\nIl n'y a pas de constructeur propre dans `Rabbit`, donc le constructeur `Animal` est appelé.\n\nCe qui est intéressant, c'est que dans les deux cas : `new Animal()` et `new Rabbit()`, l'`alert` dans la ligne `(*)` montre `animal`.\n\n**En d'autres termes, le constructeur parent utilise toujours sa propre valeur de champ, pas celle remplacée.**\n\nQu'est-ce qui est étrange à ce sujet ?\n\nSi ce n'est pas encore clair, veuillez comparer avec les méthodes.\n\nVoici le même code, mais au lieu du champ `this.name`, nous appelons la méthode `this.showName()` :\n\n```js run\nclass Animal {\n  showName() {  // au lieu de this.name = 'animal'\n    alert('animal');\n  }\n\n  constructor() {\n    this.showName(); // au lieu de alert(this.name);\n  }\n}\n\nclass Rabbit extends Animal {\n  showName() {\n    alert('rabbit');\n  }\n}\n\nnew Animal(); // animal\n*!*\nnew Rabbit(); // rabbit\n*/!*\n```\n\nRemarque : maintenant la sortie est différente.\n\nEt c'est ce à quoi nous nous attendons naturellement. Lorsque le constructeur parent est appelé dans la classe dérivée, il utilise la méthode substituée.\n\n... Mais ce n'est pas le cas pour les champs de classe. Comme nous l'avons dit, le constructeur parent utilise toujours le champ parent.\n\nPourquoi y a-t-il une différence ?\n\nEh bien, la raison est dans l'ordre d'initialisation du champ. Le champ de classe est initialisé :\n\n- Avant le constructeur de la classe de base (qui n'étend rien),\n- Immédiatement après `super()` pour la classe dérivée.\n\nDans notre cas, `Rabbit` est la classe dérivée. Il n'y a pas de `constructor()` dedans. Comme dit précédemment, c'est la même chose que s'il y avait un constructeur vide avec seulement `super(...args)`.\n\nAinsi, `new Rabbit()` appelle `super()`, exécutant ainsi le constructeur parent, et (selon la règle pour les classes dérivées) seulement après cela ses champs de classe sont initialisés. Au moment de l'exécution du constructeur parent, il n'y a pas encore de champs de classe `Rabbit`, c'est pourquoi les champs `Animal` sont utilisés.\n\nCette subtile différence entre les champs et les méthodes est propre à JavaScript.\n\nHeureusement, ce comportement ne se révèle que si un champ surchargé est utilisé dans le constructeur parent. Ensuite, il peut être difficile de comprendre ce qui se passe, alors nous l'expliquons ici.\n\nSi cela devient un problème, on peut le résoudre en utilisant des méthodes ou des getters / setters au lieu de champs.\n\n## Super: les internes, [[HomeObject]]\n\n```warn header=\"Informations avancées\"\nSi vous lisez le tutoriel pour la première fois - cette section peut être ignorée.\n\nIl concerne les mécanismes internes de l'héritage et du \"super\".\n```\n\nPoussons un peu plus loin sous le capot de `super`. Nous y verrons des choses intéressantes.\n\nTout d'abord, d'après tout ce que nous avons appris jusqu'à présent, `super` ne devrait pas fonctionner du tout !\n\nOui, en effet, demandons-nous comment cela devrait fonctionner techniquement ? Lorsqu'une méthode d'objet est exécutée, l'objet actuel est remplacé par `this`. Si nous appelons `super.method()`, le moteur doit obtenir la `method` à partir du prototype de l'objet actuel. Mais comment ?\n\nLa tâche peut sembler simple, mais elle ne l’est pas. Le moteur connaît l'objet en cours `this`, de sorte qu'il pourrait obtenir la `method` parent sous la forme `this.__proto__.Method`. Malheureusement, une telle solution \"naïve\" ne fonctionnera pas.\n\nMontrons le problème. Sans les classes, en utilisant des objets simples pour des raisons de simplicité.\n\nSi vous ne voulez pas connaître les détails, vous pouvez sauter cette partie et aller en bas à la sous-section `[[HomeObject]]`. Cela ne fera pas de mal. Ou lisez si vous souhaitez comprendre les choses en profondeur.\n\nDans l'exemple ci-dessous, `rabbit.__proto__=animal`. Essayons maintenant : dans `rabbit.eat()` nous appellerons `animal.eat()`, en utilisant `this.__proto__` :\n\n```js run\nlet animal = {\n  name: \"Animal\",\n  eat() {\n    alert(`${this.name} eats.`);\n  }\n};\n\nlet rabbit = {\n  __proto__: animal,\n  name: \"Rabbit\",\n  eat() {\n*!*\n    // c'est ainsi que super.eat() pourrait fonctionner\n    this.__proto__.eat.call(this); // (*)\n*/!*\n  }\n};\n\nrabbit.eat(); // Rabbit eats.\n```\n\nÀ la ligne `(*)` nous prenons `eat` du prototype (`animal`) et l'appelons dans le contexte de l'objet actuel. Veuillez noter que `.call(this)` est important ici, car un simple `this.__proto__.eat()` exécuterait le `eat` du parent dans le contexte du prototype et non de l'objet actuel.\n\nEt dans le code ci-dessus, cela fonctionne réellement comme prévu : nous avons la bonne `alert`.\n\nAjoutons maintenant un objet de plus à la chaîne. Nous verrons comment les choses se cassent :\n\n```js run\nlet animal = {\n  name: \"Animal\",\n  eat() {\n    alert(`${this.name} eats.`);\n  }\n};\n\nlet rabbit = {\n  __proto__: animal,\n  eat() {\n    // ...rebondir et appeler la méthode du parent (animal)\n    this.__proto__.eat.call(this); // (*)\n  }\n};\n\nlet longEar = {\n  __proto__: rabbit,\n  eat() {\n    // ...faire quelque chose puis appeler la méthode du parent (rabbit)\n    this.__proto__.eat.call(this); // (**)\n  }\n};\n\n*!*\nlongEar.eat(); // Error: Maximum call stack size exceeded\n*/!*\n```\n\nLe code ne fonctionne plus ! Nous pouvons voir l'erreur en essayant d'appeler `longEar.eat()`.\n\nCe n'est peut-être pas si évident, mais si nous suivons l'appel de `longEar.eat()`, nous pouvons voir pourquoi. Dans les deux lignes `(*)` et `(**)`, la valeur de `this` est l'objet actuel (`longEar`). C'est essentiel : toutes les méthodes d'objet obtiennent l'objet actuel sous la forme `this`, pas un prototype ou quelque chose d'autre.\n\nAinsi, dans les deux lignes `(*)` et `(**)`, la valeur de `this.__proto__` est exactement la même : `rabbit`. Ils appellent tous deux `rabbit.eat` sans remonter la chaîne, dans une boucle infinie.\n\nVoici l'image de ce qui se passe :\n\n![](this-super-loop.svg)\n\n1. Dans `longEar.eat()`, la ligne `(**)` appelle `rabbit.eat` et lui fournit `this = longEar`.\n\n    ```js\n    // dans longEar.eat() nous avons this = longEar\n    this.__proto__.eat.call(this) // (**)\n    // devient\n    longEar.__proto__.eat.call(this)\n    // à savoir\n    rabbit.eat.call(this);\n    ```\n\n2. Ensuite, dans la ligne `(*)` de `rabbit.eat`, nous aimerions passer l'appel encore plus haut dans la chaîne, mais `this = longEar`, donc `this.__proto__.eat` est encore `rabbit.eat` !\n\n    ```js\n    // dans rabbit.eat() nous avons aussi this = longEar\n    this.__proto__.eat.call(this) // (*)\n    // devient\n    longEar.__proto__.eat.call(this)\n    // ou (encore)\n    rabbit.eat.call(this);\n    ```\n\n3. ...Donc `rabbit.eat` s’appelle lui-même dans une boucle infinie, car il ne peut pas monter plus loin.\n\nLe problème ne peut pas être résolu en utilisant seulement `this`.\n\n### `[[HomeObject]]`\n\nPour fournir la solution, JavaScript ajoute une propriété interne spéciale supplémentaire pour les fonctions : `[[HomeObject]]`.\n\nLorsqu'une fonction est spécifiée en tant que méthode de classe ou d'objet, sa propriété `[[HomeObject]]` devient cet objet.\n\nEnsuite, `super` l'utilise pour résoudre le prototype parent et ses méthodes.\n\nVoyons comment cela fonctionne, d'abord avec les objets simples :\n\n```js run\nlet animal = {\n  name: \"Animal\",\n  eat() {         // animal.eat.[[HomeObject]] == animal\n    alert(`${this.name} eats.`);\n  }\n};\n\nlet rabbit = {\n  __proto__: animal,\n  name: \"Rabbit\",\n  eat() {         // rabbit.eat.[[HomeObject]] == rabbit\n    super.eat();\n  }\n};\n\nlet longEar = {\n  __proto__: rabbit,\n  name: \"Long Ear\",\n  eat() {         // longEar.eat.[[HomeObject]] == longEar\n    super.eat();\n  }\n};\n\n*!*\n// fonctionne correctement\nlongEar.eat();  // Long Ear eats.\n*/!*\n```\n\nCela fonctionne comme prévu, en raison de la mécanique `[[HomeObject]]`. Une méthode, telle que `longEar.eat`, connaît son `[[HomeObject]]` et prend la méthode parente de son prototype. Sans aucune utilisation de `this`.\n\n### Les méthodes ne sont pas \"libres\"\n\nComme nous l'avons vu précédemment, les fonctions sont généralement \"libres\" et ne sont pas liées à des objets en JavaScript. Ils peuvent donc être copiés entre des objets et appelés avec un autre `this`.\n\nL'existence même de `[[HomeObject]]` viole ce principe, car les méthodes se souviennent de leurs objets. `[[HomeObject]]` ne peut pas être changé, ce lien est donc permanent.\n\nLe seul endroit dans le langage où `[[HomeObject]]` est utilisé est `super`. Donc, si une méthode n'utilise pas `super`, on peut toujours la considérer comme libre et la copier entre les objets. Mais avec `super`, les choses peuvent mal tourner.\n\nVoici la démo d'un mauvais résultat de `super` après la copie :\n\n```js run\nlet animal = {\n  sayHi() {\n    alert(`I'm an animal`);\n  }\n};\n\n// rabbit hérite de animal\nlet rabbit = {\n  __proto__: animal,\n  sayHi() {\n    super.sayHi();\n  }\n};\n\nlet plant = {\n  sayHi() {\n    alert(\"I'm a plant\");\n  }\n};\n\n// tree hérite de plant\nlet tree = {\n  __proto__: plant,\n*!*\n  sayHi: rabbit.sayHi // (*)\n*/!*\n};\n\n*!*\ntree.sayHi();  // I'm an animal (?!?)\n*/!*\n```\n\nUn appel à `tree.sayHi()` indique \"I'm an animal\". Certainement faux.\n\nLa raison est simple :\n\n- Dans la ligne `(*)`, la méthode `tree.sayHi` a été copiée à partir de `rabbit`. Peut-être que nous voulions simplement éviter la duplication de code ?\n- Son `[[[HomeObject]]` est `rabbit`, comme il a été créé dans `rabbit`. Il n'y a aucun moyen de changer `[[HomeObject]]`.\n- Le code de `tree.sayHi()` a `super.sayHi()` à l'intérieur. Il monte de `rabbit` et prend la méthode de `animal`.\n\nVoici le schéma de ce qui se passe :\n\n![](super-homeobject-wrong.svg)\n\n### Méthodes, pas des propriétés de fonction\n\n`[[HomeObject]]` est défini pour les méthodes à la fois dans les classes et dans les objets simples. Mais pour les objets, les méthodes doivent être spécifiées exactement comme `method()`, pas comme `\"method: function()\"`.\n\nLa différence peut être non essentielle pour nous, mais c'est important pour JavaScript.\n\nDans l'exemple ci-dessous, une syntaxe non-méthode est utilisée pour la comparaison. La propriété `[[HomeObject]]` n'est pas définie et l'héritage ne fonctionne pas :\n\n```js run\nlet animal = {\n  eat: function() { // écrire intentionnellement comme ceci au lieu de eat() {...\n    // ...\n  }\n};\n\nlet rabbit = {\n  __proto__: animal,\n  eat: function() {\n    super.eat();\n  }\n};\n\n*!*\nrabbit.eat();  // Error calling super (parce qu'il n'y a pas de [[HomeObject]])\n*/!*\n```\n\n## Résumé\n\n1. Pour étendre une classe : `class Child extends Parent` :\n    - Cela signifie que `Child.prototype.__proto__` sera `Parent.prototype`, donc les méthodes sont héritées.\n2. Lors du remplacement d'un constructeur :\n    - Nous devons appeler le constructeur parent en tant que `super()` dans le constructeur `Child` avant d'utiliser `this`.\n3. Lors du remplacement d'une autre méthode :\n    - Nous pouvons utiliser `super.method()` dans une méthode `Child` pour appeler la méthode `Parent`.\n4. Internes :\n    - Les méthodes se souviennent de leur classe/objet dans la propriété interne `[[HomeObject]]`. C'est ainsi que `super` résout les méthodes parent.\n    - Il n'est donc pas prudent de copier une méthode avec `super` d'un objet à un autre.\n\nÉgalement :\n\n- Les fonctions fléchées n'ont pas leurs propre `this` ou `super`, elles s'adaptent donc de manière transparente au contexte environnant.\n"
  },
  {
    "path": "1-js/09-classes/03-static-properties-methods/3-class-extend-object/solution.md",
    "content": "Voyons d'abord pourquoi ce dernier code ne fonctionne pas.\n\nLa raison devient évidente si nous essayons de l'exécuter. Un constructeur de classe héritant doit appeler `super()`. Sinon `\"this\"` ne sera pas \"défini\".\n\nAlors, voici la solution :\n\n```js run\nclass Rabbit extends Object {\n  constructor(name) {\n*!*\n    super(); // besoin d'appeler le constructeur parent lors de l'héritage\n*/!*\n    this.name = name;\n  }\n}\n\nlet rabbit = new Rabbit(\"Rab\");\n\nalert( rabbit.hasOwnProperty('name') ); // true\n```\n\nMais ce n'est pas tout.\n\nMême après le correctif, il existe toujours une différence importante entre `\"class Rabbit extends Object\"` et `class Rabbit`.\n\nComme on le sait, la syntaxe \"extends\" configure deux prototypes :\n\n1. Entre le `\"prototype\"` des fonctions du constructeur (pour les méthodes).\n2. Entre les fonctions constructeur elles-mêmes (pour les méthodes statiques).\n\nDans notre cas, pour `class Rabbit extends Object`, cela signifie :\n\n```js run\nclass Rabbit extends Object {}\n\nalert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true\nalert( Rabbit.__proto__ === Object ); // (2) true\n```\n\nDonc `Rabbit` donne maintenant accès aux méthodes statiques de `Object` via `Rabbit`, comme ceci :\n\n```js run\nclass Rabbit extends Object {}\n\n*!*\n// normalement nous appelons Object.getOwnPropertyNames\nalert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a,b\n*/!*\n```\n\nMais si nous n’avons pas `extends Object`, alors `Rabbit.__proto__` n'est pas défini sur `Object`.\n\nVoici la démo :\n\n```js run\nclass Rabbit {}\n\nalert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true\nalert( Rabbit.__proto__ === Object ); // (2) false (!)\nalert( Rabbit.__proto__ === Function.prototype ); // comme toute fonction par défaut\n\n*!*\n// error, no such function in Rabbit\nalert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error\n*/!*\n```\n\nDonc, `Rabbit` ne donne pas accès aux méthodes statiques de `Object` dans ce cas.\n\nEn passant, `Function.prototype` a des méthodes de fonction  \"génériques\", comme `call`, `bind`, etc. Elles sont finalement disponibles dans les deux cas, car pour le constructeur `Object` intégré, `Object.__proto__ === Function.prototype`.\n\nVoici l'image :\n\n![](rabbit-extends-object.svg)\n\nDonc, pour faire court, il y a deux différences :\n\n| class Rabbit | class Rabbit extends Object  |\n|--------------|------------------------------|\n| --             | doit appeler `super()` dans le constructeur |\n| `Rabbit.__proto__ === Function.prototype` | `Rabbit.__proto__ === Object` |\n"
  },
  {
    "path": "1-js/09-classes/03-static-properties-methods/3-class-extend-object/task.md",
    "content": "importance: 3\n\n---\n\n# Class extends Object ?\n\nComme nous le savons, tous les objets héritent normalement de `Object.prototype` et ont accès à des méthodes d'objet \"génériques\" comme `hasOwnProperty` etc.\n\nPar exemple :\n\n```js run\nclass Rabbit {\n  constructor(name) {\n    this.name = name;\n  }\n}\n\nlet rabbit = new Rabbit(\"Rab\");\n\n*!*\n// la méthode hasOwnProperty provient de Object.prototype\nalert( rabbit.hasOwnProperty('name') ); // true\n*/!*\n```\n\nMais si nous l’épelons explicitement comme suit : `\"class Rabbit extends Object\"`, le résultat serait alors différent d´un simple `\"class Rabbit\"` ?\n\nQuelle est la différence ?\n\nVoici un exemple d'un tel code (cela ne fonctionne pas - pourquoi ? Réparez le ?) :\n\n```js\nclass Rabbit extends Object {\n  constructor(name) {\n    this.name = name;\n  }\n}\n\nlet rabbit = new Rabbit(\"Rab\");\n\nalert( rabbit.hasOwnProperty('name') ); // Error\n```\n"
  },
  {
    "path": "1-js/09-classes/03-static-properties-methods/article.md",
    "content": "\n# Propriétés et méthodes statiques\n\nNous pouvons aussi assigner une méthode à la fonction de classe elle-même, pas à son `\"prototype\"`. De telles méthodes sont appelées *statique*.\n\nDans une classe, elles sont précédées du mot clé `static`, comme ceci :\n\n```js run\nclass User {\n*!*\n  static staticMethod() {\n*/!*\n    alert(this === User);\n  }\n}\n\nUser.staticMethod(); // true\n```\n\nCela revient en fait à l'affecter directement à une propriété :\n\n```js run\nclass User { }\n\nUser.staticMethod = function() {\n  alert(this === User);\n};\n\nUser.staticMethod(); // true\n```\n\nLa valeur de `this` dans l'appel `User.staticMethod()` est le constructeur de la classe `User` lui-même (la règle \"objet avant le point\").\n\nGénéralement, les méthodes statiques sont utilisées pour implémenter des fonctions appartenant à la classe, mais pas à un objet particulier de celle-ci.\n\nPar exemple, nous avons des objets `Article` et avons besoin d'une fonction pour les comparer.\n\nUne solution naturelle serait d’ajouter la méthode `Article.compare`, comme ceci :\n\n```js run\nclass Article {\n  constructor(title, date) {\n    this.title = title;\n    this.date = date;\n  }\n\n*!*\n  static compare(articleA, articleB) {\n    return articleA.date - articleB.date;\n  }\n*/!*\n}\n\n// usage\nlet articles = [\n  new Article(\"HTML\", new Date(2019, 1, 1)),\n  new Article(\"CSS\", new Date(2019, 0, 1)),\n  new Article(\"JavaScript\", new Date(2019, 11, 1))\n];\n\n*!*\narticles.sort(Article.compare);\n*/!*\n\nalert( articles[0].title ); // CSS\n```\n\nIci, `Article.compare` est \"au dessus\" des articles, comme un moyen de les comparer. Ce n'est pas une méthode d'article, mais plutôt de toute la classe.\n\nUn autre exemple serait une méthode dite \"d'usine\".\n\nDisons que nous avons besoin de plusieurs façons de créer un article :\n\n1. Créez avec des paramètres donnés (`title`, `date`, etc.).\n2. Créez un article vide avec la date du jour.\n3. ... ou d'une certaine manière.\n\nLe premier moyen peut être implémenté par le constructeur. Et pour le second, nous pouvons créer une méthode statique de la classe.\n\nComme `Article.createTodays()` ici :\n\n```js run\nclass Article {\n  constructor(title, date) {\n    this.title = title;\n    this.date = date;\n  }\n\n*!*\n  static createTodays() {\n    // rappelez vous, this = Article\n    return new this(\"Today's digest\", new Date());\n  }\n*/!*\n}\n\nlet article = Article.createTodays();\n\nalert( article.title ); // Today's digest\n```\n\nMaintenant, chaque fois que nous avons besoin de créer le résumé d'aujourd'hui, nous pouvons appeler `Article.createTodays()`. Encore une fois, ce n'est pas une méthode d'article, mais une méthode de toute la classe.\n\nLes méthodes statiques sont également utilisées dans les classes liées à la base de données pour rechercher/enregistrer/supprimer des entrées dans la base de données, comme ceci :\n\n```js\n// en supposant que Article est une classe spéciale pour la gestion d'articles\n// méthode statique pour supprimer l'article :\nArticle.remove({id: 12345});\n```\n\n````warn header=\"Les méthodes statiques ne sont pas disponibles pour les objets individuels\"\nLes méthodes statiques peuvent être appelées sur des classes, pas sur des objets individuels.\n\nPar exemple, ce genre de code ne fonctionnera pas :\n\n```js\n// ...\narticle.createTodays(); // Error: article.createTodays is not a function\n```\n````\n\n## Propriétés statiques\n\n[recent browser=Chrome]\n\nLes propriétés statiques sont également possibles, elles ressemblent aux propriétés de classe ordinaires, mais précédées de `static` :\n\n```js run\nclass Article {\n  static publisher = \"Ilya Kantor\";\n}\n\nalert( Article.publisher ); // Ilya Kantor\n```\n\nC’est la même chose qu’une assignation directe à `Article` :\n\n```js\nArticle.publisher = \"Ilya Kantor\";\n```\n\n## Héritage de méthodes et de propriétés statiques [#heritage-de-methodes-et-proprietes-statiques]\n\nLes propriétés et méthodes statiques sont héritées.\n\nPar exemple, `Animal.compare` et `Animal.planet` dans le code ci-dessous sont hérités et accessibles par `Rabbit.compare` et `Rabbit.planet` :\n\n```js run\nclass Animal {\n  static planet = \"Earth\";\n\n  constructor(name, speed) {\n    this.speed = speed;\n    this.name = name;\n  }\n\n  run(speed = 0) {\n    this.speed += speed;\n    alert(`${this.name} runs with speed ${this.speed}.`);\n  }\n\n*!*\n  static compare(animalA, animalB) {\n    return animalA.speed - animalB.speed;\n  }\n*/!*\n\n}\n\n// Hérite de Animal\nclass Rabbit extends Animal {\n  hide() {\n    alert(`${this.name} hides!`);\n  }\n}\n\nlet rabbits = [\n  new Rabbit(\"White Rabbit\", 10),\n  new Rabbit(\"Black Rabbit\", 5)\n];\n\n*!*\nrabbits.sort(Rabbit.compare);\n*/!*\n\nrabbits[0].run(); // Black Rabbit runs with speed 5.\n\nalert(Rabbit.planet); // Earth\n```\n\nMaintenant, lorsque nous appellerons `Rabbit.compare`, le `Animal.compare` hérité sera appelé.\n\nComment cela fonctionne-t-il ? Encore une fois, en utilisant des prototypes. Comme vous l'avez peut-être déjà deviné, `extends` donne à `Rabbit` la référence de `[[Prototype]]` à `Animal`.\n\n![](animal-rabbit-static.svg)\n\nAinsi, `Rabbit extends Animal` crée deux références `[[Prototype]]` :\n\n1. La fonction `Rabbit` hérite de façon prototypique de la fonction `Animal`.\n2. `Rabbit.prototype` hérite de façon prototypique de `Animal.prototype`.\n\nEn conséquence, l'héritage fonctionne à la fois pour les méthodes régulières et statiques.\n\nVérifions cela par code :\n\n```js run\nclass Animal {}\nclass Rabbit extends Animal {}\n\n// pour les méthodes statiques\nalert(Rabbit.__proto__ === Animal); // true\n\n// pour les méthodes régulières\nalert(Rabbit.prototype.__proto__ === Animal.prototype); // true\n```\n\n## Résumé\n\nLes méthodes statiques sont utilisées pour les fonctionnalités appartenant à la classe \"dans son ensemble\". Cela ne concerne pas une instance de classe concrète.\n\nPar exemple, une méthode de comparaison `Article.compare(article1, article2)` ou une méthode d'usine `Article.createTodays()`.\n\nIls sont étiquetés par le mot `static` dans la déclaration de classe.\n\nLes propriétés statiques sont utilisées lorsque nous souhaitons stocker des données au niveau de la classe, également non liées à une instance.\n\nLa syntaxe est la suivante :\n\n```js\nclass MyClass {\n  static property = ...;\n\n  static method() {\n    ...\n  }\n}\n```\n\nTechniquement, la déclaration statique revient à assigner à la classe elle-même :\n\n```js\nMyClass.property = ...\nMyClass.method = ...\n```\n\nLes propriétés et méthodes statiques sont héritées.\n\nPour `class B extends A`, le prototype de la classe `B` pointe lui-même à `A` : `B.[[Prototype]] = A`. Donc, si un champ n'est pas trouvé dans `B`, la recherche continue dans `A`.\n"
  },
  {
    "path": "1-js/09-classes/04-private-protected-properties-methods/article.md",
    "content": "\n# Propriétés et méthodes privées et protégées\n\nL'un des principes les plus importants de la programmation orientée objet - délimiter l'interface interne de l'interface externe.\n\nC’est une pratique «incontournable» dans le développement de quelque chose de plus complexe que l’application «hello world».\n\nPour comprendre cela, écartons nous du développement et tournons nos yeux dans le monde réel.\n\nHabituellement, les appareils que nous utilisons sont assez complexes. Mais délimiter l'interface interne de l'interface externe permet de les utiliser sans problèmes.\n\n## Un exemple concret\n\nPar exemple, une machine à café. Simple de l'extérieur : un bouton, un écran, quelques trous... Et le résultat : du bon café ! :)\n\n![](coffee.jpg)\n\nMais à l'intérieur... (une image du manuel de réparation)\n\n![](coffee-inside.jpg)\n\nBeaucoup de détails. Mais on peut l'utiliser sans rien savoir.\n\nLes machines à café sont assez fiables, n'est-ce pas ? Nous pouvons en utiliser une pendant des années, et seulement en cas de problème, la faire réparer.\n\nLe secret de la fiabilité et de la simplicité d'une machine à café - tous les détails sont bien réglés et cachés à l'intérieur.\n\nSi nous retirons le capot de protection de la machine à café, son utilisation sera beaucoup plus complexe (où appuyer ?) et dangereuse (elle peut électrocuter).\n\nComme nous le verrons, les objets de programmation ressemblent à des machines à café.\n\nMais pour masquer les détails intérieurs, nous n'utiliserons pas une couverture de protection, mais une syntaxe spéciale du langage et des conventions.\n\n## Interface interne et externe\n\nEn programmation orientée objet, les propriétés et les méthodes sont divisées en deux groupes :\n\n- *Interface interne* - méthodes et propriétés, accessibles à partir d'autres méthodes de la classe, mais pas de l'extérieur.\n- *Interface externe* - méthodes et propriétés, accessibles aussi de l'extérieur de la classe.\n\nSi nous continuons l'analogie avec la machine à café - ce qui est caché à l'intérieur : un tube de chaudière, un élément chauffant, etc. -, c'est son interface interne.\n\nUne interface interne est utilisée pour que l’objet fonctionne, ses détails s’utilisent les uns les autres. Par exemple, un tube de chaudière est attaché à l'élément chauffant.\n\nMais de l'extérieur, une machine à café est fermée par le capot de protection, de sorte que personne ne puisse y accéder. Les détails sont cachés et inaccessibles. Nous pouvons utiliser ses fonctionnalités via l'interface externe.\n\nIl suffit donc de connaître son interface externe pour utiliser un objet. Nous ne savons peut-être pas comment cela fonctionne à l'intérieur, et c'est très bien.\n\nC'était une introduction générale.\n\nEn JavaScript, il existe deux types de champs d’objet (propriétés et méthodes) :\n\n- Publique : accessible de n'importe où. Ils comprennent l'interface externe. Jusqu'à présent, nous utilisions uniquement des propriétés et méthodes publiques.\n- Privée : accessible uniquement de l'intérieur de la classe. Ce sont pour l'interface interne.\n\nDans de nombreux autres langages, il existe également des champs \"protégés\" : accessibles uniquement de l'intérieur de la classe et de ceux qui en héritent (comme privé, mais avec accès des classes héritées). Ils sont également utiles pour l'interface interne. En un sens, elles sont plus répandues que les méthodes privées, car nous souhaitons généralement que les classes héritées puissent y accéder.\n\nLes champs protégés ne sont pas implémentés en JavaScript au niveau du langage, mais dans la pratique, ils sont très pratiques, ils sont donc imités.\n\nNous allons maintenant créer une machine à café en JavaScript avec tous ces types de propriétés. Une machine à café a beaucoup de détails, nous ne les modéliserons pas pour rester simples (bien que nous puissions).\n\n## Protection de \"waterAmount\"\n\nFaisons d'abord une classe de machine à café simple :\n\n```js run\nclass CoffeeMachine {\n  waterAmount = 0; // la quantité d'eau à l'intérieur\n\n  constructor(power) {\n    this.power = power;\n    alert( `Created a coffee-machine, power: ${power}` );\n  }\n\n}\n\n// créer la machine à café\nlet coffeeMachine = new CoffeeMachine(100);\n\n// ajoutez de l'eau\ncoffeeMachine.waterAmount = 200;\n```\n\nÀ l'heure actuelle, les propriétés `waterAmount` et `power` sont publiques. Nous pouvons facilement les accéder/muter de l’extérieur à n’importe quelle valeur.\n\nChangeons la propriété `waterAmount` en protégée pour avoir plus de contrôle sur celle-ci. Par exemple, nous ne voulons pas que quiconque la règle en dessous de zéro.\n\n**Les propriétés protégées sont généralement précédées d'un trait de soulignement `_`.**\n\nCela n'est pas appliqué au niveau du langage, mais il existe une convention bien connue entre les programmeurs selon laquelle ces propriétés et méthodes ne doivent pas être accessibles de l'extérieur.\n\nDonc notre propriété s'appellera `_waterAmount` :\n\n```js run\nclass CoffeeMachine {\n  _waterAmount = 0;\n\n  set waterAmount(value) {\n    if (value < 0) {\n      value = 0;\n    }\n    this._waterAmount = value;\n  }\n\n  get waterAmount() {\n    return this._waterAmount;\n  }\n\n  constructor(power) {\n    this._power = power;\n  }\n\n}\n\n// créer la machine à café\nlet coffeeMachine = new CoffeeMachine(100);\n\n// ajoutez de l'eau\ncoffeeMachine.waterAmount = -10; // _waterAmount va devenir 0, pas -10\n```\n\nMaintenant, l'accès est sous contrôle, donc le réglage de l'eau en dessous de zéro échoue.\n\n## \"power\" en lecture seule\n\nPour la propriété `power`, rendons-la en lecture seule. Il arrive parfois qu'une propriété doive être définie au moment de la création, puis ne jamais être modifiée.\n\nC'est exactement le cas pour une machine à café : la puissance ne change jamais.\n\nPour ce faire, il suffit de définir l'accesseur, mais pas le mutateur :\n\n```js run\nclass CoffeeMachine {\n  // ...\n\n  constructor(power) {\n    this._power = power;\n  }\n\n  get power() {\n    return this._power;\n  }\n\n}\n\n// créer la machine à café\nlet coffeeMachine = new CoffeeMachine(100);\n\nalert(`Power is: ${coffeeMachine.power}W`); // Power is: 100W\n\ncoffeeMachine.power = 25; // Error (no setter)\n```\n\n````smart header=\"Fonctions Accesseur/Mutateur\"\nIci, nous avons utilisé la syntaxe accesseur/mutateur.\n\nMais la plupart du temps, les fonctions `get...` / `set...` sont préférées, comme ceci :\n\n```js\nclass CoffeeMachine {\n  _waterAmount = 0;\n\n  *!*setWaterAmount(value)*/!* {\n    if (value < 0) value = 0;\n    this._waterAmount = value;\n  }\n\n  *!*getWaterAmount()*/!* {\n    return this._waterAmount;\n  }\n}\n\nnew CoffeeMachine().setWaterAmount(100);\n```\n\nCela semble un peu plus long, mais les fonctions sont plus flexibles. Elles peuvent accepter plusieurs arguments (même si nous n'en avons pas besoin maintenant).\n\nD'un autre côté, la syntaxe accesseur/mutateur est plus courte, donc il n'y a pas de règle stricte, c'est à vous de décider.\n````\n\n```smart header=\"Les champs protégés sont hérités\"\nSi nous héritons de `classe MegaMachine extends CoffeeMachine`, rien ne nous empêche d'accéder à `this._waterAmount` ou `this._power` à partir des méthodes de la nouvelle classe.\n\nLes champs protégés sont donc naturellement héritables. Contrairement aux champs privés que nous verrons ci-dessous.\n```\n\n## \"#waterLimit\" privée\n\n[recent browser=none]\n\nIl existe une proposition JavaScript finie, presque dans la norme, qui fournit une prise en charge au niveau du langage pour les propriétés et méthodes privées.\n\nLes propriétés privées devraient commencer par `#`. Elles ne sont accessibles que de l'intérieur de la classe.\n\nPar exemple, voici une propriété privée `#waterLimit` et la méthode privée de vérification du niveau de l'eau `#fixWaterAmount` :\n\n```js run\nclass CoffeeMachine {\n*!*\n  #waterLimit = 200;\n*/!*\n\n*!*\n  #fixWaterAmount(value) {\n    if (value < 0) return 0;\n    if (value > this.#waterLimit) return this.#waterLimit;\n  }\n*/!*\n\n  setWaterAmount(value) {\n    this.#waterLimit = this.#fixWaterAmount(value);\n  }\n\n}\n\nlet coffeeMachine = new CoffeeMachine();\n\n*!*\n// ne peut pas accéder aux propriétés privées de l'extérieur de la classe\ncoffeeMachine.#fixWaterAmount(123); // Error\ncoffeeMachine.#waterLimit = 1000; // Error\n*/!*\n```\n\nAu niveau du langage, `#` est un signe spécial spécifiant que le champ est privé. Nous ne pouvons pas y accéder de l'extérieur ou des classes héritées.\n\nLes champs privés n'entrent pas en conflit avec les champs publics. Nous pouvons avoir les champs privés `#waterAmount` et publics `waterAmount` en même temps.\nPour l'exemple, faisons de `waterAmount` un accesseur pour `#waterAmount` :\n\n```js run\nclass CoffeeMachine {\n\n  #waterAmount = 0;\n\n  get waterAmount() {\n    return this.#waterAmount;\n  }\n\n  set waterAmount(value) {\n    if (value < 0) value = 0;\n    this.#waterAmount = value;\n  }\n}\n\nlet machine = new CoffeeMachine();\n\nmachine.waterAmount = 100;\nalert(machine.#waterAmount); // Error\n```\n\nContrairement aux champs protégés, les champs privés sont imposés par le langage lui-même. C'est une bonne chose.\n\nMais si nous héritons de `CoffeeMachine`, nous n’aurons aucun accès direct à `#waterAmount`. Nous aurons besoin de compter sur l'accesseur/mutateur `waterAmount` :\n\n```js\nclass MegaCoffeeMachine extends CoffeeMachine {\n  method() {\n*!*\n    alert( this.#waterAmount ); // Error: can only access from CoffeeMachine\n*/!*\n  }\n}\n```\n\nDans de nombreux scénarios, une telle limitation est trop sévère. Si nous étendons une `CoffeeMachine`, nous pouvons avoir une raison légitime d’accéder à ses composants internes. C'est pourquoi les champs protégés sont utilisés plus souvent, même s'ils ne sont pas pris en charge par la syntaxe du langage.\n\n````warn header=\"Les champs privés ne sont pas disponibles par this[nom]\"\nLes champs privés sont spéciaux.\n\nComme nous le savons, nous pouvons généralement accéder aux champs en utilisant `this[name]` :\n\n```js\nclass User {\n  ...\n  sayHi() {\n    let fieldName = \"name\";\n    alert(`Hello, ${*!*this[fieldName]*/!*}`);\n  }\n}\n```\n\nC'est impossible avec les champs privés : `this['#name']` ne fonctionne pas. C'est une limitation de syntaxe pour assurer la confidentialité.\n````\n\n## Résumé\n\nEn termes de POO, la délimitation de l'interface interne de l'interface externe est appelée [encapsulation](\"https://fr.wikipedia.org/wiki/Encapsulation_(programmation)\").\n\nCela offre les avantages suivants :\n\nProtection des utilisateurs pour qu'ils ne se tirent pas une balle dans le pied\n: Imaginez, il y a une équipe de développeurs utilisant une machine à café. Elle a été fabriquée par la société \"Best CoffeeMachine\" et fonctionne parfaitement, mais une coque de protection a été retirée. Donc, l'interface interne est exposée.\n\n    Tous les développeurs sont civilisés - ils utilisent la machine à café comme prévu. Mais l'un d'entre eux, John, a décidé qu'il était le plus intelligent et a apporté quelques modifications aux éléments internes de la machine à café. La machine à café a donc échoué deux jours plus tard.\n\n    Ce n’est sûrement pas la faute de John, mais bien de la personne qui a enlevé le capot de protection et laissé John manipuler.\n\n    La même chose en programmation. Si un utilisateur d'une classe va changer des choses qui ne sont pas destinées à être modifiées de l'extérieur, les conséquences sont imprévisibles.\n\nMaintenable\n: La programmation est plus complexe qu’une machine à café réelle, car nous ne l’achetons pas une seule fois. Le code est en constante évolution et amélioration.\n\n    **Si nous délimitons strictement l'interface interne, le développeur de la classe peut modifier librement ses propriétés et méthodes internes, même sans en informer les utilisateurs.**\n\n    Si vous êtes développeur d'une telle classe, il est bon de savoir que les méthodes privées peuvent être renommées en toute sécurité, que leurs paramètres peuvent être modifiés, voire supprimés, car aucun code externe ne dépend d'eux.\n\n    Pour les utilisateurs, lorsqu'une nouvelle version est disponible, il peut s'agir d'une refonte totale en interne, mais reste simple à mettre à niveau si l'interface externe est la même.\n\nCacher la complexité\n: Les gens adorent utiliser des choses simples. Au moins de l'extérieur. Ce qui est à l'intérieur est une chose différente.\n\n    Les programmeurs ne sont pas une exception.\n\n    **C'est toujours pratique lorsque les détails de l'implémentation sont cachés et qu'une interface externe simple et bien documentée est disponible.**\n\nPour masquer l'interface interne, nous utilisons des propriétés protégées ou privées :\n\n- Les champs protégés commencent par `_`. C'est une convention bien connue, non appliquée au niveau du langage. Les programmeurs doivent uniquement accéder à un champ commençant par `_` depuis sa classe et les classes qui en héritent.\n- Les champs privés commencent par `#`. JavaScript garantit que nous ne pouvons accéder à ceux la que de l'intérieur de la classe.\n\nPour le moment, les champs privés ne sont pas bien supportés par les navigateurs, mais peuvent être polyfilled.\n"
  },
  {
    "path": "1-js/09-classes/05-extend-natives/article.md",
    "content": "\n# Extension des classes intégrées\n\nLes classes intégrées telles que Array, Map et autres sont également extensibles.\n\nPar exemple, ici, `PowerArray` hérite du `Array` natif :\n\n```js run\n// ajoutez-y une méthode supplémentaire\nclass PowerArray extends Array {\n  isEmpty() {\n    return this.length === 0;\n  }\n}\n\nlet arr = new PowerArray(1, 2, 5, 10, 50);\nalert(arr.isEmpty()); // false\n\nlet filteredArr = arr.filter(item => item >= 10);\nalert(filteredArr); // 10, 50\nalert(filteredArr.isEmpty()); // false\n```\n\nNotez une chose très intéressante. Les méthodes intégrées telles que `filter`, `map` et autres renvoient des nouveaux objets exactement du type hérité `PowerArray`. Leur implémentation interne utilise la propriété d'objet `constructor` pour cela.\n\nDans l'exemple ci-dessus,\n\n```js\narr.constructor === PowerArray\n```\n\nLorsque `arr.filter()` est appelé, elle crée en interne le nouveau tableau de résultats en utilisant exactement `arr.constructor`, et non pas `Array`. C'est en fait très intéressant, car nous pouvons continuer à utiliser les méthodes `PowerArray` sur le résultat.\n\nEncore plus, nous pouvons personnaliser ce comportement.\n\nNous pouvons ajouter un accésseur statique spécial `Symbol.species` à la classe. S'il existe, il devrait renvoyer le constructeur que JavaScript utilisera en interne pour créer de nouvelles entités dans `map`, `filter`, etc.\n\nSi nous souhaitons que des méthodes intégrées comme `map` ou `filter` renvoient des tableaux classiques, nous pouvons retourner `Array` dans `Symbol.species`, comme ici :\n\n```js run\nclass PowerArray extends Array {\n  isEmpty() {\n    return this.length === 0;\n  }\n\n*!*\n  // les méthodes intégrées l'utiliseront comme constructeur\n  static get [Symbol.species]() {\n    return Array;\n  }\n*/!*\n}\n\nlet arr = new PowerArray(1, 2, 5, 10, 50);\nalert(arr.isEmpty()); // false\n\n// filter crée un nouveau tableau en utilisant arr.constructor [Symbol.species] comme constructeur\nlet filteredArr = arr.filter(item => item >= 10);\n\n*!*\n// filteredArr n'est pas un PowerArray, mais un Array\n*/!*\nalert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function\n```\n\nComme vous pouvez le constater, maintenant, `.filter` renvoie `Array`. La fonctionnalité étendue n'est donc plus transmise.\n\n```smart header=\"D'autres collections fonctionnent de la même manière\"\nD'autres collections, telles que `Map` et `Set`, fonctionnent de la même manière. Ils utilisent également `Symbol.species`.\n```\n\n## Pas d'héritage statique dans les éléments intégrés\n\nLes objets intégrés ont leurs propres méthodes statiques, par exemple `Object.keys`, `Array.isArray`, etc.\n\nComme nous le savons déjà, les classes natives s'étendent les uns des autres. Par exemple, `Array` étend (hérite de) `Object`.\n\nNormalement, lorsqu'une classe en étend une autre, les méthodes statiques et non statiques sont héritées. Cela a été expliqué en détail dans le chapitre [](info:static-properties-methods#statics-and-inheritance).\n\nMais les classes intégrées sont une exception. Elles n'héritent pas des méthodes statiques les unes des autres.\n\nPar exemple, `Array` et `Date` héritent de `Object`, de sorte que leurs instances ont des méthodes issues de `Object.prototype`. Mais `Array.[[Prototype]]` ne fait pas référence à `Object`, il n'y a donc pas, par exemple, de méthode statique `Array.keys()` (ou `Date.keys()`).\n\nVoici le schéma la structure pour `Date` et `Object` :\n\n![](object-date-inheritance.svg)\n\nComme vous pouvez le constater, il n'y a pas de lien entre `Date` et `Object`. Ils sont indépendants, seul `Date.prototype` hérite de `Object.prototype`.\n\nC'est une différence d'héritage importante entre les objets intégrés par rapport à ce que nous obtenons avec `extends`.\n"
  },
  {
    "path": "1-js/09-classes/06-instanceof/1-strange-instanceof/solution.md",
    "content": "Ouais, ça a l'air étrange.\n\nMais `instanceof` ne se soucie pas de la fonction, mais plutôt de son `prototype`, qui correspond à la chaîne de prototypes.\n\nEt ici `a.__ proto__ == B.prototype`, ainsi `instanceof` renvoie `true`.\n\nAinsi, par la logique de `instanceof`, le `prototype` définit en fait le type, pas la fonction constructeur.\n"
  },
  {
    "path": "1-js/09-classes/06-instanceof/1-strange-instanceof/task.md",
    "content": "importance: 5\n\n---\n\n# instanceof étrange\n\nDans le code ci-dessous, pourquoi `instanceof` renvoie `true` ? Nous pouvons facilement voir que `a` n'est pas créé par `B()`.\n\n```js run\nfunction A() {}\nfunction B() {}\n\nA.prototype = B.prototype = {};\n\nlet a = new A();\n\n*!*\nalert( a instanceof B ); // true\n*/!*\n```\n"
  },
  {
    "path": "1-js/09-classes/06-instanceof/article.md",
    "content": "# Vérification de classe : \"instanceof\"\n\nL'opérateur `instanceof` permet de vérifier si un objet appartient à une certaine classe. Il prend également en compte l'héritage.\n\nUne telle vérification peut être nécessaire dans de nombreux cas. Nous l'utilisons ici pour construire une fonction *polymorphique*, celle qui traite les arguments différemment en fonction de leur type.\n\n## L'opérateur instanceof [#ref-instanceof]\n\nLa syntaxe est la suivante :\n\n```js\nobj instanceof Class\n```\n\nCela renvoie `true` si `obj` appartient à la `Class` ou à une classe qui en hérite.\n\nPar exemple :\n\n```js run\nclass Rabbit {}\nlet rabbit = new Rabbit();\n\n// est-ce un objet de la classe Rabbit ?\n*!*\nalert( rabbit instanceof Rabbit ); // true\n*/!*\n```\n\nCela fonctionne aussi avec les fonctions constructeur :\n\n```js run\n*!*\n// au lieu de classe\nfunction Rabbit() {}\n*/!*\n\nalert( new Rabbit() instanceof Rabbit ); // true\n```\n\n...Et avec des classes intégrées comme `Array` :\n\n```js run\nlet arr = [1, 2, 3];\nalert( arr instanceof Array ); // true\nalert( arr instanceof Object ); // true\n```\n\nVeuillez noter que `arr` appartient également à la classe `Object`. C'est parce que `Array` hérite de manière prototypale de `Object`.\n\nNormalement, l’opérateur `instanceof` examine la chaîne prototypale pour la vérification. Nous pouvons également définir une logique personnalisée dans la méthode statique `Symbol.hasInstance`.\n\nL'algorithme de `obj instanceof Class` fonctionne à peu près comme suit :\n\n1. S'il existe une méthode statique `Symbol.hasInstance`, appelez-la simplement : `Class[Symbol.hasInstance](obj)`. Cela devrait renvoyer `true` ou `false`, et nous avons terminé. C'est ainsi que nous pouvons personnaliser le comportement de `instanceof`.\n\n     Par exemple :\n\n```js run\n// configuration du contrôle de instanceof qui suppose que\n// tout ce qui a la propriété canEat est un animal\nclass Animal {\n  static [Symbol.hasInstance](obj) {\n    if (obj.canEat) return true;\n  }\n}\n\nlet obj = { canEat: true };\n\nalert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) est appelée\n```\n\n2. La plupart des classes n'ont pas `Symbol.hasInstance`. Dans ce cas, la logique standard est utilisée : `obj instanceof Class` vérifie si `Class.prototype` est égale à l'un des prototypes de la chaîne prototypale `obj`.\n\n    En d'autres termes, on compare l'un après l'autre :\n\n```js\nobj.__proto__ === Class.prototype?\nobj.__proto__.__proto__ === Class.prototype?\nobj.__proto__.__proto__.__proto__ === Class.prototype?\n...\n// si une réponse est vraie, renvoie true\n// sinon, si nous arrivons au bout de la chaîne, renvoie false\n```\n\n    Dans l'exemple ci-dessus, `rabbit.__ proto__ === Rabbit.prototype`, donne donc la réponse immédiatement.\n\n    Dans le cas d'un héritage, la correspondance se fera à la deuxième étape :\n\n```js run\nclass Animal {}\nclass Rabbit extends Animal {}\n\nlet rabbit = new Rabbit();\n*!*\nalert(rabbit instanceof Animal); // true\n*/!*\n\n// rabbit.__proto__ === Animal.prototype (pas de correspondance)\n*!*\n// rabbit.__proto__.__proto__ === Animal.prototype (ça correspond!)\n*/!*\n```\n\nVoici l'illustration de ce que `rabbit instanceof Animal` compare avec `Animal.prototype` :\n\n![](instanceof.svg)\n\nÀ propos, il y a aussi une méthode [objA.isPrototypeOf(objB)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Object/isPrototypeOf), qui renvoie `true` si `objA` se trouve quelque part dans la chaîne de prototypes pour `objB`. Ainsi, le test de `obj instanceof Class` peut être reformulé comme suit : `Class.prototype.isPrototypeOf(obj)`.\n\nC'est drôle, mais le constructeur `Class` lui-même ne participe pas au contrôle ! Seule la chaîne de prototypes et `Class.prototype` compte.\n\nCela peut avoir des conséquences intéressantes lorsqu'une propriété `prototype` est modifiée après la création de l'objet.\n\nComme ici :\n\n```js run\nfunction Rabbit() {}\nlet rabbit = new Rabbit();\n\n// le prototype est changé\nRabbit.prototype = {};\n\n// ...plus un rabbit !\n*!*\nalert( rabbit instanceof Rabbit ); // false\n*/!*\n```\n\n## Bonus : Object.prototype.toString pour le type\n\nNous savons déjà que les objets simples sont convertis en chaîne sous la forme `[objet Objet]` :\n\n```js run\nlet obj = {};\n\nalert(obj); // [object Object]\nalert(obj.toString()); // la même chose\n```\n\nC'est leur implémentation de `toString`. Mais il existe une fonctionnalité cachée qui rend `toString` beaucoup plus puissant que cela. Nous pouvons l'utiliser comme un `typeof` étendu et une alternative pour `instanceof`.\n\nCela semble étrange ? Effectivement. Démystifions.\n\nPar [spécification](https://tc39.github.io/ecma262/#sec-object.prototype.tostring), le `toString` intégré peut être extrait de l'objet et exécuté dans le contexte de toute autre valeur. Et son résultat dépend de cette valeur.\n\n- Pour un nombre, ce sera `[object Number]`\n- Pour un booléen, ce sera `[object Boolean]`\n- Pour `null` : `[objet Null]`\n- Pour `undefined` : `[objet Undefined]`\n- Pour les tableaux : `[objet Array]`\n- ... etc. (personnalisable).\n\nMontrons cela :\n\n```js run\n// copier la méthode toString dans une variable pour plus d'utilité\nlet objectToString = Object.prototype.toString;\n\n// quel type est-ce ?\nlet arr = [];\n\nalert( objectToString.call(arr) ); // [object *!*Array*/!*]\n```\n\nIci nous avons utilisé [call](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Function/call) comme décrit dans le chapitre [](info:call-apply-decorators) pour exécuter la fonction `objectToString` dans le contexte `this=arr`.\n\nEn interne, l'algorithme `toString` examine `this` et renvoie le résultat correspondant. Plus d'exemples :\n\n```js run\nlet s = Object.prototype.toString;\n\nalert( s.call(123) ); // [object Number]\nalert( s.call(null) ); // [object Null]\nalert( s.call(alert) ); // [object Function]\n```\n\n### Symbol.toStringTag\n\nLe comportement de Object `toString` peut être personnalisé à l'aide d'une propriété d'objet spéciale `Symbol.toStringTag`.\n\nPar exemple :\n\n```js run\nlet user = {\n  [Symbol.toStringTag]: \"User\"\n};\n\nalert( {}.toString.call(user) ); // [object User]\n```\n\nPour la plupart des objets spécifiques à l'environnement, il existe une telle propriété. Voici quelques exemples spécifiques à votre navigateur :\n\n```js run\n// toStringTag pour l'objet et la classe spécifiques à l'environnement :\nalert( window[Symbol.toStringTag]); // Window\nalert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest\n\nalert( {}.toString.call(window) ); // [object Window]\nalert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest]\n```\n\nComme vous pouvez le constater, le résultat est exactement `Symbol.toStringTag` (s'il existe), encapsulé dans `[objet ...]`.\n\nAu final, nous avons un \"typeof sous stéroïdes\" qui fonctionne non seulement pour les types de données primitifs, mais aussi pour les objets intégrés et qui peut même être personnalisé.\n\nNous pouvons utiliser `{}.toString.call` au lieu de `instanceof` pour les objets intégrés lorsque nous voulons obtenir le type sous forme de chaîne de caractères plutôt que pour simplement vérifier.\n\n## Résumé\n\nRésumons les méthodes de vérification de type que nous connaissons :\n\n|               | fonctionne pour                                               | renvoie    |\n|---------------|---------------------------------------------------------------|------------|\n| `typeof`      | primitives                                                    | string     |\n| `{}.toString` | primitives, objets intégrés, objets avec `Symbol.toStringTag` | string     |\n| `instanceof`  | objects                                                       | true/false |\n\nComme on peut le constater, `{}.toString` est techniquement un `typeof` \"plus avancé\".\n\nEt l'opérateur `instanceof` excelle lorsque nous travaillons avec une hiérarchie de classes et voulons vérifier si la classe prend en compte l'héritage.\n"
  },
  {
    "path": "1-js/09-classes/07-mixins/article.md",
    "content": "# Les mixins\n\nEn JavaScript, nous ne pouvons hériter que d'un seul objet. Il ne peut y avoir qu'un `[[Prototype]]` pour un objet. Et une classe peut étendre qu'une seule autre classe.\n\nMais parfois, cela semble limitant. Par exemple, nous avons une classe `StreetSweeper` et une classe `Bicycle`, et nous voulons faire leur mélange : un `StreetSweepingBicycle`.\n\nOu nous avons une classe `User` et une classe `EventEmitter` qui implémente la génération d'événements, et nous aimerions ajouter la fonctionnalité de `EventEmitter` à `User` afin que nos utilisateurs puissent émettre des événements.\n\nIl existe un concept qui peut aider ici, appelé \"mixins\".\n\nComme défini dans Wikipedia, un [mixin](https://fr.wikipedia.org/wiki/Mixin) est une classe contenant des méthodes qui peuvent être utilisées par d'autres classes sans avoir à en hériter.\n\nEn d'autres termes, un *mixin* fournit des méthodes qui implémentent un certain comportement, mais nous ne l'utilisons pas seul, nous l'utilisons pour ajouter le comportement à d'autres classes.\n\n## Un exemple de mixin\n\nLe moyen le plus simple d'implémenter un mixin en JavaScript est de créer un objet avec des méthodes utiles, de sorte que nous puissions facilement les fusionner dans un prototype de n'importe quelle classe.\n\nPar exemple ici, le mixin `sayHiMixin` est utilisé pour ajouter un peu de \"discours\" à `User` :\n\n```js run\n*!*\n// mixin\n*/!*\nlet sayHiMixin = {\n  sayHi() {\n    alert(`Hello ${this.name}`);\n  },\n  sayBye() {\n    alert(`Bye ${this.name}`);\n  }\n};\n\n*!*\n// usage:\n*/!*\nclass User {\n  constructor(name) {\n    this.name = name;\n  }\n}\n\n// copier les méthodes\nObject.assign(User.prototype, sayHiMixin);\n\n// maintenant User peut dire bonjour\nnew User(\"Dude\").sayHi(); // Hello Dude!\n```\n\nIl n'y a pas d'héritage, mais une simple copie de méthode. Ainsi, `User` peut hériter d'une autre classe et inclure le mixin pour ajouter les méthodes supplémentaires, comme ceci :\n\n```js\nclass User extends Person {\n  // ...\n}\n\nObject.assign(User.prototype, sayHiMixin);\n```\n\nLes mixins peuvent utiliser l'héritage à l'intérieur d'eux-mêmes.\n\nPar exemple, ici `sayHiMixin` hérite de `sayMixin` :\n\n```js run\nlet sayMixin = {\n  say(phrase) {\n    alert(phrase);\n  }\n};\n\nlet sayHiMixin = {\n  __proto__: sayMixin, // (ou nous pourrions utiliser Object.setPrototypeOf pour définir le prototype ici)\n\n  sayHi() {\n    *!*\n    // appeler la méthode du parent\n    */!*\n    super.say(`Hello ${this.name}`); // (*)\n  },\n  sayBye() {\n    super.say(`Bye ${this.name}`); // (*)\n  }\n};\n\nclass User {\n  constructor(name) {\n    this.name = name;\n  }\n}\n\n// copier les méthodes\nObject.assign(User.prototype, sayHiMixin);\n\n// maintenant User peut dire bonjour\nnew User(\"Dude\").sayHi(); // Hello Dude!\n```\n\nVeuillez noter que l’appel à la méthode du parent `super.say()` à partir de `sayHiMixin` (aux lignes étiquetées avec `(*)`) recherche la méthode dans le prototype de ce mixin, pas la classe.\n\nVoici le schéma (voir la partie droite) :\n\n![](mixin-inheritance.svg)\n\nC'est parce que les méthodes `sayHi` et `sayBye` ont été initialement créées dans `sayHiMixin`. Ainsi, même si elles ont été copiées, leur propriété interne `[[HomeObject]]` fait référence à `sayHiMixin`, comme indiqué sur l’image ci-dessus.\n\nComme `super` cherche des méthodes du parent dans `[[HomeObject]].[[Prototype]]`, cela signifie qu'il cherche `sayHiMixin.[[Prototype]]`.\n\n## EventMixin\n\nFaisons maintenant un mixin concret.\n\nUne caractéristique importante de nombreux objets de navigateur (par exemple) est qu'ils peuvent générer des événements. Les événements sont un excellent moyen de \"diffuser des informations\" à tous ceux qui le souhaitent. Faisons donc un mixin qui permet d’ajouter facilement des fonctions relatives aux événements à n’importe quelle classe/objet.\n\n- Le mixin fournira une méthode `.trigger(name, [... data])` pour \"générer un événement\" quand quelque chose d'important lui arrive. L'argument `name` est un nom de l'événement, éventuellement suivi d'arguments supplémentaires avec les données d'événement.\n- Également la méthode `.on(name, handler)` qui ajoute la fonction `handler` en tant qu'écouteur aux événements portant le nom donné. Il sera appelé lorsqu’un événement avec le `name` donné se déclenche, et récupérera les arguments de l’appel `.trigger`.\n- ... Et la méthode `.off(name, handler)` qui supprime le programme d'écoute `handler`.\n\nAprès avoir ajouté le mixin, un objet `user` sera capable de générer un événement `\"login\"` lorsque le visiteur se connectera. Un autre objet, par exemple, `calendar` peut vouloir écouter de tels événements pour charger le calendrier de la personne connectée.\n\nOu bien, un `menu` peut générer l'événement `\"select\"` lorsqu'un élément de menu est sélectionné, et d'autres objets peuvent affecter des gestionnaires pour réagir à cet événement. Etc.\n\nVoici le code :\n\n```js run\nlet eventMixin = {\n  /**\n   * Souscrire à l'événement, usage :\n   *  menu.on('select', function(item) { ... }\n  */\n  on(eventName, handler) {\n    if (!this._eventHandlers) this._eventHandlers = {};\n    if (!this._eventHandlers[eventName]) {\n      this._eventHandlers[eventName] = [];\n    }\n    this._eventHandlers[eventName].push(handler);\n  },\n\n  /**\n   * Annuler la souscription, usage :\n   *  menu.off('select', handler)\n   */\n  off(eventName, handler) {\n    let handlers = this._eventHandlers?.[eventName];\n    if (!handlers) return;\n    for (let i = 0; i < handlers.length; i++) {\n      if (handlers[i] === handler) {\n        handlers.splice(i--, 1);\n      }\n    }\n  },\n\n  /**\n   * Générer un événement avec le nom et les données donnés\n   *  this.trigger('select', data1, data2);\n   */\n  trigger(eventName, ...args) {\n    if (!this._eventHandlers?.[eventName]) {\n      return; // aucun gestionnaire pour ce nom d'événement\n    }\n\n    // appeler les gestionnaires\n    this._eventHandlers[eventName].forEach(handler => handler.apply(this, args));\n  }\n};\n```\n\n- `.on(eventName, handler)` - assigne la fonction `handler` à exécuter lorsque l'événement portant ce nom se produit. Techniquement, il existe une propriété `_eventHandlers`, qui stocke un tableau de gestionnaires pour chaque nom d'événement, et il l'ajoute simplement à la liste.\n- `.off(eventName, handler)` - supprime la fonction de la liste des gestionnaires.\n- `.trigger(eventName, ... args)` - génère l'événement : tous les gestionnaires de `_eventHandlers[eventName]` sont appelés, avec une liste d'arguments `...args`.\n\nUsage :\n\n```js run\n// Créez une classe\nclass Menu {\n  choose(value) {\n    this.trigger(\"select\", value);\n  }\n}\n// Ajouter le mixin avec les méthodes liées aux événements\nObject.assign(Menu.prototype, eventMixin);\n\nlet menu = new Menu();\n\n// ajouter un gestionnaire, à appeler lors de la sélection :\n*!*\nmenu.on(\"select\", value => alert(`Value selected: ${value}`));\n*/!*\n\n// déclenche l'événement => le gestionnaire ci-dessus s'exécute et affiche :\n// Value selected: 123\nmenu.choose(\"123\");\n```\n\nMaintenant, si nous souhaitons que le code réagisse lors de la sélection du menu, nous pouvons l'écouter avec `menu.on(...)`.\n\nEt le mixin `eventMixin` facilite l'ajout d'un tel comportement à autant de classes que nous le voudrions, sans interférer avec la chaîne d'héritage.\n\n## Résumé\n\n*Mixin* est un terme générique de programmation orienté objet : une classe contenant des méthodes pour d’autres classes.\n\nD'autres langages autorisent l'héritage multiple. JavaScript ne prend pas en charge l'héritage multiple, mais les mixins peuvent être implémentés en copiant les méthodes dans le prototype.\n\nNous pouvons utiliser les mixins comme moyen d'ajouter à une classe plusieurs comportements, comme la gestion d'événements, comme nous l'avons vu ci-dessus.\n\nLes mixins peuvent devenir un point de conflit s'ils écrasent accidentellement les méthodes de classe existantes. En règle générale, il convient de bien réfléchir aux méthodes de nommage d’un mixin, afin de minimiser la probabilité que cela se produise.\n"
  },
  {
    "path": "1-js/09-classes/07-mixins/head.html",
    "content": "<script>\nlet eventMixin = {\n\n  /**\n   * Souscrire à l'événement, usage:\n   *  menu.on('select', function(item) { ... }\n  */\n  on(eventName, handler) {\n    if (!this._eventHandlers) this._eventHandlers = {};\n    if (!this._eventHandlers[eventName]) {\n      this._eventHandlers[eventName] = [];\n    }\n    this._eventHandlers[eventName].push(handler);\n  },\n\n  /**\n   * Annuler la souscription, usage:\n   *  menu.off('select', handler)\n   */\n  off(eventName, handler) {\n    let handlers = this._eventHandlers?.[eventName];\n    if (!handlers) return;\n    for(let i = 0; i < handlers.length; i++) {\n      if (handlers[i] == handler) {\n        handlers.splice(i--, 1);\n      }\n    }\n  },\n\n  /**\n   * Générer un événement avec le nom et les données donnés\n   *  this.trigger('select', data1, data2);\n   */\n  trigger(eventName, ...args) {\n    if (!this._eventHandlers || !this._eventHandlers[eventName]) {\n      return; // aucun gestionnaire pour ce nom d'événement\n    }\n\n    // appeler les gestionnaires\n    this._eventHandlers[eventName].forEach(handler => handler.apply(this, args));\n  }\n};\n</script>\n"
  },
  {
    "path": "1-js/09-classes/index.md",
    "content": "# Classes\n"
  },
  {
    "path": "1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md",
    "content": "La différence devient évidente quand on regarde le code dans une fonction.\n\nLe comportement est différent s'il y a un \"saut en dehors\" de `try..catch`.\n\nPar exemple, quand il y a un `return` dans `try..catch`. La clause `finally` fonctionne en cas de *toute* sortie de `try..catch`, même via l'instruction `return` : juste après la fin de `try..catch`, mais avant que le code appelant obtienne le contrôle.\n\n```js run\nfunction f() {\n  try {\n    alert('start');\n*!*\n    return \"result\";\n*/!*\n  } catch (err) {\n    // ...\n  } finally {\n    alert('cleanup!');\n  }\n}\n\nf(); // cleanup!\n```\n\n...Ou quand il y a un `throw`, comme ici :\n\n```js run\nfunction f() {\n  try {\n    alert('start');\n    throw new Error(\"an error\");\n  } catch (err) {\n    // ...\n    if(\"can't handle the error\") {\n*!*\n      throw err;\n*/!*\n    }\n\n  } finally {\n    alert('cleanup!')\n  }\n}\n\nf(); // cleanup!\n```\n\nC'est `finally` qui garantit le nettoyage ici. Si nous mettons simplement le code à la fin de `f`, il ne fonctionnerait pas dans ces situations.\n"
  },
  {
    "path": "1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md",
    "content": "importance: 5\n\n---\n\n# Finally ou juste le code ?\n\nComparez les deux fragments de code.\n\n1. Le premier utilise `finally` pour exécuter le code après `try..catch` :\n\n    ```js\n    try {\n      work work\n    } catch (err) {\n      handle errors\n    } finally {\n    *!*\n      cleanup the working space\n    */!*\n    }\n    ```\n\n2. Le deuxième fragment fait le nettoyage juste après `try..catch` :\n\n    ```js\n    try {\n      work work\n    } catch (err) {\n      handle errors\n    }\n\n    *!*\n    cleanup the working space\n    */!*\n    ```\n\nNous avons absolument besoin du nettoyage après le travail, peu importe qu'il y ait une erreur ou non.\n\nY at-il un avantage ici à utiliser `finally` ou les deux fragments de code sont égaux ? Si un tel avantage existe, donnez un exemple lorsque cela compte.\n"
  },
  {
    "path": "1-js/10-error-handling/1-try-catch/article.md",
    "content": "# Gestion des erreurs, \"try...catch\"\n\nPeu importe notre niveau en programmation, nos scripts comportent parfois des erreurs. Elles peuvent être dues à nos erreurs, à une entrée utilisateur imprévue, à une réponse erronée du serveur et à mille autres raisons.\n\nGénéralement, un script \"meurt\" (s'arrête immédiatement) en cas d'erreur, en l'affichant dans la console.\n\nMais il existe une structure de syntaxe `try...catch` qui permet au script \"d'attraper\" les erreurs et, au lieu de mourir en cas de problème, de faire quelque chose de plus raisonnable.\n\n## La syntaxe \"try...catch\"\n\nLa structure `try...catch` a deux blocs principaux : `try`, puis `catch` :\n\n```js\ntry {\n\n  // code...\n\n} catch (err) {\n\n  // Gestion des erreurs\n\n}\n```\n\nCela fonctionne comme ceci :\n\n1. Tout d'abord, le code dans `try {...}` est exécuté.\n2. S'il n'y a pas eu d'erreur, alors `catch(err)` est ignoré : l'exécution arrive à la fin de `try` et continue en ignorant `catch`.\n3. Si une erreur survient, alors l'exécution de `try` est arrêtée et le contrôle se place au début de `catch(err)`. La variable `err` (qui peut utiliser n'importe quel nom) contient un objet d'erreur avec des détails sur ce qui s'est passé.\n\n![](try-catch-flow.svg)\n\nDonc, une erreur dans le bloc `try {...}` ne tue pas le script -- nous avons une opportunité de la gérer dans `catch`.\n\nVoyons des exemples.\n\n- Un exemple sans erreur : affiche `alert` `(1)` et `(2)` :\n\n    ```js run\n    try {\n\n      alert('Start of try runs');  // *!*(1) <--*/!*\n\n      // ...pas d'erreur\n\n      alert('End of try runs');   // *!*(2) <--*/!*\n\n    } catch (err) {\n\n      alert('Catch is ignored, because there are no errors'); // (3)\n\n    }\n    ```\n\n- Un exemple avec une erreur : montre `(1)` et `(3)` :\n\n    ```js run\n    try {\n\n      alert('Start of try runs');  // *!*(1) <--*/!*\n\n    *!*\n      lalala; // error, variable is not defined!\n    */!*\n\n      alert('End of try (never reached)');  // (2)\n\n    } catch (err) {\n\n      alert(`Error has occurred!`); // *!*(3) <--*/!*\n\n    }\n    ```\n\n````warn header=\"`try...catch` ne fonctionne que pour les erreurs d'exécution\"\nPour que `try...catch` fonctionne, le code doit être exécutable. En d'autres termes, le code doit être du JavaScript valide.\n\nCela ne fonctionnera pas si le code est syntaxiquement incorrect, par exemple, il a des accolades inégalées :\n\n```js run\ntry {\n  {{{{{{{{{{{{\n} catch (err) {\n  alert(\"The engine can't understand this code, it's invalid\");\n}\n```\n\nLe moteur JavaScript lit d'abord le code, puis l'exécute. Les erreurs qui se produisent lors de la phase de lecture sont appelées erreurs \"d'analyse\" et sont irrécupérables (de l'intérieur de ce code). C'est parce que le moteur ne peut pas comprendre le code.\n\nAinsi, `try...catch` ne peut gérer que les erreurs qui se produisent dans du code valide. De telles erreurs sont appelées \"erreurs d'exécution\" ou, parfois, \"exceptions\".\n````\n\n````warn header=\"`try...catch` fonctionne de manière synchrone\"\nSi une exception se produit dans le code \"planifié\", comme dans `setTimeout`, `try...catch` ne l'attrapera pas :\n\n```js run\ntry {\n  setTimeout(function() {\n    noSuchVariable; // le script mourra ici\n  }, 1000);\n} catch (err) {\n  alert( \"won't work\" );\n}\n```\n\nC’est parce que la fonction elle-même est exécutée ultérieurement, lorsque le moteur a déjà quitté la structure `try...catch`.\n\nPour capturer une exception dans une fonction planifiée, `try...catch` doit être à l'intérieur de cette fonction :\n```js run\nsetTimeout(function() {\n  try {\n    noSuchVariable; // try...catch gère l'erreur !\n  } catch {\n    alert( \"error is caught here!\" );\n  }\n}, 1000);\n```\n````\n\n## Objet d'erreur\n\nEn cas d'erreur, JavaScript génère un objet contenant les détails à son sujet. L'objet est ensuite passé en argument à `catch` :\n\n```js\ntry {\n  // ...\n} catch(err) { // <-- \"l'objet d'erreur\", pourrait utiliser un autre mot au lieu de err\n  // ...\n}\n```\n\nPour toutes les erreurs intégrées, l'objet d'erreur a deux propriétés principales :\n\n`name`\n: Nom de l'erreur. Par exemple, pour une variable non définie, il s'agit de `\"ReferenceError\"`.\n\n`message`\n: Message textuel sur les détails de l'erreur.\n\nIl existe d'autres propriétés non standard disponibles dans la plupart des environnements. L'un des plus largement utilisés et supportés est :\n\n`stack`\n: Pile d'exécution en cours : chaîne contenant des informations sur la séquence d'appels imbriqués ayant entraîné l'erreur. Utilisé à des fins de débogage.\n\nPar exemple :\n\n```js run untrusted\ntry {\n*!*\n  lalala; // error, variable is not defined!\n*/!*\n} catch (err) {\n  alert(err.name); // ReferenceError\n  alert(err.message); // lalala is not defined\n  alert(err.stack); // ReferenceError: lalala is not defined at (...call stack)\n\n  // Peut aussi montrer une erreur dans son ensemble\n  // L'erreur est convertie en chaîne comme \"name: message\"\n  alert(err); // ReferenceError: lalala is not defined\n}\n```\n\n## Omission facultative pour \"catch\"\n\n[recent browser=new]\n\nSi nous n'avons pas besoin de détails d'erreur, `catch` peut l'omettre :\n\n```js\ntry {\n  // ...\n} catch { // <-- sans (err)\n  // ...\n}\n```\n\n## L'utilisation de \"try..catch\"\n\nExplorons un cas d'utilisation réel de `try...catch`.\n\nComme nous le savons déjà, JavaScript prend en charge la méthode [JSON.parse(str)](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/JSON/parse) pour lire les valeurs encodées en JSON.\n\nIl est généralement utilisé pour décoder les données reçues sur le réseau, par le serveur ou par une autre source.\n\nNous le recevons et appelons `JSON.parse` comme ceci :\n\n```js run\nlet json = '{\"name\":\"John\", \"age\": 30}'; // données du serveur\n\n*!*\nlet user = JSON.parse(json); // convertir la représentation textuelle en objet JS\n*/!*\n\n// maintenant, user est un objet avec les propriétés de la chaîne\nalert( user.name ); // John\nalert( user.age );  // 30\n```\n\nVous trouverez des informations plus détaillées sur JSON dans le chapitre <info:json>.\n\n**Si `json` est malformé, `JSON.parse` génère une erreur, de sorte que le script \"meurt\".**\n\nDevrions-nous en être satisfaits ? Bien sûr que non !\n\nDe cette façon, si quelque chose ne va pas avec les données, le visiteur ne le saura jamais (à moins d'ouvrir la console du développeur). Et les gens n'aiment vraiment pas quand quelque chose \"meurt\" sans aucun message d'erreur.\n\nUtilisons `try...catch` pour gérer l'erreur :\n\n```js run\nlet json = \"{ bad json }\";\n\ntry {\n\n*!*\n  let user = JSON.parse(json); // <-- quand une erreur se produit...\n*/!*\n  alert( user.name ); // ne fonctionne pas\n\n} catch (err) {\n*!*\n  // ...l'exécution saute ici\n  alert( \"Our apologies, the data has errors, we'll try to request it one more time.\" );\n  alert( err.name );\n  alert( err.message );\n*/!*\n}\n```\n\nIci, nous utilisons le bloc `catch` uniquement pour afficher le message, mais nous pouvons faire beaucoup plus : envoyer une nouvelle requête réseau, suggérer une alternative au visiteur, envoyer des informations sur l'erreur à un système de journalisation, ... Bien mieux que de juste mourir.\n\n## Lever nos propres exceptions\n\nQue se passe-t-il si `json` est correct du point de vue syntaxique, mais qu'il n'a pas de propriété requise `name` ?\n\nComme ceci :\n\n```js run\nlet json = '{ \"age\": 30 }'; // données incomplètes\n\ntry {\n\n  let user = JSON.parse(json); // <-- pas d'erreurs\n*!*\n  alert( user.name ); // pas de \"name\" !\n*/!*\n\n} catch (err) {\n  alert( \"doesn't execute\" );\n}\n```\n\nIci, `JSON.parse` fonctionne normalement, mais l'absence de `name` est en réalité une erreur pour nous.\n\nPour unifier le traitement des erreurs, nous allons utiliser l'opérateur `throw`.\n\n### L'instruction \"throw\"\n\nL'instruction `throw` génère une erreur.\n\nLa syntaxe est la suivante :\n\n```js\nthrow <error object>\n```\n\nTechniquement, on peut utiliser n'importe quoi comme objet d'erreur. Cela peut même être une primitive, comme un nombre ou une chaîne, mais il est préférable d’utiliser des objets, de préférence avec les propriétés `name` et `message` (pour rester quelque peu compatibles avec les erreurs intégrées).\n\nJavaScript comporte de nombreux constructeurs intégrés pour les erreurs standards : `Error`, `SyntaxError`, `ReferenceError`, `TypeError` et autres. Nous pouvons également les utiliser pour créer des objets d'erreur.\n\nLeur syntaxe est la suivante :\n\n```js\nlet error = new Error(message);\n// ou\nlet error = new SyntaxError(message);\nlet error = new ReferenceError(message);\n// ...\n```\n\nPour les erreurs intégrées (pas pour les objets, mais pour les erreurs), la propriété `name` est exactement le nom du constructeur. Et `message` est tiré de l'argument.\n\nPar exemple :\n\n```js run\nlet error = new Error(\"Things happen o_O\");\n\nalert(error.name); // Error\nalert(error.message); // Things happen o_O\n```\n\nVoyons quel type d'erreur `JSON.parse` génère :\n\n```js run\ntry {\n  JSON.parse(\"{ bad json o_O }\");\n} catch (err) {\n*!*\n  alert(err.name); // SyntaxError\n*/!*\n  alert(err.message); // Unexpected token b in JSON at position 2\n}\n```\n\nComme on peut le constater, c’est une `SyntaxError`.\n\nEt dans notre cas, l’absence de `name` est une erreur, car les utilisateurs doivent avoir un `name`.\n\nAlors utilisons `throw` :\n\n```js run\nlet json = '{ \"age\": 30 }'; // données incomplètes\n\ntry {\n\n  let user = JSON.parse(json); // <-- pas d'erreurs\n\n  if (!user.name) {\n*!*\n    throw new SyntaxError(\"Incomplete data: no name\"); // (*)\n*/!*\n  }\n\n  alert( user.name );\n\n} catch (err) {\n  alert( \"JSON Error: \" + err.message ); // JSON Error: Incomplete data: no name\n}\n```\n\nÀ la ligne `(*)`, l'instruction `throw` génère une `SyntaxError` avec le `message` donné, de la même manière que JavaScript le générerait lui-même. L'exécution de `try` s'arrête immédiatement et le flux de contrôle saute dans `catch`.\n\nMaintenant, `catch` est devenu un emplacement unique pour toutes les erreurs de traitement : à la fois pour `JSON.parse` et d'autres cas.\n\n## Propager une exception\n\nDans l'exemple ci-dessus, nous utilisons `try...catch` pour gérer des données incorrectes. Mais est-il possible qu’*une autre erreur inattendue* se produise dans le bloc `try {...}` ? Comme une erreur de programmation (variable is not defined) ou quelque chose d'autre, pas seulement cette \"donnée incorrecte\".\n\nPar exemple :\n\n```js run\nlet json = '{ \"age\": 30 }'; // données incomplètes\n\ntry {\n  user = JSON.parse(json); // <-- oublié de mettre \"let\" avant user\n\n  // ...\n} catch (err) {\n  alert(\"JSON Error: \" + err); // JSON Error: ReferenceError: user is not defined\n  // (aucune erreur JSON)\n}\n```\n\nBien sûr, tout est possible ! Les programmeurs font des erreurs. Même dans les utilitaires à code source ouvert utilisés par des millions de personnes pendant des décennies, on découvre soudainement un bug qui conduit à de terribles piratages.\n\nDans notre cas, `try...catch` est destiné à intercepter les erreurs \"incorrect data\". Mais par sa nature, `catch` obtient *toutes* les erreurs de `try`. Ici, une erreur inattendue se produit, mais le même message `\"JSON Error\"` est toujours affiché. C'est faux et rend également le code plus difficile à déboguer.\n\nPour éviter de tels problèmes, nous pouvons utiliser la technique du \"rethrowing\" (re-lancement). La règle est simple :\n\n**Catch ne doit traiter que les erreurs qu'il connaît et \"renvoyer\" toutes les autres.**\n\nLa technique \"rethrowing\" peut être expliqué plus en détail comme :\n\n1. Catch obtient toutes les erreurs.\n2. Dans le bloc `catch (err) {...}` nous analysons l'objet d'erreur `err`.\n3. Si nous ne savons pas comment le gérer, nous faisons `throw err`.\n\nHabituellement, nous pouvons vérifier le type d'erreur en utilisant l'opérateur `instanceof` :\n\n```js run\ntry {\n  user = { /*...*/ };\n} catch (err) {\n*!*\n  if (err instanceof ReferenceError) {\n*/!*\n    alert('ReferenceError'); // \"ReferenceError\" pour avoir accédé à une variable non définie\n  }\n}\n```\n\nNous pouvons également obtenir le nom de la classe d'erreur à partir de la propriété `err.name`. Toutes les erreurs natives l'ont. Une autre option est de lire `err.constructor.name`.\n\nDans le code ci-dessous, nous utilisons la technique de \"propagation\" pour que `catch` ne traite que `SyntaxError` :\n\n```js run\nlet json = '{ \"age\": 30 }'; // données incomplètes\ntry {\n\n  let user = JSON.parse(json);\n\n  if (!user.name) {\n    throw new SyntaxError(\"Incomplete data: no name\");\n  }\n\n*!*\n  blabla(); // erreur inattendue\n*/!*\n\n  alert( user.name );\n\n} catch (err) {\n\n*!*\n  if (err instanceof SyntaxError) {\n    alert( \"JSON Error: \" + err.message );\n  } else {\n    throw err; // propager (*)\n  }\n*/!*\n\n}\n```\n\nL’erreur de l’instruction `throw` à la ligne `(*)` de l’intérieur du bloc `catch` \"sort\" de `try...catch` et peut être soit capturée par une structure `try...catch` externe (si elle existe), soit elle arrête le script.\n\nAinsi, le bloc `catch` ne traite que les erreurs qu’il sait gérer et \"ignore\" toutes les autres.\n\nL'exemple ci-dessous montre comment de telles erreurs peuvent être capturées par un niveau supplémentaire de `try...catch` :\n\n```js run\nfunction readData() {\n  let json = '{ \"age\": 30 }';\n\n  try {\n    // ...\n*!*\n    blabla(); // error!\n*/!*\n  } catch (err) {\n    // ...\n    if (!(err instanceof SyntaxError)) {\n*!*\n      throw err; // propager l'erreur (ne sachant pas comment la gérer)\n*/!*\n    }\n  }\n}\n\ntry {\n  readData();\n} catch (err) {\n*!*\n  alert( \"External catch got: \" + err ); // attrapé !\n*/!*\n}\n```\n\nIci, `readData` ne sait que gérer `SyntaxError`, alors que le `try...catch` extérieur sait comment tout gérer.\n\n## try...catch...finally\n\nMais ce n'est pas tout.\n\nLa structure `try...catch` peut avoir un bloc de code supplémentaire : `finally`.\n\nS'il existe, il s'exécute dans tous les cas :\n\n- après `try`, s'il n'y a pas eu d'erreur,\n- après `catch`, s'il y a eu des erreurs.\n\nLa syntaxe étendue ressemble à ceci :\n\n```js\n*!*try*/!* {\n   ... try to execute the code ...\n} *!*catch*/!* (err) {\n   ... handle errors ...\n} *!*finally*/!* {\n   ... execute always ...\n}\n```\n\nEssayez d'exécuter ce code :\n\n```js run\ntry {\n  alert( 'try' );\n  if (confirm('Make an error?')) BAD_CODE();\n} catch (err) {\n  alert( 'catch' );\n} finally {\n  alert( 'finally' );\n}\n```\n\nCe code a deux manières de s'exécuter :\n\n1. Si vous répondez \"Yes\" à \"Make an error?\", Alors `try -> catch -> finally`.\n2. Si vous dites \"No\", alors `try -> finally`.\n\nLa clause `finally` est souvent utilisée lorsque nous commençons à faire quelque chose et que nous voulons le finaliser dans tous les cas de figure.\n\nPar exemple, nous voulons mesurer le temps que prend une fonction de nombre de Fibonacci `fib(n)`. Naturellement, nous pouvons commencer à mesurer avant l'exécution et finir ensuite. Mais que se passe-t-il s'il y a une erreur lors de l'appel de la fonction ? En particulier, la mise en oeuvre de `fib(n)` dans le code ci-dessous renvoie une erreur pour les nombres négatifs ou non entiers.\n\nLa clause `finally` est un bon endroit pour finir les mesures, quoi qu’il arrive.\n\nIci, `finally` garantit que le temps sera correctement mesuré dans les deux situations - en cas d’exécution réussie de `fib` et en cas d’erreur :\n\n```js run\nlet num = +prompt(\"Enter a positive integer number.\", 35)\n\nlet diff, result;\n\nfunction fib(n) {\n  if (n < 0 || Math.trunc(n) != n) {\n    throw new Error(\"Must not be negative, and also an integer.\");\n  }\n  return n <= 1 ? n : fib(n - 1) + fib(n - 2);\n}\n\nlet start = Date.now();\n\ntry {\n  result = fib(num);\n} catch (err) {\n  result = 0;\n*!*\n} finally {\n  diff = Date.now() - start;\n}\n*/!*\n\nalert(result || \"error occurred\");\n\nalert( `execution took ${diff}ms` );\n```\n\nVous pouvez vérifier en exécutant le code en entrant `35` dans `prompt` - il s'exécute normalement, `finally` après `try`. Puis entrez `-1` -- il y aura une erreur immédiate, puis l'exécution prendra `0ms`. Les deux mesures sont effectuées correctement.\n\nEn d'autres termes, la fonction peut finir par `return` ou `throw`, cela n'a pas d'importance. La clause `finally` s'exécute dans les deux cas.\n\n```smart header=\"Les variables sont locales à l'intérieur de `try...catch...finally`\"\nVeuillez noter que les variables `result` et `diff` dans le code ci-dessus sont déclarées *avant* `try...catch`.\n\nSinon, si nous déclarions `let` dans le bloc `try`, il ne serait visible qu'à l'intérieur de celui-ci.\n```\n\n````smart header=\"`finally` et `return`\"\nLa clause `finally` fonctionne pour *toute* sortie de `try...catch`. Cela inclut un `return` explicite.\n\nDans l'exemple ci-dessous, il y a `return` dans `try`. Dans ce cas, `finally` est exécuté juste avant que le contrôle ne retourne au code externe.\n\n```js run\nfunction func() {\n  try {\n*!*\n    return 1;\n*/!*\n  } catch (err) {\n    /* ... */\n  } finally {\n*!*\n    alert( 'finally' );\n*/!*\n  }\n}\n\nalert( func() ); // en premier l'alert du `finally`, puis celle-ci (`1`)\n```\n````\n\n````smart header=\"`try...finally`\"\n\nLa structure `try...finally`, sans la clause `catch`, est également utile. Nous l'appliquons lorsque nous ne voulons pas gérer les erreurs ici (les laisser passer), mais nous voulons être sûrs que les processus que nous avons démarrés sont finalisés.\n\n```js\nfunction func() {\n  // commencer à faire quelque chose qui doit être complété (comme des mesures)\n  try {\n    // ...\n  } finally {\n    // compléter cette chose, même si tout meurt\n  }\n}\n```\nDans le code ci-dessus, une erreur à l'intérieur de `try` ressort toujours, car il n'y a pas de `catch`. Mais `finally` fonctionne avant que le flux d’exécution ne quitte la fonction.\n````\n\n## Catch global\n\n```warn header=\"Spécifique à l'environnement\"\nLes informations de cette section ne font pas partie du JavaScript de base.\n```\n\nImaginons que nous ayons une erreur fatale en dehors de `try...catch` et que le script soit mort. Comme une erreur de programmation ou une autre chose terrible.\n\nY a-t-il un moyen de réagir à de tels événements ? Nous pouvons vouloir enregistrer l'erreur, montrer quelque chose à l'utilisateur (normalement, ils ne voient pas les messages d'erreur), etc.\n\nIl n'y en a pas dans la spécification, mais les environnements le fournissent généralement, car c'est vraiment utile. Par exemple, Node.js a [`process.on(\"uncaughtException\")`](https://nodejs.org/api/process.html#process_event_uncaughtexception) pour ça. Et dans le navigateur, nous pouvons attribuer une fonction à la propriété [window.onerror](https://developer.mozilla.org/fr/docs/Web/API/GlobalEventHandlers/onerror), qui fonctionnera en cas d'erreur non interceptée.\n\nLa syntaxe :\n\n```js\nwindow.onerror = function(message, url, line, col, error) {\n  // ...\n};\n```\n\n`message`\n: Message d'erreur.\n\n`url`\n: URL du script où l'erreur s'est produite.\n\n`line`, `col`\n: Numéros de ligne et de colonne où une erreur s'est produite.\n\n`error`\n: Objet d'erreur.\n\nPar exemple :\n\n```html run untrusted refresh height=1\n<script>\n*!*\n  window.onerror = function(message, url, line, col, error) {\n    alert(`${message}\\n At ${line}:${col} of ${url}`);\n  };\n*/!*\n\n  function readData() {\n    badFunc(); // Whoops, quelque chose s'est mal passé !\n  }\n\n  readData();\n</script>\n```\n\nLe rôle du gestionnaire global `window.onerror` est généralement de ne pas récupérer l'exécution du script - c'est probablement impossible en cas d'erreur de programmation, mais d'envoyer le message d'erreur aux développeurs.\n\nIl existe également des services Web fournissant un journal des erreurs pour de tels cas, comme <https://errorception.com> ou <https://www.muscula.com>.\n\nIls fonctionnent comme ceci :\n\n1. Nous nous inscrivons au service et obtenons un morceau de JS (ou une URL de script) à insérer sur des pages.\n2. Ce script JS définit une fonction `window.onerror` personnalisée.\n3. Lorsqu'une erreur se produit, une demande réseau à ce sujet est envoyée au service.\n4. Nous pouvons nous connecter à l'interface Web du service et voir les erreurs.\n\n## Résumé\n\nLa structure `try...catch` permet de gérer les erreurs d'exécution. Cela permet littéralement \"d'essayer\" (`try`) d'exécuter le code et \"d’attraper\" (`catch`) les erreurs qui peuvent s'y produire.\n\nLa syntaxe est la suivante :\n\n```js\ntry {\n  // exécuter ce code\n} catch(err) {\n  // si une erreur s'est produite, alors saute ici\n  // err est l'objet d'erreur\n} finally {\n  // faire dans tous les cas après try / catch\n}\n```\n\nIl peut ne pas y avoir de section `catch` ou de `finally`, donc les structures plus courtes `try...catch` et `try...finally` sont également valides.\n\nLes objets d'erreur ont les propriétés suivantes :\n\n- `message` - le message d'erreur.\n- `name` - la chaîne avec le nom d'erreur (nom du constructeur de l'erreur).\n- `stack` (non standard, mais bien supporté) - la pile d'exécution au moment de la création de l'erreur.\n\nSi un objet d'erreur n'est pas nécessaire, nous pouvons l'omettre en utilisant `catch {...}` au lieu de `catch(err) {...}`.\n\nNous pouvons également générer nos propres erreurs en utilisant l'opérateur `throw`. Techniquement, l'argument de `throw` peut être n'importe quoi, mais il s'agit généralement d'un objet d'erreur héritant de la classe `Error` intégrée. Plus d'informations sur l'extension des erreurs dans le chapitre suivant.\n\nLa technique de *propagation* est un modèle très important de gestion des erreurs : un bloc `catch` s'attend généralement à un type d'erreur particulier et sait comment le gérer, il doit donc \"propager\" (renvoyer) les erreurs qu'il ne connaît pas.\n\nMême si nous n'avons pas `try...catch`, la plupart des environnements permettent de configurer un gestionnaire d'erreurs \"globales\" pour intercepter les erreurs qui \"tombent\". Dans le navigateur, c'est `window.onerror`.\n"
  },
  {
    "path": "1-js/10-error-handling/2-custom-errors/1-format-error/solution.md",
    "content": "```js run untrusted\nclass FormatError extends SyntaxError {\n  constructor(message) {\n    super(message);\n    this.name = this.constructor.name;\n  }\n}\n\nlet err = new FormatError(\"formatting error\");\n\nalert( err.message ); // formatting error\nalert( err.name ); // FormatError\nalert( err.stack ); // stack\n\nalert( err instanceof SyntaxError ); // true\n```\n"
  },
  {
    "path": "1-js/10-error-handling/2-custom-errors/1-format-error/task.md",
    "content": "importance: 5\n\n---\n\n# Hériter de SyntaxError\n\nCréez une classe `FormatError` qui hérite de la classe `SyntaxError` intégrée.\n\nIl devrait supporter les propriétés `message`, `name` et `stack`.\n\nExemple d'utilisation :\n\n```js\nlet err = new FormatError(\"formatting error\");\n\nalert( err.message ); // formatting error\nalert( err.name ); // FormatError\nalert( err.stack ); // stack\n\nalert( err instanceof FormatError ); // true\nalert( err instanceof SyntaxError ); // true (hérite de SyntaxError)\n```\n"
  },
  {
    "path": "1-js/10-error-handling/2-custom-errors/article.md",
    "content": "# Les erreurs personnalisées, extension de Error\n\nLorsque nous développons quelque chose, nous avons souvent besoin de nos propres classes d'erreur pour refléter des problèmes spécifiques qui peuvent mal tourner dans nos tâches. Pour les erreurs dans les opérations réseau, nous aurons peut-être besoin de `HttpError`, pour les opérations de base de données `DbError`, pour les opérations de recherche `NotFoundError`, etc.\n\nNos erreurs devraient prendre en charge des propriétés d'erreur de base telles que `message`, `name` et, de préférence, `stack`. Mais elles peuvent aussi avoir d’autres propriétés propres, par exemple les objets `HttpError` peuvent avoir une propriété `statusCode` avec une valeur telle que `404` ou `403` ou `500`.\n\nJavaScript permet d'utiliser `throw` avec n'importe quel argument. Par conséquent, techniquement, nos classes d'erreur personnalisées n'ont pas besoin d'hériter de `Error`. Mais si nous héritons, il devient alors possible d'utiliser `obj instanceof Error` pour identifier les objets d'erreur. Il vaut donc mieux en hériter.\n\nAu fur et à mesure que l'application grandit, nos propres erreurs forment naturellement une hiérarchie. Par exemple, `HttpTimeoutError` peut hériter de `HttpError`, etc.\n\n## Étendre Error\n\nA titre d'exemple, considérons une fonction `readUser(json)` qui devrait lire JSON avec des données utilisateur.\n\nVoici un exemple de l'apparence d'un `json` valide :\n\n```js\nlet json = `{ \"name\": \"John\", \"age\": 30 }`;\n```\n\nEn interne, nous utiliserons `JSON.parse`. S'il reçoit un `json` malformé, il renvoie `SyntaxError`. Mais même si `json` est syntaxiquement correct, cela ne signifie pas que c'est un utilisateur valide, non ? Il peut manquer les données nécessaires. Par exemple, il peut ne pas avoir les propriétés `name` et `age` qui sont essentielles pour nos utilisateurs.\n\nNotre fonction `readUser(json)` va non seulement lire JSON, mais aussi vérifier (\"valider\") les données. S'il n'y a pas de champs obligatoires ou si le format est incorrect, c'est une erreur. Et ce n’est pas une `SyntaxError`, car les données sont syntaxiquement correctes, mais un autre type d’erreur. Nous l'appellerons `ValidationError` et créerons une classe pour cela. Une erreur de ce type devrait également comporter des informations sur le champ fautif.\n\nNotre classe `ValidationError` devrait hériter de la classe `Error`.\n\nLa class `Error` est une classe intégrée, voici le code approximatif pour que nous comprenions ce que nous étendons :\n\n```js\n// Le \"pseudocode\" pour la classe d'erreur intégrée définie par JavaScript lui-même\nclass Error {\n  constructor(message) {\n    this.message = message;\n    this.name = \"Error\"; // (noms différents pour différentes classes d'erreur intégrées)\n    this.stack = <call stack>; // non standard, mais la plupart des environnements le supportent\n  }\n}\n```\n\nMaintenant, héritons de `ValidationError` et mettons-le en action :\n\n```js run\n*!*\nclass ValidationError extends Error {\n*/!*\n  constructor(message) {\n    super(message); // (1)\n    this.name = \"ValidationError\"; // (2)\n  }\n}\n\nfunction test() {\n  throw new ValidationError(\"Whoops!\");\n}\n\ntry {\n  test();\n} catch(err) {\n  alert(err.message); // Whoops!\n  alert(err.name); // ValidationError\n  alert(err.stack); // une liste des appels imbriqués avec le numéro de ligne pour chacun d'entre eux\n}\n```\n\nRemarque : à la ligne `(1)`, nous appelons le constructeur parent. JavaScript exige que nous appelions `super` dans le constructeur de l'enfant, donc c'est obligatoire. Le constructeur parent définit la propriété `message`.\n\nLe constructeur parent définit également la propriété `name` sur `\"Error\"`, donc à la ligne `(2)` nous la réinitialisons à la valeur correcte.\n\nEssayons de l'utiliser dans `readUser(json)` :\n\n```js run\nclass ValidationError extends Error {\n  constructor(message) {\n    super(message);\n    this.name = \"ValidationError\";\n  }\n}\n\n// Usage\nfunction readUser(json) {\n  let user = JSON.parse(json);\n\n  if (!user.age) {\n    throw new ValidationError(\"No field: age\");\n  }\n  if (!user.name) {\n    throw new ValidationError(\"No field: name\");\n  }\n\n  return user;\n}\n\n// un example avec try..catch\n\ntry {\n  let user = readUser('{ \"age\": 25 }');\n} catch (err) {\n  if (err instanceof ValidationError) {\n*!*\n    alert(\"Invalid data: \" + err.message); // Invalid data: No field: name\n*/!*\n  } else if (err instanceof SyntaxError) { // (*)\n    alert(\"JSON Syntax Error: \" + err.message);\n  } else {\n    throw err; // erreur inconnue, on la propage (**)\n  }\n}\n```\n\nLe bloc `try..catch` dans le code ci-dessus gère à la fois notre `ValidationError` et le `SyntaxError` intégré de `JSON.parse`.\n\nVeuillez regarder comment nous utilisons `instanceof` pour vérifier le type d'erreur spécifique à la ligne `(*)`.\n\nNous pourrions aussi regarder `err.name`, comme ceci :\n\n```js\n// ...\n// au lieu de (err instanceof SyntaxError)\n} else if (err.name == \"SyntaxError\") { // (*)\n// ...\n```\n\nLa version `instanceof` est bien meilleure, car dans le futur nous allons étendre `ValidationError`, en créer des sous-types, comme `PropertyRequiredError`. Et `instanceof` continuera à fonctionner pour les nouvelles classes héritées. Donc, c'est à l'épreuve du futur.\n\nIl est également important que si `catch` rencontre une erreur inconnue, il la renvoie à la ligne `(**)`. Le bloc `catch` ne sait gérer que les erreurs de validation et de syntaxe, les autres types (causés par une faute de frappe dans le code ou d'autres raisons inconnues) devraient êtres propagés.\n\n## Héritage complémentaire\n\nLa classe `ValidationError` est très générique. Beaucoup de choses peuvent mal se passer. La propriété peut être absente ou dans un format incorrect (comme une valeur de chaîne de caractères pour `age` au lieu d’un nombre). Faisons une classe plus concrète `PropertyRequiredError`, exactement pour les propriétés absentes. Elle contiendra des informations supplémentaires sur la propriété qui manque.\n\n```js run\nclass ValidationError extends Error {\n  constructor(message) {\n    super(message);\n    this.name = \"ValidationError\";\n  }\n}\n\n*!*\nclass PropertyRequiredError extends ValidationError {\n  constructor(property) {\n    super(\"No property: \" + property);\n    this.name = \"PropertyRequiredError\";\n    this.property = property;\n  }\n}\n*/!*\n\n// Usage\nfunction readUser(json) {\n  let user = JSON.parse(json);\n\n  if (!user.age) {\n    throw new PropertyRequiredError(\"age\");\n  }\n  if (!user.name) {\n    throw new PropertyRequiredError(\"name\");\n  }\n\n  return user;\n}\n\n// example avec try..catch\n\ntry {\n  let user = readUser('{ \"age\": 25 }');\n} catch (err) {\n  if (err instanceof ValidationError) {\n*!*\n    alert(\"Invalid data: \" + err.message); // Invalid data: No property: name\n    alert(err.name); // PropertyRequiredError\n    alert(err.property); // name\n*/!*\n  } else if (err instanceof SyntaxError) {\n    alert(\"JSON Syntax Error: \" + err.message);\n  } else {\n    throw err; // erreur inconnue, on la propage\n  }\n}\n```\n\nLa nouvelle classe `PropertyRequiredError` est facile à utiliser : il suffit de passer le nom de la propriété : `new PropertyRequiredError(property)`. Le `message` est généré par le constructeur.\n\nVeuillez noter que `this.name` dans le constructeur `PropertyRequiredError` est à nouveau attribué manuellement. Cela peut devenir un peu fastidieux -- d'assigner `this.name = <class name>` dans chaque classe d'erreur personnalisée. Nous pouvons l'éviter en créant notre propre classe \"d'erreur de base\" qui assigne `this.name = this.constructor.name`. Puis nous en ferons hériter toutes nos classes d'erreur personnalisées.\n\nAppelons cela `MyError`.\n\nVoici le code avec `MyError` et d'autres classes d'erreur personnalisées, simplifié :\n\n```js run\nclass MyError extends Error {\n  constructor(message) {\n    super(message);\n*!*\n    this.name = this.constructor.name;\n*/!*\n  }\n}\n\nclass ValidationError extends MyError { }\n\nclass PropertyRequiredError extends ValidationError {\n  constructor(property) {\n    super(\"No property: \" + property);\n    this.property = property;\n  }\n}\n\n// le nom est correcte\nalert( new PropertyRequiredError(\"field\").name ); // PropertyRequiredError\n```\n\nMaintenant, les erreurs personnalisées sont beaucoup plus courtes, en particulier `ValidationError`, car nous nous sommes débarrassés de la ligne `\"this.name = ...\"` dans le constructeur.\n\n## Le wrapping des exceptions\n\nLe but de la fonction `readUser` dans le code ci-dessus est \"de lire les données de l'utilisateur\". Il peut y avoir différents types d’erreurs dans le processus. À l'heure actuelle, nous avons `SyntaxError` et `ValidationError`, mais à l'avenir, la fonction `readUser` pourrait croître et générer probablement d'autres types d'erreurs.\n\nLe code qui appelle `readUser` devrait gérer ces erreurs. À l'heure actuelle, il utilise plusieurs if dans le bloc `catch`, qui vérifient la classe et gèrent les erreurs connues et rejettent les inconnues.\n\nLe schéma est le suivant :\n\n```js\ntry {\n  ...\n  readUser()  // la source d'erreur potentielle\n  ...\n} catch (err) {\n  if (err instanceof ValidationError) {\n    // handle validation errors\n  } else if (err instanceof SyntaxError) {\n    // handle syntax errors\n  } else {\n    throw err; // erreur inconnue, la relancer\n  }\n}\n```\n\nDans le code ci-dessus, nous pouvons voir deux types d'erreurs, mais il peut y en avoir plus.\n\nSi la fonction `readUser` génère plusieurs types d'erreurs, alors nous devrions nous demander : voulons-nous vraiment vérifier tous les types d'erreur un par un à chaque fois ?\n\nSouvent, la réponse est \"non\", nous aimerions être \"un niveau au-dessus de tout cela\". Nous voulons simplement savoir s'il y a eu une \"erreur de lecture des données\" -- pourquoi exactement cela s'est produit est souvent hors de propos (le message d'erreur le décrit). Ou, encore mieux, nous aimerions avoir un moyen d'obtenir les détails de l'erreur, mais seulement si nous en avons besoin.\n\nLa technique que nous décrivons ici est appelée \"encapsulation d'exceptions\".\n\n1. Nous allons créer une nouvelle classe `ReadError` pour représenter une erreur générique de \"lecture des données\".\n2. La fonction `readUser` interceptera les erreurs de lecture de données qui se produisent à l'intérieur, telles que `ValidationError` et `SyntaxError`, et générera à la place une `ReadError`.\n3. L'objet `ReadError` conservera la référence à l'erreur d'origine dans sa propriété `cause`.\n\nEnsuite, le code qui appelle `readUser` n'aura qu'à vérifier `ReadError`, pas pour tous les types d'erreurs de lecture de données. Et s'il a besoin de plus de détails sur une erreur, il peut vérifier sa propriété `cause`.\n\nVoici le code qui définit `ReadError` et illustre son utilisation dans `readUser` et `try..catch` :\n\n```js run\nclass ReadError extends Error {\n  constructor(message, cause) {\n    super(message);\n    this.cause = cause;\n    this.name = 'ReadError';\n  }\n}\n\nclass ValidationError extends Error { /*...*/ }\nclass PropertyRequiredError extends ValidationError { /* ... */ }\n\nfunction validateUser(user) {\n  if (!user.age) {\n    throw new PropertyRequiredError(\"age\");\n  }\n\n  if (!user.name) {\n    throw new PropertyRequiredError(\"name\");\n  }\n}\n\nfunction readUser(json) {\n  let user;\n\n  try {\n    user = JSON.parse(json);\n  } catch (err) {\n*!*\n    if (err instanceof SyntaxError) {\n      throw new ReadError(\"Syntax Error\", err);\n    } else {\n      throw err;\n    }\n*/!*\n  }\n\n  try {\n    validateUser(user);\n  } catch (err) {\n*!*\n    if (err instanceof ValidationError) {\n      throw new ReadError(\"Validation Error\", err);\n    } else {\n      throw err;\n    }\n*/!*\n  }\n\n}\n\ntry {\n  readUser('{bad json}');\n} catch (e) {\n  if (e instanceof ReadError) {\n*!*\n    alert(e);\n    // Original error: SyntaxError: Unexpected token b in JSON at position 1\n    alert(\"Original error: \" + e.cause);\n*/!*\n  } else {\n    throw e;\n  }\n}\n```\n\nDans le code ci-dessus, `readUser` fonctionne exactement comme décrit - il intercepte les erreurs de syntaxe et de validation et propage des erreurs `ReadError` (les erreurs inconnues sont propagées comme d'habitude).\n\nDonc, le code externe vérifie `instanceof ReadError` et c'est tout. Pas besoin de lister tous les types d'erreur possibles.\n\nL'approche est appelée \"encapsulation d'exceptions\", car nous prenons les exceptions \"de bas niveau\" et les \"encapsulons\" dans `ReadError` qui est plus abstrait. Il est largement utilisé dans la programmation orientée objet.\n\n## Résumé\n\n- Nous pouvons hériter de `Error` et d'autres classes d'erreurs intégrées normalement. Nous devons juste nous occuper de la propriété `name` et ne pas oublier d'appeler `super`.\n- Nous pouvons utiliser `instanceof` pour vérifier des erreurs particulières. Cela fonctionne aussi avec l'héritage. Mais parfois, nous avons un objet d'erreur provenant d'une bibliothèque tierce et il n'y a pas de moyen facile d'obtenir la classe. Dans ce cas, la propriété `name` peut être utilisée pour de telles vérifications.\n- Le wrapping des exceptions est une technique répandue : une fonction gère les exceptions de bas niveau et crée des erreurs de niveau supérieur au lieu de diverses erreurs de bas niveau. Les exceptions de bas niveau deviennent parfois des propriétés de cet objet comme `err.cause` dans les exemples ci-dessus, mais ce n'est pas strictement requis.\n"
  },
  {
    "path": "1-js/10-error-handling/index.md",
    "content": "# La gestion des erreurs\n"
  },
  {
    "path": "1-js/11-async/01-callbacks/article.md",
    "content": "\n\n# Introduction: callbacks\n\n```warn header=\"Nous utilisons ici des méthodes du navigateur dans les exemples\"\nPour démontrer l'utilisation des callbacks, des promesses et d'autres concepts abstraits, nous utiliserons certaines méthodes du navigateur : plus précisément, nous chargerons des scripts et effectuerons des manipulations simples de documents.\n\nSi vous n'êtes pas familier avec ces méthodes, et que leur utilisation dans les exemples est confuse, vous pouvez lire quelques chapitres de la [partie suivante](/document) du tutoriel.\n\nMais nous allons quand même essayer de rendre les choses claires. Il n'y aura rien de vraiment complexe au niveau du navigateur.\n```\n\nDe nombreuses fonctions sont fournies par les environnements hôtes JavaScript qui vous permettent de planifier des actions *asynchrones*. En d'autres termes, des actions que nous lançons maintenant, mais qui se terminent plus tard.\n\nPar exemple, une de ces fonctions est la fonction `setTimeout`.\n\nIl existe d'autres exemples concrets d'actions asynchrones, par exemple le chargement de scripts et de modules (nous les aborderons dans les chapitres suivants).\n\nRegardez la fonction `loadScript(src)`, qui charge un script avec le `src` donné:\n\n```js\nfunction loadScript(src) {\n  // crée une balise <script> et l'ajoute à la page\n  // ceci fait que le script avec la src donnée commence à se charger et s'exécute une fois terminé.\n  let script = document.createElement('script');\n  script.src = src;\n  document.head.append(script);\n}\n```\n\nIl insère dans le document une nouvelle balise, créée dynamiquement, `<script src=\"...\">` avec le `src` donné. Le navigateur commence automatiquement à la charger et l'exécute lorsqu'elle est terminée.\n\nNous pouvons utiliser cette fonction comme suit :\n\n```js\n// charger et exécuter le script au chemin donné\nloadScript('/my/script.js');\n```\n\nLe script est exécuté de manière \"asynchrone\", car il commence à se charger maintenant, mais s'exécute plus tard, lorsque la fonction est déjà terminée.\n\nS'il y a du code sous `loadScript(...)`, il n'attend pas que le chargement du script soit terminé.\n\n```js\nloadScript('/my/script.js');\n// le code dessous loadScript\n// n'attend pas que le chargement du script soit terminé\n// ...\n```\n\nDisons que nous devons utiliser le nouveau script dès qu'il est chargé. Il déclare de nouvelles fonctions, et nous voulons les exécuter.\n\nMais si nous le faisons immédiatement après l'appel `loadScript(...)`, cela ne fonctionnera pas:\n\n```js\nloadScript('/my/script.js'); // le script a \"function newFunction() {…}\"\n\n*!*\nnewFunction(); // aucune fonction de ce type!\n*/!*\n```\n\nNaturellement, le navigateur n'a probablement pas eu le temps de charger le script. Pour l'instant, la fonction `loadScript` ne permet pas de suivre l'achèvement du chargement. Le script se charge et finit par s'exécuter, c'est tout. Mais nous aimerions savoir quand cela se produit, pour utiliser les nouvelles fonctions et variables de ce script.\n\nAjoutons une fonction `callback` comme second argument à `loadScript` qui doit s'exécuter lorsque le script se charge :\n\n```js\nfunction loadScript(src, *!*callback*/!*) {\n  let script = document.createElement('script');\n  script.src = src;\n\n*!*\n  script.onload = () => callback(script);\n*/!*\n\n  document.head.append(script);\n}\n```\n\nL'événement `onload` est décrit dans l'article <info:onload-onerror#loading-a-script>, il exécute essentiellement une fonction après le chargement et l'exécution du script.\n\nMaintenant, si nous voulons appeler de nouvelles fonctions depuis le script, nous devons l'écrire dans le callback:\n\n```js\nloadScript('/my/script.js', function() {\n  // le callback est exécuté après le chargement du script\n  newFunction(); // maintenant cela fonctionne\n  ...\n});\n```\n\nC'est l'idée: le deuxième argument est une fonction (généralement anonyme) qui s'exécute lorsque l'action est terminée.\n\nVoici un exemple exécutable avec un vrai script :\n\n```js run\nfunction loadScript(src, callback) {\n  let script = document.createElement('script');\n  script.src = src;\n  script.onload = () => callback(script);\n  document.head.append(script);\n}\n\n*!*\nloadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', script => {\n  alert(`Cool, the script ${script.src} is loaded`);\n  alert( _ ); // _ est une fonction déclarée dans le script chargé\n});\n*/!*\n```\n\nC'est ce qu'on appelle un style de programmation asynchrone basé sur les \"callbacks\". Une fonction qui fait quelque chose de manière asynchrone doit fournir un argument `callback` où nous mettons la fonction à exécuter après qu'elle soit terminée.\n\nIci nous l'avons fait dans `loadScript`, mais bien sûr c'est une approche générale.\n\n## Callback imbriqué\n\nComment charger deux scripts de manière séquentielle: le premier, puis le second après lui ?\n\nLa solution naturelle serait de placer le second appel `loadScript` à l'intérieur du callback, comme ceci:\n\n```js\nloadScript('/my/script.js', function(script) {\n\n  alert(`Cool, the ${script.src} is loaded, let's load one more`);\n\n*!*\n  loadScript('/my/script2.js', function(script) {\n    alert(`Cool, the second script is loaded`);\n  });\n*/!*\n\n});\n```\n\nUne fois que le `loadScript` externe est terminé, le callback lance le `loadScript` interne.\n\nEt si nous voulons un script de plus... ?\n\n```js\nloadScript('/my/script.js', function(script) {\n\n  loadScript('/my/script2.js', function(script) {\n\n*!*\n    loadScript('/my/script3.js', function(script) {\n      // ...continue après que tous les scripts soient chargés\n    });\n*/!*\n\n  });\n\n});\n```\n\nAinsi, chaque nouvelle action se trouve dans une callback. C'est bien pour peu d'actions, mais pas pour beaucoup, donc nous verrons bientôt d'autres variantes.\n\n## Gestion des erreurs\n\nDans les exemples ci-dessus, nous n'avons pas tenu compte des erreurs. Que se passe-t-il si le chargement du script échoue ? Notre callback doit être capable de réagir à cette situation.\n\nVoici une version améliorée de `loadScript` qui suit les erreurs de chargement :\n\n```js\nfunction loadScript(src, callback) {\n  let script = document.createElement('script');\n  script.src = src;\n\n*!*\n  script.onload = () => callback(null, script);\n  script.onerror = () => callback(new Error(`Script load error for ${src}`));\n*/!*\n\n  document.head.append(script);\n}\n```\n\nIl appelle `callback(null, script)` en cas de chargement réussi et `callback(error)` dans le cas contraire.\n\nL'utilisation:\n```js\nloadScript('/my/script.js', function(error, script) {\n  if (error) {\n    // erreur dans le chargement du script\n  } else {\n    // script chargé avec succès\n  }\n});\n```\n\nUne fois encore, la recette que nous avons utilisée pour `loadScript` est en fait assez commune. C'est le style \"error-first callback\".\n\nLa convention est:\n1. Le premier argument de la `callback` est réservé pour une erreur si elle se produit. Ensuite, `callback(err)` est appelé.\n2. Le deuxième argument (et les suivants si nécessaire) sont pour le résultat réussi. Ensuite, `callback(null, result1, result2...)` est appelé.\n\nAinsi, la fonction unique `callback` est utilisée à la fois pour signaler les erreurs et pour renvoyer les résultats.\n\n## Pyramide du malheur\n\nÀ première vue, il s'agit d'un moyen viable de codage asynchrone. Et c'est effectivement le cas. Pour un ou peut-être deux appels imbriqués, cela semble correct.\n\nMais pour de multiples actions asynchrones qui se succèdent, nous aurons un code comme celui-ci:\n\n```js\nloadScript('1.js', function(error, script) {\n\n  if (error) {\n    handleError(error);\n  } else {\n    // ...\n    loadScript('2.js', function(error, script) {\n      if (error) {\n        handleError(error);\n      } else {\n        // ...\n        loadScript('3.js', function(error, script) {\n          if (error) {\n            handleError(error);\n          } else {\n  *!*\n            // ...continue après que tous les scripts soient chargés (*)\n  */!*\n          }\n        });\n\n      }\n    });\n  }\n});\n```\n\nDans le code ci-dessus:\n1. Nous chargeons `1.js`, puis s'il n'y a pas d'erreur …\n2. Nous chargeons `2.js`, puis s'il n'y a pas d'erreur …\n3. Nous chargeons `3.js`, puis s'il n'y a pas d'erreur -- fait autre chose `(*)`.\n\nAu fur et à mesure que les appels deviennent plus imbriqués, le code devient plus profond et de plus en plus difficile à gérer, surtout si nous avons du vrai code au lieu de `...` qui peut inclure plus de boucles, des déclarations conditionnelles et ainsi de suite.\n\nC'est ce qu'on appelle parfois \"l'enfer du rappel\" ou \"la pyramide du malheur\".\n\n<!--\nloadScript('1.js', function(error, script) {\n  if (error) {\n    handleError(error);\n  } else {\n    // ...\n    loadScript('2.js', function(error, script) {\n      if (error) {\n        handleError(error);\n      } else {\n        // ...\n        loadScript('3.js', function(error, script) {\n          if (error) {\n            handleError(error);\n          } else {\n            // ...\n          }\n        });\n      }\n    });\n  }\n});\n-->\n\n![](callback-hell.svg)\n\nLa \"pyramide\" d'appels imbriqués croît vers la droite à chaque action asynchrone. Bientôt, elle devient incontrôlable.\n\nDonc cette façon de coder n'est pas très bonne.\n\nNous pouvons essayer d'atténuer le problème en faisant de chaque action une fonction autonome, comme ceci:\n\n```js\nloadScript('1.js', step1);\n\nfunction step1(error, script) {\n  if (error) {\n    handleError(error);\n  } else {\n    // ...\n    loadScript('2.js', step2);\n  }\n}\n\nfunction step2(error, script) {\n  if (error) {\n    handleError(error);\n  } else {\n    // ...\n    loadScript('3.js', step3);\n  }\n}\n\nfunction step3(error, script) {\n  if (error) {\n    handleError(error);\n  } else {\n    // ...continue après que tous les scripts soient chargés (*)\n  }\n}\n```\n\nVous voyez ? Il fait la même chose, et il n'y a pas d'imbrication profonde maintenant parce que nous avons fait de chaque action une fonction séparée de haut niveau.\n\nCela fonctionne, mais le code ressemble à une feuille de calcul déchirée. Il est difficile à lire, et vous avez probablement remarqué qu'il faut passer d'un morceau à l'autre en le lisant. Ce n'est pas pratique, surtout si le lecteur n'est pas familier avec le code et ne sait pas où sauter du regard.\n\nDe plus, les fonctions nommées `step*` sont toutes à usage unique, elles sont créées uniquement pour éviter la \"pyramide du malheur\". Personne ne va les réutiliser en dehors de la chaîne d'action. Il y a donc un peu d'encombrement de l'espace de noms ici.\n\nNous aimerions avoir quelque chose de mieux.\n\nHeureusement, il existe d'autres moyens d'éviter de telles pyramides. L'un des meilleurs moyens est d'utiliser des \"promesses\", décrites dans le chapitre suivant.\n"
  },
  {
    "path": "1-js/11-async/01-callbacks/one.js",
    "content": "function one() {\n  alert(1);\n}\n"
  },
  {
    "path": "1-js/11-async/02-promise-basics/01-re-resolve/solution.md",
    "content": "Le résultat est : `1`.\n\nLe second appel à `resolve` est ignoré, puisque seul le premier appel à `reject/resolve` est pris en compte. Les autres appels sont simplement ignorés."
  },
  {
    "path": "1-js/11-async/02-promise-basics/01-re-resolve/task.md",
    "content": "\n# Re-résoudre une promesse ?\n\n\nQuel est le résultat du code ci-dessous ?\n\n```js\nlet promise = new Promise(function(resolve, reject) {\n  resolve(1);\n\n  setTimeout(() => resolve(2), 1000);\n});\n\npromise.then(alert);\n```\n"
  },
  {
    "path": "1-js/11-async/02-promise-basics/02-delay-promise/solution.md",
    "content": "```js run\nfunction delay(ms) {\n  return new Promise(resolve => setTimeout(resolve, ms));\n}\n\ndelay(3000).then(() => alert('runs after 3 seconds'));\n```\n\nNotez bien que dans cette tâche, `resolve` est appelée sans arguments. Nous ne retournons aucune valeur de `delay`, nous nous assurons seulement du délai."
  },
  {
    "path": "1-js/11-async/02-promise-basics/02-delay-promise/task.md",
    "content": "\n# Un délai avec une promesse\n\nLa fonction de base `setTimeout` utilise des fonctions de retour. Créez une alternative avec une promesse.\n\nLa fonction `delay(ms)` doit retourner une promesse. Cette promesse doit s'acquitter après `ms` milliseconds, pour que l'on puisse ajouter `.then` à celle-ci, comme cela :\n\n```js\nfunction delay(ms) {\n  // votre code\n}\n\ndelay(3000).then(() => alert('runs after 3 seconds'));\n```\n"
  },
  {
    "path": "1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.md",
    "content": ""
  },
  {
    "path": "1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    .message-ball {\n      font-size: 20px;\n      line-height: 200px;\n      text-align: center;\n    }\n    .circle {\n      transition-property: width, height;\n      transition-duration: 2s;\n      position: fixed;\n      transform: translateX(-50%) translateY(-50%);\n      background-color: red;\n      border-radius: 50%;\n    }\n  </style>\n</head>\n\n<body>\n\n  <button onclick=\"go()\">Click me</button>\n\n  <script>\n\n  function go() {\n    showCircle(150, 150, 100).then(div => {\n      div.classList.add('message-ball');\n      div.append(\"Hello, world!\");\n    });\n  }\n\n  function showCircle(cx, cy, radius) {\n    let div = document.createElement('div');\n    div.style.width = 0;\n    div.style.height = 0;\n    div.style.left = cx + 'px';\n    div.style.top = cy + 'px';\n    div.className = 'circle';\n    document.body.append(div);\n\n    return new Promise(resolve => {\n      setTimeout(() => {\n        div.style.width = radius * 2 + 'px';\n        div.style.height = radius * 2 + 'px';\n\n        div.addEventListener('transitionend', function handler() {\n          div.removeEventListener('transitionend', handler);\n          resolve(div);\n        });\n      }, 0);\n    })\n  }\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "1-js/11-async/02-promise-basics/03-animate-circle-promise/task.md",
    "content": "\n# Animer un cercle avec une promesse\n\nRé-écrivez la fonction `showCircle` dans la solution de la tâche <info:task/animate-circle-callback> pour qu'elle renvoie une promesse au lieu d'une fonction de retour.\n\nLa nouvelle utilisation :\n\n```js\nshowCircle(150, 150, 100).then(div => {\n  div.classList.add('message-ball');\n  div.append(\"Hello, world!\");\n});\n```\n\nPrenez la solution de la tâche <info:task/animate-circle-callback> comme base.\n"
  },
  {
    "path": "1-js/11-async/02-promise-basics/article.md",
    "content": "# Promesse (promise)\n\nImaginez que vous êtes un grand chanteur et les fans vous demandent jour et nuit votre prochaine chanson.\n\nPour avoir un peu de paix, vous promettez de leur envoyer dès que celle-ci est publiée. Vous donnez à vos fans une liste d'abonnement. Ils peuvent y ajouter leur adresse mail, comme cela, quand le single est sorti, tous les emails reçoivent votre single. Et même si quelque chose arrive, comme un feu dans le studio, et que vous ne pouvez pas sortir le single, ils en seront aussi notifiés.\n\nTout le monde est content : vous, puisque l'on vous laisse plus tranquille, et vos fans parce qu'ils savent qu'ils ne rateront pas la chanson.\n\nC'est une analogie réelle à un problème courant de programmation :\n\n1. Un \"producteur de code\" qui réalise quelque chose mais nécessite du temps. Par exemple, un code qui charge des données à travers un réseau. C'est le \"chanteur\".\n2. Un \"consommateur de code\" qui attend un résultat du \"producteur de code\" quand il est prêt. Beaucoup de fonctions peuvent avoir besoin de ce résultat. Ces fonctions sont les \"fans\".\n3. Une *promesse* (promise) est un objet spécial en JavaScript qui lie le \"producteur de code\" et le \"consommateur de code\" ensemble. En comparant à notre analogie c'est la \"liste d'abonnement\". Le \"producteur de code\" prend le temps nécessaire pour produire le résultat promis, et la \"promesse\" donne le résultat disponible pour le code abonné quand c'est prêt.\n\n\nL'analogie n'est pas la plus correcte, car les promesses en JavaScript sont un peu plus complexes qu'une simple liste d'abonnement : elles ont d'autres possibilités mais aussi certaines limitations. Toutefois c'est suffisant pour débuter.\n\n\nLa syntaxe du constructeur pour une promesse est :\n\n```js\nlet promise = new Promise(function(resolve, reject) {\n  // L'exécuteur (le code produit, le \"chanteur\")\n});\n```\n\nLa fonction passée à `new Promise` est appelée l'*exécuteur*. Quand `new Promise` est créée, elle est lancée automatiquement. Elle contient le producteur de code, qui doit produire un résulat final. Dans l'analogie ci-dessus : l'exécuteur est le \"chanteur\".\n\nSes arguments `resolve` (tenir) et `reject` (rompre) sont les fonctions de retour directement fournies par JavaScript. Notre code est inclus seulement dans l'exécuteur.\n\nQuand l'exécuteur obtient un résultat, qu'il soit rapide ou pas, cela n'a pas d'importance, il appellera une des deux fonctions de retour :\n\n- `resolve(value)` -  si la tâche s'est terminée avec succès, avec le résultat `value`.\n- `reject(error)` - si une erreur est survenue, `error` est l'objet erreur.\n\nDonc, pour résumer : l'exécuteur s'exécute automatiquement et tente d’effectuer un travail. Ensuite, il devrait appeler `resolve` s'il a réussi ou `reject` s'il y avait une erreur.\n\nL'objet `promise` retourné par le constructeur `new Promise` a des propriétés internes :\n\n- `state` (état) - initialement à `\"pending\"` (en attente), se change soit en `\"fulfilled\"` (tenue) lorsque `resolve` est appelé ou `\"rejected\"` (rompue) si `reject` est appelé.\n- `result` - initialement à `undefined` se change en `value` quand `resolve(value)` est appelé ou en `error` quand `reject(error)` est appelé.\n\nAinsi l'exécuteur changera la promesse à un de ces états :\n\n![](promise-resolve-reject.svg)\n\nPlus tard nous verrons comment les \"fans\" peuvent s'abonner à ces changements.\n\nVoici un exemple d'un constructeur d'une promesse et d'une fonction exécutrice simple avec un \"code produit\" qui prend du temps (utilisant `setTimeout`) :\n\n```js run\nlet promise = new Promise(function(resolve, reject) {\n  // la fonction est exécutée automatiquement quand la promesse est construite\n\n  // On signale au bout d'une seconde que la tâche est terminée avec le résultat \"done\"\n  setTimeout(() => *!*resolve(\"done\")*/!*, 1000);\n});\n```\nOn peut voir deux choses en lançant le code ci-dessus :\n\n1. L'exécuteur est appelé automatiquement et immédiatement (avec `new Promise`).\n2. L'exécuteur reçoit deux arguments : `resolve` et `reject` - ces deux fonctions sont pré-définies par le moteur JavaScript, ainsi nous n'avons pas besoin de les créer. Nous devons seulement appeler l'une ou l'autre quand le résultat est prêt.\n\n    Après une seconde de \"traitement\" l'exécuteur appelle `resolve(\"done\")` pour produire le résultat. Cela change l'état de l'objet `promise` :\n\n    ![](promise-resolve-1.svg)\n\nNous avons vu un exemple d'une tâche terminée avec succès, une promesse \"tenue\".\n\nVoyons maintenant un exemple d'un exécuteur rompant la promesse avec une erreur :\n\n```js\nlet promise = new Promise(function(resolve, reject) {\n  // On signale après 1 seconde que la tâche est terminée avec une erreur\n  setTimeout(() => *!*reject(new Error(\"Whoops!\"))*/!*, 1000);\n});\n```\n\nL'appel a `reject(...)` change l'object promesse à l'état `\"rejected\"` :\n\n![](promise-reject-1.svg)\n\nPour résumer, l'exécuteur devrait réaliser une tâche (normalement quelque chose qui prend du temps) puis appelle `resolve` ou `reject` pour changer l'état de l'objet promesse correspondant.\n\nUne promesse qui est soit tenue soit rejetée est appelée \"settled\" (acquitttée) par opposition à une promesse initialisée à \"en attente\".\n\n````smart header=\"Il ne peut y avoir qu'un seul résultat ou une erreur\"\nL'exécuteur devrait appeler seulement une fois `resolve` ou `reject`. N'importe quel changement d'état est définitif.\n\nLes appels supplémentaires à `resolve` et `reject` sont ignorés :\n\n```js\nlet promise = new Promise(function(resolve, reject) {\n*!*\n  resolve(\"done\");\n*/!*\n\n  reject(new Error(\"…\")); // ignoré\n  setTimeout(() => resolve(\"…\")); // ignoré\n});\n```\n\nL'idée est que la tâche exécutée par un exécuteur ne peut avoir qu'un seul résultat ou une erreur.\n\nDe plus, `resolve`/`reject` n'attend qu'un seul argument (ou aucun) et ignorera les arguments suivants.\n````\n\n```smart header=\"Rompre avec l'objet `Error`\"\nDans le cas ou quelque chose se passe mal, l'exécuteur doit appeler `reject`. Cela est possible avec n'importe type d'argument (comme pour `resolve`). Mais  il est plutôt recommandé d'utiliser l'objet `Error` (ou les objets en héritant). La raison va vous paraître évidente dans un instant.\n```\n\n````smart header=\"Appel de `resolve`/`reject` immédiat\"\nEn pratique, un exécuteur réalise normalement une opération asynchrone et appelle `resolve`/`reject` après un certain temps, mais il n'est pas obligatoire d'être asynchrone. On peut aussi appeler immédiatement `resolve` ou `reject`, comme cela :\n\n```js\nlet promise = new Promise(function(resolve, reject) {\n  // La tâche ne prend pas de temps\n  resolve(123); // rend immédiatement le résultat : 123\n});\n```\n\nPar exemple, cela peut arriver quand nous commençons une tâche mais nous voyons que la tâche est déja réalisée et en cache.\n\nPas de soucis. Nous acquittons immédiatement la promesse.\n````\n\n```smart header=\"Le `state` et `result` est interne\"\nLes propriétés `state` et `result` de l'objet `Promise` sont internes. Nous ne pouvons directement accéder à celles-ci. Nous pouvons utiliser `.then`/`.catch`/`.finally` pour cela. Elles sont décrites ci-dessous.\n```\n\n## Les consommateurs : then, catch\n\nUn objet promesse permet le lien entre l'exécuteur (le \"code produit\" ou \"chanteur\") et les fonctions consommatrices (les \"fans\"), lesquels recevront un résultat ou une erreur. Ces fonctions consommatrices peuvent s'abonner (subscribed) en utilisant les méthodes `.then`, `.catch`.\n\n### then (alors)\n\nLe plus important, le plus crucial est `.then`.\n\nLa syntaxe est :\n\n```js\npromise.then(\n  function(result) { *!*/* gère un résultat correct */*/!* },\n  function(error) { *!*/* gère une erreur */*/!* }\n);\n```\n\nLe premier argument de `.then` est une fonction qui se lance si la promesse est tenue, et reçoit le résultat.\n\nLe deuxième argument de `.then` est une fonction qui se lance si la promesse est rompue, et reçoit l'erreur.\n\nPar exemple, voyons la réponse à une requête correctement tenue :\n\n```js run\nlet promise = new Promise(function(resolve, reject) {\n  setTimeout(() => resolve(\"done!\"), 1000);\n});\n\n// resolve lance la première fonction dans .then\npromise.then(\n*!*\n  result => alert(result), // affiche \"done!\" après 1 seconde\n*/!*\n  error => alert(error) // ne se lance pas\n);\n```\n\nLa première fonction s'est exécutée.\n\nEt dans le cas d'un rejet -- la deuxième seulement s'exécute :\n\n```js run\nlet promise = new Promise(function(resolve, reject) {\n  setTimeout(() => reject(new Error(\"Whoops!\")), 1000);\n});\n\n// reject lance la seconde fonction dans .then\npromise.then(\n  result => alert(result), // ne se lance pas\n*!*\n  error => alert(error) // affiche \"Error: Whoops!\" après 1 seconde\n*/!*\n);\n```\nSi nous sommes seulement intéressés par les promesses tenues, nous pouvons alors seulement fournir une fonction en argument à `.then` :\n\n```js run\nlet promise = new Promise(resolve => {\n  setTimeout(() => resolve(\"done!\"), 1000);\n});\n\n*!*\npromise.then(alert); // affiche \"done!\" après 1 seconde\n*/!*\n```\n\n### catch\n\nSi nous sommes seulement intéressés par les erreurs, alors nous pouvons mettre `null` comme premier argument : `.then(null, fonctionGerantLErreur)`. Ou nous pouvons utiliser `.catch(fonctionGerantLErreur)`, qui revient au même :\n\n```js run\nlet promise = new Promise((resolve, reject) => {\n  setTimeout(() => reject(new Error(\"Whoops!\")), 1000);\n});\n\n*!*\n// .catch(f) est similaire à promise.then(null, f)\npromise.catch(alert); // affiche \"Error: Whoops!\" après 1 seconde\n*/!*\n```\n\nL'appel à `.catch(f)` est complètement analogue à `.then(null, f)`, c'est juste un raccourci.\n\n## Cleanup: finally\n\nComme il y a un terme `finally` dans un `try {...} catch {...}`, il y a des `finally` dans les promesses.\n\nL'appel à `.finally(f)` est similaire à `.then(f, f)` dans le sens où `f` se lance toujours quand la promesse est aquittée : qu'elle soit tenue ou rompue.\n\nL'idée de `finally` est de configurer un gestionnaire pour effectuer le nettoyage/la finalisation une fois les opérations précédentes terminées.\n\nPar exemple l'arrêt des voyants de charge, la fermeture des connexions devenues inutiles, etc.\n\nConsidérez-le comme un nettoyeur de fête. Peu importe qu'une fête soit bonne ou mauvaise, combien d'amis y participaient, nous devons toujours (ou du moins devrions) faire un nettoyage après.\n\nLe code peut ressembler à ceci :\n\n```js\nnew Promise((resolve, reject) => {\n  /* faire quelque chose qui prend du temps, puis appeler resolve ou peut-être reject */\n})\n*!*\n  // se lance quand la promesse est acquittée, peu importe si celle-ci est tenue ou rompue\n  .finally(() => stop loading indicator)\n  // donc l'indicateur de chargement est toujours arrêté avant de continuer\n*/!*\n  .then(result => show result, err => show error)\n```\n\nVeuillez noter que `finally(f)` n'est pas exactement un alias de `then(f,f)`.\n\nIl existe des différences importantes :\n\n1. Un gestionnaire `finally` n'a pas d'arguments. Dans `finally` nous ne savons pas si la promesse est réussie ou non. Ce n'est pas grave, car notre tâche consiste généralement à effectuer des procédures de finalisation \"générales\".\n\n    Veuillez jeter un coup d'œil à l'exemple ci-dessus : comme vous pouvez le voir, le gestionnaire \"finally\" n'a pas d'arguments et le résultat de la promesse est géré par le gestionnaire suivant.\n2. Un gestionnaire \"finally\" \"transmet\" le résultat ou l'erreur au prochain gestionnaire approprié.\n\n    Par exemple, ici, le résultat est passé de `finally` à `then` :\n\n    ```js run\n    new Promise((resolve, reject) => {\n      setTimeout(() => resolve(\"value\"), 2000);\n    })\n      .finally(() => alert(\"Promise ready\")) // triggers first\n      .then(result => alert(result)); // <-- .then shows \"value\"\n    ```\n\n    Comme vous pouvez le voir, la `value` renvoyée par la première promesse est transmise par `finally` au prochain `then`.\n\n    C'est très pratique, car `finally` n'est pas destiné à traiter un résultat de promesse. Comme déjà dit, c'est un endroit pour faire un nettoyage générique, quel que soit le résultat.\n\n    Et voici un exemple d'erreur, pour que nous puissions voir comment elle est passée de `finally` à `catch` :\n\n    ```js run\n    new Promise((resolve, reject) => {\n      throw new Error(\"error\");\n    })\n      .finally(() => alert(\"Promise ready\")) // triggers first\n      .catch(err => alert(err));  // <-- .catch shows the error\n    ```\n\n3. Un gestionnaire `finally` ne devrait pas non plus renvoyer quoi que ce soit. Si c'est le cas, la valeur renvoyée est silencieusement ignorée.\n\n    La seule exception à cette règle est lorsqu'un gestionnaire `finally` génère une erreur. Ensuite, cette erreur passe au gestionnaire suivant, à la place de tout résultat précédent.\n\nPour résumer :\n\n- Un gestionnaire `finally` n'obtient pas le résultat du gestionnaire précédent (il n'a pas d'arguments). Ce résultat est transmis à la place au prochain gestionnaire approprié.\n- Si un gestionnaire `finally` renvoie quelque chose, il est ignoré.\n- Lorsque `finally` génère une erreur, l'exécution passe au gestionnaire d'erreurs le plus proche.\n\nCes fonctionnalités sont utiles et permettent aux choses de fonctionner correctement si nous utilisons `finally` comme elles sont censées être utilisées : pour les procédures de nettoyage génériques.\n\n````smart header=\"Nous pouvons attacher des gestionnaires aux promesses réglées\"\nSi une promesse est en attente, les gestionnaires `.then/catch/finally` attendent son résultat.\n\nParfois, il se peut qu'une promesse soit déjà réglée lorsque nous y ajoutons un gestionnaire.\n\nDans ce cas, ces gestionnaires s'exécutent immédiatement :\n\n```js run\n// la prommesse est acquittée immédiatement à la création\nlet promise = new Promise(resolve => resolve(\"done!\"));\n\npromise.then(alert); // done! (s'affiche immédiatement)\n```\n\nNotez que cela rend les promesses plus puissantes que le scénario réel de \"liste d'abonnement\". Si le chanteur a déjà sorti sa chanson et qu'une personne s'inscrit sur la liste d'abonnement, elle ne recevra probablement pas cette chanson. Les abonnements dans la vraie vie doivent être effectués avant l'événement.\n\nLes promesses sont plus flexibles. Nous pouvons ajouter des gestionnaires à tout moment : si le résultat est déjà là, ils s'exécutent simplement.\n````\n\n## Example: loadScript [#loadscript]\n\nEnsuite, voyons des exemples plus pratiques pour lesquels les promesses nous aident à écrire du code asynchrone.\n\nNous avons la fonction `loadScript` pour charger un script du chapitre précédent.\n\nPour rappel voyons la solution avec des fonctions de retour :\n\n```js\nfunction loadScript(src, callback) {\n  let script = document.createElement('script');\n  script.src = src;\n\n  script.onload = () => callback(null, script);\n  script.onerror = () => callback(new Error(`Script load error for ${src}`));\n\n  document.head.append(script);\n}\n```\n\nRe-écrivons-la avec une promesse.\n\nLa nouvelle fonction `loadScript` ne nécessite aucune fonction de retour. À la place, elle va créer et retournera une promesse qui s'acquittera lorque le chargement sera complet. Le code externe peut ajouter des gestionnaires (fonction s'abonnant) à celle-ci en utilisant `.then`.\n\n```js run\nfunction loadScript(src) {\n  return new Promise(function(resolve, reject) {\n    let script = document.createElement('script');\n    script.src = src;\n\n    script.onload = () => resolve(script);\n    script.onerror = () => reject(new Error(`Script load error for ${src}`));\n\n    document.head.append(script);\n  });\n}\n```\n\nUtilisation:\n\n```js run\nlet promise = loadScript(\"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js\");\n\npromise.then(\n  script => alert(`${script.src} is loaded!`),\n  error => alert(`Error: ${error.message}`)\n);\n\npromise.then(script => alert('Another handler...'));\n```\n\nOn peut remarquer immédiatement quelques avantages par rapport aux fonctions de retour :\n\n| Promesses | Fonctions de retour |\n|-----------|----------------------|\n| Les promesses nous permettent de faire des choses dans un ordre naturel. D'abord, nous lançons `loadScript(script)`, puis avec `.then` nous codons quoi faire avec le résultat. | Nous devons avoir une fonction de retour à notre disposition quand nous appelons `loadScript(script, callback)`. En d'autres termes, nous devons savoir quoi faire du résultat *avant* que `loadScript` soit appelé. |\n| Nous pouvons appeler `.then` sur une promesse autant de fois que nécessaire. À chaque fois, que nous ajoutons un nouveau \"fan\", une nouvelle fonction s'abonne à la \"liste d'abonnés\". Nous en verrons plus à ce sujet dans le prochain chapitre : [](info:promise-chaining). | Il ne peut y avoir qu'une seule fonction de retour. |\n\nLes promesses nous permettent donc d'avoir plus de sens et une meilleure flexibilité. Mais il y a plus. Nous allons voir cela dans les chapitres suivants.\n"
  },
  {
    "path": "1-js/11-async/02-promise-basics/head.html",
    "content": "<script>\nfunction loadScript(src) {\n  return new Promise(function(resolve, reject) {\n    let script = document.createElement('script');\n    script.src = src;\n\n    script.onload = () => resolve(script);\n    script.onerror = () => reject(new Error(\"Script load error: \" + src));\n\n    document.head.append(script);\n  });\n}\n</script>\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/01-then-vs-catch/solution.md",
    "content": "La réponse courte est : **non, ils ne sont pas égaux** :\n\nLa différence est que si une erreur survient dans `f1`, elle est gérée par `.catch` ici :\n\n```js run\npromise\n  .then(f1)\n  .catch(f2);\n```\n\n...Mais pas ici:\n\n```js run\npromise\n  .then(f1, f2);\n```\n\nEn effet, une erreur est transmise dans la chaîne et, dans le second code, il n'y a pas de chaîne à la suite de `f1`.\n\nEn d'autres termes, `.then` transmet les résultats/erreurs au prochain `.then/catch`. Donc, dans le premier exemple, il y a un `catch` en dessous, et dans le second - il n'y en a pas, donc l'erreur n'est pas gérée.\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md",
    "content": "# Promesse: then contre catch\n\nCes fragments de code sont-ils égaux? En d'autres termes, se comportent-ils de la même manière en toutes circonstances, pour toutes les fonctions gestionnaires?\n\n```js\npromise.then(f1).catch(f2);\n```\n\nContre:\n\n```js\npromise.then(f1, f2);\n```\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/article.md",
    "content": "\n# Chaînage des promesses\n\nRevenons au problème mentionné dans le chapitre <info:callbacks>: nous avons une séquence de tâches asynchrones à effectuer l'une après l'autre. Par exemple, charger des scripts. Comment pouvons-nous bien le coder ?\n\nLes promesses fournissent quelques options pour le faire.\n\nDans ce chapitre, nous traitons de l'enchaînement des promesses.\n\nCela ressemble à ceci :\n\n```js run\nnew Promise(function(resolve, reject) {\n\n  setTimeout(() => resolve(1), 1000); // (*)\n\n}).then(function(result) { // (**)\n\n  alert(result); // 1\n  return result * 2;\n\n}).then(function(result) { // (***)\n\n  alert(result); // 2\n  return result * 2;\n\n}).then(function(result) {\n\n  alert(result); // 4\n  return result * 2;\n\n});\n```\n\nL'idée est que le résultat est transmis à travers la chaîne de gestionnaires `.then`.\n\nIci, le flux est :\n\n1. La promesse initiale est résolue en 1 seconde `(*)`,\n2. Ensuite, le gestionnaire `.then` est appelé `(**)`, qui à son tour crée une nouvelle promesse (résolue avec la valeur `2`).\n3. Le `then` suivant `(***)` obtient le résultat du précédent, le traite (double) et le passe au gestionnaire suivant.\n4. ...et ainsi de suite.\n\nLorsque le résultat est transmis le long de la chaîne de gestionnaires, nous pouvons voir une séquence d'appels `alert`: `1` -> `2` -> `4`.\n\n![](promise-then-chain.svg)\n\nLe tout fonctionne, parce qu’un appel à `.then` renvoie une nouvelle promesse, de sorte que nous puissions appeler le prochain `.then` dessus.\n\nLorsqu'un gestionnaire renvoie une valeur, cela devient le résultat de cette promesse. Le prochain `.then` est appelé avec.\n\n**Une erreur classique pour les débutants: techniquement, nous pouvons également ajouter plusieurs `.then` à une seule promesse. Ceci n'est pas le chaînage des promesses.**\n\nPar example :\n\n```js run\nlet promise = new Promise(function(resolve, reject) {\n  setTimeout(() => resolve(1), 1000);\n});\n\npromise.then(function(result) {\n  alert(result); // 1\n  return result * 2;\n});\n\npromise.then(function(result) {\n  alert(result); // 1\n  return result * 2;\n});\n\npromise.then(function(result) {\n  alert(result); // 1\n  return result * 2;\n});\n```\n\nCe que nous avons fait ici n’est qu'ajouter plusieurs gestionnaires pour une promesse. Ils ne se transmettent pas le résultat, ils la traitent de manière indépendante.\n\nVoici la representation (comparez-la avec l'enchaînement ci-dessus):\n\n![](promise-then-many.svg)\n\nTous les `.then` sur la même promesse obtiennent le même résultat - le résultat de cette promesse. Donc, dans le code ci-dessus, les `alert` montre la même chose : `1`.\n\nEn pratique, nous avons rarement besoin de plusieurs gestionnaires pour une même promesse. Le chaînage est utilisé beaucoup plus souvent.\n\n## Renvoie de promesses\n\nUn gestionnaire, utilisé dans `.then(handler)` peut créer et renvoyer une promesse.\n\nDans ce cas, les autres gestionnaires attendent que le problème soit réglé, puis le résultat est obtenu.\n\nPar exemple:\n\n```js run\nnew Promise(function(resolve, reject) {\n\n  setTimeout(() => resolve(1), 1000);\n\n}).then(function(result) {\n\n  alert(result); // 1\n\n*!*\n  return new Promise((resolve, reject) => { // (*)\n    setTimeout(() => resolve(result * 2), 1000);\n  });\n*/!*\n\n}).then(function(result) { // (**)\n\n  alert(result); // 2\n\n  return new Promise((resolve, reject) => {\n    setTimeout(() => resolve(result * 2), 1000);\n  });\n\n}).then(function(result) {\n\n  alert(result); // 4\n\n});\n```\n\nIci, le premier `.then` affiche `1` et renvoie `new Promise(…)` à la ligne `(*)`. Au bout d'une seconde c'est résolu et le résultat (l'argument de `resolve`, ici, `result * 2`) est transmis au gestionnaire du deuxième `.then`. Ce gestionnaire est à la ligne `(**)`, il affiche `2` et fait la même chose.\n\nLe résultat est donc le même que dans l'exemple précédent: 1 -> 2 -> 4, mais maintenant avec un délai d'une seconde entre les appels `alert`.\n\nLe renvoie des promesses nous permet de construire des chaînes d’actions asynchrones.\n\n## Exemple: loadScript\n\nUtilisons cette fonctionnalité avec le `loadScript` promisifié, défini dans le [chapitre précédent](info:promise-basics#loadscript), afin de charger les scripts un à un, dans l'ordre:\n\n```js run\nloadScript(\"/article/promise-chaining/one.js\")\n  .then(function(script) {\n    return loadScript(\"/article/promise-chaining/two.js\");\n  })\n  .then(function(script) {\n    return loadScript(\"/article/promise-chaining/three.js\");\n  })\n  .then(function(script) {\n    // utiliser les fonctions déclarées dans les scripts\n    // pour montrer qu'ils ont effectivement chargé\n    one();\n    two();\n    three();\n  });\n```\n\nCe code peut être un peu plus court avec les fonctions fléchées:\n\n```js run\nloadScript(\"/article/promise-chaining/one.js\")\n  .then(script => loadScript(\"/article/promise-chaining/two.js\"))\n  .then(script => loadScript(\"/article/promise-chaining/three.js\"))\n  .then(script => {\n    // les scripts sont chargés, on peut utiliser les fonctions qui y sont déclarées\n    one();\n    two();\n    three();\n  });\n```\n\n\nIci, chaque appel à `loadScript` renvoie une promesse et le prochain `.then` s'exécute lorsqu'il est résolu. Ensuite, il lance le chargement du script suivant. Les scripts sont donc chargés les uns après les autres.\n\nNous pouvons ajouter plus d'actions asynchrones à la chaîne. Noter que le code est toujours \"plat\", il grandit verticallement, pas vers la droite. Il n'y a aucun signe de \"pyramid of doom\".\n\nTechniquement, nous pourrions ajouter `.then` directement à chaque `loadScript`, comme ceci:\n\n```js run\nloadScript(\"/article/promise-chaining/one.js\").then(script1 => {\n  loadScript(\"/article/promise-chaining/two.js\").then(script2 => {\n    loadScript(\"/article/promise-chaining/three.js\").then(script3 => {\n      // cette fonction a accès aux variables script1, script2 et script3\n      one();\n      two();\n      three();\n    });\n  });\n});\n```\n\nCe code fait la même chose: charge 3 scripts en séquence. Mais il \"pousse vers la droite\". Nous avons donc le même problème qu'avec les callbacks.\n\nLes gens qui commencent à utiliser leurs promesses ne savent parfois pas comment enchaîner, alors ils l'écrivent de cette façon. Généralement, le chaînage est préféré.\n\nParfois, il est correct d'écrire directement `.then`, car la fonction imbriquée a accès à la portée externe. Dans l'exemple ci-dessus, le rappel le plus imbriqué a accès à toutes les variables `script1`, `script2`, `script3`. Mais c'est une exception plutôt qu'une règle.\n\n\n````smart header=\"Thenables\"\nPour être précis, un gestionnaire peut renvoyer pas exactement une promesse, mais un soi-disant objet \"thenable\" - un objet arbitraire doté de la méthode `.then`. Il sera traité de la même manière q'une promesse.\n\nL'idée est que les bibliothèques tierces peuvent implémenter leurs propres objets \"compatibles avec les promesses\". Elles peuvent avoir un ensemble étendu de méthodes, mais aussi être compatibles avec les promesses natives, car ils implémentent `.then`.\n\nVoici un exemple d'un objet \"thenable\":\n\n```js run\nclass Thenable {\n  constructor(num) {\n    this.num = num;\n  }\n  then(resolve, reject) {\n    alert(resolve); // function() { native code }\n    // promesse tenu avec this.num * 2 après 1 seconde\n    setTimeout(() => resolve(this.num * 2), 1000); // (**)\n  }\n}\n\nnew Promise(resolve => resolve(1))\n  .then(result => {\n*!*\n    return new Thenable(result); // (*)\n*/!*\n  })\n  .then(alert); // shows 2 after 1000ms\n```\n\nJavaScript vérifie l'objet retourné par le gestionnaire `.then` à la ligne `(*)` : s il a une méthode appelable nommé `then`, il appelle cette méthode fournissant les fonctions natives `resolve` et `reject` comme arguments (semblable à un executeur) et attend que l’un d’eux soit appelé. Dans l'exemple ci-dessus, `resolve(2)` est appelé après 1 seconde `(**)`. Ensuite, le résultat est transmis plus loin dans la chaîne.\n\nCette fonctionnalité nous permet d'intégrer des objets personnalisés avec des chaînes de promesse sans avoir à hériter de `Promise`.\n````\n\n\n## Un plus grand exemple: fetch\n\nDans la programmation du front-end, les promesses sont souvent utilisées pour les requêtes réseau. Voyons donc un exemple étendu de cela.\n\nNous allons utiliser la méthode [fetch](info:fetch) pour charger les informations sur l'utilisateur à partir du serveur distant. Il a beaucoup de paramètres optionnels couverts dans [des chapitres séparés](info:fetch), mais la syntaxe de base est assez simple:\n\n```js\nlet promise = fetch(url);\n```\n\nCela fait une requête réseau à la `url` et renvoie une promesse. La promesse se résout avec un objet `response` lorsque le serveur distant répond avec des en-têtes, mais *avant le téléchargement complet de la réponse*.\n\nPour lire la réponse complète, nous devons appeler la méthode `response.text()` : elle renvoie une promesse qui résout le téléchargement du texte intégral à partir du serveur distant, avec ce texte en tant que résultat.\n\nLe code ci-dessous envoie une requête à `user.json` et charge son texte depuis le serveur:\n\n```js run\nfetch('/article/promise-chaining/user.json')\n  // .then ci-dessous s'exécute lorsque le serveur distant répond\n  .then(function(response) {\n    // response.text() renvoie une nouvelle promesse qui résout avec le texte de réponse complet\n    // quand ça charge\n    return response.text();\n  })\n  .then(function(text) {\n    // ...et voici le contenu du fichier distant\n    alert(text); // {\"name\": \"iliakan\", \"isAdmin\": true}\n  });\n```\n\nL'objet `response` renvoyé par `fetch` comprend également la méthode `response.json()` qui lit les données distantes et les analyse en JSON. Dans notre cas, c'est encore plus pratique, alors passons-y.\n\nNous allons également utiliser les fonctions fléchées pour la brièveté:\n\n```js run\n// comme ci-dessus, mais response.json() analyse le contenu distant en tant que JSON\nfetch('/article/promise-chaining/user.json')\n  .then(response => response.json())\n  .then(user => alert(user.name)); // iliakan, nom d'utilisateur obtenu\n```\n\nMaintenant faisons quelque chose avec l'utilisateur chargé.\n\nPar exemple, nous pouvons faire une demande supplémentaire à GitHub, charger le profil de l'utilisateur et afficher l'avatar:\n\n```js run\n// Faire une demande pour user.json\nfetch('/article/promise-chaining/user.json')\n  // Charger en tant que json\n  .then(response => response.json())\n  // Faire une demande à GitHub\n  .then(user => fetch(`https://api.github.com/users/${user.name}`))\n  // Charger la réponse en tant que json\n  .then(response => response.json())\n  // Afficher l'image de l'avatar (githubUser.avatar_url) pendant 3 secondes (peut-être l'animer)\n  .then(githubUser => {\n    let img = document.createElement('img');\n    img.src = githubUser.avatar_url;\n    img.className = \"promise-avatar-example\";\n    document.body.append(img);\n\n    setTimeout(() => img.remove(), 3000); // (*)\n  });\n```\n\nLe code fonctionne ; voir les commentaires à propos des détails. Pourtant, il y a un problème potentiel, une erreur typique de ceux qui commencent à utiliser les promesses.\n\nRegardez la ligne `(*)`: comment pouvons-nous faire quelque chose *après* l'avatar a fini d'afficher et d'être supprimé? Par exemple, nous aimerions montrer un formulaire pour éditer cet utilisateur ou autre chose. Pour l'instant, il n'y a pas moyen.\n\nPour rendre la chaîne extensible, nous devons retourner une promesse qui sera résolue une fois que l'avatar aura fini de s'afficher.\n\nComme ceci:\n\n```js run\nfetch('/article/promise-chaining/user.json')\n  .then(response => response.json())\n  .then(user => fetch(`https://api.github.com/users/${user.name}`))\n  .then(response => response.json())\n*!*\n  .then(githubUser => new Promise(function(resolve, reject) { // (*)\n*/!*\n    let img = document.createElement('img');\n    img.src = githubUser.avatar_url;\n    img.className = \"promise-avatar-example\";\n    document.body.append(img);\n\n    setTimeout(() => {\n      img.remove();\n*!*\n      resolve(githubUser); // (**)\n*/!*\n    }, 3000);\n  }))\n  // se déclenche après 3 secondes\n  .then(githubUser => alert(`Finished showing ${githubUser.name}`));\n```\n\nEn d’autres termes, le gestionnaire `.then` à la ligne `(*)` renvoie `new Promise`, qui ne sera réglé qu’après l’appel de `resolve(githubUser)` dans `setTimeout` `(**)`. Le prochain `.then` dans la chaîne attendra cela.\n\nComme bonne pratique, une action asynchrone doit toujours renvoyer une promesse. Cela permet de planifier des actions après. Même si nous n'avons pas l'intention d'étendre la chaîne maintenant, nous en aurons peut-être besoin plus tard.\n\nEnfin, nous pouvons scinder le code en fonctions réutilisables:\n\n```js run\nfunction loadJson(url) {\n  return fetch(url)\n    .then(response => response.json());\n}\n\nfunction loadGithubUser(name) {\n  return loadJson(`https://api.github.com/users/${name}`);\n}\n\nfunction showAvatar(githubUser) {\n  return new Promise(function(resolve, reject) {\n    let img = document.createElement('img');\n    img.src = githubUser.avatar_url;\n    img.className = \"promise-avatar-example\";\n    document.body.append(img);\n\n    setTimeout(() => {\n      img.remove();\n      resolve(githubUser);\n    }, 3000);\n  });\n}\n\n// Utilise les:\nloadJson('/article/promise-chaining/user.json')\n  .then(user => loadGithubUser(user.name))\n  .then(showAvatar)\n  .then(githubUser => alert(`Finished showing ${githubUser.name}`));\n  // ...\n```\n\n## Résumé\n\nSi un gestionnaire `.then` (ou `catch/finally`, peu importe) renvoie une promesse, le reste de la chaîne attend jusqu'à ce qu'elle se règle. Quand cela se produit, son résultat (ou son erreur) est passé plus loin.\n\nVoici une image complète:\n\n![](promise-handler-variants.svg)\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/getMessage.js",
    "content": "function getMessage() {\n  return \"Hello, world!\";\n}\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/head.html",
    "content": "<script>\nfunction loadScript(src) {\n  return new Promise(function(resolve, reject) {\n    let script = document.createElement('script');\n    script.src = src;\n\n    script.onload = () => resolve(script);\n    script.onerror = () => reject(new Error(\"Script load error: \" + src));\n\n    document.head.append(script);\n  });\n}\n</script>\n\n<style>\n.promise-avatar-example {\n  border-radius: 50%;\n  position: fixed;\n  left: 10px;\n  top: 10px;\n}\n</style>\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/one.js",
    "content": "function one() {\n  alert(1);\n}\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/three.js",
    "content": "function three() {\n  alert(3);\n}\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/two.js",
    "content": "function two() {\n  alert(2);\n}\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/user.json",
    "content": "{\n  \"name\": \"iliakan\",\n  \"isAdmin\": true\n}\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/01-error-async/solution.md",
    "content": "La réponse est: **Non, cela n'arrivera pas:**:\n\n```js run\nnew Promise(function(resolve, reject) {\n  setTimeout(() => {\n    throw new Error(\"Whoops!\");\n  }, 1000);\n}).catch(alert);\n```\n\nComme décrit dans le chapitre, il y a un \"`try..catch` implicite\" autour du code de la fonction. Toutes les erreurs synchrones sont donc traitées.\n\nMais ici, l'erreur n'est pas générée pendant l'exécution de l'exécuteur, mais plus tard. Donc la promesse ne peut pas tenir. \n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/01-error-async/task.md",
    "content": "# Erreur dans setTimeout\n\nQu'en pensez-vous ? Est-ce que le `.catch` va se déclencher ? Expliquez votre réponse.\n\n```js\nnew Promise(function(resolve, reject) {\n  setTimeout(() => {\n    throw new Error(\"Whoops!\");\n  }, 1000);\n}).catch(alert);\n```\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/article.md",
    "content": "\n# Gestion des erreurs avec des promesses\n\nLes chaînes de promesses sont excellentes pour la gestion des erreurs. Lorsqu'une promesse est rejetée, le contrôle saute au gestionnaire de rejet le plus proche. C'est très pratique en pratique.\n\nPar exemple, dans le code en dessous de l'URL de `fetch` est faux (aucun site de ce type) et `.catch` gère l'erreur :\n\n```js run\n*!*\nfetch('https://no-such-server.blabla') // rejets\n*/!*\n  .then(response => response.json())\n  .catch(err => alert(err)) // TypeError: failed to fetch (le texte peut varier)\n```\n\nComme vous pouvez le voir, le `.catch` n'a pas besoin d'être immédiat. Il peut apparaître après un ou peut-être plusieurs `.then`.\n\nOu, peut-être, que tout va bien avec le site, mais la réponse JSON n'est pas valide. La façon la plus simple d'attraper toutes les erreurs est d'ajouter `.catch` à la fin de la chaîne :\n\n```js run\nfetch('/article/promise-chaining/user.json')\n  .then(response => response.json())\n  .then(user => fetch(`https://api.github.com/users/${user.name}`))\n  .then(response => response.json())\n  .then(githubUser => new Promise((resolve, reject) => {\n    let img = document.createElement('img');\n    img.src = githubUser.avatar_url;\n    img.className = \"promise-avatar-example\";\n    document.body.append(img);\n\n    setTimeout(() => {\n      img.remove();\n      resolve(githubUser);\n    }, 3000);\n  }))\n*!*\n  .catch(error => alert(error.message));\n*/!*\n```\n\nNormalement, un tel `.catch` ne se déclenche pas du tout. Mais si l'une des promesses ci-dessus rejette (un problème de réseau, un json invalide ou autre), alors il l'attraperait.\n\n## try..catch implicite\n\nLe code d'un exécuteur de promesses et d'un gestionnaire de promesses est entouré d'un \"`try...catch` invisible\". Si une exception se produit, elle est prise en compte et traitée comme un rejet.\n\nPar exemple, ce code:\n\n```js run\nnew Promise((resolve, reject) => {\n*!*\n  throw new Error(\"Whoops!\");\n*/!*\n}).catch(alert); // Error: Whoops!\n```\n\n...Fonctionne exactement de la même façon que ceci:\n\n```js run\nnew Promise((resolve, reject) => {\n*!*\n  reject(new Error(\"Whoops!\"));\n*/!*\n}).catch(alert); // Error: Whoops!\n```\n\nLe \"`try..catch` invisible\" autour de l'exécuteur attrape automatiquement l'erreur et la transforme en promesse rejetée.\n\nCela se produit non seulement dans la fonction exécuteur, mais aussi dans ses gestionnaires. Si nous utilisons `throw` à l'intérieur d'un gestionnaire `.then', cela signifie une promesse rejetée, donc le contrôle saute au gestionnaire d'erreur le plus proche.\n\nEn voici un exemple:\n\n```js run\nnew Promise((resolve, reject) => {\n  resolve(\"ok\");\n}).then((result) => {\n*!*\n  throw new Error(\"Whoops!\"); // rejette la promesse\n*/!*\n}).catch(alert); // Error: Whoops!\n```\n\nCela se produit pour toutes les erreurs, pas seulement celles causées par l'état `throw`. Par exemple, une erreur de programmation :\n\n```js run\nnew Promise((resolve, reject) => {\n  resolve(\"ok\");\n}).then((result) => {\n*!*\n  blabla(); // aucune fonction de ce type\n*/!*\n}).catch(alert); // ReferenceError: blabla is not defined\n```\n\nLe `.catch` final n'attrape pas seulement les rejets explicites, mais aussi les erreurs occasionnelles dans les gestionnaires ci-dessus.\n\n## Renouvellement\n\nComme nous l'avons déjà remarqué, `.catch` à la fin de la chaîne est similaire à `try...catch`. Nous pouvons avoir autant de gestionnaires `.then` que nous le voulons, puis utiliser un seul `.catch` à la fin pour gérer les erreurs dans chacun d'eux.\n\nDans un `try...catch` classique nous pouvons analyser l'erreur et peut-être la relancer si nous ne pouvons pas la gérer. La même chose est possible pour les promesses.\n\nSi nous utilisons `throw` dans `.catch`, alors le contrôle passe au gestionnaire d'erreur suivant qui est plus proche. Et si nous gérons l'erreur et finissons normalement, alors elle continue jusqu'au gestionnaire `.then` le plus proche.\n\nIn the example below the `.catch` successfully handles the error:\n\n```js run\n// l'exécution: catch -> then\nnew Promise((resolve, reject) => {\n\n  throw new Error(\"Whoops!\");\n\n}).catch(function(error) {\n\n  alert(\"The error is handled, continue normally\");\n\n}).then(() => alert(\"Next successful handler runs\"));\n```\n\nIci, le bloc `.catch` se termine normalement. Le prochain gestionnaire `.then` réussi est donc appelé.\n\nDans l'exemple ci-dessous nous voyons l'autre situation avec `.catch`. Le gestionnaire `(*)` attrape l'erreur et ne peut tout simplement pas la gérer (par ex: il sait seulement comment gérer `URIError`), donc il la relance:\n\n```js run\n// l'exécution: catch -> catch \nnew Promise((resolve, reject) => {\n\n  throw new Error(\"Whoops!\");\n\n}).catch(function(error) { // (*)\n\n  if (error instanceof URIError) {\n    // handle it\n  } else {\n    alert(\"Can't handle such error\");\n\n*!*\n    throw error; // lancer cette erreur ou une autre saute au prochain catch.\n*/!*\n  }\n\n}).then(function() {\n  /* ne s'exécute pas ici */\n}).catch(error => { // (**)\n\n  alert(`The unknown error has occurred: ${error}`);\n  // ne retourne rien => l'exécution se déroule normalement\n\n});\n```\n\nThe execution jumps from the first `.catch` `(*)` to the next one `(**)` down the chain.\n\n## Rejets non traités\n\nQue se passe-t-il lorsqu'une erreur n'est pas traitée ? Par exemple, nous avons oublié d'ajouter `.catch` à la fin de la chaîne, comme ici :\n\n```js untrusted run refresh\nnew Promise(function() {\n  noSuchFunction(); // Erreur ici (aucune fonction de ce type)\n})\n  .then(() => {\n    // gestionnaires de promesses réussit, une ou plus\n  }); // sans .catch à la fin!\n```\n\nEn cas d'erreur, la promesse est rejetée et l'exécution doit passer au gestionnaire de rejet le plus proche. Mais il n'y en a pas. L'erreur est donc \"coincée\". Il n'y a pas de code pour le gérer.\n\nEn pratique, tout comme pour les erreurs régulières qui sont non gérées dans le code, cela signifie que quelque chose a très mal tourné.\n\nQue se passe-t-il lorsqu'une erreur régulière se produit et n'est pas détectée par `try...catch` ? Le script meurt avec un message dans la console. Il se produit la même chose lors du rejet de promesses non tenues.\n\nLe moteur JavaScript suit ces rejets et génère une erreur globale dans ce cas. Vous pouvez le voir dans la console si vous exécutez l'exemple ci-dessus.\n\nDans le navigateur, nous pouvons détecter de telles erreurs en utilisant l'événement `unhandledrejection`:\n\n```js run\n*!*\nwindow.addEventListener('unhandledrejection', function(event) {\n  // l'objet event possède deux propriétés spéciales:\n  alert(event.promise); // [object Promise] - la promesse qui a généré l'erreur\n  alert(event.reason); // Error: Whoops! - l'objet d'erreur non géré\n});\n*/!*\n\nnew Promise(function() {\n  throw new Error(\"Whoops!\");\n}); // no catch to handle the error\n```\n\nL'événement fait partie des [standards HTML](https://html.spec.whatwg.org/multipage/webappapis.html#unhandled-promise-rejections) *(en anglais)*.\n\nSi une erreur se produit, et qu'il n'y a pas de `.catch`, le gestionnaire `unhandledrejection` se déclenche, et reçoit l'objet `event` avec les informations sur l'erreur, donc nous pouvons faire quelque chose.\n\nHabituellement, de telles erreurs sont irrécupérables, donc notre meilleure solution est d'informer l'utilisateur à propos du problème et probablement de signaler l'incident au serveur.\n\nDans les environnements sans navigateur comme Node.js, il existe d'autres moyens de suivre les erreurs non gérées.\n\n## Résumé\n\n- `.catch` gère les erreurs dans les promesses de toutes sortes : qu'il s'agisse d'un appel `reject()`, ou d'une erreur lancée dans un gestionnaire.\n- `.then` intercepte également les erreurs de la même manière, si on lui donne le deuxième argument (qui est le gestionnaire d'erreurs).\n- Nous devrions placer `.catch` exactement aux endroits où nous voulons traiter les erreurs et savoir comment les traiter. Le gestionnaire doit analyser les erreurs (les classes d'erreurs personnalisées aident) et relancer les erreurs inconnues (ce sont peut-être des erreurs de programmation).\n- C'est acceptable de ne pas utiliser `.catch` du tout, s'il n'y a aucun moyen de récupérer d'une erreur.\n- Dans tous les cas, nous devrions avoir le gestionnaire d'événements `unhandledrejection` (pour les navigateurs, et les analogues pour les autres environnements), pour suivre les erreurs non gérées et informer l'utilisateur (et probablement notre serveur) à leur sujet, afin que notre application ne \"meurt jamais\".\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/getMessage.js",
    "content": "function getMessage() {\n  return \"Hello, world!\";\n}\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/head.html",
    "content": "<script>\nclass HttpError extends Error {\n  constructor(response) {\n    super(`${response.status} for ${response.url}`);\n    this.name = 'HttpError';\n    this.response = response;\n  }\n}\n\nfunction loadJson(url) {\n  return fetch(url)\n    .then(response => {\n      if (response.status == 200) {\n        return response.json();\n      } else {\n        throw new HttpError(response);\n      }\n    })\n}\n</script>\n\n<style>\n.promise-avatar-example {\n  border-radius: 50%;\n  position: fixed;\n  left: 10px;\n  top: 10px;\n}\n</style>\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/one.js",
    "content": "function one() {\n  alert(1);\n}\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/three.js",
    "content": "function three() {\n  alert(3);\n}\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/two.js",
    "content": "function two() {\n  alert(2);\n}\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/user.json",
    "content": "{\n  \"name\": \"iliakan\",\n  \"isAdmin\": true\n}\n"
  },
  {
    "path": "1-js/11-async/05-promise-api/article.md",
    "content": "# Promesse API\n\nIl y a 6 méthodes statiques dans la classe `Promise`. Nous allons rapidement couvrir leurs usages ici.\n\n## Promise.all\n\nDisons que nous voulons exécuter de nombreuses promesses en parallèle, et attendre qu'elles soient toutes prêtes.\n\nPar exemple, téléchargez plusieurs URLs en parallèle et traitez le contenu lorsque tout est terminé.\n\nC'est à cela que sert `Promise.all`.\n\nLa syntaxe est:\n\n```js\nlet promise = Promise.all(iterable);\n```\n\n`Promise.all` prend un itérable (généralement un tableau de promesses) et renvoie une nouvelle promesse.\n\nLa nouvelle promesse est résolue lorsque toutes les promesses énumérées sont résolues et que le tableau de leurs résultats devient son résultat.\n\nPar exemple, le `Promise.all` ci-dessous se règle après 3 secondes, et ensuite son résultat est un tableau `[1, 2, 3]`:\n\n```js run\nPromise.all([\n  new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1\n  new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2\n  new Promise(resolve => setTimeout(() => resolve(3), 1000))  // 3\n]).then(alert); // 1,2,3 quand les promesses sont prêtes : chaque promesse apporte un élément du tableau\n```\n\nVeuillez noter que l'ordre des éléments du tableau résultant est le même que celui des promesses sources. Même si la première promesse prend le plus de temps à se résoudre, elle est toujours la première dans le tableau des résultats.\n\nUne astuce courante consiste à mapper un tableau de données de tâches dans un tableau de promesses, puis à l'intégrer dans `Promise.all`.\n\nPar exemple, si nous avons un tableau d'URLs, nous pouvons tous les récupérer comme ceci:\n\n```js run\nlet urls = [\n  'https://api.github.com/users/iliakan',\n  'https://api.github.com/users/remy',\n  'https://api.github.com/users/jeresig'\n];\n\n// mappe chaque url à la promesse du fetch\nlet requests = urls.map(url => fetch(url));\n\n// Promise.all attend jusqu'à ce que toutes les tâches soient résolues\nPromise.all(requests)\n  .then(responses => responses.forEach(\n    response => alert(`${response.url}: ${response.status}`)\n  ));\n```\n\nVoici un plus gros exemple avec la récupération des informations des utilisateurs GitHub dans un tableau, par leurs noms (nous pourrions récupérer un tableau d'informations par leurs identifiants, la logique est la même) :\n\n```js run\nlet names = ['iliakan', 'remy', 'jeresig'];\n\nlet requests = names.map(name => fetch(`https://api.github.com/users/${name}`));\n\nPromise.all(requests)\n  .then(responses => {\n    // toutes les réponses sont résolues avec succès\n    for(let response of responses) {\n      alert(`${response.url}: ${response.status}`); // affiche 200 pour chaque url\n    }\n\n    return responses;\n  })\n  // mappe le tableau de \"responses\" dans le tableau \"response.json()\" pour lire leurs contenus\n  .then(responses => Promise.all(responses.map(r => r.json())))\n  // toutes les réponses JSON sont analysées : \"users\" est leur tableau\n  .then(users => users.forEach(user => alert(user.name)));\n```\n\n**Si l'une des promesses est rejetée, la promesse retournée par `Promise.all` est rejetée immédiatement avec cette erreur.**\n\nPar exemple:\n\n```js run\nPromise.all([\n  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),\n*!*\n  new Promise((resolve, reject) => setTimeout(() => reject(new Error(\"Whoops!\")), 2000)),\n*/!*\n  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))\n]).catch(alert); // Error: Whoops!\n```\n\nIci, la deuxième promesse est rejetée en deux secondes. Cela conduit au rejet immédiat de `Promise.all`, donc `.catch` s'exécute : l'erreur de rejet devient le résultat de l'ensemble `Promise.all`.\n\n```warn header=\"En cas d'erreur, les autres promesses sont ignorées.\"\nSi une promesse est rejetée, `Promise.all` est immédiatement rejetée, oubliant complètement les autres dans la liste. Leurs résultats sont ignorés.\n\nPar exemple, s'il y a plusieurs appels `fetch, comme dans l'exemple ci-dessus, et que l'un d'eux échoue, les autres continueront à s'exécuter, mais `Promise.all` ne les considérera plus. Ils vont probablement se résoudre, mais leurs résultats sera ignoré.\n\n`Promise.all` ne fait rien pour les annuler, car il n'y a pas de concept \"d'annulation\" dans les promesses. Dans[un autre chapitre](info:fetch-abort) nous couvrirons `AbortController` qui peut vous aider avec cela, mais ce n'est pas une partie de l'API Promise.\n```\n\n````smart header=\"`Promise.all(iterable)` autorise toutes les valeurs \\\"régulières\\\" qui ne sont pas une promesse dans `iterable`\"\nNormallement, `Promise.all(...)` accepte un itérable (dans la plupart des cas, un tableau) de promesses. Mais si l'un de ces objets n'est pas une promesse, il est transmis au tableau résultant \"tel quel\".\n\nPar exemple, ici les résultats sont les suivants `[1, 2, 3]`:\n\n```js run\nPromise.all([\n  new Promise((resolve, reject) => {\n    setTimeout(() => resolve(1), 1000)\n  }),\n  2,\n  3\n]).then(alert); // 1, 2, 3\n```\n\nAinsi, nous sommes en mesure de passer des valeurs disponibles à `Promise.all` où cela nous convient.\n````\n\n## Promise.allSettled\n\n[recent browser=\"new\"]\n\n`Promise.all` rejette dans son ensemble si une quelconque promesse est rejetée. Cela est bon pour les cas \"tout ou rien\", quand on a besoin de *tous* les résultats pour continuer :\n\n```js\nPromise.all([\n  fetch('/template.html'),\n  fetch('/style.css'),\n  fetch('/data.json')\n]).then(render); // la méthode \"render\" a besoin des résultats de tous les \"fetchs\" \n```\n\n`Promise.allSettled` attend juste que toutes les promesses se résolvent, quel que soit le résultat. Le tableau résultant a :\n\n- `{status:\"fulfilled\", value:result}` pour les réponses réussies,\n- `{status:\"rejected\", reason:error}` pour les erreurs.\n\nPar exemple, nous aimerions récupérer l'information sur les utilisateurs multiples. Même si une demande échoue, les autres nous intéressent.\n\nUtilisons `Promise.allSettled`:\n\n```js run\nlet urls = [\n  'https://api.github.com/users/iliakan',\n  'https://api.github.com/users/remy',\n  'https://no-such-url'\n];\n\nPromise.allSettled(urls.map(url => fetch(url)))\n  .then(results => { // (*)\n    results.forEach((result, num) => {\n      if (result.status == \"fulfilled\") {\n        alert(`${urls[num]}: ${result.value.status}`);\n      }\n      if (result.status == \"rejected\") {\n        alert(`${urls[num]}: ${result.reason}`);\n      }\n    });\n  });\n```\n\nLes `résultats` dans la ligne `(*)` ci-dessus seront:\n```js\n[\n  {status: 'fulfilled', value: ...response...},\n  {status: 'fulfilled', value: ...response...},\n  {status: 'rejected', reason: ...error object...}\n]\n```\n\nAinsi, pour chaque promesse, nous obtenons son statut et `value/error`.\n\n### Polyfill\n\nSi le navigateur ne prend pas en charge `Promise.allSettled`, il est facile de le polyfill:\n\n```js\nif (!Promise.allSettled) {\n  const rejectHandler = reason => ({ status: 'rejected', reason });\n\n  const resolveHandler = value => ({ status: 'fulfilled', value });\n\n  Promise.allSettled = function (promises) {\n    const convertedPromises = promises.map(p => Promise.resolve(p).then(resolveHandler, rejectHandler));\n    return Promise.all(convertedPromises);\n  };\n}\n```\n\nDans ce code, `promises.map` prend les valeurs d'entrée, les transforme en promesses (juste au cas où autre chose qu'une promesse serait transmis) avec `p => Promise.resolve(p)`, puis ajoute le gestionnaire `.then` à chacun.\n\nCe gestionnaire transforme un résultat réussi `value` en `{state:'fulfilled', value}`, et une erreur `reason` en `{state:'rejected', reason}`. C'est exactement le format de `Promise.allSettled`.\n\nDorénavant, nous pouvons utiliser `Promise.allSettled` pour obtenir les résultats ou toutes les promesses données, même si certaines d'entre elles sont rejetées.\n\n## Promise.race\n\nSimilaire à `Promise.all`, mais n'attend que la première promesse soit résolue, et obtient son résultat (ou erreur).\n\nLa syntaxe est :\n\n```js\nlet promise = Promise.race(iterable);\n```\n\nPar exemple, ici, le résultat sera `1` :\n\n```js run\nPromise.race([\n  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),\n  new Promise((resolve, reject) => setTimeout(() => reject(new Error(\"Whoops!\")), 2000)),\n  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))\n]).then(alert); // 1\n```\n\nLa première promesse a été la plus rapide, donc, elle est devenue le résultat. Après la première promesse faite \" vainqueur de la course \", tous les autres résultats/erreurs sont ignorés.\n\n\n## Promise.any\n\nSimilar to `Promise.race`, but waits only for the first fulfilled promise and gets its result. If all of the given promises are rejected, then the returned promise is rejected with [`AggregateError`](mdn:js/AggregateError) - a special error object that stores all promise errors in its `errors` property.\n\nThe syntax is:\n\n```js\nlet promise = Promise.any(iterable);\n```\n\nFor instance, here the result will be `1`:\n\n```js run\nPromise.any([\n  new Promise((resolve, reject) => setTimeout(() => reject(new Error(\"Whoops!\")), 1000)),\n  new Promise((resolve, reject) => setTimeout(() => resolve(1), 2000)),\n  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))\n]).then(alert); // 1\n```\n\nThe first promise here was fastest, but it was rejected, so the second promise became the result. After the first fulfilled promise \"wins the race\", all further results are ignored.\n\nHere's an example when all promises fail:\n\n```js run\nPromise.any([\n  new Promise((resolve, reject) => setTimeout(() => reject(new Error(\"Ouch!\")), 1000)),\n  new Promise((resolve, reject) => setTimeout(() => reject(new Error(\"Error!\")), 2000))\n]).catch(error => {\n  console.log(error.constructor.name); // AggregateError\n  console.log(error.errors[0]); // Error: Ouch!\n  console.log(error.errors[1]); // Error: Error!\n});\n```\n\nAs you can see, error objects for failed promises are available in the `errors` property of the `AggregateError` object.\n\n## Promise.resolve/reject\n\nLes méthodes `Promise.resolve` et `Promise.reject` sont rarement nécessaires dans le code moderne, parce que la syntaxe `async/await` (nous les couvrirons dans [un peu plus tard](info:async-await)) les rend en quelque sorte obsolètes.\n\nNous les couvrons ici par souci de clarté, et pour ceux qui ne peuvent pas utiliser `async/await` pour une quelconque raison.\n\n### Promise.resolve\n\n- `Promise.resolve(value)` crée une promesse résolue avec le résultat `value`.\n\nComme pour:\n\n```js\nlet promise = new Promise(resolve => resolve(value));\n```\n\nLa méthode est utilisée pour la compatibilité, lorsqu'une fonction est censée renvoyer une promesse.\n\nPar exemple, la fonction `loadCached` ci-dessous récupère l'URL et mémorise (met en cache) son contenu. Pour les appels futurs avec la même URL, elle récupère immédiatement le contenu précédent du cache, mais utilise `Promise.resolve` pour en faire une promesse, de sorte que la valeur retournée soit toujours une promesse :\n\n```js\nlet cache = new Map();\n\nfunction loadCached(url) {\n  if (cache.has(url)) {\n*!*\n    return Promise.resolve(cache.get(url)); // (*)\n*/!*\n  }\n\n  return fetch(url)\n    .then(response => response.text())\n    .then(text => {\n      cache.set(url,text);\n      return text;\n    });\n}\n```\n\nNous pouvons écrire `loadCached(url).then(...)`, car la fonction est garantie de renvoyer une promesse. Nous pouvons toujours utiliser `.then` après `loadCached`. C'est le but de `Promise.resolve` dans la ligne `(*)`.\n\n### Promise.reject\n\n- `Promise.reject(error)` crée une promesse rejetée avec `error`.\n\nComme pour:\n\n```js\nlet promise = new Promise((resolve, reject) => reject(error));\n```\n\nEn pratique, cette méthode n'est presque jamais utilisée.\n\n## Résumé\n\nIl y a 6 méthodes statiques de la classe `Promise`:\n\n1. `Promise.all(promises)` -- attend que toutes les promesses se résolvent et retourne un tableau de leurs résultats. Si l'une des promesses données est rejetée, alors elle devient l'erreur de `Promise.all`, et tous les autres résultats sont ignorés.\n2. `Promise.allSettled(promises)` (méthode récemment ajoutée) -- attend que toutes les promesses se règlent et retourne leurs résultats sous forme de tableau d'objets avec:\n    - `state`: `\"fulfilled\"` ou `\"rejected\"`\n    - `value` (si rempli) ou `reason` (en cas de rejet).\n3. `Promise.race(promises)` -- attend que la première promesse soit réglée, et son résultat/erreur devient le résultat.\n4. `Promise.any(promises)` (méthode récemment ajoutée) -- attend que la première promesse se réalise, et son résultat devient le résultat. Si toutes les promesses données sont rejetées, [`AggregateError`](mdn:js/AggregateError) devient l'erreur de `Promise.any`.\n5. `Promise.resolve(value)` -- fait une promesse résolue avec la valeur donnée.\n6. `Promise.reject(error)` -- fait une promesse rejetée avec l'erreur donnée.\n\nDe tous ceux-ci, `Promise.all` est probablement le plus courant dans la pratique.\n"
  },
  {
    "path": "1-js/11-async/05-promise-api/head.html",
    "content": "<script>\nfunction loadScript(src) {\n  return new Promise(function(resolve, reject) {\n    let script = document.createElement('script');\n    script.src = src;\n\n    script.onload = () => resolve(script);\n    script.onerror = () => reject(new Error(\"Script load error: \" + src));\n\n    document.head.append(script);\n  });\n}\n</script>\n"
  },
  {
    "path": "1-js/11-async/05-promise-api/iliakan.json",
    "content": "{\n  \"name\": \"iliakan\",\n  \"isAdmin\": true\n}\n"
  },
  {
    "path": "1-js/11-async/05-promise-api/one.js",
    "content": "function one() {\n  alert(1);\n}\n"
  },
  {
    "path": "1-js/11-async/05-promise-api/two.js",
    "content": "function two() {\n  alert(2);\n}\n"
  },
  {
    "path": "1-js/11-async/06-promisify/article.md",
    "content": "# Promisification\n\n\"Promisification\" est un long mot pour une simple transformation. Il s'agit de la conversion d'une fonction qui accepte une fonction de rappel (\"callback\") en une fonction renvoyant une promesse.\n\nDe telles transformations sont souvent nécessaires dans la vie réelle, car de nombreuses fonctions et bibliothèques sont basées sur des callback. Mais les promesses sont plus pratiques. Il est donc logique de les transformer.\n\nPour une meilleure compréhension, voyons un exemple.\n\nPar exemple, nous avons `loadScript(src, callback)` du chapitre <info:callbacks>.\n\n```js run\nfunction loadScript(src, callback) {\n  let script = document.createElement('script');\n  script.src = src;\n\n  script.onload = () => callback(null, script);\n  script.onerror = () => callback(new Error(`Script load error for ${src}`));\n\n  document.head.append(script);\n}\n\n// usage:\n// loadScript('path/script.js', (err, script) => {...})\n```\n\nLa fonction charge un script avec le `src` donné, puis appelle `callback(err)` en cas d'erreur, ou `callback (null, script) `en cas de chargement réussi. C'est un accord répandu pour l'utilisation des rappels, nous l'avons vu auparavant.\n\nPromisifions le.\n\nNous allons créer une nouvelle fonction `loadScriptPromise(src)`, qui fait la même chose (charge le script), mais retourne une promesse au lieu d'utiliser des rappels.\n\nEn d'autres termes, nous le transmettons uniquement `src` (pas de `callback`) et obtenons une promesse en retour, qui se résout avec `script` lorsque le chargement est réussi, et sinon rejette avec l'erreur.\n\nHere it is:\n```js\nlet loadScriptPromise = function(src) {\n  return new Promise((resolve, reject) => {\n    loadScript(src, (err, script) => {\n      if (err) reject(err);\n      else resolve(script);\n    });\n  });\n};\n\n// usage:\n// loadScriptPromise('path/script.js').then(...)\n```\n\nComme nous pouvons le voir, la nouvelle fonction est un wrapper autour de la fonction originale `loadScript`. Il l'appelle en fournissant son propre rappel qui se traduit par la promesse de `resolve/reject`.\n\nDorénavant `loadScriptPromise` s'intègre bien dans le code basé sur la promesse. Si nous aimons les promesses plus que les rappels (et bientôt nous verrons plus de raisons à cela), alors nous les utiliserons à la place.\n\nDans la pratique, nous pouvons avoir besoin de promettre plus d'une fonction, il est donc logique d'utiliser un assistant.\n\nNous l'appellerons `promisify (f)` : il accepte une fonction à promettre `f` et renvoie une fonction wrapper.\n\n```js\nfunction promisify(f) {\n  return function (...args) { // return a wrapper-function (*)\n    return new Promise((resolve, reject) => {\n      function callback(err, result) { // our custom callback for f (**)\n        if (err) {\n          reject(err);\n        } else {\n          resolve(result);\n        }\n      }\n\n      args.push(callback); // ajoute notre rappel personnalisé à la fin des arguments de f\n\n      f.call(this, ...args); // appeler la fonction d'origine\n    });\n  };\n}\n\n// usage:\nlet loadScriptPromise = promisify(loadScript);\nloadScriptPromise(...).then(...);\n```\n\nLe code peut sembler un peu complexe, mais c'est essentiellement le même que celui que nous avons écrit ci-dessus, tout en promettant la fonction `loadScript`.\n\nUn appel à `promisify(f)` retourne un wrapper autour de `f` `(*)`. Ce wrapper renvoie une promesse et transmet l'appel au `f` d'origine, en suivant le résultat dans le rappel personnalisé `(**)`.\n\nIci, `promisify` suppose que la fonction d'origine attend un rappel avec exactement deux arguments `(err, result) `. C'est ce que nous rencontrons le plus souvent. Ensuite, notre rappel personnalisé est exactement dans le bon format, et `promisify` fonctionne très bien dans un tel cas.\n\nMais que se passe-t-il si le `f` original attend un rappel avec plus d'arguments `callback(err, res1, res2, ...)`?\n\nNous pouvons améliorer notre helper. Faisons une version plus avancée de `promisify`.\n\n- Lorsqu'il est appelé en tant que `promisify(f)`, il devrait fonctionner de la même manière que la version ci-dessus.\n- Lorsqu'il est appelé en tant que `promisify(f, true)`, il doit retourner la promesse qui se résout avec le tableau des résultats de rappel. C'est exactement pour les rappels avec de nombreux arguments.\n\n```js\n// promisify(f, true) pour obtenir un tableau de résultats\nfunction promisify(f, manyArgs = false) {\n  return function (...args) {\n    return new Promise((resolve, reject) => {\n      function *!*callback(err, ...results*/!*) { // notre rappel personnalisé pour f\n        if (err) {\n          reject(err);\n        } else {\n          // résoudre avec tous les résultats de rappel si manyArgs est spécifié\n          *!*resolve(manyArgs ? results : results[0]);*/!*\n        }\n      }\n\n      args.push(callback);\n\n      f.call(this, ...args);\n    });\n  };\n}\n\n// usage:\nf = promisify(f, true);\nf(...).then(arrayOfResults => ..., err => ...);\n```\n\nComme vous pouvez le voir, c'est essentiellement la même chose que ci-dessus, mais `resolve` est appelé avec un seul ou tous les arguments selon que `manyArgs` est vrai.\n\nPour des formats de rappel plus exotiques, comme ceux sans `err` : `callback(result)`, nous pouvons promettre de telles fonctions manuellement sans utiliser l'assistant.\n\nIl existe également des modules avec des fonctions de promisification un peu plus flexibles, e.g. [es6-promisify](https://github.com/digitaldesignlabs/es6-promisify). Dans Node.js, il existe une fonction intégrée `util.promisify` pour cela.\n\n```smart\nLa promisification est une excellente approche, en particulier lorsque vous utilisez `async/await` (traité plus loin dans le chapitre <info:async-await>), mais ne remplace pas totalement les callbacks.\n\nN'oubliez pas qu'une promesse peut avoir un seul résultat, mais un rappel peut techniquement être appelé plusieurs fois.\n\nLa promisification ne concerne donc que les fonctions qui appellent le rappel une fois. D'autres appels seront ignorés.\n```\n"
  },
  {
    "path": "1-js/11-async/07-microtask-queue/article.md",
    "content": "\n# Les micro-tâches\n\nLes gestionnaires de promesses `.then`/`.catch`/`.finally` sont toujours asynchrones.\n\nMême lorsqu'une promesse est immédiatement résolue, le code sur les lignes situées *ci-dessous* `.then`/`.catch`/`.finally` sera toujours exécuté avant ces gestionnaires.\n\nVoici la démo:\n\n```js run\nlet promise = Promise.resolve();\n\npromise.then(() => alert(\"promise done!\"));\n\nalert(\"code finished\"); // cette alerte s'affiche d'abord\n```\n\nSi vous exécutez, vous voyez `code finished` d'abord, puis `promise done!`.\n\nC'est étrange, car la promesse est certainement résolue depuis le début.\n\nPourquoi le `.then` se déclenche par la suite? Que se passe-t-il?\n\n## File d'attente pour micro-tâches\n\nLes tâches asynchrones nécessitent une gestion appropriée. Pour cela, la norme ECMA spécifie une file d'attente interne `PromiseJobs`, plus souvent appelée \"microtask queue\" en anglais (terme V8).\n\nComme indiqué dans la [spécification](https://tc39.github.io/ecma262/#sec-jobs-and-job-queues):\n\n- La file d'attente est premier entré, premier sorti: les tâches mises en file d'attente en premier sont exécutées en premier.\n- L'exécution d'une tâche est lancée uniquement lorsque rien d'autre n'est en cours d'exécution.\n\nOu, simplement, lorsqu'une promesse est prête, ses gestionnaires `.then/catch/finally` sont mis en file d'attente ; ils ne sont pas encore exécutés. Lorsque le moteur JavaScript est libéré du code actuel, il extrait une tâche de la file d'attente et l'exécute.\n\nC'est pourquoi \"code finished\" dans l'exemple ci-dessus s'affiche en premier.\n\n![](promiseQueue.svg)\n\nLes gestionnaires de promesses passent toujours par cette file d'attente interne.\n\nS'il existe une chaîne avec plusieurs `.then/catch/finally`, chacun d'entre eux est exécuté de manière asynchrone. C'est-à-dire qu'il est d'abord mis en file d'attente et exécuté lorsque le code actuel est terminé et que les gestionnaires précédemment placés en file d'attente sont terminés.\n\n**Et si l'ordre importait pour nous ? Comment pouvons-nous faire en sorte que `code finished` apparaisse après `promise done` ?**\n\nFacile, il suffit de le mettre dans la file d'attente avec `.then`:\n\n```js run\nPromise.resolve()\n  .then(() => alert(\"promise done!\"))\n  .then(() => alert(\"code finished\"));\n```\n\nMaintenant, l'ordre est comme prévu.\n\n## Rejet non traité\n\nSouvenez-vous de l'événement `unhandledrejection` du chapitre <info:promise-error-handling> ?\n\nMaintenant, nous pouvons voir exactement comment JavaScript découvre qu'il y a eu un rejet non géré\n\n**Un \"rejet non traité\" se produit lorsqu'une erreur de promesse n'est pas traitée à la fin de la file d'attente des micro-tâches.**\n\nNormalement, si nous nous attendons à une erreur, nous ajoutons `.catch` dans la chaîne de promesse pour la gérer:\n\n```js run\nlet promise = Promise.reject(new Error(\"Promise Failed!\"));\n*!*\npromise.catch(err => alert('caught'));\n*/!*\n\n// n'exécute pas: erreur gérée\nwindow.addEventListener('unhandledrejection', event => alert(event.reason));\n```\n\n… Mais si nous oublions d’ajouter `.catch`, dans ce cas le moteur déclenche l’événement une fois que la file d’attente de micro-tâches est vide :\n\n```js run\nlet promise = Promise.reject(new Error(\"Promise Failed!\"));\n\n// Promise Failed!\nwindow.addEventListener('unhandledrejection', event => alert(event.reason));\n```\n\nEt si nous gérons l'erreur plus tard? Comme ceci:\n\n```js run\nlet promise = Promise.reject(new Error(\"Promise Failed!\"));\n*!*\nsetTimeout(() => promise.catch(err => alert('caught')), 1000);\n*/!*\n\n// Error: Promise Failed!\nwindow.addEventListener('unhandledrejection', event => alert(event.reason));\n```\n\nMaintenant, si vous l'exécutez, nous verrons d'abord le message `Promise Failed!`, Puis `caught`.\n\nSi nous ne connaissions pas la file d'attente de micro-tâches, nous pourrions nous demander : \"Pourquoi le gestionnaire `unhandledrejection` a-t-il été exécuté ? Nous avons capturé et géré l'erreur !\".\n\nMais nous comprenons maintenant que `unhandledrejection` est généré à la fin de la file d'attente des micro-tâches : le moteur examine les promesses et, si l'une d'entre elles est à l'état \"rejected\", l'événement se déclenche.\n\nDans l'exemple ci-dessus, `.catch` ajouté par `setTimeout` se déclenche également, mais plus tard, après que `unhandledrejection` se soit déjà produit, mais cela ne change rien.\n\n## Résumé\n\nLe traitement des promesses est toujours asynchrone, car toutes les actions de promesse passent par la file d'attente interne \"promise jobs\", également appelée \"microtask queue\" (terme V8).\n\nAinsi, les gestionnaires `.then/catch/finally` sont toujours appelés une fois le code actuel terminé.\n\nSi nous devons garantir qu'un morceau de code est exécuté après `.then/catch/finally`, nous pouvons l'ajouter à un appel `.then` enchaîné.\n\nDans la plupart des moteurs JavaScript, y compris les navigateurs et Node.js, le concept de micro-tâches est étroitement lié à la \"boucle d'événement\" et aux \"macrotaches\". Comme elles n’ont pas de relation directe avec les promesses, elles sont décrites dans une autre partie du didacticiel, au chapitre <info:event-loop>.\n"
  },
  {
    "path": "1-js/11-async/08-async-await/01-rewrite-async/solution.md",
    "content": "\nLes notes sont en dessous du code:\n\n```js run\nasync function loadJson(url) { // (1)\n  let response = await fetch(url); // (2)\n\n  if (response.status == 200) {\n    let json = await response.json(); // (3)\n    return json;\n  }\n\n  throw new Error(response.status);\n}\n\nloadJson('https://javascript.info/no-such-user.json')\n  .catch(alert); // Error: 404 (4)\n```\n\nNotes:\n\n1. La fonction `loadJson` devient `async`.\n2. Tous les `.then` intérieurs sont remplacés par `await`..\n3. Nous pouvons `return response.json()` au lieu de l'attendre, comme ceci:\n\n    ```js\n    if (response.status == 200) {\n      return response.json(); // (3)\n    }\n    ```\n\n    Ensuite, le code externe devra \"attendre\" la résolution de cette promesse. Dans notre cas, cela n'a pas d'importance.\n4. L'erreur émise par `loadJson` est gérée par `.catch`. Nous ne pouvons pas utiliser `await loadJson(...)` ici, car nous ne sommes pas dans une fonction `async`..\n"
  },
  {
    "path": "1-js/11-async/08-async-await/01-rewrite-async/task.md",
    "content": "\n# Réécriture avec async/await\n\nRéécrire cet exemple de code du chapitre <info:promise-chaining> en utilisant `async/await` au lieu de `.then/catch`:\n\n```js run\nfunction loadJson(url) {\n  return fetch(url)\n    .then(response => {\n      if (response.status == 200) {\n        return response.json();\n      } else {\n        throw new Error(response.status);\n      }\n    });\n}\n\nloadJson('https://javascript.info/no-such-user.json')\n  .catch(alert); // Error: 404\n```\n"
  },
  {
    "path": "1-js/11-async/08-async-await/02-rewrite-async-2/solution.md",
    "content": "\nIl n'y a pas d'astuces ici. Remplacez simplement `.catch` par `try..catch` dans `demoGithubUser` et ajoutez `async/await` là où c'est nécessaire:\n\n```js run\nclass HttpError extends Error {\n  constructor(response) {\n    super(`${response.status} for ${response.url}`);\n    this.name = 'HttpError';\n    this.response = response;\n  }\n}\n\nasync function loadJson(url) {\n  let response = await fetch(url);\n  if (response.status == 200) {\n    return response.json();\n  } else {\n    throw new HttpError(response);\n  }\n}\n\n// demander un nom d'utilisateur jusqu'à ce que github renvoie un utilisateur valide\nasync function demoGithubUser() {\n\n  let user;\n  while(true) {\n    let name = prompt(\"Enter a name?\", \"iliakan\");\n\n    try {\n      user = await loadJson(`https://api.github.com/users/${name}`);\n      break; // pas d'erreur, sortie de la boucle\n    } catch(err) {\n      if (err instanceof HttpError && err.response.status == 404) {\n        // la boucle continue après l'alerte\n        alert(\"No such user, please reenter.\");\n      } else {\n        // erreur inconnue, rejeter\n        throw err;\n      }\n    }      \n  }\n\n\n  alert(`Full name: ${user.name}.`);\n  return user;\n}\n\ndemoGithubUser();\n```\n"
  },
  {
    "path": "1-js/11-async/08-async-await/02-rewrite-async-2/task.md",
    "content": "\n# Réécriture de \"rethrow\" avec async/await\n\nVous trouverez ci-dessous l'exemple \"rethrow\". Réécrivez-le en utilisant `async/await` au lieu de `.then/catch`.\n\nEt débarrassez-vous de la récursion en faveur d'une boucle dans `demoGithubUser` : avec `async/await`, cela devient facile à faire.\n\n```js run\nclass HttpError extends Error {\n  constructor(response) {\n    super(`${response.status} for ${response.url}`);\n    this.name = 'HttpError';\n    this.response = response;\n  }\n}\n\nfunction loadJson(url) {\n  return fetch(url)\n    .then(response => {\n      if (response.status == 200) {\n        return response.json();\n      } else {\n        throw new HttpError(response);\n      }\n    });\n}\n\n// demander un nom d'utilisateur jusqu'à ce que github renvoie un utilisateur valide\nfunction demoGithubUser() {\n  let name = prompt(\"Enter a name?\", \"iliakan\");\n\n  return loadJson(`https://api.github.com/users/${name}`)\n    .then(user => {\n      alert(`Full name: ${user.name}.`);\n      return user;\n    })\n    .catch(err => {\n      if (err instanceof HttpError && err.response.status == 404) {\n        alert(\"No such user, please reenter.\");\n        return demoGithubUser();\n      } else {\n        throw err;\n      }\n    });\n}\n\ndemoGithubUser();\n```\n"
  },
  {
    "path": "1-js/11-async/08-async-await/03-async-from-regular/solution.md",
    "content": "\nC'est le cas quand il est utile de savoir comment ça marche à l'intérieur.\n\nIl suffit de traiter l'appel `async` comme une promesse et d'y attacher `.then`:\n```js run\nasync function wait() {\n  await new Promise(resolve => setTimeout(resolve, 1000));\n\n  return 10;\n}\n\nfunction f() {\n  // affiche 10 après 1 seconde\n*!*\n  wait().then(result => alert(result));\n*/!*\n}\n\nf();\n```\n"
  },
  {
    "path": "1-js/11-async/08-async-await/03-async-from-regular/task.md",
    "content": "\n# Appeler l'asynchrone à partir du non-asynchrone\n\nNous avons une fonction \"normale\" appelée `f`. Comment pouvez-vous appeler la fonction `async` `wait()` et utiliser son résultat à l'intérieur de `f` ?\n\n```js\nasync function wait() {\n  await new Promise(resolve => setTimeout(resolve, 1000));\n\n  return 10;\n}\n\nfunction f() {\n  // ...que devez-vous écrire ici?\n  // nous devons appeler async wait() et attendre pour obtenir 10\n  // Souvenez-vous, on ne peut pas utiliser \"await\".\n}\n```\n\nP.S. La tâche est techniquement très simple, mais la question est assez courante pour les développeurs novices en matière d'async/await.\n"
  },
  {
    "path": "1-js/11-async/08-async-await/article.md",
    "content": "# Async/await\n\nIl existe une syntaxe spéciale pour travailler avec les promesses d'une manière plus confortable, appelée \"async/await\". Elle est étonnamment facile à comprendre et à utiliser.\n\n## Fonctions asynchrones\n\nCommençons par le mot-clé `async`. Il peut être placé avant une fonction, comme ceci:\n\n```js\nasync function f() {\n  return 1;\n}\n```\n\nLe mot \"async\" devant une fonction signifie une chose simple : une fonction renvoie toujours une promesse. Les autres valeurs sont enveloppées dans une promesse résolue automatiquement.\n\nPar exemple, cette fonction renvoie une promesse résolue avec le résultat `1` ; testons-la:\n\n```js run\nasync function f() {\n  return 1;\n}\n\nf().then(alert); // 1\n```\n\n...Nous pourrions explicitement renvoyer une promesse, ce qui reviendrait au même:\n\n```js run\nasync function f() {\n  return Promise.resolve(1);\n}\n\nf().then(alert); // 1\n```\n\nAinsi, `async` s'assure que la fonction renvoie une promesse, et enveloppe les non-promesses dans celle-ci. Assez simple, non ? Mais pas seulement. Il y a un autre mot-clé, `await`, qui ne fonctionne qu'à l'intérieur des fonctions `async`, et c'est plutôt cool.\n\n## Await\n\nLa syntaxe:\n\n```js\n// ne fonctionne que dans les fonctions asynchrones\nlet value = await promise;\n```\n\nLe mot-clé `await` fait en sorte que JavaScript attende que cette promesse se réalise et renvoie son résultat.\n\nVoici un exemple avec une promesse qui se résout en 1 seconde:\n```js run\nasync function f() {\n\n  let promise = new Promise((resolve, reject) => {\n    setTimeout(() => resolve(\"done!\"), 1000)\n  });\n\n*!*\n  let result = await promise; // attendre que la promesse soit résolue (*)\n*/!*\n\n  alert(result); // \"done!\"\n}\n\nf();\n```\n\nL'exécution de la fonction fait une \"pause\" à la ligne `(*)` et reprend lorsque la promesse s'installe, `result` devenant son résultat. Ainsi le code ci-dessus affiche \"done!\" en une seconde.\n\nSoulignons-le : `await` suspend littéralement l'exécution de la fonction jusqu'à ce que la promesse soit réglée, puis la reprend avec le résultat de la promesse. Cela ne coûte pas de ressources CPU, car le moteur JavaScript peut faire d'autres travaux pendant ce temps : exécuter d'autres scripts, gérer des événements, etc.\n\nC'est juste une syntaxe plus élégante pour obtenir le résultat de la promesse que `promise.then`. Et c'est plus facile à lire et à écrire.\n\n````warn header=\"On ne peut pas utiliser `await` dans les fonctions régulières\"\nSi nous essayons d'utiliser `await` dans une fonction non-async, il y aurait une erreur de syntaxe:\n\n```js run\nfunction f() {\n  let promise = Promise.resolve(1);\n*!*\n  let result = await promise; // Syntax error\n*/!*\n}\n```\n\nNous pouvons obtenir cette erreur si nous oublions de mettre `async` avant une fonction. Comme indiqué précédemment, `await` ne fonctionne qu'à l'intérieur d'une fonction `async`.\n````\n\nPrenons l'exemple `showAvatar()` du chapitre <info:promise-chaining> et réécrivons-le en utilisant `async/await`:\n\n1. Nous devons remplacer les appels `.then` par `await`.\n2. Aussi, nous devrions faire la fonction `async` pour qu'ils fonctionnent.\n\n```js run\nasync function showAvatar() {\n\n  // lire notre JSON\n  let response = await fetch('/article/promise-chaining/user.json');\n  let user = await response.json();\n\n  // lire l'utilisateur de github\n  let githubResponse = await fetch(`https://api.github.com/users/${user.name}`);\n  let githubUser = await githubResponse.json();\n\n  // montrer l'avatar\n  let img = document.createElement('img');\n  img.src = githubUser.avatar_url;\n  img.className = \"promise-avatar-example\";\n  document.body.append(img);\n\n  // attendre 3 secondes\n  await new Promise((resolve, reject) => setTimeout(resolve, 3000));\n\n  img.remove();\n\n  return githubUser;\n}\n\nshowAvatar();\n```\n\nPlutôt propre et facile à lire, non ? Bien mieux qu'avant.\n\n````smart header=\"Les navigateurs modernes permettent l'utilisation de `await` au niveau supérieur dans les modules\"\nDans les navigateurs modernes, `await` au niveau supérieur fonctionne très bien, lorsque nous sommes dans un module. Nous couvrirons les modules dans l'article <info:modules-intro>.\n\nPar exemple:\n\n```js run module\n// nous supposons que ce code s'exécute au niveau supérieur, dans un module\nlet response = await fetch('/article/promise-chaining/user.json');\nlet user = await response.json();\n\nconsole.log(user);\n```\n\nSi nous n'utilisons pas de modules, ou [des navigateurs plus anciens](https://caniuse.com/mdn-javascript_operators_await_top_level) doivent être supportés, il y a une recette universelle: enveloppement dans une fonction asynchrone anonyme.\n\nComme ceci :\n\n```js\n(async () => {\n  let response = await fetch('/article/promise-chaining/user.json');\n  let user = await response.json();\n  ...\n})();\n```\n\n````\n\n````smart header=\"`await` accepte \\\"thenables\\\"\"\nComme `promise.then`, `await` nous permet d'utiliser des objets \"thenables\" (ceux qui ont une méthode `then` appelable). L'idée est qu'un objet tiers peut ne pas être une promesse, mais être compatible avec les promesses : s'il supporte `.then`, c'est suffisant pour l'utiliser avec `await`..\n\nVoici une classe `Thenable` de démonstration ; le `await` ci-dessous accepte ses instances:\n\n```js run\nclass Thenable {\n  constructor(num) {\n    this.num = num;\n  }\n  then(resolve, reject) {\n    alert(resolve);\n    // résous this.num*2 après 1000ms\n    setTimeout(() => resolve(this.num * 2), 1000); // (*)\n  }\n}\n\nasync function f() {\n  // attend pendant 1 seconde, puis le résultat devient 2\n  let result = await new Thenable(1);\n  alert(result);\n}\n\nf();\n```\n\nSi `await` reçoit un objet non-promis avec `.then`, il appelle cette méthode en fournissant les fonctions intégrées `resolve` et `reject` comme arguments (comme pour un exécuteur `Promise` normal). Ensuite, `await` attend que l'une d'entre elles soit appelée (dans l'exemple ci-dessus, cela se produit à la ligne `(*)`) et procède ensuite avec le résultat.\n````\n\n````smart header=\"Méthodes de classe asynchrones\"\nPour déclarer une méthode de classe asynchrone, il suffit de la faire précéder de `async`:\n\n```js run\nclass Waiter {\n*!*\n  async wait() {\n*/!*\n    return await Promise.resolve(1);\n  }\n}\n\nnew Waiter()\n  .wait()\n  .then(alert); // 1 (c'est la même chose que (result => alert(result)))\n```\nLa signification est la même : elle assure que la valeur retournée est une promesse et active `await`.\n\n````\n## Gestion des erreurs\n\nSi une promesse se résout normalement, alors `await promise` renvoie le résultat. Mais dans le cas d'un rejet, il jette l'erreur, comme s'il y avait une instruction `throw` à cette ligne.\n\nCe code:\n\n```js\nasync function f() {\n*!*\n  await Promise.reject(new Error(\"Whoops!\"));\n*/!*\n}\n```\n\n...est le même que celui-ci:\n\n```js\nasync function f() {\n*!*\n  throw new Error(\"Whoops!\");\n*/!*\n}\n```\n\nDans des situations réelles, la promesse peut prendre un certain temps avant de rejeter. Dans ce cas, il y aura un délai avant que `await` ne lance une erreur.\n\nNous pouvons rattraper cette erreur en utilisant `try..catch`, de la même manière qu'un `throw` normal:\n\n```js run\nasync function f() {\n\n  try {\n    let response = await fetch('http://no-such-url');\n  } catch(err) {\n*!*\n    alert(err); // TypeError: failed to fetch\n*/!*\n  }\n}\n\nf();\n```\n\nEn cas d'erreur, le contrôle saute au bloc `catch`. Nous pouvons également envelopper plusieurs lignes:\n\n```js run\nasync function f() {\n\n  try {\n    let response = await fetch('/no-user-here');\n    let user = await response.json();\n  } catch(err) {\n    // attrape les erreurs à la fois dans fetch et response.json\n    alert(err);\n  }\n}\n\nf();\n```\n\nSi nous n'avons pas `try..catch`, alors la promesse générée par l'appel de la fonction asynchrone `f()` sera rejetée. Nous pouvons ajouter `.catch` pour le gérer:\n\n```js run\nasync function f() {\n  let response = await fetch('http://no-such-url');\n}\n\n// f() devient une promesse rejetée\n*!*\nf().catch(alert); // TypeError: failed to fetch // (*)\n*/!*\n```\n\nSi nous oublions d'ajouter `.catch` à cet endroit, nous obtenons une erreur de promesse non gérée (visible dans la console). Nous pouvons attraper de telles erreurs en utilisant un gestionnaire d'événement global `unhandledrejection` comme décrit dans le chapitre <info:promise-error-handling>.\n\n\n```smart header=\"`async/await` et `promise.then/catch`\"\nLorsque nous utilisons `async/await`, nous avons rarement besoin de `.then`, car `await` gère l'attente pour nous. Et nous pouvons utiliser un `try..catch` normal au lieu de `.catch`. C'est généralement (mais pas toujours) plus pratique.\n\nMais au niveau supérieur du code, lorsque nous sommes en dehors de toute fonction `async`, nous sommes syntaxiquement incapables d'utiliser `await`, donc c'est une pratique normale d'ajouter `.then/catch` pour gérer le résultat final ou l'erreur de chute, comme dans la ligne `(*)` de l'exemple ci-dessus.\n```\n\n````smart header=\"`async/await` fonctionne bien avec `Promise.all`\"\nLorsque nous devons attendre plusieurs promesses, nous pouvons les envelopper dans `Promise.all` et ensuite `await`:\n\n```js\n// attendre le tableau de résultats\nlet results = await Promise.all([\n  fetch(url1),\n  fetch(url2),\n  ...\n]);\n```\n\nDans le cas d'une erreur, elle se propage comme d'habitude, de la promesse échouée à `Promise.all`, et devient alors une exception que nous pouvons attraper en utilisant `try..catch` autour de l'appel.\n\n````\n\n## Résumé\n\nLe mot-clé `async` devant une fonction a deux effets:\n\n1. Fait en sorte qu'elle retourne toujours une promesse.\n2. Permet l'utilisation de `await` dans celle-ci.\n\nLe mot-clé `await` devant une promesse fait en sorte que JavaScript attende jusqu'à ce que cette promesse se règle, puis:\n\n1. Si c'est une erreur, l'exception est générée - comme si `throw error` était appelé à cet endroit précis.\n2. Sinon, il renvoie le résultat.\n\nEnsemble, ils fournissent un cadre idéal pour écrire du code asynchrone facile à lire et à écrire.\n\nAvec `async/await`, nous avons rarement besoin d'écrire `promise.then/catch`, mais nous ne devons pas oublier qu'ils sont basés sur des promesses, parce que parfois (par exemple dans le scope le plus externe) nous devons utiliser ces méthodes. De plus, `Promise.all` est très utile lorsque l'on attend plusieurs tâches simultanément.\n"
  },
  {
    "path": "1-js/11-async/08-async-await/head.html",
    "content": "<style>\n.promise-avatar-example {\n  border-radius: 50%;\n  position: fixed;\n  left: 10px;\n  top: 10px;\n}\n</style>\n"
  },
  {
    "path": "1-js/11-async/index.md",
    "content": "# Promesses, async/await\n"
  },
  {
    "path": "1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/_js.view/solution.js",
    "content": "function* pseudoRandom(seed) {\n  let value = seed;\n\n  while(true) {\n    value = value * 16807 % 2147483647\n    yield value;\n  }\n\n};\n"
  },
  {
    "path": "1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/_js.view/test.js",
    "content": "describe(\"pseudoRandom\", function() {\n\n  it(\"follows the formula\", function() {\n    let generator = pseudoRandom(1);\n\n    assert.equal(generator.next().value, 16807);\n    assert.equal(generator.next().value, 282475249);\n    assert.equal(generator.next().value, 1622650073);\n  });\n\n\n  it(\"returns same value for the same seed\", function() {\n    let generator1 = pseudoRandom(123);\n    let generator2 = pseudoRandom(123);\n\n    assert.deepEqual(generator1.next(), generator2.next());\n    assert.deepEqual(generator1.next(), generator2.next());\n    assert.deepEqual(generator1.next(), generator2.next());\n  });\n\n});\n"
  },
  {
    "path": "1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/solution.md",
    "content": "```js run demo\nfunction* pseudoRandom(seed) {\n  let value = seed;\n\n  while(true) {\n    value = value * 16807 % 2147483647;\n    yield value;\n  }\n\n};\n\nlet generator = pseudoRandom(1);\n\nalert(generator.next().value); // 16807\nalert(generator.next().value); // 282475249\nalert(generator.next().value); // 1622650073\n```\n\nVeuillez noter que la même chose peut être faite avec une fonction régulière, comme ceci:\n\n```js run\nfunction pseudoRandom(seed) {\n  let value = seed;\n\n  return function() {\n    value = value * 16807 % 2147483647;\n    return value;\n  }\n}\n\nlet generator = pseudoRandom(1);\n\nalert(generator()); // 16807\nalert(generator()); // 282475249\nalert(generator()); // 1622650073\n```\n\nCela fonctionne aussi. Mais alors nous perdons la capacité à itérer avec  `for..of` et d'utiliser une composition de générateur, qui pourrait être utile ailleurs.\n"
  },
  {
    "path": "1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/task.md",
    "content": "\n# Pseudo-random generator\n\nIl y a de nombreux cas où nous avons besoin de données aléatoires.\n\nL'un d'eux est le test. Nous aurons peut-être besoin de données aléatoires: texte, chiffres, etc., pour bien tester les choses.\n\nEn JavaScript, nous pourrions utiliser `Math.random()`. Mais si quelque chose ne va pas, nous aimerions pouvoir répéter le test en utilisant exactement les mêmes données.\n\nPour cela, on utilise des \"générateurs pseudo-aléatoires\"(seeded pseudo-random generators). Ils prennent une \"graine\", la première valeur, puis génèrent les suivantes à l'aide d'une formule. De sorte que la même graine donne la même séquence, de sorte que tout le flux est facilement reproductible. Il suffit de rappeler la graine pour la répéter.\n\nUn exemple d'une telle formule, qui génère des valeurs distribuées de manière assez uniforme:\n\n```\nnext = previous * 16807 % 2147483647\n```\n\nSi on utilise `1` comme graine, les valeurs seront:\n1. `16807`\n2. `282475249`\n3. `1622650073`\n4. ...etc...\n\nLa tâche ici est de créer une fonction de générateur `pseudoRandom(seed)` qui prend une `seed`(graine) et crée le générateur avec cette formule.\n\nExemple d'utilisation:\n\n```js\nlet generator = pseudoRandom(1);\n\nalert(generator.next().value); // 16807\nalert(generator.next().value); // 282475249\nalert(generator.next().value); // 1622650073\n```\n"
  },
  {
    "path": "1-js/12-generators-iterators/1-generators/article.md",
    "content": "# Générateurs\n\nLes fonctions régulières ne renvoient qu'une seule valeur (ou rien).\n\nLes générateurs peuvent renvoyer (\"rendement\") plusieurs valeurs, l'une après l'autre, à la demande. Ils fonctionnent très bien avec les [iterables](info:iterable), permettant de créer des flux de données en toute simplicité.\n\n## Fonctions de générateur\n\nPour créer un générateur, nous avons besoin d'une construction syntaxique spéciale: `fonction*`, appelée \"fonction générateur\".\n\nCela ressemble à ça:\n\n```js\nfunction* generateSequence() {\n  yield 1;\n  yield 2;\n  return 3;\n}\n```\n\nLes fonctions du générateur se comportent différemment des fonctions normales. Lorsqu'une telle fonction est appelée, elle n'exécute pas son code. Au lieu de cela, elle renvoie un objet spécial, appelé \"objet générateur\", pour gérer l'exécution.\n\nJetez un oeil ici:\n\n```js run\nfunction* generateSequence() {\n  yield 1;\n  yield 2;\n  return 3;\n}\n\n// \"fonction générateur\" crée \"objet générateur\"\nlet generator = generateSequence();\n*!*\nalert(generator); // [object Generator]\n*/!*\n```\n\nL'exécution du code de la fonction n'a pas encore commencé:\n\n![](generateSequence-1.svg)\n\nLa principale méthode d'un générateur est `next()`. Lorsqu'il est appelé, il est exécuté jusqu'à la déclaration de `<valeur> yield` la plus proche (la `valeur` peut être omise, alors il est `undefined`). Ensuite, l'exécution de la fonction s'interrompt et la `valeur` yield est renvoyée au code externe.\n\nLe résultat de `next()` est toujours un objet avec deux propriétés:\n- `value`: la valeur yielded.\n- `done`: `true` si le code de la fonction est terminé, sinon `false`.\n\nPar exemple, ici nous créons le générateur et obtenons sa première valeur yield:\n\n```js run\nfunction* generateSequence() {\n  yield 1;\n  yield 2;\n  return 3;\n}\n\nlet generator = generateSequence();\n\n*!*\nlet one = generator.next();\n*/!*\n\nalert(JSON.stringify(one)); // {value: 1, done: false}\n```\n\nPour l'instant, nous n'avons obtenu que la première valeur, et l'exécution de la fonction est sur la deuxième ligne:\n\n![](generateSequence-2.svg)\n\nAppelons `generator.next()` encore une fois. Il reprend l'exécution du code et retourne le `yield` suivant:\n\n```js\nlet two = generator.next();\n\nalert(JSON.stringify(two)); // {value: 2, done: false}\n```\n\n![](generateSequence-3.svg)\n\nEt, si nous l'appelons une troisième fois, l'exécution atteint l'instruction `return` qui termine la fonction:\n\n```js\nlet three = generator.next();\n\nalert(JSON.stringify(three)); // {value: 3, *!*done: true*/!*}\n```\n\n![](generateSequence-4.svg)\n\nLe générateur est maintenant terminé. Nous devrions voir avec `done:true` et traiter `value:3` comme résultat final.\n\nDe nouveaux appels de `generator.next()` n'ont plus de sens maintenant. Si nous les faisons quand même, ils retournent le même objet: `{done: true}`.\n\n```smart header=\"`function* f(…)` or `function *f(…)`?\"\nLes deux syntaxes sont correctes.\n\nMais généralement, la première syntaxe est préférée, car l'étoile `*` indique qu'il s'agit d'une fonction de générateur, elle décrit le type, pas le nom, elle doit donc rester avec le mot clé `function`.\n```\n\n## Les générateurs sont itérables\n\nComme vous l'avez probablement déjà deviné en regardant la méthode `next()`, les générateurs sont [iterable](info:iterable).\n\nNous pouvons parcourir leurs valeurs en utilisant `for..of` :\n\n```js run\nfunction* generateSequence() {\n  yield 1;\n  yield 2;\n  return 3;\n}\n\nlet generator = generateSequence();\n\nfor(let value of generator) {\n  alert(value); // 1, then 2\n}\n```\n\nCela semble beaucoup plus agréable que d'appeler `.next().value`, non?\n\n...Mais veuillez noter: l'exemple ci-dessus montre `1`, puis `2`, et c'est tout. le `3` n'est pas montré!\n\nC'est parce que l'iteration `for..of` ignore la dernière `value`, quand`done: true`. Donc, si nous voulons que tous les résultats soient affichés par `for..of`, nous devons les retourner avec `yield`:\n\n```js run\nfunction* generateSequence() {\n  yield 1;\n  yield 2;\n*!*\n  yield 3;\n*/!*\n}\n\nlet generator = generateSequence();\n\nfor(let value of generator) {\n  alert(value); // 1, then 2, then 3\n}\n```\n\nComme les générateurs sont itérables, nous pouvons appeler toutes les fonctionnalités associées, par exemple la syntaxe spread `...` :\n\n```js run\nfunction* generateSequence() {\n  yield 1;\n  yield 2;\n  yield 3;\n}\n\nlet sequence = [0, ...generateSequence()];\n\nalert(sequence); // 0, 1, 2, 3\n```\n\nDans le code ci-dessus, `...generateSequence()` transforme l'objet générateur itérable en tableau d'éléments (Essayer d'en savoir plus sur la syntaxe spread dans le chapitre [](info:rest-parameters-spread-operator#spread-operator))\n\n## Utilisation de générateurs pour les itérables\n\nIl y a quelque temps, dans le chapitre [](info:iterable) nous avons créé un objet `range` qui retourne les valeurs `from..to`.\n\nIci, rappelons-nous ce code:\n\n```js run\nlet range = {\n  from: 1,\n  to: 5,\n\n  // for..of range appelle cette méthode une fois au tout début\n  [Symbol.iterator]() {\n    // ...il renvoie l'objet itérateur:\n    // en avant, for..of ne fonctionne qu'avec cet objet, lui demandant les valeurs suivantes\n    return {\n      current: this.from,\n      last: this.to,\n\n      // next() est appelé à chaque itération par la boucle for..of\n      next() {\n        // il doit renvoyer la valeur en tant qu'objet {done:.., value :...}\n        if (this.current <= this.last) {\n          return { done: false, value: this.current++ };\n        } else {\n          return { done: true };\n        }\n      }\n    };\n  }\n};\n\n// l'itération sur la plage renvoie des nombres de range.from à range.to\nalert([...range]); // 1,2,3,4,5\n```\n\nNous pouvons utiliser une fonction de générateur pour l'itération en la fournissant comme pour `Symbol.iterator`.\n\nVoici la même `range`, mais beaucoup plus compact:\n\n```js run\nlet range = {\n  from: 1,\n  to: 5,\n\n  *[Symbol.iterator]() { // un raccourci pour [Symbol.iterator]: function*()\n    for(let value = this.from; value <= this.to; value++) {\n      yield value;\n    }\n  }\n};\n\nalert( [...range] ); // 1,2,3,4,5\n```\n\nCela fonctionne, car `range[Symbol.iterator]()` renvoie maintenant un générateur, et les méthodes de générateur sont exactement ce que `for..of` attend:\n- il a la méthode `.next()`\n- qui renvoie des valeurs sous la forme `{value: ..., done: true/false}`\n\nCe n'est pas une coïncidence, bien sûr. Des générateurs ont été ajoutés au langage JavaScript en pensant aux itérateurs, pour les implémenter plus facilement.\n\nLa variante avec générateur est beaucoup plus concise que le code itérable original de `range`, et garde la même fonctionnalité.\n\n```smart header=\"Les générateurs peuvent générer des valeurs pour toujours\"\nDans les exemples ci-dessus, nous avons généré des séquences finies, mais nous pouvons également créer un générateur qui donne des valeurs pour toujours. Par exemple, une séquence sans fin de nombres pseudo-aléatoires.\n\nCela nécessiterait sûrement un `break` (ou un `return`) dans `for..of` sur un tel générateur, sinon la boucle se répéterait pour toujours et se bloquerait.\n```\n\n## Composition du générateur\n\nLa composition des générateurs est une caractéristique spéciale des générateurs qui permet \"d'incorporer\" les générateurs de manière transparente les uns dans les autres.\n\nPar exemple, nous avons une fonction qui génère une séquence de nombres:\n\n```js\nfunction* generateSequence(start, end) {\n  for (let i = start; i <= end; i++) yield i;\n}\n```\n\nMaintenant, nous aimerions le réutiliser pour une séquence plus complexe:\n- d'abord, les chiffres `0..9` (avec des codes de caractères 48..57),\n- suivi de lettres de l'alphabet en majuscules `A..Z` (codes de caractères 65..90)\n- suivi de lettres de l'alphabet en minuscules `a..z` (codes de caractères 97..122)\n\nNous pouvons utiliser cette séquence, par exemple pour créer des mots de passe en sélectionnant des caractères (pourrait également ajouter des caractères de syntaxe), mais générons-le d'abord.\n\nDans une fonction régulière, pour combiner les résultats de plusieurs autres fonctions, nous les appelons, stockons les résultats, puis les rejoignons à la fin.\n\nPour les générateurs, il existe une syntaxe spéciale `yield*` pour \"incorporer\" (composer) un générateur dans un autre.\n\nLe générateur composé:\n\n```js run\nfunction* generateSequence(start, end) {\n  for (let i = start; i <= end; i++) yield i;\n}\n\nfunction* generatePasswordCodes() {\n\n*!*\n  // 0..9\n  yield* generateSequence(48, 57);\n\n  // A..Z\n  yield* generateSequence(65, 90);\n\n  // a..z\n  yield* generateSequence(97, 122);\n*/!*\n\n}\n\nlet str = '';\n\nfor(let code of generatePasswordCodes()) {\n  str += String.fromCharCode(code);\n}\n\nalert(str); // 0..9A..Za..z\n```\n\nLa directive `yield*` *délègue* l'exécution à un autre générateur. Ce terme signifie que `yield* gen` itère sur le générateur `gen` et transmet de manière transparente ses yiels à l'extérieur. Comme si les valeurs étaient fournies par le générateur extérieur.\n\nLe résultat est le même que si nous insérions le code des générateurs imbriqués:\n\n```js run\nfunction* generateSequence(start, end) {\n  for (let i = start; i <= end; i++) yield i;\n}\n\nfunction* generateAlphaNum() {\n\n*!*\n  // yield* generateSequence(48, 57);\n  for (let i = 48; i <= 57; i++) yield i;\n\n  // yield* generateSequence(65, 90);\n  for (let i = 65; i <= 90; i++) yield i;\n\n  // yield* generateSequence(97, 122);\n  for (let i = 97; i <= 122; i++) yield i;\n*/!*\n\n}\n\nlet str = '';\n\nfor(let code of generateAlphaNum()) {\n  str += String.fromCharCode(code);\n}\n\nalert(str); // 0..9A..Za..z\n```\n\nUne composition de générateur est un moyen naturel d'insérer le flux d'un générateur dans un autre. Il n'utilise pas de mémoire supplémentaire pour stocker les résultats intermédiaires.\n\n## \"yield\" est une route à double sens\n\nJusqu'à présent, les générateurs étaient similaires aux objets itérables, avec une syntaxe spéciale pour générer des valeurs. Mais en fait, ils sont beaucoup plus puissants et flexibles.\n\nC'est parce que `yield` est une route à double sens : il renvoie non seulement le résultat à l'extérieur, mais peut également transmettre la valeur à l'intérieur du générateur.\n\nPour ce faire, nous devons appeler `generator.next(arg)`, avec un argument. Cet argument devient le résultat de `yield`.\n\nVoyons un exemple:\n\n```js run\nfunction* gen() {\n*!*\n  // Passe une question au code externe et attend une réponse\n  let result = yield \"2 + 2 = ?\"; // (*)\n*/!*\n\n  alert(result);\n}\n\nlet generator = gen();\n\nlet question = generator.next().value; // <-- yield retournes une valeur\n\ngenerator.next(4); // --> passe le résultat dans le générateur  \n```\n\n![](genYield2.svg)\n\n1. Le premier appel `generator.next()` est toujours sans argument. Il démarre l'exécution et renvoie le résultat du premier `yield \"2+2=?\"`. À ce stade, le générateur suspend l'exécution (toujours sur cette ligne).\n2. Ensuite, comme le montre l'image ci-dessus, le résultat de `yield` entre dans la variable `question` du code appelant.\n3. Sur `generator.next(4)`, le générateur reprend et `4` entre comme résultat: `let result = 4`.\n\nVeuillez noter que le code externe n'a pas à appeler immédiatement `next(4)`. Cela peut prendre du temps. Ce n'est pas un problème : le générateur attendra.\n\nPar exemple:\n\n```js\n// reprend le générateur après un certain temps\nsetTimeout(() => generator.next(4), 1000);\n```\n\nComme nous pouvons le voir, contrairement aux fonctions régulières, un générateur et le code appelant peuvent échanger les résultats en passant des valeurs dans `next/yield`.\n\nPour rendre les choses plus évidentes, voici un autre exemple, avec plus d'appels:\n\n```js run\nfunction* gen() {\n  let ask1 = yield \"2 + 2 = ?\";\n\n  alert(ask1); // 4\n\n  let ask2 = yield \"3 * 3 = ?\"\n\n  alert(ask2); // 9\n}\n\nlet generator = gen();\n\nalert( generator.next().value ); // \"2 + 2 = ?\"\n\nalert( generator.next(4).value ); // \"3 * 3 = ?\"\n\nalert( generator.next(9).done ); // true\n```\n\nL'image d'exécution:\n\n![](genYield2-2.svg)\n\n1. Le premier `.next()` démarre l'exécution... Il atteint le premier `yield`.\n2. Le résultat est renvoyé au code externe.\n3. Le second `.next(4)` retourne `4` au générateur à la suite du premier `yield`, et reprend l'exécution.\n4. ...Il atteint le deuxième `yield`, qui devient le résultat de l'appel du générateur.\n5. Le troisième `next(9)` passe `9` dans le générateur à la suite du deuxième `yield` et reprend l'exécution qui atteint la fin de la fonction, donc `done: true`.\n\nC'est comme un jeu de \"ping-pong\". Chaque `next(value)` (à l'exclusion du premier) passe une valeur dans le générateur, qui devient le résultat du `yield` actuel, puis récupère le résultat du prochain `yield`.\n\n## generator.throw\n\nComme nous l'avons observé dans les exemples ci-dessus, le code externe peut transmettre une valeur au générateur, à la suite de `yield`.\n\n...Mais il peut aussi y initier (lancer) une erreur. C'est naturel, car une erreur est une sorte de résultat.\n\nPour passer une erreur dans un `yield`, nous devons appeler `generator.throw(err)`. Dans ce cas, le `err` est jeté dans la ligne avec ce `yield`.\n\nPar exemple, ici le yield de `\"2 + 2 = ?\"` conduit à une erreur:\n\n```js run\nfunction* gen() {\n  try {\n    let result = yield \"2 + 2 = ?\"; // (1)\n\n    alert(\"L'exécution n'atteint pas ici, car l'exception est levée juste au-dessus\");\n  } catch(e) {\n    alert(e); // montre l'erreur\n  }\n}\n\nlet generator = gen();\n\nlet question = generator.next().value;\n\n*!*\ngenerator.throw(new Error(\"La réponse est introuvable dans ma base de données\")); // (2)\n*/!*\n```\n\nL'erreur, jetée dans le générateur sur la ligne `(2)` conduit à une exception dans la ligne `(1)` avec `yield`. Dans l'exemple ci-dessus, `try..catch` l'attrape et s'affiche.\n\nSi nous ne l'attrapons pas, alors comme toute exception, il fait \"retomber\" le générateur dans le code appelant.\n\nLa ligne actuelle du code appelant est la ligne avec `generator.throw`, étiquetée comme `(2)`. Nous pouvons donc l'attraper ici, comme ceci:\n\n```js run\nfunction* generate() {\n  let result = yield \"2 + 2 = ?\"; // Error in this line\n}\n\nlet generator = generate();\n\nlet question = generator.next().value;\n\n*!*\ntry {\n  generator.throw(new Error(\"La réponse est introuvable dans ma base de données\"));\n} catch(e) {\n  alert(e); // shows the error\n}\n*/!*\n```\n\nSi nous n'attrapons pas l'erreur là, alors, comme d'habitude, elle passe au code d'appel externe (le cas échéant) et, s'il n'est pas détecté, tue le script.\n\n## generator.return\n\n`generator.return(value)` termine l'exécution du générateur et renvoie la `value` donnée.\n\n```js\nfunction* gen() {\n  yield 1;\n  yield 2;\n  yield 3;\n}\n\nconst g = gen();\n\ng.next();        // { value: 1, done: false }\ng.return('foo'); // { value: \"foo\", done: true }\ng.next();        // { value: undefined, done: true }\n```\n\nSi nous utilisons à nouveau `generator.return()` dans un générateur terminé, il renverra à nouveau cette valeur ([MDN](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Generator/return)).\n\nSouvent, nous ne l'utilisons pas, car la plupart du temps, nous voulons obtenir toutes les valeurs de retour, mais cela peut être utile lorsque nous voulons arrêter le générateur dans une condition spécifique.\n\n## Résumé\n\n- Les générateurs sont créés par des fonctions de générateur `function* f(…) {…}`.\n- À l'intérieur des générateurs (uniquement), il existe un operateur `yield`.\n- Le code externe et le générateur peuvent échanger les résultats via les appels `next/yield`.\n\nDans le JavaScript moderne, les générateurs sont rarement utilisés. Mais parfois, ils sont utiles, car la capacité d'une fonction à échanger des données avec le code appelant pendant l'exécution est tout à fait unique. Et, certainement, ils sont parfaits pour fabriquer des objets itérables.\n\nDe plus, dans le chapitre suivant, nous apprendrons les générateurs asynchrones, qui sont utilisés pour lire des flux de données générées de manière asynchrone (par exemple, des récupérations paginées sur un réseau) dans la boucle `for wait ... of`.\n\nDans la programmation Web, nous travaillons souvent avec des données en streaming, c'est donc un autre cas d'utilisation très important.\n"
  },
  {
    "path": "1-js/12-generators-iterators/2-async-iterators-generators/article.md",
    "content": "# Itérateurs et générateurs asynchrones\n\nLes itérateurs asynchrones permettent d'itérer sur des données qui arrivent de manière asynchrone, à la demande. Par exemple, quand nous téléchargeons quelque chose morceau par morceau sur un réseau. Les générateurs asynchrones rendent cela encore plus pratique.\n\n\n## Rappeler les itérables\n\nRappelons le sujet des itérables.\n\nL'idée est que nous avons un objet, tel que `range` ici :\n```js\nlet range = {\n  from: 1,\n  to: 5\n};\n```\n\n...Et nous aimerions utiliser la boucle `for..of` dessus, comme `for(value of range)`, pour obtenir des valeurs de `1` à `5`.\n\nEn d'autres termes, nous voulons ajouter une *capacité d'itération* à l'objet.\n\nCela peut être implémenté en utilisant une méthode spéciale avec le nom `Symbol.iterator` :\n\n- Cette méthode est appelée par la construction `for..of` lorsque la boucle est lancée, et elle doit renvoyer un objet avec la méthode `next`.\n- Pour chaque itération, la méthode `next()` est invoquée pour la valeur suivante.\n- Le `next()` doit retourner une valeur sous la forme `{done: true/false, value:<loop value>}`, où `done:true` signifie la fin de la boucle.\n\nVoici une implémentation pour l'itérable `range` :\n\n```js run\nlet range = {\n  from: 1,\n  to: 5,\n\n*!*\n  [Symbol.iterator]() { // appelé une fois, au début de for..of\n*/!*\n    return {\n      current: this.from,\n      last: this.to,\n\n*!*\n      next() { // appelé à chaque itération, pour obtenir la valeur suivante\n*/!*\n        if (this.current <= this.last) {\n          return { done: false, value: this.current++ };\n        } else {\n          return { done: true };\n        }\n      }\n    };\n  }\n};\n\nfor(let value of range) {\n  alert(value); // 1 puis 2, puis 3, puis 4, puis 5\n}\n```\n\nSi quelque chose n'est pas clair, veuillez consulter le chapitre [](info:iterable), il donne tous les détails sur les itérables réguliers.\n\n## Itérables asynchrones\n\nUne itération asynchrone est nécessaire lorsque les valeurs arrivent de manière asynchrone: après `setTimeout` ou un autre type de retard.\n\nLe cas le plus courant est que l'objet doit faire une requête réseau pour fournir la valeur suivante, nous en verrons un exemple réel un peu plus tard.\n\nPour rendre un objet itérable de manière asynchrone :\n\n1. Utiliser `Symbol.asyncIterator` au lieu de `Symbol.iterator`.\n2. La méthode `next()` devrait retourner une promesse (à remplir avec la valeur suivante).\n    - Le mot-clé `async` le gère, nous pouvons simplement faire `async next()`.\n3. Pour itérer sur un tel objet, nous devrions utiliser une boucle `for await (let item of iterable)`.\n    - Notez le mot `await`.\n\nComme exemple de départ, créons un objet `range` itérable, similaire à celui d'avant, mais maintenant il retournera des valeurs de manière asynchrone, une par seconde.\n\nTout ce que nous devons faire est d'effectuer quelques remplacements dans le code ci-dessus :\n\n```js run\nlet range = {\n  from: 1,\n  to: 5,\n\n*!*\n  [Symbol.asyncIterator]() { // (1)\n*/!*\n    return {\n      current: this.from,\n      last: this.to,\n\n*!*\n      async next() { // (2)\n*/!*\n\n*!*\n        // note: nous pouvons utiliser \"await\" dans l'async suivant :\n        await new Promise(resolve => setTimeout(resolve, 1000)); // (3)\n*/!*\n\n        if (this.current <= this.last) {\n          return { done: false, value: this.current++ };\n        } else {\n          return { done: true };\n        }\n      }\n    };\n  }\n};\n\n(async () => {\n\n*!*\n  for await (let value of range) { // (4)\n    alert(value); // 1,2,3,4,5\n  }\n*/!*\n\n})()\n```\n\nNous pouvons observer que la structure est similaire aux itérateurs réguliers :\n\n1. Pour rendre un objet itérable, asynchrone, il doit avoir une méthode `Symbol.asyncIterator` `(1)`.\n2. Cette méthode doit retourner l'objet avec la méthode `next()` retournant une promesse `(2)`.\n3. La méthode `next()` n'a pas besoin d'être `async`, elle peut être une méthode normale retournant une promesse, mais `async` permet d'utiliser `await`, donc c'est pratique. Ici, nous ne faisons qu'attendre une seconde `(3)`.\n4. Pour itérer, nous utilisons `for await(let value of range)` `(4)`, c'est-à-dire que nous ajoutons \"await\" après \"for\". Il appelle `range[Symbol.asyncIterator]()` une fois, et ensuite son `next()` pour chaque valeur.\n\nVoici un petit tableau avec les différences :\n\n|       | itérateurs | itérateurs asynchrones |\n|-------|------------|------------------------|\n| Méthode de l'objet qui fournit un itérateur | `Symbol.iterator` | `Symbol.asyncIterator` |\n| valeur de retour de la fonction `next()`    | peu importe       | `Promise`  |\n| pour boucler, utilisez                      | `for..of`         | `for await..of` |\n\n````warn header=\"la 'spread syntax' `...` ne fonctionne pas de manière asynchrone\"\nLes fonctionnalités qui nécessitent des itérateurs réguliers et synchrones ne fonctionnent pas avec les asynchrones.\n\nPar exemple, la 'spread syntax' ne fonctionnera pas :\n```js\nalert( [...range] ); // Erreur, pas de Symbol.iterator\n```\n\nC'est naturel, comme il s'attend à trouver `Symbol.iterator`, pas `Symbol.asyncIterator`.\n\nC'est aussi le cas pour `for..of` : la syntaxe sans `await` a besoin de `Symbol.iterator`.\n````\n\n## Rappeler les générateurs\n\nRappelons maintenant les générateurs, car ils permettent de raccourcir le code d'itération. La plupart du temps, lorsque nous souhaitons créer un itérable, nous utiliserons des générateurs.\n\nPar soucis de simplicité, nous omettons certaines choses importantes, ce sont des \"fonctions qui génèrent (produisent) des valeurs\". Elles sont expliquées en détails dans le chapitre [](info:generators).\n\n\nLes générateurs sont étiquetés avec `function*` (notez l'étoile) et utilisent `yield` pour générer une valeur, puis nous pouvons utiliser `for..of` pour boucler par dessus.\n\n\nCet exemple génère une séquence de valeurs de `start` à `end` :\n\n```js run\nfunction* generateSequence(start, end) {\n  for (let i = start; i <= end; i++) {\n    yield i;\n  }\n}\n\nfor(let value of generateSequence(1, 5)) {\n  alert(value); // 1, puis 2, puis 3, puis 4, puis 5\n}\n```\n\nComme nous le savons déjà, pour rendre un objet itérable, nous devons lui ajouter `Symbol.iterator`.\n\n```js\nlet range = {\n  from: 1,\n  to: 5,\n*!*\n  [Symbol.iterator]() {\n    return <object with next to make range iterable>\n  }\n*/!*\n}\n```\n\nUne pratique courante pour `Symbol.iterator` est de renvoyer un générateur, cela raccourcit le code, comme vous pouvez le voir :\n\n```js run\nlet range = {\n  from: 1,\n  to: 5,\n\n  *[Symbol.iterator]() { // a shorthand for [Symbol.iterator]: function*()\n    for(let value = this.from; value <= this.to; value++) {\n      yield value;\n    }\n  }\n};\n\nfor(let value of range) {\n  alert(value); // 1, ensuite 2, ensuite 3, ensuite 4, ensuite 5\n}\n```\n\nVeuillez consulter le chapitre [](info:generators) si vous souhaitez plus de détails.\n\nDans les générateurs standards, nous ne pouvons pas utiliser `await`. Toutes les valeurs doivent être synchronisées, comme l'exige la construction `for..of`.\n\nEt si nous souhaitons générer des valeurs de manière asynchrone ? À partir de requêtes réseau, par exemple.\n\nPassons aux générateurs asynchrones pour rendre cela possible.\n\n## Générateurs asynchrones (finally)\n\nPour la plupart des applications pratiques, lorsque nous souhaitons créer un objet qui génère de manière asynchrone une séquence de valeurs, nous pouvons utiliser un générateur asynchrone.\n\nLa syntaxe est simple : ajoutez `function*` à `async`. Cela rend le générateur asynchrone.\n\nEt puis utilisez `for await (...)` pour itérer dessus, comme ceci :\n\n```js run\n*!*async*/!* function* generateSequence(start, end) {\n\n  for (let i = start; i <= end; i++) {\n\n*!*\n    // Wow, on peut utiliser await!\n    await new Promise(resolve => setTimeout(resolve, 1000));\n*/!*\n\n    yield i;\n  }\n\n}\n\n(async () => {\n\n  let generator = generateSequence(1, 5);\n  for *!*await*/!* (let value of generator) {\n    alert(value); // 1, puis 2, puis 3, puis 4, puis 5 (avec un délai entre)\n  }\n\n})();\n```\n\nComme le générateur est asynchrone, nous pouvons utiliser `await` à l'intérieur, nous fier aux promesses, effectuer des requêtes réseau et ainsi de suite.\n\n````smart header=\"Différence sous le capot\"\nTechniquement, si vous êtes un lecteur avancé qui se souvient des détails sur les générateurs, il y a une différence interne.\n\nPour les générateurs asynchrones, la méthode `generator.next()` est asynchrone, elle renvoie des promesses.\n\nDans un générateur classique, nous utiliserions `result = generator.next()` pour obtenir des valeurs. Alors que, dans un générateur asynchrone, nous devrions ajouter `await`, comme ceci :\n\n```js\nresult = await generator.next(); // result = {value: ..., done: true/false}\n```\nC'est pourquoi les générateurs asynchrones fonctionnent avec `for await...of`.\n````\n\n### Plage itérative asynchrone\n\nLes générateurs réguliers peuvent être utilisés comme `Symbol.iterator` pour raccourcir le code d'itération.\n\nSimilaire à cela, les générateurs asynchrones peuvent être utilisés comme `Symbol.asyncIterator` pour implémenter l'itération asynchrone.\n\nPar exemple, nous pouvons faire en sorte que l'objet `range` génère des valeurs de manière asynchrone, une fois par seconde, en remplaçant `Symbol.iterator` synchrone par `Symbol.asyncIterator` asynchrone :\n\n```js run\nlet range = {\n  from: 1,\n  to: 5,\n\n  // cette ligne est la même que [Symbol.asyncIterator]: async function*() {\n*!*\n  async *[Symbol.asyncIterator]() {\n*/!*\n    for(let value = this.from; value <= this.to; value++) {\n\n      // faire une pause entre les valeurs, attendre quelque chose\n      await new Promise(resolve => setTimeout(resolve, 1000));\n\n      yield value;\n    }\n  }\n};\n\n(async () => {\n\n  for *!*await*/!* (let value of range) {\n    alert(value); // 1, puis 2, puis 3, puis 4, puis 5\n  }\n\n})();\n```\n\nDésormais, les valeurs sont accompagnées d'un délai de 1 seconde entre elles.\n\n```smart\nTechniquement, nous pouvons ajouter à la fois `Symbol.iterator` et `Symbol.asyncIterator` à l'objet, donc il est à la fois itérable de manière synchrone (`for..of`) et asynchrone (`for await..of`).\n\nEn pratique cependant, ce serait une chose étrange à faire.\n```\n\n## Exemple réel : données paginées\n\nJusqu'à présent, nous avons vu des exemples de base pour mieux comprendre. Passons maintenant en revue un cas d'utilisation réel.\n\n\nCe modèle est très courant. Il ne s'agit pas d'utilisateurs, mais de n'importe quoi.\n\nPar exemple, GitHub nous permet de récupérer les commits de la même manière paginée :\n\n- Nous devrions faire une demande avec `fetch` sous la forme `https://api.github.com/repos/<repo>/commits`.\n- Il répond avec un JSON de 30 commits et fournit également un lien vers la page suivante dans l'en-tête `Link`.\n- Ensuite, nous pouvons utiliser ce lien pour la prochaine demande, pour obtenir plus de commits, etc.\n\nPour notre code, nous aimerions avoir un moyen plus simple d'obtenir des commits.\n\nFaisons une fonction `fetchCommits(repo)` qui obtient des commits pour nous, faisant des requêtes chaque fois que nécessaire. Et laissez-le se soucier de tous les trucs de pagination. Pour nous, ce sera une simple itération asynchrone `for await..of`.\n\nDonc, l'utilisation sera comme ceci :\n\n```js\nfor await (let commit of fetchCommits(\"username/repository\")) {\n  // process commit\n}\n```\n\nVoici une telle fonction, implémentée en tant que générateur asynchrone :\n\n```js\nasync function* fetchCommits(repo) {\n  let url = `https://api.github.com/repos/${repo}/commits`;\n\n  while (url) {\n    const response = await fetch(url, { // (1)\n      headers: {'User-Agent': 'Our script'}, // Github a besoin de l'en-tête user-agent\n    });\n\n    const body = await response.json(); // (2) la réponse est un JSON (tableau de commits)\n\n    // (3) l'URL de la page suivante est dans le header, il faut l'extraire\n    let nextPage = response.headers.get('Link').match(/<(.*?)>; rel=\"next\"/);\n    nextPage = nextPage?.[1];\n\n    url = nextPage;\n\n    for(let commit of body) { // (4) génère les commits un par un, jusqu'à la fin de la page\n      yield commit;\n    }\n  }\n}\n```\n\nPlus d'explications sur son fonctionnement :\n\n1. Nous utilisons la méthode du navigateur [fetch](info:fetch) pour télécharger les commits.\n\n    - L'URL initiale est `https://api.github.com/repos/<repo>/commits`, et la page suivante sera dans l'en-tête `Link` de la réponse.\n    - La méthode `fetch` nous permet de fournir une autorisation et d'autres en-têtes si nécessaire - ici GitHub nécessite `User-Agent`.\n2. Les commits sont renvoyés au format JSON.\n3. Nous devrions obtenir l'URL de la page suivante à partir de l'en-tête `Link` de la réponse. Il a un format spécial, nous utilisons donc une [expression régulière](info:regular-expressions)).\npour cela.\n    - L'URL de la page suivante peut ressembler à `https://api.github.com/repositories/93253246/commits?page=2`. Elle est générée par GitHub lui-même.\n4. Ensuite, nous donnons les commits reçus un par un, et quand ils se terminent, la prochaine itération `while(url)` se déclenchera, faisant une demande de plus.\n\nUn exemple d'utilisation (montrant les auteurs de chaque commit en console) :\n\n```js run\n(async () => {\n\n  let count = 0;\n\n  for await (const commit of fetchCommits('javascript-tutorial/en.javascript.info')) {\n\n    console.log(commit.author.login);\n\n    if (++count == 100) { // Arrêtons-nous à 100 commits\n      break;\n    }\n  }\n\n})();\n\n// Note: If you are running this in an external sandbox, you'll need to paste here the function fetchCommits described above\n```\n\nC'est exactement ce que nous voulions.\n\nLa mécanique interne des pages est invisible de l'extérieur. Pour nous, c'est juste un générateur asynchrone qui retourne chacun des commits.\n\n## Résumé\n\nLes itérateurs et générateurs normaux fonctionnent bien avec les données dont la génération est rapide.\n\nLorsque nous nous attendons à ce que les données arrivent de manière asynchrone, puisque leur génération est possiblement chronophage, les équivalents asynchrones ainsi que \" for await..of \" au lieu de \" for..of \" peuvent être utilisés.\n\nDifférences de syntaxe entre les itérateurs asynchrones et synchrones :\n\n|       | itérateurs | itérateurs asynchrones |\n|-------|------------|------------------------|\n| Méthode de l'objet qui fournit un itérateur | `Symbol.iterator` | `Symbol.asyncIterator` |\n| valeur de retour de la fonction `next()`   | `{value:…, done: true/false}`         | Promesse qui se resout en `{value:…, done: true/false}`  |\n\nDifférences de syntaxe entre les générateurs asynchrones et synchrones :\n\n|       | Générateurs | Générateurs asynchrones |\n|-------|-------------|-------------------------|\n| Déclaration | `function*` | `async function*` |\n| valeur de retour de la fonction `next()`      | `{value:…, done: true/false}`         | Promesse qui se résout en `{value:…, done: true/false}`  |\n\nDans le développement Web, nous rencontrons souvent des flux de données, circulant morceau par morceau. Par exemple, dans le téléchargement ou l'envoi de gros fichiers.\n\nNous pouvons utiliser des générateurs asynchrones pour traiter ce genre de données. Il est également intéressant de noter que dans certains environnements, comme les navigateurs, il existe une autre API appelée Streams, qui fournit des interfaces spéciales pour travailler avec de tels flux, pour transformer les données et pour les faire passer d'un flux à l'autre (par exemple, télécharger à partir d'un endroit et envoyer immédiatement ailleurs).\n"
  },
  {
    "path": "1-js/12-generators-iterators/2-async-iterators-generators/head.html",
    "content": "<script>\n  async function* fetchCommits(repo) {\n    let url = `https://api.github.com/repos/${repo}/commits`;\n\n    while (url) {\n      const response = await fetch(url, {\n        headers: {'User-Agent': 'Our script'}, // github requires user-agent header\n      });\n\n      const body = await response.json(); // parses response as JSON (array of commits)\n\n      // the URL of the next page is in the headers, extract it\n      let nextPage = response.headers.get('Link').match(/<(.*?)>; rel=\"next\"/);\n      nextPage = nextPage?.[1];\n\n      url = nextPage;\n\n      // yield commits one by one, when they finish - fetch a new page url\n      for(let commit of body) {\n        yield commit;\n      }\n    }\n  }\n</script>\n"
  },
  {
    "path": "1-js/12-generators-iterators/index.md",
    "content": "# Générateurs, itération avancée\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/article.md",
    "content": "\n# Modules, introduction\n\nAu fur et à mesure que notre application grandit, nous souhaitons la scinder en plusieurs fichiers, appelés \"modules\". Un module contient généralement une classe ou une bibliothèque de fonctions pour une tâche précise.\n\nPendant longtemps, JavaScript n'avait pas de module. Ce n’était pas un problème, car au départ les scripts étaient petits et simples, il n’était donc pas nécessaire.\n\nMais les scripts sont devenus de plus en plus complexes et la communauté a donc inventé diverses méthodes pour organiser le code en modules, des bibliothèques spéciales pour charger des modules à la demande.\n\nPour en nommer quelques-uns (pour des raisons historiques) :\n\n- [AMD](https://en.wikipedia.org/wiki/Asynchronous_module_definition) -- un des systèmes de modules les plus anciens, initialement mis en œuvre par la bibliothèque [require.js](https://requirejs.org/).\n- [CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1) -- le système de module créé pour Node.js\n- [UMD](https://github.com/umdjs/umd) -- un système de module supplémentaire, proposé comme universel, compatible avec AMD et CommonJS\n\nMaintenant, tous ces éléments deviennent lentement du passé, mais nous pouvons toujours les trouver dans d’anciens scripts.\n\nLe système de modules au niveau du langage est apparu dans la norme en 2015, a progressivement évolué depuis, et est désormais pris en charge par tous les principaux navigateurs et dans Node.js. Nous allons donc étudier les modules JavaScript modernes à partir de maintenant.\n\n## Qu'est-ce qu'un module?\n\nUn module n'est qu'un fichier. Un script est un module. Aussi simple que cela.\n\nLes modules peuvent se charger mutuellement et utiliser des directives spéciales, `export` et `import`, pour échanger des fonctionnalités, appeler les fonctions d’un module dans un autre:\n\n- Le mot-clé `export` labelise les variables et les fonctions qui doivent être accessibles depuis l'extérieur du module actuel.\n- `import` permet l'importation de fonctionnalités à partir d'autres modules.\n\nPar exemple, si nous avons un fichier `sayHi.js` exportant une fonction\n\n```js\n// 📁 sayHi.js\nexport function sayHi(user) {\n  alert(`Hello, ${user}!`);\n}\n```\n\n...Un autre fichier peut l'importer et l'utiliser:\n\n```js\n// 📁 main.js\nimport {sayHi} from './sayHi.js';\n\nalert(sayHi); // function...\nsayHi('John'); // Hello, John!\n```\n\nLa directive `import` charge le module qui a pour chemin `./sayHi.js` par rapport au fichier actuel et affecte la fonction exportée `sayHi` à la variable correspondante.\n\nLançons l’exemple dans le navigateur.\n\nComme les modules prennent en charge des mots-clés et des fonctionnalités spéciales, nous devons indiquer au navigateur qu'un script doit être traité comme un module, en utilisant l'attribut `<script type=\"module\">`.\n\nComme ça:\n\n[codetabs src=\"say\" height=\"140\" current=\"index.html\"]\n\nLe navigateur extrait et évalue automatiquement le module importé (et, le cas échéant, ses importations), puis exécute le script.\n\n```warn header=\"Les modules fonctionnent uniquement via HTTP(s), pas localement\"\nSi vous essayez d'ouvrir une page Web localement, via le protocole `file://`, vous verrez que les directives `import/export` ne fonctionnent pas. Utilisez un serveur Web local, tel que [static-server](https://www.npmjs.com/package/static-server#getting-started) ou utilisez la fonctionnalité \"live server\" de votre éditeur, tel que VS Code [Live Server Extension](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) pour tester les modules.\n```\n\n## Caractéristiques du module de base\n\nQu'est-ce qui est différent dans les modules par rapport aux scripts \"normaux\"?\n\nIl existe des fonctionnalités de base, valables à la fois pour le navigateur et le JavaScript côté serveur.\n\n### Toujours en mode \"use strict\"\n\nLes modules fonctionnent toujours en mode strict. Par exemple. l'affectation à une variable non déclarée donnera une erreur.\n\n```html run\n<script type=\"module\">\n  a = 5; // error\n</script>\n```\n\n### Portée au niveau du module\n\nChaque module a sa propre portée globale. En d'autres termes, les variables et les fonctions globales d'un module ne sont pas visibles dans les autres scripts.\n\nDans l'exemple ci-dessous, deux scripts sont importés et `hello.js` essaie d'utiliser la variable `user` déclarée dans `user.js`. Il échoue, car il s'agit d'un module distinct (vous verrez l'erreur dans la console) :\n\n[codetabs src=\"scopes\" height=\"140\" current=\"index.html\"]\n\nLes modules doivent `export` ce qu'ils veulent être accessible de l'extérieur et `import` ce dont ils ont besoin.\n\n- `user.js` devrait exporter la variable `user`.\n- `hello.js` devrait l'importer depuis le module `user.js`.\n\nEn d'autres termes, avec les modules, nous utilisons l'import/export au lieu de nous appuyer sur des variables globales.\n\nCeci est la bonne variante :\n\n[codetabs src=\"scopes-working\" height=\"140\" current=\"hello.js\"]\n\nDans le navigateur, si nous parlons de pages HTML, une portée de niveau supérieur indépendante existe également pour chaque `<script type=\"module\">`.\n\nVoici deux scripts sur la même page, tous deux `type=\"module\"`. Ils ne voient pas les variables de niveau supérieur de l'autre :\n\n```html run\n<script type=\"module\">\n  // La variable est uniquement visible dans ce module\n  let user = \"John\";\n</script>\n\n<script type=\"module\">\n  *!*\n  alert(user); // Error: user is not defined\n  */!*\n</script>\n```\n\n```smart\nDans le navigateur, nous pouvons rendre une variable globale au niveau de la fenêtre en l'affectant explicitement à une propriété `window`, par exemple `window.user = \"John\"`.\n\nEnsuite, tous les scripts la verront, à la fois avec `type=\"module\"` et sans.\n\nCela dit, faire de telles variables globales est mal vu. Veuillez essayer de les éviter.\n```\n\n### Un code de module est chargé la première fois lorsqu'il est importé\n\nSi le même module est importé dans plusieurs autres modules, son code n'est exécuté qu'une seule fois, lors de la première importation. Ensuite, ses exportations sont données à tous les autres importateurs.\n\nL'évaluation ponctuelle a des conséquences importantes, dont nous devons être conscients.\n\nVoyons quelques exemples.\n\nPremièrement, si exécuter un code de module entraîne des effets secondaires, comme afficher un message, l'importer plusieurs fois ne le déclenchera qu'une seule fois - la première fois:\n\n```js\n// 📁 alert.js\nalert(\"Module is evaluated!\");\n```\n\n```js\n// Importer le même module à partir de fichiers différents\n\n// 📁 1.js\nimport `./alert.js`; // le module est chargé\n\n// 📁 2.js\nimport `./alert.js`; // (n'affiche rien)\n```\n\nLa deuxième importation ne montre rien, car le module a déjà été évalué.\n\nIl y a une règle : le code du module de niveau supérieur doit être utilisé pour l'initialisation, la création de structures de données internes spécifiques au module. Si nous devons rendre quelque chose appelable plusieurs fois, nous devons l'exporter en tant que fonction, comme nous l'avons fait avec `sayHi` ci-dessus.\n\nMaintenant, considérons un exemple plus profond.\n\nDisons qu'un module exporte un objet:\n\n```js\n// 📁 admin.js\nexport let admin = {\n  name: \"John\"\n};\n```\n\nSi ce module est importé à partir de plusieurs fichiers, il n'est chargé que la première fois, un objet `admin` est créé, puis transmis à tous les autres importateurs.\n\nTous les importateurs obtiennent exactement le seul et unique objet `admin`:\n\n```js\n// 📁 1.js\nimport {admin} from './admin.js';\nadmin.name = \"Pete\";\n\n// 📁 2.js\nimport {admin} from './admin.js';\nalert(admin.name); // Pete\n\n*!*\n// 1.js et 2.js font référence au même objet admin\n// Les modifications apportées dans 1.js sont visibles dans 2.js\n*/!*\n```\n\nComme vous pouvez le voir, lorsque `1.js` modifie la propriété `name` dans le `admin` importé, alors `2.js` peut voir le nouveau `admin.name`.\n\nC'est précisément parce que le module n'est exécuté qu'une seule fois. Les exportations sont générées, puis partagées entre les importateurs, donc si quelque chose change l'objet `admin`, les autres modules le verront.\n\n**Un tel comportement est en fait très pratique, car il nous permet de *configurer* des modules.**\n\nEn d'autres termes, un module peut fournir une fonctionnalité générique qui nécessite une configuration. Par exemple. l'authentification a besoin d'informations d'identification. Ensuite, il peut exporter un objet de configuration en attendant que le code externe lui soit affecté.\n\nVoici le modèle classique :\n1. Un module exporte certains moyens de configuration, par exemple un objet de configuration.\n2. Lors de la première importation, nous l'initialisons, écrivons dans ses propriétés. Le script d'application de niveau supérieur peut le faire.\n3. D'autres importations utilisent le module.\n\nPar exemple, le module `admin.js` peut fournir certaines fonctionnalités (par exemple, l'authentification), mais s'attend à ce que les informations d'identification entrent dans l'objet `config` de l'extérieur :\n\n```js\n// 📁 admin.js\nexport let config = { };\n\nexport function sayHi() {\n  alert(`Ready to serve, ${config.user}!`);\n}\n```\n\nIci, `admin.js` exporte l'objet `config` (initialement vide, mais peut également avoir des propriétés par défaut).\n\nEnsuite, dans `init.js`, le premier script de notre application, nous en importons `config` et définissons `config.user` :\n\n```js\n// 📁 init.js\nimport {config} from './admin.js';\nconfig.user = \"Pete\";\n```\n\n...Maintenant, le module `admin.js` est configuré.\n\nFurther importers can call it, and it correctly shows the current user:\n\n```js\n// 📁 another.js\nimport {sayHi} from './admin.js';\n\nsayHi(); // Prêt à être utilisé, *!*Pete*/!*!\n```\n\n\n### import.meta\n\nL'objet `import.meta` contient les informations sur le module actuel.\n\nSon contenu dépend de l'environnement. Dans le navigateur, il contient l'URL du script ou une URL de page Web actuelle si elle est en HTML:\n\n```html run height=0\n<script type=\"module\">\n  alert(import.meta.url); // URL du script\n  // pour un script en ligne - l'URL de la page HTML actuelle\n</script>\n```\n\n### Dans un module, \"this\" n'est pas défini\n\nC’est un peu une caractéristique mineure, mais pour être complet, nous devrions le mentionner.\n\nDans un module, l'objet global `this` est indéfini.\n\nComparez-le à des scripts sans module, là où il est un object global:\n\n```html run height=0\n<script>\n  alert(this); // window\n</script>\n\n<script type=\"module\">\n  alert(this); // undefined\n</script>\n```\n\n## Fonctionnalités spécifiques au navigateur\n\nIl existe également plusieurs différences de scripts spécifiques au navigateur avec `type=\"module\"` par rapport aux scripts classiques.\n\nVous devriez peut-être ignorer cette section pour l'instant si vous lisez pour la première fois ou si vous n'utilisez pas JavaScript dans un navigateur.\n\n### Les modules sont différés\n\nLes modules sont *toujours* différés, avec le même effet que l'attribut `defer` (décrit dans le chapitre [](info:script-async-defer)), pour les scripts externes et intégrés.\n\nEn d'autres termes:\n- télécharger des modules externe `<script type=\"module\" src=\"...\">` ne bloque pas le traitement HTML, ils se chargent en parallèle avec d’autres ressources.\n- Les modules attendent que le document HTML soit complètement prêt (même s'ils sont minuscules et se chargent plus rapidement que le HTML), puis s'exécutent.\n- l'ordre relatif des scripts est maintenu : les scripts qui entrent en premier dans le document sont exécutés en premier.\n\nComme effet secondaire, les modules \"voient\" toujours la page HTML entièrement chargée, y compris les éléments HTML situés en dessous.\n\nPar exemple:\n\n```html run\n<script type=\"module\">\n*!*\n  alert(typeof button); // object: le script peut 'voir' le bouton ci-dessous\n*/!*\n  // à mesure que les modules sont différés, le script s'exécute après le chargement de la page entière\n</script>\n\nComparez au script habituel ci-dessous:\n\n<script>\n*!*\n  alert(typeof button); // button est undefined, le script ne peut pas voir les éléments ci-dessous\n*/!*\n  // les scripts normaux sont exécutés immédiatement, avant que le reste de la page ne soit traité\n</script>\n\n<button id=\"button\">Button</button>\n```\n\nRemarque : le deuxième script fonctionne avant le premier ! Nous verrons donc d'abord `undefined`, puis `object`.\n\nC’est parce que les modules sont différés, nous attendons donc que le document soit traité. Les scripts réguliers s'exécutent immédiatement, nous avons donc vu son resultat en premier.\n\nLorsque nous utilisons des modules, nous devons savoir que la page HTML apparaît lors de son chargement et que les modules JavaScript s'exécutent par la suite, afin que l'utilisateur puisse voir la page avant que l'application JavaScript soit prête. Certaines fonctionnalités peuvent ne pas encore fonctionner. Nous devons définir des \"indicateurs de chargement\" ou veiller à ce que le visiteur ne soit pas confus par cela.\n\n### Async fonctionne sur les scripts en ligne\n\nPour les scripts non modulaires, l'attribut `async` ne fonctionne que sur les scripts externes. Les scripts asynchrones s'exécutent immédiatement lorsqu'ils sont prêts, indépendamment des autres scripts ou du document HTML.\n\nPour les modules, cela fonctionne sur tous les scripts.\n\nPar exemple, le script ci-dessous est `async` et n’attend donc personne.\n\nIl effectue l'importation (récupère `./analytics.js`) et s'exécute lorsqu'il est prêt, même si le document HTML n'est pas encore terminé ou si d'autres scripts sont toujours en attente.\n\nC’est bon pour une fonctionnalité qui ne dépend de rien, comme des compteurs, des annonces, des écouteurs d’événements au niveau du document.\n\n```html\n<!-- toutes les dépendances sont récupérées (analytics.js) et le script s'exécute -->\n<!-- il n'attend pas le document ou d'autres balises <script> -->\n<script *!*async*/!* type=\"module\">\n  import {counter} from './analytics.js';\n\n  counter.count();\n</script>\n```\n\n### Scripts externes\n\nLes scripts externes de `type=\"module\"` se distinguent sous deux aspects:\n\n1. Les scripts externes avec le même `src` ne s'exécutent qu'une fois:\n    ```html\n    <!-- le script my.js est récupéré et exécuté une seule fois -->\n    <script type=\"module\" src=\"my.js\"></script>\n    <script type=\"module\" src=\"my.js\"></script>\n    ```\n\n2. Les scripts externes extraits d’une autre origine (par exemple, un autre site) nécessitent [CORS](https://developer.mozilla.org/fr/docs/Web/HTTP/CORS) en-têtes, comme décrit dans le chapitre <info:fetch-crossorigin>. En d'autres termes, si un module est extrait d'une autre origine, le serveur distant doit fournir un en-tête `Access-Control-Allow-Origin` permettant l'extraction.\n    ```html\n    <!-- another-site.com doit fournir Access-Control-Allow-Origin -->\n    <!-- sino, le script ne sera pas exécuté -->\n    <script type=\"module\" src=\"*!*http://another-site.com/their.js*/!*\"></script>\n    ```\n\n    Cela garantit une meilleure sécurité par défaut.\n\n### Aucun module \"nu\" autorisé\n\nDans le navigateur, `import` doit avoir une URL relative ou absolue. Les modules sans chemin sont appelés modules \"nus\". De tels modules ne sont pas autorisés lors de l'importation.\n\nPar exemple, cette `import` n'est pas valide:\n```js\nimport {sayHi} from 'sayHi'; // Error, \"bare\" module\n// le module doit avoir un chemin, par exemple './sayHi.js'\n```\n\nCertains environnements, tels que Node.js ou les outils de bundle autorisent les modules nus, sans chemin d'accès, car ils disposent de moyens propres de recherche de modules trouver des modules et des hooks pour les ajuster. Mais les navigateurs ne supportent pas encore les modules nus.\n\n### Compatibilité, “nomodule”\n\nLes anciens navigateurs ne comprennent pas `type=\"module\"`. Les scripts de type inconnu sont simplement ignorés. Pour eux, il est possible de fournir une solution de secours en utilisant l’attribut `nomodule` :\n\n```html run\n<script type=\"module\">\n  alert(\"Runs in modern browsers\");\n</script>\n\n<script nomodule>\n  alert(\"Modern browsers know both type=module and nomodule, so skip this\")\n  alert(\"Old browsers ignore script with unknown type=module, but execute this.\");\n</script>\n```\n\n## Construire des outils\n\nDans la vie réelle, les modules de navigateur sont rarement utilisés sous leur forme \"brute\". Généralement, nous les regroupons avec un bundle tel que [Webpack](https://webpack.js.org/) et les déployons sur le serveur de production.\n\nL'un des avantages de l'utilisation des bundles est -- qu'ils permettent de mieux contrôler la façon dont les modules sont résolus, permettant ainsi des modules nus et bien plus encore, comme les modules CSS / HTML.\n\nLes outils de construction font ce qui suit:\n\n1. Prenons un module \"principal\", celui qui est destiné à être placé dans `<script type=\"module\">` dans le HTML.\n2. Analyser ses dépendances : importations puis importations d'importations etc.\n3. Construire un seul fichier avec tous les modules (ou plusieurs fichiers configurables), en remplaçant les appels `import` natifs par des fonctions d’assemblage, pour que cela fonctionne. Les types de modules \"spéciaux\" tels que les modules HTML/CSS sont également pris en charge.\n4. Dans le processus, d'autres transformations et optimisations peuvent être appliquées:\n    - Le code inaccessible est supprimé.\n    - Les exportations non utilisées sont supprimées (\"tree-shaking\").\n    - Les instructions spécifiques au développement telles que `console` et le `debugger` sont supprimées.\n    - La syntaxe JavaScript moderne et ultramoderne peut être transformée en une ancienne version dotée de fonctionnalités similaires avec [Babel](https://babeljs.io/).\n    - Le fichier résultant est minifié (espaces supprimés, variables remplacées par des noms plus courts, etc.).\n\nSi nous utilisons des outils d'ensemble, alors que les scripts sont regroupés dans un seul fichier (ou quelques fichiers), les instructions `import/export` contenues dans ces scripts sont remplacées par des fonctions spéciales de regroupeur. Ainsi, le script \"fourni\" résultant ne contient aucune `import/export`, il ne nécessite pas `type=\"module\"`, et nous pouvons le mettre dans un script standard:\n\n```html\n<!-- En supposant que nous ayons bundle.js d'un outil tel que Webpack -->\n<script src=\"bundle.js\"></script>\n```\n\nCela dit, les modules natifs sont également utilisables. Nous n’utilisons donc pas Webpack ici: vous pourrez le configurer plus tard.\n\n## Résumé\n\nPour résumer, les concepts de base sont les suivants:\n\n1. Un module est un fichier. Pour que `import/export` fonctionne, les navigateurs ont besoin de `<script type=\"module\">`. Les modules ont plusieurs différences:\n    - Différé par défaut.\n    - Async fonctionne sur les scripts en ligne.\n    - Pour charger des scripts externes d'une autre origine (domain/protocol/port), des en-têtes CORS sont nécessaires.\n    - Les scripts externes en double sont ignorés.\n2. Les modules ont leur propre portée globale et leurs fonctionnalités d’échange via `import/export`.\n3. Les modules utilisent toujours `use strict`.\n4. Le code des modules est exécuté une seule fois. Les exportations sont créées une fois et partagées entre les importateurs\n\nLorsque nous utilisons des modules, chaque module implémente la fonctionnalité et l'exporte. Nous utilisons ensuite `import` pour l’importer directement là où il le faut. Le navigateur charge et exécute les scripts automatiquement.\n\nEn production, les gens utilisent souvent des \"bundlers\" tels que [Webpack](https://webpack.js.org) qui regroupe des modules pour des raisons de performances ou pour d’autres raisons.\n\nDans le chapitre suivant, nous verrons plus d’exemples de modules et comment des choses peuvent être importé / exporté.\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/say.view/index.html",
    "content": "<!doctype html>\n<script type=\"module\">\n  import {sayHi} from './say.js';\n\n  document.body.innerHTML = sayHi('John');\n</script>\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/say.view/say.js",
    "content": "export function sayHi(user) {\n  return `Hello, ${user}!`;\n}\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/scopes-working.view/hello.js",
    "content": "import {user} from './user.js';\n\ndocument.body.innerHTML = user; // John\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/scopes-working.view/index.html",
    "content": "<!doctype html>\n<script type=\"module\" src=\"hello.js\"></script>\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/scopes-working.view/user.js",
    "content": "export let user = \"John\";\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/scopes.view/hello.js",
    "content": "alert(user); // pas de variable user (chaque module a des variables indépendantes)\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/scopes.view/index.html",
    "content": "<!doctype html>\n<script type=\"module\" src=\"user.js\"></script>\n<script type=\"module\" src=\"hello.js\"></script>\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/scopes.view/user.js",
    "content": "let user = \"John\";\n"
  },
  {
    "path": "1-js/13-modules/02-import-export/article.md",
    "content": "# Exporter et importer\n\nLes directives d'exportation et d'importation ont plusieurs variantes de syntaxe.\n\nDans l'article précédent, nous avons vu une utilisation simple, explorons maintenant plus d'exemples.\n\n## Exporter avant les déclarations\n\nNous pouvons étiqueter n'importe quelle déclaration comme exportée en plaçant `export` devant elle, que ce soit une variable, une fonction ou une classe.\n\nPar exemple, ici toutes les exportations sont valides:\n\n```js\n// exporter un tableau\n*!*export*/!* let months = ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n\n// exporter une constante\n*!*export*/!* const MODULES_BECAME_STANDARD_YEAR = 2015;\n\n// exporter une classe\n*!*export*/!* class User {\n  constructor(name) {\n    this.name = name;\n  }\n}\n```\n\n````smart header=\"Pas de point-virgule après la classe/fonction d'exportation\"\nVeuillez noter que l'`export` avant une classe ou une fonction n'en fait pas une [function expression](info:function-expressions). C’est toujours une fonction déclaration, bien qu’elle soit exportée.\n\nLa plupart des guides de bonnes pratiques JavaScript ne recommandent pas les points-virgules après les déclarations de fonctions et de classes.\n\nC’est pourquoi il n’est pas nécessaire d’utiliser un point-virgule à la fin de `export class` et de `export function`:\n\n```js\nexport function sayHi(user) {\n  alert(`Hello, ${user}!`);\n} *!* // pas de ; à la fin */!*\n```\n\n````\n\n## Exporter en dehors des déclarations\n\nEn outre, nous pouvons mettre l'`export` séparément.\n\nIci, nous déclarons d'abord, puis exportons:\n\n```js\n// 📁 say.js\nfunction sayHi(user) {\n  alert(`Hello, ${user}!`);\n}\n\nfunction sayBye(user) {\n  alert(`Bye, ${user}!`);\n}\n\n*!*\nexport {sayHi, sayBye}; // une liste de variables exportées\n*/!*\n```\n\n… Ou, techniquement, nous pourrions également définir les fonctions d'`export` au-dessus des fonctions.\n\n## Import *\n\nHabituellement, nous mettons une liste de ce qu'il faut importer entre accolades `import {...}`, comme ceci:\n\n```js\n// 📁 main.js\n*!*\nimport {sayHi, sayBye} from './say.js';\n*/!*\n\nsayHi('John'); // Hello, John!\nsayBye('John'); // Bye, John!\n```\n\nMais s’il y a beaucoup à importer, nous pouvons tout importer en tant qu’objet en utilisant `import * as <obj>`, par exemple:\n\n```js\n// 📁 main.js\n*!*\nimport * as say from './say.js';\n*/!*\n\nsay.sayHi('John');\nsay.sayBye('John');\n```\n\nÀ première vue, \"importer tout\" semble être une chose tellement cool, simple a écrire, pourquoi devrions-nous explicitement énumérer ce que nous devons importer?\n\nEh bien, il y a quelques raisons.\n\n1. Lister explicitement ce qu'il faut importer donne des noms plus courts : `sayHi()` au lieu de `say.sayHi()`.\n2. La liste explicite des importations donne un meilleur aperçu de la structure du code : ce qui est utilisé et où. Cela facilite la prise en charge du code et la refactorisation.\n\n```smart header=\"N'ayez pas peur d'importer trop\"\nLes outils de construction modernes, tels que [webpack](https://webpack.js.org/) et d'autres, regroupent les modules et les optimisent pour accélérer le chargement. Ils ont également supprimé les importations inutilisées.\n\nPar exemple, si vous importer `import * as library` à partir d'une énorme bibliothèque de codes, puis n'utilisez que quelques méthodes, celles qui ne sont pas utilisées [ne seront pas incluses] (https://github.com/webpack/webpack/tree/main/ examples/harmony-unused#examplejs) dans le bundle optimisé.\n```\n\n## Import \"as\"\n\nNous pouvons également utiliser `as` pour importer sous différents noms.\n\nPar exemple, importons `sayHi` dans la variable locale `hi` par souci de concision, et importons `sayBye` en `bye`:\n\n```js\n// 📁 main.js\n*!*\nimport {sayHi as hi, sayBye as bye} from './say.js';\n*/!*\n\nhi('John'); // Hello, John!\nbye('John'); // Bye, John!\n```\n\n## Export \"as\"\n\nLa syntaxe similaire existe pour l'`export`.\n\nExportons les fonctions en tant que `hi` et `bye`:\n\n```js\n// 📁 say.js\n...\nexport {sayHi as hi, sayBye as bye};\n```\n\nMaintenant, `hi` et `bye` sont les noms à utiliser dans les importations:\n\n```js\n// 📁 main.js\nimport * as say from './say.js';\n\nsay.*!*hi*/!*('John'); // Hello, John!\nsay.*!*bye*/!*('John'); // Bye, John!\n```\n\n## Export default\n\nEn pratique, il existe principalement deux types de modules.\n\n1. Les modules qui contiennent une bibliothèque, un pack de fonctions, comme `say.js` ci-dessus.\n2. Les modules qui déclarent une seule entité, par exemple un module `user.js` qui exporte uniquement la `class User`.\n\nLa deuxième approche est généralement privilégiée, de sorte que chaque \"chose\" réside dans son propre module.\n\nNaturellement, cela nécessite beaucoup de fichiers, car toute chose veut son propre module, mais ce n’est pas un problème du tout. En fait, la navigation dans le code devient plus facile si les fichiers sont bien nommés et structurés en dossiers.\n\nLes modules fournissent une syntaxe spéciale `export default` (\"l'exportation par défaut\") afin d'améliorer l'aspect \"une chose par module\".\n\nPlacez `export default` avant l'entité à exporter:\n\n```js\n// 📁 user.js\nexport *!*default*/!* class User { // ajouter juste \"default\"\n  constructor(name) {\n    this.name = name;\n  }\n}\n```\n\nIl ne peut y avoir qu'un seul `export default` par fichier.\n\n… Et ensuite importez-le sans accolades:\n\n```js\n// 📁 main.js\nimport *!*User*/!* from './user.js'; // pas {User}, juste User\n\nnew User('John');\n```\n\nLes importations sans accolades sont plus agréables. Une erreur courante lorsque vous commencez à utiliser des modules est d’oublier les accolades. Par conséquent, rappelez-vous que l’`import` nécessite des accolades pour les exportations nommées et ne les utilise pas pour celle par défaut.\n\n| Export nommé | Export par défaut |\n|--------------|-------------------|\n| `export class User {...}` | `export default class User {...}` |\n| `import {User} from ...` | `import User from ...`|\n\nTechniquement, nous pouvons avoir à la fois des exportations par défaut et des exportations nommées dans un seul module, mais dans la pratique, les gens ne les mélangent généralement pas. Un module a soit, des exports nommés, soit celui par défaut.\n\nComme il peut y avoir au plus une exportation par défaut par fichier, l'entité exportée peut ne pas avoir de nom.\n\nPar exemple, ce sont toutes des exportations par défaut parfaitement valides:\n\n```js\nexport default class { // pas de nom de classe\n  constructor() { ... }\n}\n```\n\n```js\nexport default function(user) { // pas de nom de fonction\n  alert(`Hello, ${user}!`);\n}\n```\n\n```js\n// exporter une seule valeur sans créer de variable\nexport default ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n```\n\nNe pas donner de nom, c'est bien, car l'`export default` est unique par fichier. Par conséquent, l'importation sans accolades sait ce qu'il faut importer.\n\nSans `defaut`, une telle exportation donnerait une erreur:\n\n```js\nexport class { // Erreur! (un export autre que par défaut nécessite un nom)\n  constructor() {}\n}\n```\n\n### Le nom par \"default\"\n\nDans certaines situations, le mot clé `default` est utilisé pour référencer l'exportation par défaut.\n\nPar exemple, pour exporter une fonction séparément de sa définition:\n\n```js\nfunction sayHi(user) {\n  alert(`Hello, ${user}!`);\n}\n\n// comme si nous avions ajouté \"export default\" avant la fonction\nexport {sayHi as default};\n```\n\nOu, dans un autre cas, supposons qu'un module `user.js` exporte un élément principal par \"défaut\" et quelques éléments nommés (rarement le cas, mais ça arrive):\n\n```js\n// 📁 user.js\nexport default class User {\n  constructor(name) {\n    this.name = name;\n  }\n}\n\nexport function sayHi(user) {\n  alert(`Hello, ${user}!`);\n}\n```\n\nVoici comment importer l'exportation par défaut avec celle nommée:\n\n```js\n// 📁 main.js\nimport {*!*default as User*/!*, sayHi} from './user.js';\n\nnew User('John');\n```\n\nEt, enfin, si vous importez tout `*` comme objet, la propriété `default` est exactement l'exportation par défaut:\n\n```js\n// 📁 main.js\nimport * as user from './user.js';\n\nlet User = user.default; // l'exportation par défaut\nnew User('John');\n```\n\n### Un mot contre les exportations par défaut\n\nLes exportations nommées sont explicites. Ils nomment exactement ce qu’ils importent, nous avons donc ces informations, c’est une bonne chose.\n\nLes exportations nommées nous obligent à utiliser exactement le bon nom pour importer :\n\n```js\nimport {User} from './user.js';\n// importer {MyUser} ne fonctionnera pas, le nom devrait être {User}\n```\n\n...Alors que pour une exportation par défaut, nous choisissons toujours le nom lors de l'importation:\n\n```js\nimport User from './user.js'; // fonctionne\nimport MyUser from './user.js'; // fonctionne aussi\n// n'importe quoi pourrait être importé ..., cela continuera de fonctionner\n```\nLes membres de l'équipe peuvent donc utiliser des noms différents pour importer la même chose, et ce n'est pas bien.\n\nHabituellement, pour éviter cela et garder le code cohérent, il existe une règle voulant que les variables importées correspondent aux noms de fichier, par exemple:\n\n```js\nimport User from './user.js';\nimport LoginForm from './loginForm.js';\nimport func from '/path/to/func.js';\n...\n```\n\nNéanmoins, certaines équipes considèrent qu'il s'agit d'un grave inconvénient des exportations par défaut. Ils préfèrent donc toujours utiliser des exportations nommées. Même si une seule chose est exportée, elle est toujours exportée sous un nom, sans `default`.\n\nCela facilite également la réexportation (voir ci-dessous).\n\n## Réexportation\n\nLa syntaxe \"re-export\" `export ... from ...` permet d'importer et d'exporter immédiatement des éléments (éventuellement sous un autre nom), comme ceci:\n\n```js\nexport {sayHi} from './say.js'; // réexportez sayHi\n\nexport {default as User} from './user.js'; // réexportez default\n```\n\nPourquoi cela peut être nécessaire ? Voyons un cas d'utilisation pratique.\n\nImaginez, nous écrivons un \"package\": un dossier avec beaucoup de modules, avec une partie des fonctionnalités exportées à l'extérieur (des outils comme NPM nous permettent de publier et de distribuer de tels packages, mais nous n'avons pas à les utiliser), et de nombreux modules ne sont que des \"helpers\", destinés à une utilisation interne dans d'autres modules de package.\n\nLa structure de fichier pourrait être comme ceci :\n```\nauth/\n    index.js\n    user.js\n    helpers.js\n    tests/\n        login.js\n    providers/\n        github.js\n        facebook.js\n        ...\n```\n\nNous aimerions exposer les fonctionnalités du paquet via un seul point d’entrée.\n\nEn d'autres termes, une personne souhaitant utiliser notre package ne doit importer que depuis le \"fichier principal\" `auth/index.js`.\n\nComme ceci :\n\n```js\nimport {login, logout} from 'auth/index.js'\n```\n\nLe \"fichier principal\", `auth / index.js` exporte toutes les fonctionnalités que nous aimerions fournir dans notre package.\n\nL'idée est que les tiers, les développeurs qui utilisent notre package, ne doivent pas se mêler de sa structure interne, rechercher des fichiers dans notre dossier de packages. Nous n'exportons que ce qui est nécessaire dans `auth / index.js` et gardons le reste caché des regards indiscrets.\n\nLa fonctionnalité exportée étant dispersée dans le package, nous pouvons l'importer dans `auth / index.js` et l'exporter:\n\n```js\n// 📁 auth/index.js\n\n// importer les login / logout et les exporter immédiatement\nimport {login, logout} from './helpers.js';\nexport {login, logout};\n\n// importer par défaut en tant qu'utilisateur et l'exporter\nimport User from './user.js';\nexport {User};\n...\n```\n\nMaintenant, les utilisateurs de notre paquet peuvent `import {login} from \"auth/index.js\"`.\n\nLa syntaxe `export ... from ...` est juste une notation plus courte pour importer et exporter directement:\n\n```js\n// 📁 auth/index.js\n// re-export login/logout\nexport {login, logout} from './helpers.js';\n\n// re-export l'exportation par défaut en tant qu'User\nexport {default as User} from './user.js';\n...\n```\n\nLa différence notable entre `export ... from` et `import/export` est que les modules réexportés ne sont pas disponibles dans le fichier actuel. Donc, dans l'exemple ci-dessus de `auth/index.js`, nous ne pouvons pas utiliser les fonctions `login/logout` réexportées.\n\n### Ré-exportation de l'exportation par défaut\n\nL'exportation par défaut nécessite un traitement séparé lors de la réexportation.\n\nSupposons que nous ayons `user.js` avec le `export default class User` et que nous souhaitons le réexporter :\n\n```js\n// 📁 user.js\nexport default class User {\n  // ...\n}\n```\n\nOn peut y rencontrer deux problèmes :\n\n1. `export User from './user.js'` çe ne fonctionnera pas... Cela conduirait à une erreur de syntaxe.\n\n    Pour réexporter l'exportation par défaut, nous devrions écrire `export {default as User}`, comme dans l'exemple ci-dessus.\n\n2. `export * from './user.js'` ne réexporte que les exportations nommées, et ignore celle par défaut.\n\n    Si nous souhaitons réexporter l'export nommé et l'export par défaut, deux instructions sont nécessaires:\n    ```js\n    export * from './user.js'; // réexporter les exportations nommées\n    export {default} from './user.js'; // réexporter l'exportation par défaut\n    ```\n\nCes bizarreries de réexporter une exportation par défaut sont l'une des raisons pour lesquelles certains développeurs n'aiment pas les exportations par défaut et préfèrent les exportations nommées.\n\n## Résumé\n\nVoici tous les types d'`export` que nous avons abordés dans ce chapitre et dans les chapitres précédents.\n\nVous pouvez vérifier vous-même en les lisant et en vous rappelant leur signification:\n\n- Avant la déclaration d'une classe / fonction / ..:\n  - `export [default] class/function/variable ...`\n- Exportation autonome:\n  - `export {x [as y], ...}`.\n- Réexportation:\n  - `export {x [as y], ...} from \"module\"`\n  - `export * from \"module\"` (ne ré-exporte pas par défaut).\n  - `export {default [as y]} from \"module\"` (ré-export par défaut).\n\nImport:\n\n- Importations d’exports nommés :\n  - `import {x [as y], ...} from \"module\"`\n- Importation de l’export par défaut :\n  - `import x from \"module\"`\n  - `import {default as x} from \"module\"`\n- Tout importer :\n  - `import * as obj from \"module\"`\n- Importer le module (son code s'exécute), mais ne l'affecte pas à une variable :\n  - `import \"module\"`\n\nNous pouvons mettre des déclarations `import/export` en haut ou en bas d'un script, cela n'a pas d'importance.\n\nDonc, techniquement, ce code est correct:\n```js\nsayHi();\n\n// ...\n\nimport {sayHi} from './say.js'; // importer à la fin du fichier\n```\n\nEn pratique, les importations se font généralement au début du fichier, mais ce n'est que pour des raisons de commodité.\n\n**Veuillez noter que les instructions import/export ne fonctionnent pas si elles sont à l'intérieur `{...}`.**\n\nUne importation conditionnelle, comme celle-ci, ne fonctionnera pas:\n```js\nif (something) {\n  import {sayHi} from \"./say.js\"; // Erreur: l'importation doit être au plus haut niveau\n}\n```\n\n...Mais que se passe-t-il si nous devons vraiment importer quelque chose de manière conditionnelle? Ou au bon moment? Aimez-vous, charger un module sur demande, quand c'est vraiment nécessaire?\n\nNous verrons les importations dynamiques dans le chapitre suivant.\n"
  },
  {
    "path": "1-js/13-modules/03-modules-dynamic-imports/article.md",
    "content": "# Importations dynamiques\n\nLes déclarations d'exportation et d'importation décrites dans les chapitres précédents sont appelées \"statiques\". La syntaxe est très simple et stricte.\n\nPremièrement, nous ne pouvons générer dynamiquement aucun paramètre d'`import`.\n\nLe chemin du module doit être une chaîne de caractères, il ne peut pas être un appel de fonction. Exemple, cela ne fonctionnera pas:\n\n```js\nimport ... from *!*getModuleName()*/!*; // Erreur, seulement une chaîne de caractères est autorisé\n```\n\nDeuxièmement, nous ne pouvons pas importer de manière conditionnelle ou au moment de l’exécution:\n\n```js\nif(...) {\n  import ...; // Erreur, pas autorisé!\n}\n\n{\n  import ...; // Erreur, nous ne pouvons pas importer dans un bloc\n}\n```\n\nC’est parce que `import`/`export` vise à fournir une structure de base à la structure du code. C’est une bonne chose, car la structure du code peut être analysée, les modules peuvent être rassemblés et regroupés dans un fichier à l’aide d’outils spéciaux, les exportations inutilisées peuvent être supprimées (\"tree-shaken\"). Cela n’est possible que parce que la structure des importations / exportations est simple et fixe.\n\nMais comment importer un module de manière dynamique, à la demande?\n\n## L'expression import()\n\nL'expression `import(module)` charge le module et renvoie une promesse résolue en un objet de module contenant toutes ses exportations. Il peut être appelé de n’importe quel endroit du code.\n\nNous pouvons l’utiliser dynamiquement à n’importe quel endroit du code, par exemple:\n\n```js\nlet modulePath = prompt(\"Which module to load?\");\n\nimport(modulePath)\n  .then(obj => <module object>)\n  .catch(err => <loading error, e.g. si ce module n'existe pas>)\n```\n\nOu bien, nous pourrions utiliser `let module = await import(modulePath)` s'il se trouve dans une fonction asynchrone.\n\nPar exemple, si nous avons le module suivant, `say.js`:\n\n```js\n// 📁 say.js\nexport function hi() {\n  alert(`Hello`);\n}\n\nexport function bye() {\n  alert(`Bye`);\n}\n```\n\n...Alors l'importation dynamique peut être comme ça:\n\n```js\nlet {hi, bye} = await import('./say.js');\n\nhi();\nbye();\n```\n\nOu, si `say.js` a l'exportation par défaut:\n\n```js\n// 📁 say.js\nexport default function() {\n  alert(\"Module loaded (export default)!\");\n}\n```\n\n...Ensuite, pour y accéder, nous pouvons utiliser la propriété `default` de l'objet module:\n\n```js\nlet obj = await import('./say.js');\nlet say = obj.default;\n// ou en une ligne: let {default: say} = await import('./say.js');\n\nsay();\n```\n\nVoici l’exemple complet:\n\n[codetabs src=\"say\" current=\"index.html\"]\n\n```smart\nLes importations dynamiques fonctionnent dans des scripts standard, elles n’exigent pas de `script type=\"module\"`.\n```\n\n```smart\nBien que `import()` ressemble à un appel de fonction, il s’agit d’une syntaxe spéciale qui utilise des parenthèses (similaire à `super()`).\n\nNous ne pouvons donc pas copier `import` dans une variable ni utiliser `call/apply` avec elle. Ce n'est pas une fonction.\n```\n"
  },
  {
    "path": "1-js/13-modules/03-modules-dynamic-imports/say.view/index.html",
    "content": "<!doctype html>\n<script>\n  async function load() {\n    let say = await import('./say.js');\n    say.hi(); // Hello!\n    say.bye(); // Bye!\n    say.default(); // Module loaded (export default)!\n  }\n</script>\n<button onclick=\"load()\">Click me</button>\n"
  },
  {
    "path": "1-js/13-modules/03-modules-dynamic-imports/say.view/say.js",
    "content": "export function hi() {\n  alert(`Hello`);\n}\n\nexport function bye() {\n  alert(`Bye`);\n}\n\nexport default function() {\n  alert(\"Module loaded (export default)!\");\n}\n"
  },
  {
    "path": "1-js/13-modules/index.md",
    "content": "# Modules\n"
  },
  {
    "path": "1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md",
    "content": "\n```js run\nlet user = {\n  name: \"John\"\n};\n\nfunction wrap(target) {\n  return new Proxy(target, {\n    get(target, prop, receiver) {\n      if (prop in target) {\n        return Reflect.get(target, prop, receiver);\n      } else {\n        throw new ReferenceError(`Property doesn't exist: \"${prop}\"`)\n      }\n    }\n  });\n}\n\nuser = wrap(user);\n\nalert(user.name); // John\nalert(user.age); // ReferenceError: Property doesn't exist: \"age\"\n```\n"
  },
  {
    "path": "1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md",
    "content": "# Erreur lors de la lecture d'une propriété inexistante\n\nHabituellement, une tentative de lecture d'une propriété inexistante renvoie `undefined`.\n\nCréez à la place un proxy qui génère une erreur pour une tentative de lecture d'une propriété inexistante.\n\nCela peut aider à détecter précocement les erreurs de programmation.\n\nÉcrivez une fonction `wrap(target)` qui prend un objet `target` et retourne un proxy qui ajoute cet aspect fonctionnel.\n\nVoilà comment cela devrait fonctionner:\n\n```js\nlet user = {\n  name: \"John\"\n};\n\nfunction wrap(target) {\n  return new Proxy(target, {\n*!*\n      /* your code */\n*/!*\n  });\n}\n\nuser = wrap(user);\n\nalert(user.name); // John\n*!*\nalert(user.age); // ReferenceError: la propriété n'existe pas : \"age\"\n*/!*\n```\n"
  },
  {
    "path": "1-js/99-js-misc/01-proxy/02-array-negative/solution.md",
    "content": "\n```js run\nlet array = [1, 2, 3];\n\narray = new Proxy(array, {\n  get(target, prop, receiver) {\n    if (prop < 0) {\n      // même si on y accède comme arr[1]\n      // prop est une chaîne, il faut donc la convertir en nombre\n      prop = +prop + target.length;\n    }\n    return Reflect.get(target, prop, receiver);\n  }\n});\n\n\nalert(array[-1]); // 3\nalert(array[-2]); // 2\n```\n"
  },
  {
    "path": "1-js/99-js-misc/01-proxy/02-array-negative/task.md",
    "content": "\n# Accès au tableau [-1]\n\nDans certains langages de programmation, nous pouvons accéder aux éléments du tableau à l'aide d'index négatifs, comptés à partir de la fin.\n\ncomme ça:\n\n```js\nlet array = [1, 2, 3];\n\narray[-1]; // 3, le premier élément en partant de la fin\narray[-2]; // 2, le second élément en partant de la fin\narray[-3]; // 1, le troisième élément en partant de la fin\n```\n\nEn d'autres termes, `array[-N]` est identique à `array[array.length - N]`.\n\nCréez un proxy pour implémenter ce comportement.\n\nVoilà comment cela devrait fonctionner:\n\n```js\nlet array = [1, 2, 3];\n\narray = new Proxy(array, {\n  /* your code */\n});\n\nalert( array[-1] ); // 3\nalert( array[-2] ); // 2\n\n// Les autres fonctionnalités de array doivent être conservées\n```\n"
  },
  {
    "path": "1-js/99-js-misc/01-proxy/03-observable/solution.md",
    "content": "La solution se compose de deux parties:\n\n1. Chaque fois que `.observe(handler)` est appelé, nous devons nous souvenir du handler quelque part, pour pouvoir l'appeler plus tard. Nous pouvons stocker des handler directement dans l'objet, en utilisant notre symbole comme clé de propriété\n2. Nous avons besoin d'un proxy avec le piège `set` pour appeler les handler en cas de changement\n\n```js run\nlet handlers = Symbol('handlers');\n\nfunction makeObservable(target) {\n  // 1. initialiser le stockage de l'handler\n  target[handlers] = [];\n\n  // Stocker la fonction de l'handler dans un tableau pour les appels futurs\n  target.observe = function(handler) {\n    this[handlers].push(handler);\n  };\n\n  // 2. Créer un proxy pour gérer les modifications\n  return new Proxy(target, {\n    set(target, property, value, receiver) {\n      let success = Reflect.set(...arguments); // transmettre l'opération à l'objet\n      if (success) { // s'il n'y a pas eu d'erreur lors de la définition de la propriété\n        // appeler tous les handler\n        target[handlers].forEach(handler => handler(property, value));\n      }\n      return success;\n    }\n  });\n}\n\nlet user = {};\n\nuser = makeObservable(user);\n\nuser.observe((key, value) => {\n  alert(`SET ${key}=${value}`);\n});\n\nuser.name = \"John\";\n```\n"
  },
  {
    "path": "1-js/99-js-misc/01-proxy/03-observable/task.md",
    "content": "\n# Observable\n\nCréez une fonction `makeObservable(target)` qui \"rend l'objet observable\" en renvoyant un proxy.\n\nVoici comment cela devrait fonctionner:\n\n```js run\nfunction makeObservable(target) {\n  /* your code */\n}\n\nlet user = {};\nuser = makeObservable(user);\n\nuser.observe((key, value) => {\n  alert(`SET ${key}=${value}`);\n});\n\nuser.name = \"John\"; // alerts: SET name=John\n```\n\nEn d'autres termes, un objet retourné par `makeObservable` est exactement comme celui d'origine, mais possède également la méthode `observe(handler)` qui définit la fonction de `handler` à appeler lors de tout changement de propriété.\n\nChaque fois qu'une propriété change, le `handler(key, value)` est appelé avec le nom et la valeur de la propriété.\n\nP.S. Dans cette tâche, veillez uniquement à écrire sur une propriété. D'autres opérations peuvent être implémentées de manière similaire.\n"
  },
  {
    "path": "1-js/99-js-misc/01-proxy/article.md",
    "content": "# Proxy et Reflect\n\nUn objet `Proxy` encapsule un autre objet et intercepte des opérations, comme la lecture / écriture de propriétés et d'autres, éventuellement en les manipulant de lui-même ou en permettant à l'objet de les gérer de manière transparente.\n\nLes proxys sont utilisés dans de nombreuses bibliothèques et certains frameworks de navigateur. Nous verrons de nombreux cas pratiques dans cet article.\n\n## Proxy\n\nLa syntaxe:\n\n```js\nlet proxy = new Proxy(target, handler)\n```\n\n- `target` (cible) -- est un objet à envelopper, cela peut être n'importe quoi, y compris des fonctions.\n- `handler` -- configuration du proxy: un objet avec des \"pièges\" qui interceptent les opérations. - par exemple. `get` pour lire une propriété de `target`, `set` pour écrire une propriété dans `target`, etc.\n\nPour les opérations sur le `proxy`, s'il existe un piège correspondant dans le `handler`, il s'exécute et le proxy a une chance de le gérer, sinon l'opération est effectuée sur `target`.\n\nComme exemple de départ, créons un proxy sans aucun piège:\n\n```js run\nlet target = {};\nlet proxy = new Proxy(target, {}); // handler vide\n\nproxy.test = 5; // écrire dans proxy (1)\nalert(target.test); // 5, la propriété est apparue dans target!\n\nalert(proxy.test); // 5, nous pouvons aussi la lire à partir du proxy (2)\n\nfor(let key in proxy) alert(key); // test, les itérations fonctionne (3)\n```\n\nComme il n'y a pas de pièges, toutes les opérations sur le `proxy` sont transmises à `target`.\n\n1. Une opération d'écriture `proxy.test =` définit la valeur sur `target`.\n2. Une opération de lecture `proxy.test` renvoie la valeur de `target`.\n3. L'itération sur le `proxy` renvoie les valeurs de `target`.\n\nComme nous pouvons le voir, sans aucun piège, le `proxy` est un \"wrapper transparent\" autour de `target`.\n\n![](proxy.svg)\n\nLe `proxy` est un \"objet exotique\" spécial. Il n'a pas de propriétés propres. Avec un `handler` vide, il transfère de manière transparente les opérations vers `target`.\n\nPour activer plus de fonctionnalités, ajoutons des pièges.\n\nQue pouvons-nous intercepter avec eux?\n\nPour la plupart des opérations sur les objets, il existe une soi-disant \"méthode interne\" dans la spécification JavaScript qui décrit comment cela fonctionne au plus bas niveau. Par exemple `[[Get]]`, la méthode interne pour lire une propriété, `[[Set]]`, la méthode interne pour écrire une propriété, etc. Ces méthodes ne sont utilisées que dans la spécification, nous ne pouvons pas les appeler directement par leur nom.\n\nLes pièges proxy interceptent les invocations de ces méthodes. Ils sont répertoriés dans le [Spécification du proxy](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots) et dans le tableau ci-dessous\n\nPour chaque méthode interne, il y a un piège dans ce tableau: le nom de la méthode que nous pouvons ajouter au `handler` du `new Proxy` pour intercepter l'opération:\n\n| Méthode interne | Méthode d'handler | Se déclenche lorsque... |\n|-----------------|----------------|-------------|\n| `[[Get]]` | `get` | lit une propriété |\n| `[[Set]]` | `set` | écrit une propriété |\n| `[[HasProperty]]` | `has` | utilise l'opérateur `in` |\n| `[[Delete]]` | `deleteProperty` | utilise l'opérateur `delete`  |\n| `[[Call]]` | `apply` | appel une fonction |\n| `[[Construct]]` | `construct` | utilise l'opérateur `new` |\n| `[[GetPrototypeOf]]` | `getPrototypeOf` | [Object.getPrototypeOf](mdn:/JavaScript/Reference/Global_Objects/Object/getPrototypeOf) |\n| `[[SetPrototypeOf]]` | `setPrototypeOf` | [Object.setPrototypeOf](mdn:/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) |\n| `[[IsExtensible]]` | `isExtensible` | [Object.isExtensible](mdn:/JavaScript/Reference/Global_Objects/Object/isExtensible) |\n| `[[PreventExtensions]]` | `preventExtensions` | [Object.preventExtensions](mdn:/JavaScript/Reference/Global_Objects/Object/preventExtensions) |\n| `[[DefineOwnProperty]]` | `defineProperty` | [Object.defineProperty](mdn:/JavaScript/Reference/Global_Objects/Object/defineProperty), [Object.defineProperties](mdn:/JavaScript/Reference/Global_Objects/Object/defineProperties) |\n| `[[GetOwnProperty]]` | `getOwnPropertyDescriptor` | [Object.getOwnPropertyDescriptor](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor), `for..in`, `Object.keys/values/entries` |\n| `[[OwnPropertyKeys]]` | `ownKeys` | [Object.getOwnPropertyNames](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames), [Object.getOwnPropertySymbols](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertySymbols), `for..in`, `Object.keys/values/entries` |\n\n```warn header=\"Invariants\"\nJavaScript applique certains invariants -- conditions qui doivent être remplies par des méthodes et des pièges internes.\n\nLa plupart d'entre eux sont destinés aux valeurs de retour:\n- `[[Set]]` doit retourner `true` si la valeur a été écrite avec succès, sinon `false`.\n- `[[Delete]]` doit retourner `true` si la valeur a été supprimée avec succès, sinon `false`.\n- ...et ainsi de suite, nous en verrons plus dans les exemples ci-dessous.\n\nIl y a d'autres invariants, comme:\n- `[[GetPrototypeOf]]`, appliqué à l'objet proxy doit renvoyer la même valeur que `[[GetPrototypeOf]]` appliquée à l'objet cible de l'objet proxy. En d'autres termes, la lecture du prototype d'un proxy doit toujours renvoyer le prototype de l'objet cible.\n\nLes pièges peuvent intercepter ces opérations, mais ils doivent suivre ces règles.\n\nLes invariants garantissent un comportement correct et cohérent des fonctionnalités du langage. La liste complète des invariants est dans [la spécification](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots). Vous ne les violerez probablement pas si vous ne faites pas quelque chose de bizarre.\n```\n\nVoyons comment cela fonctionne dans des cas pratiques.\n\n## Valeur par défaut avec le piège \"get\"\n\nLes pièges les plus courants concernent les propriétés de lecture / écriture.\n\nPour intercepter la lecture, l'`handler` doit avoir une méthode `get (target, property, receiver)`.\n\nIl se déclenche lorsqu'une propriété est lue, avec les arguments suivants:\n\n- `target` -- est l'objet cible, celui passé comme premier argument au `new proxy`,\n- `property` -- nom de la propriété,\n- `receiver` -- si la propriété cible est un getter, le `receiver` est l'objet qui sera utilisé comme `this` dans son appel. Habituellement, c'est l'objet `proxy` lui-même (ou un objet qui en hérite, si nous héritons du proxy). Pour l'instant, nous n'avons pas besoin de cet argument, il sera donc expliqué plus en détail plus tard.\n\nUtilisons `get` pour implémenter les valeurs par défaut d'un objet.\n\nNous allons créer un tableau numérique qui renvoie `0` pour les valeurs inexistantes.\n\nHabituellement, quand on essaie d'obtenir un élément de tableau non existant, il est `undefined`, mais nous encapsulerons un tableau normal dans le proxy qui interceptera la lecture et retournera `0` s'il n'y a pas une telle propriété:\n\n```js run\nlet numbers = [0, 1, 2];\n\nnumbers = new Proxy(numbers, {\n  get(target, prop) {\n    if (prop in target) {\n      return target[prop];\n    } else {\n      return 0; // valeur par défaut\n    }\n  }\n});\n\n*!*\nalert( numbers[1] ); // 1\nalert( numbers[123] ); // 0 (élément inexistant)\n*/!*\n```\n\nComme nous pouvons le voir, c'est assez facile à faire avec un piège `get`.\n\nNous pouvons utiliser `Proxy` pour implémenter n'importe quelle logique pour les valeurs \"par défaut\".\n\nImaginez que nous ayons un dictionnaire, avec des phrases et leurs traductions:\n\n```js run\nlet dictionary = {\n  'Hello': 'Hola',\n  'Bye': 'Adiós'\n};\n\nalert( dictionary['Hello'] ); // Hola\nalert( dictionary['Welcome'] ); // undefined\n```\n\nÀ l'heure actuelle, s'il n'y a pas de phrase, la lecture de `dictionary` renvoie `undefined`. Mais en pratique, laisser une phrase non traduite est généralement mieux que `undefined`. Faisons donc renvoyer une phrase non traduite dans ce cas au lieu de `undefined`.\n\nPour y parvenir, nous allons envelopper le `dictionary` dans un proxy qui intercepte les opérations de lecture:\n\n```js run\nlet dictionary = {\n  'Hello': 'Hola',\n  'Bye': 'Adiós'\n};\n\ndictionary = new Proxy(dictionary, {\n*!*\n  get(target, phrase) { // intercepter la lecture d'une propriété du dictionnaire\n*/!*\n    if (phrase in target) { // si nous l'avons dans le dictionnaire\n      return target[phrase]; // retourne la traduction\n    } else {\n      // sinon, retourne la phrase non traduite\n      return phrase;\n    }\n  }\n});\n\n// Rechercher des phrases arbitraires dans le dictionnaire!\n// Au pire, ils ne sont pas traduits\nalert( dictionary['Hello'] ); // Hola\n*!*\nalert( dictionary['Welcome to Proxy']); // Welcome to Proxy (pas de traduction)\n*/!*\n```\n\n````smart\nVeuillez noter comment le proxy écrase la variable:\n\n```js\ndictionary = new Proxy(dictionary, ...);\n```\n\nLe proxy doit remplacer totalement l'objet cible partout. Personne ne devrait jamais référencer l'objet cible après qu'il a été utilisé comme target du proxy.\n````\n\n## Validation avec le piège \"set\"\n\nDisons que nous voulons un tableau exclusivement pour les nombres. Si une valeur d'un autre type est ajoutée, il devrait y avoir une erreur.\n\nLe piège `set` se déclenche lorsqu'une propriété est écrite.\n\n`set(target, property, value, receiver)`:\n\n- `target` -- est l'objet cible, celui passé comme premier argument au `new proxy`,\n- `property` -- nom de la propriété,\n- `value` -- valeur de la propriété,\n- `receiver` -- similaire au piège `get`, ne concerne que les propriétés du setter.\n\nLe piège `set` doit retourner `true` si le réglage est réussi et `false` dans le cas contraire (déclenche `TypeError`).\n\nUtilisons-le pour valider de nouvelles valeurs:\n\n```js run\nlet numbers = [];\n\nnumbers = new Proxy(numbers, { // (*)\n*!*\n  set(target, prop, val) { // intercepter l'écriture de propriété\n*/!*\n    if (typeof val == 'number') {\n      target[prop] = val;\n      return true;\n    } else {\n      return false;\n    }\n  }\n});\n\nnumbers.push(1); // ajouté avec succès\nnumbers.push(2); // ajouté avec succès\nalert(\"Length is: \" + numbers.length); // 2\n\n*!*\nnumbers.push(\"test\"); // TypeError ('set' sur proxy retourne false)\n*/!*\n\nalert(\"This line is never reached (error in the line above)\");\n```\n\nNote: la fonctionnalité intégrée des tableaux fonctionne toujours! Les valeurs sont ajoutées par `push`. La propriété `length` augmente automatiquement lorsque des valeurs sont ajoutées. Notre proxy ne casse rien.\n\nNous n'avons pas à remplacer les méthodes de tableau à valeur ajoutée comme `push` et `unshift`, etc., pour y ajouter des vérifications, car en interne, elles utilisent l'opération `[[Set]]` interceptée par le proxy.\n\nLe code est donc propre et concis.\n\n```warn header=\"N'oubliez pas de retouner `true`\"\nComme indiqué ci-dessus, il y a des invariants à tenir\n\nPour `set`, il doit retourner `true` pour une écriture réussie.\n\nSi nous oublions de le faire ou retournons une valeur fausse, l'opération déclenche `TypeError`.\n```\n\n## Itération avec \"ownKeys\" et \"getOwnPropertyDescriptor\"\n\nLa boucle `Object.keys`, `for..in` et la plupart des autres méthodes qui itèrent sur les propriétés des objets utilisent la méthode interne `[[OwnPropertyKeys]]` (interceptée par le piège `ownKeys`) pour obtenir une liste des propriétés.\n\nCes méthodes diffèrent dans les détails:\n- `Object.getOwnPropertyNames(obj)` renvoie des clés non symboliques.\n- `Object.getOwnPropertySymbols(obj)` renvoie des clés symboliques.\n- `Object.keys/values()` renvoie les clés / valeurs non symboliques avec l'indicateur `enumerable` (les indicateurs de propriété ont été expliqués dans l'article <info:property-descriptors>).\n- `for..in` boucle sur les clés non symboliques avec le drapeau `enumerable`, ainsi que sur les clés prototypes.\n\n... Mais tous commencent par cette liste.\n\nDans l'exemple ci-dessous, nous utilisons le piège `ownKeys` pour faire une boucle `for..in` sur `user`, ainsi que `Object.keys` et `Object.values`, pour ignorer les propriétés commençant par un trait de soulignement`_` :\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30,\n  _password: \"***\"\n};\n\nuser = new Proxy(user, {\n*!*\n  ownKeys(target) {\n*/!*\n    return Object.keys(target).filter(key => !key.startsWith('_'));\n  }\n});\n\n// \"ownKeys\" filtre _password\nfor(let key in user) alert(key); // name, après: age\n\n// même effet sur ces méthodes:\nalert( Object.keys(user) ); // name,age\nalert( Object.values(user) ); // John,30\n```\n\nJusqu'à présent, cela fonctionne.\n\nBien que, si nous renvoyons une clé qui n'existe pas dans l'objet, `Object.keys` ne la répertoriera pas:\n\n```js run\nlet user = { };\n\nuser = new Proxy(user, {\n*!*\n  ownKeys(target) {\n*/!*\n    return ['a', 'b', 'c'];\n  }\n});\n\nalert( Object.keys(user) ); // <empty>\n```\n\nPourquoi? La raison est simple: `Object.keys` renvoie uniquement les propriétés avec l'indicateur `enumerable`. Pour le vérifier, il appelle la méthode interne `[[GetOwnProperty]]` pour chaque propriété à obtenir [son descripteur](info:property-descriptors). Et ici, comme il n'y a pas de propriété, son descripteur est vide, pas d'indicateur `enumerable`, il est donc ignoré.\n\nPour que `Object.keys` renvoie une propriété, nous avons besoin qu'elle existe dans l'objet, avec l'indicateur `enumerable`, ou nous pouvons intercepter les appels à `[[GetOwnProperty]]` (le piège `getOwnPropertyDescriptor` le fait), et renvoyer un descripteur avec `enumerable: true`.\n\nVoici un exemple:\n\n```js run\nlet user = { };\n\nuser = new Proxy(user, {\n  ownKeys(target) { // appelé une fois pour obtenir une liste de propriétés\n    return ['a', 'b', 'c'];\n  },\n\n  getOwnPropertyDescriptor(target, prop) { // appelé pour chaque propriété\n    return {\n      enumerable: true,\n      configurable: true\n      /* ...other flags, probable \"value:...\" */\n    };\n  }\n\n});\n\nalert( Object.keys(user) ); // a, b, c\n```\n\nNotons encore une fois: nous n'avons besoin d'intercepter `[[GetOwnProperty]]` que si la propriété est absente dans l'objet.\n\n## Propriétés protégées avec \"deleteProperty\" et autres pièges\n\nIl existe une convention répandue selon laquelle les propriétés et les méthodes précédées d'un trait de soulignement `_` sont internes. Ils ne doivent pas être accessibles depuis l'extérieur de l'objet.\n\nTechniquement, c'est possible:\n\n```js run\nlet user = {\n  name: \"John\",\n  _password: \"secret\"\n};\n\nalert(user._password); // secret\n```\n\nUtilisons des proxys pour empêcher tout accès aux propriétés commençant par `_`.\n\nNous aurons besoin des pièges:\n- `get` lancer une erreur lors de la lecture d'une telle propriété,\n- `set` lancer une erreur lors de l'écriture,\n- `deleteProperty` lancer une erreur lors de la suppression,\n- `ownKeys` pour exclure les propriétés commençant par `_` de `for..in` et les méthodes comme `Object.keys`.\n\nVoici le code:\n\n```js run\nlet user = {\n  name: \"John\",\n  _password: \"***\"\n};\n\nuser = new Proxy(user, {\n*!*\n  get(target, prop) {\n*/!*\n    if (prop.startsWith('_')) {\n      throw new Error(\"Access denied\");\n    }\n    let value = target[prop];\n    return (typeof value === 'function') ? value.bind(target) : value; // (*)\n  },\n*!*\n  set(target, prop, val) { // intercepter l'écriture de propriété\n*/!*\n    if (prop.startsWith('_')) {\n      throw new Error(\"Access denied\");\n    } else {\n      target[prop] = val;\n      return true;\n    }\n  },\n*!*\n  deleteProperty(target, prop) { // pour intercepter la suppression de propriété\n*/!*\n    if (prop.startsWith('_')) {\n      throw new Error(\"Access denied\");\n    } else {\n      delete target[prop];\n      return true;\n    }\n  },\n*!*\n  ownKeys(target) { // intercepter la liste des propriétés\n*/!*\n    return Object.keys(target).filter(key => !key.startsWith('_'));\n  }\n});\n\n// \"get\" ne permet pas de lire _password\ntry {\n  alert(user._password); // Erreur: accès refusé\n} catch(e) { alert(e.message); }\n\n// \"set\" ne permet pas d'écrire _password\ntry {\n  user._password = \"test\"; // Erreur: accès refusé\n} catch(e) { alert(e.message); }\n\n// \"deleteProperty\" ne permet pas de supprimer _password\ntry {\n  delete user._password; // Erreur: accès refusé\n} catch(e) { alert(e.message); }\n\n// \"ownKeys\" filtre _password\nfor(let key in user) alert(key); // name\n```\n\nVeuillez noter les détails importants dans le piège `get`, dans la ligne `(*)`:\n\n```js\nget(target, prop) {\n  // ...\n  let value = target[prop];\n*!*\n  return (typeof value === 'function') ? value.bind(target) : value; // (*)\n*/!*\n}\n```\n\nPourquoi avons-nous besoin d'une fonction pour appeler `value.bind(target)` ?\n\nLa raison est que les méthodes d'objet, telles que `user.checkPassword()`, doivent pouvoir accéder à `_password`:\n\n```js\nuser = {\n  // ...\n  checkPassword(value) {\n    // la méthode objet doit pouvoir lire _password\n    return value === this._password;\n  }\n}\n```\n\n\nL'appel `user.checkPassword()` obtient l'`user` proxy comme `this` (l'objet avant le point devient `this`), donc quand il essaie d'accéder à `this._password`, le piège `get` s'active (il se déclenche sur n'importe quelle propriété lue) et génère une erreur.\n\nNous lions donc le contexte des méthodes objet à l'objet d'origine, `target`, dans la ligne `(*)`. Ensuite, leurs futurs appels utiliseront `target` comme `this`, sans aucun piège.\n\nCette solution fonctionne généralement, mais n'est pas idéale, car une méthode peut faire passer l'objet non sollicité ailleurs.\n\nEn outre, un objet peut être proxy plusieurs fois (plusieurs procurations peuvent ajouter différents \"réglages\" à l'objet), et si nous transmettons un objet non enveloppé à une méthode, il peut y avoir des conséquences inattendues.\n\nDonc, un tel proxy ne devrait pas être utilisé partout.\n\n```smart header=\"Propriétés privées d'une classe\"\nLes moteurs JavaScript modernes prennent en charge nativement les propriétés privées dans les classes, préfixées par `#`. Ils sont décrits dans l'article <info:private-protected-properties-methods>. Aucun proxy requis.\n\nCes propriétés ont cependant leurs propres problèmes. En particulier, ils ne sont pas hérités.\n```\n\n## \"In range\" avec le piège \"has\"\n\nVoyons plus d'exemples.\n\nNous avons un objet `range`:\n\n```js\nlet range = {\n  start: 1,\n  end: 10\n};\n```\n\nNous aimerions utiliser l'opérateur `in` pour vérifier qu'un nombre est `in range` (à portée).\n\nLe piège `has` intercepte l'opérateur `in`.\n\n`has(target, property)`\n\n- `target` -- est l'objet cible, passé comme premier argument à `new Proxy`,\n- `property` -- nom de la propriété\n\nVoici la démo:\n\n```js run\nlet range = {\n  start: 1,\n  end: 10\n};\n\nrange = new Proxy(range, {\n*!*\n  has(target, prop) {\n*/!*\n    return prop >= target.start && prop <= target.end;\n  }\n});\n\n*!*\nalert(5 in range); // true\nalert(50 in range); // false\n*/!*\n```\n\nbon sucre syntaxique, non? Et très simple à mettre en œuvre.\n\n## Wrapping functions: \"apply\" [#proxy-apply]\n\nNous pouvons également envelopper un proxy autour d'une fonction.\n\nLe piège `apply(target, thisArg, args)` gère l'appel d'un proxy en tant que fonction:\n\n- `target` est l'objet cible (la fonction est un objet en JavaScript),\n- `thisArg` est la valeur de `this`.\n- `args` est une liste d'arguments.\n\nPar exemple, rappelons le décorateur `delay(f, ms)`, que nous avons fait dans l'article <info:call-apply-decorators>.\n\nDans cet article, nous l'avons fait sans proxy. Un appel à `delay(f, ms)` a renvoyé une fonction qui transfère tous les appels à `f` après `ms` millisecondes.\n\nVoici l'implémentation précédente basée sur les fonctions:\n\n```js run\nfunction delay(f, ms) {\n  // retourner un wrapper qui passe l'appel à f après le délai d'expiration\n  return function() { // (*)\n    setTimeout(() => f.apply(this, arguments), ms);\n  };\n}\n\nfunction sayHi(user) {\n  alert(`Hello, ${user}!`);\n}\n\n// après ce wrapping, les appels à sayHi seront retardés de 3 secondes\nsayHi = delay(sayHi, 3000);\n\nsayHi(\"John\"); // Hello, John! (après 3 secondes)\n```\n\nComme nous l'avons déjà vu, cela fonctionne souvent. La fonction wrapper `(*)` effectue l'appel après le délai d'expiration.\n\nMais une fonction wrapper ne transmet pas les opérations de lecture / écriture de propriété ni rien d'autre. Après le wrapping, l'accès est perdu pour les propriétés des fonctions d'origine, telles que le `name`, `length` et autres:\n\n```js run\nfunction delay(f, ms) {\n  return function() {\n    setTimeout(() => f.apply(this, arguments), ms);\n  };\n}\n\nfunction sayHi(user) {\n  alert(`Hello, ${user}!`);\n}\n\n*!*\nalert(sayHi.length); // 1 (la longueur de la fonction est le nombre d'arguments dans sa déclaration)\n*/!*\n\nsayHi = delay(sayHi, 3000);\n\n*!*\nalert(sayHi.length); // 0 (dans la déclaration wrapper, il n'y a aucun argument)\n*/!*\n```\n\nLe `proxy` est beaucoup plus puissant, car il transmet tout à l'objet cible.\n\nUtilisons `Proxy` au lieu d'une fonction de \"wrapping\":\n\n```js run\nfunction delay(f, ms) {\n  return new Proxy(f, {\n    apply(target, thisArg, args) {\n      setTimeout(() => target.apply(thisArg, args), ms);\n    }\n  });\n}\n\nfunction sayHi(user) {\n  alert(`Hello, ${user}!`);\n}\n\nsayHi = delay(sayHi, 3000);\n\n*!*\nalert(sayHi.length); // 1 (*) le proxy transmet l'opération \"get length\" à la cible\n*/!*\n\nsayHi(\"John\"); // Hello, John! (après 3 secondes)\n```\n\nLe résultat est le même, mais maintenant non seulement les appels, mais toutes les opérations sur le proxy sont transférés vers la fonction d'origine. Donc, `sayHi.length` est renvoyé correctement après le retour à la ligne `(*)`.\n\nNous avons un wrapper \"plus riche\".\n\nD'autres pièges existent: la liste complète se trouve au début de cet article. Leur modèle d'utilisation est similaire à ce qui précède.\n\n## Reflect\n\n`Reflect` est un objet intégré qui simplifie la création de `Proxy`.\n\nIl a été dit précédemment que les méthodes internes, telles que `[[Get]]`, `[[Set]]` et d'autres ne sont que des spécifications, elles ne peuvent pas être appelées directement.\n\nL'objet `Reflect` rend cela possible. Ses méthodes sont des wrapper minimales autour des méthodes internes.\n\nVoici des exemples d'opérations et d'appels `Reflect` identiques:\n\n| Opération | Appel `Reflect` | Méthode interne |\n|-----------------|----------------|-------------|\n| `obj[prop]` | `Reflect.get(obj, prop)` | `[[Get]]` |\n| `obj[prop] = value` | `Reflect.set(obj, prop, value)` | `[[Set]]` |\n| `delete obj[prop]` | `Reflect.deleteProperty(obj, prop)` | `[[Delete]]` |\n| `new F(value)` | `Reflect.construct(F, value)` | `[[Construct]]` |\n| ... | ... | ... |\n\nPar exemple:\n\n```js run\nlet user = {};\n\nReflect.set(user, 'name', 'John');\n\nalert(user.name); // John\n```\n\n`Reflect` nous permet d'appeler des opérateurs (`new`, `delete` ...) en tant que fonctions (`Reflect.construct`, `Reflect.deleteProperty`, ...). C'est une capacité intéressante, mais ici, une autre chose est importante.\n\n**Pour chaque méthode interne, piégeable par `Proxy`, il existe une méthode correspondante dans `Reflect`, avec le même nom et les mêmes arguments que le piège dans `Proxy`.**\n\nNous pouvons donc utiliser `Reflect` pour transmettre une opération à l'objet d'origine.\n\nDans cet exemple, les deux pièges `get` et `set` de manière transparente (comme si elles n'existaient pas) transmettent les opérations de lecture / écriture à l'objet, affichant un message\n\n```js run\nlet user = {\n  name: \"John\",\n};\n\nuser = new Proxy(user, {\n  get(target, prop, receiver) {\n    alert(`GET ${prop}`);\n*!*\n    return Reflect.get(target, prop, receiver); // (1)\n*/!*\n  },\n  set(target, prop, val, receiver) {\n    alert(`SET ${prop}=${val}`);\n*!*\n    return Reflect.set(target, prop, val, receiver); // (2)\n*/!*\n  }\n});\n\nlet name = user.name; // affiche \"GET name\"\nuser.name = \"Pete\"; // affiche \"SET name=Pete\"\n```\n\nIci:\n\n- `Reflect.get` lit une propriété d'objet.\n- `Reflect.set` écrit une propriété d'objet et renvoie `true` en cas de succès, `false` dans le cas contraire\n\nAutrement dit, tout est simple: si un piège veut renvoyer l'appel à l'objet, il suffit d'appeler `Reflect.<method>` avec les mêmes arguments.\n\nDans la plupart des cas, nous pouvons faire de même sans `Reflect`, par exemple, la lecture d'une propriété `Reflect.get(target, prop, receiver)` peut être remplacée par `target[prop]`. Il y a cependant des nuances importantes.\n\n### Proxying a getter\n\nVoyons un exemple qui montre pourquoi `Reflect.get` est meilleur. Et nous verrons également pourquoi `get/set` a le troisième argument `receiver`, que nous n'avions pas utilisé auparavant.\n\nNous avons un objet `user` avec la propriété `_name` et un getter pour cela.\n\nVoici un proxy autour de lui:\n\n```js run\nlet user = {\n  _name: \"Guest\",\n  get name() {\n    return this._name;\n  }\n};\n\n*!*\nlet userProxy = new Proxy(user, {\n  get(target, prop, receiver) {\n    return target[prop];\n  }\n});\n*/!*\n\nalert(userProxy.name); // Guest\n```\n\nLe piège `get` est \"transparent\" ici, il renvoie la propriété d'origine et ne fait rien d'autre. Cela suffit pour notre exemple.\n\nTout semble aller bien. Mais rendons l'exemple un peu plus complexe.\n\nAprès avoir hérité d'un autre objet `admin` de l'`user`, nous pouvons observer le comportement incorrect:\n\n```js run\nlet user = {\n  _name: \"Guest\",\n  get name() {\n    return this._name;\n  }\n};\n\nlet userProxy = new Proxy(user, {\n  get(target, prop, receiver) {\n    return target[prop]; // (*) target = user\n  }\n});\n\n*!*\nlet admin = {\n  __proto__: userProxy,\n  _name: \"Admin\"\n};\n\n// Attendu: Admin\nalert(admin.name); // retourne: Guest (?!?)\n*/!*\n```\n\nLa lecture de `admin.name` devrait renvoyer `\"Admin\"`, pas `\"Guest\"`!\n\nQuel est le problème? Peut-être que nous avons fait quelque chose de mal avec l'héritage?\n\nMais si nous supprimons le proxy, tout fonctionnera comme prévu.\n\nLe problème est en fait dans le proxy, dans la ligne `(*)`.\n\n1. Lorsque nous lisons `admin.name`, comme l'objet `admin` n'a pas une telle propriété, la recherche va à son prototype.\n2. Le prototype est `userProxy`.\n3. Lors de la lecture de la propriété `name` du proxy, son piège `get` se déclenche et la renvoie à partir de l'objet d'origine en tant que `target[prop]` dans la ligne `(*)`.\n\n    Un appel à `target[prop]`, lorsque `prop` est un getter, exécute son code dans le contexte `this=target`. Le résultat est donc `this._name` de l'objet `target` d'origine , c'est-à-dire de l'`user`.\n\nPour résoudre de telles situations, nous avons besoin de `receiver`, le troisième argument du piège `get`. Il garde le bon `this` à transmettre à un getter. Dans notre cas, c'est `admin`.\n\nComment passer le contexte pour un getter? Pour une fonction régulière, nous pourrions utiliser `call/apply`, mais c'est un getter, ce n'est pas \"appelé\", juste accessible.\n\n`Reflect.get` peut faire ça. Tout fonctionnera bien si nous l'utilisons.\n\nVoici la variante corrigée:\n\n```js run\nlet user = {\n  _name: \"Guest\",\n  get name() {\n    return this._name;\n  }\n};\n\nlet userProxy = new Proxy(user, {\n  get(target, prop, receiver) { // receiver = admin\n*!*\n    return Reflect.get(target, prop, receiver); // (*)\n*/!*\n  }\n});\n\n\nlet admin = {\n  __proto__: userProxy,\n  _name: \"Admin\"\n};\n\n*!*\nalert(admin.name); // Admin\n*/!*\n```\n\nMaintenant, `receiver` garde une référence à `this` correct (c'est-à-dire `admin`), est transmis au getter en utilisant `Reflect.get` dans la ligne `(*)`.\n\nOn peut réécrire le piège encore plus court:\n\n```js\nget(target, prop, receiver) {\n  return Reflect.get(*!*...arguments*/!*);\n}\n```\n\n\nLes appels `Reflect` sont nommés exactement de la même manière que les pièges et acceptent les mêmes arguments. Ils ont été spécialement conçus de cette façon.\n\nDonc, `return Reflect...` fournit un moyen sûr et simple de faire avancer l'opération et assure qu'on oubliera rien.\n\n## Limitations du proxy\n\nLes proxys offrent un moyen unique de modifier ou d'améliorer le comportement des objets existants au niveau le plus bas. Pourtant, ce n'est pas parfait. Il y a des limites.\n\n### Objets intégrés: emplacements internes\n\nDe nombreux objets intégrés, par exemple `Map`, `Set`, `Date`, `Promise` et d'autres utilisent des «emplacements internes».\n\nCe sont des propriétés similaires, mais réservées à des fins internes uniquement. Par exemple, `Map` stocke les éléments dans l'emplacement interne `[[MapData]]`. Les méthodes intégrées y accèdent directement, pas via les méthodes internes `[[Get]]/[[Set]]`. Donc, `Proxy` ne peut pas intercepter cela.\n\nPourquoi s'en soucier? Ils sont internes de toute façon!\n\nEh bien, voici le problème. Une fois qu'un objet intégré comme celui-ci a été proxy, le proxy n'a pas ces emplacements internes, les méthodes intégrées échoueront donc.\n\nPar exemple:\n\n```js run\nlet map = new Map();\n\nlet proxy = new Proxy(map, {});\n\n*!*\nproxy.set('test', 1); // Erreur\n*/!*\n```\n\nEn interne, un `Map` stocke toutes les données dans son emplacement interne `[[MapData]]`. Le proxy n'a pas un tel emplacement. La [méthode intégrée `Map.prototype.set`](https://tc39.es/ecma262/#sec-map.prototype.set) essaie d'accéder à la propriété interne `this.[[MapData]]`, mais parce que `this=proxy`, elle ne peut pas la trouver dans le proxy et échoue.\n\nHeureusement, il existe un moyen de le corriger:\n\n```js run\nlet map = new Map();\n\nlet proxy = new Proxy(map, {\n  get(target, prop, receiver) {\n    let value = Reflect.get(...arguments);\n*!*\n    return typeof value == 'function' ? value.bind(target) : value;\n*/!*\n  }\n});\n\nproxy.set('test', 1);\nalert(proxy.get('test')); // 1 (ça fonctionne!)\n```\n\nMaintenant, cela fonctionne très bien, car le piège `get` lie les propriétés de la fonction, telles que `map.set`, à l'objet cible (`map`) lui-même.\n\nContrairement à l'exemple précédent, la valeur de `this` dans `proxy.set(...)` ne sera pas `proxy`, mais le `map` d'origine. Ainsi, lorsque l'implémentation interne de `set` essaie d'accéder à l'emplacement interne `this.[[MapData]]`, il réussit.\n\n```smart header=\"`Array` n'a pas d'emplacements internes\"\nUne exception notable: `Array` n'utilise pas d'emplacement internes. Pour des raisons historiques.\n\nIl n'y a donc pas de problème de ce type lors de l'utilisation d'un proxy.\n```\n\n### Champs privés\n\nLa même chose se produit avec les champs de classe privés.\n\nPar exemple, la méthode `getName()` accède à la propriété privée `#name` et s'arrête après le proxy:\n\n```js run\nclass User {\n  #name = \"Guest\";\n\n  getName() {\n    return this.#name;\n  }\n}\n\nlet user = new User();\n\nuser = new Proxy(user, {});\n\n*!*\nalert(user.getName()); // Erreur\n*/!*\n```\n\nLa raison est que les champs privés sont implémentés à l'aide d'emplacement internes. JavaScript n'utilise pas `[[Get]]/[[Set]]` pour y accéder.\n\nDans l'appel `getName()`, la valeur de `this` est l'`user` proxy, et il n'a pas l'emplacement avec des champs privés.\n\nEncore une fois, la solution avec la liaison de la méthode fonctionne:\n\n```js run\nclass User {\n  #name = \"Guest\";\n\n  getName() {\n    return this.#name;\n  }\n}\n\nlet user = new User();\n\nuser = new Proxy(user, {\n  get(target, prop, receiver) {\n    let value = Reflect.get(...arguments);\n    return typeof value == 'function' ? value.bind(target) : value;\n  }\n});\n\nalert(user.getName()); // Guest\n```\n\nCela dit, la solution présente des inconvénients, comme expliqué précédemment: elle expose l'objet d'origine à la méthode, ce qui peut potentiellement le faire passer plus loin et briser d'autres fonctionnalités proxy.\n\n### Proxy != target\n\nLe proxy et l'objet d'origine sont des objets différents. C'est normal, non?\n\nDonc, si nous utilisons l'objet d'origine comme clé, puis le proxy, le proxy ne peut pas être trouvé:\n\n```js run\nlet allUsers = new Set();\n\nclass User {\n  constructor(name) {\n    this.name = name;\n    allUsers.add(this);\n  }\n}\n\nlet user = new User(\"John\");\n\nalert(allUsers.has(user)); // true\n\nuser = new Proxy(user, {});\n\n*!*\nalert(allUsers.has(user)); // false\n*/!*\n```\n\nComme nous pouvons le voir, après le proxy, nous ne pouvons pas trouver d'`user` dans l'ensemble `allUsers`, car le proxy est un objet différent.\n\n```warn header=\"\"Les proxy ne peuvent pas intercepter un test d'égalité strict `===`\"\nLes proxys peuvent intercepter de nombreux opérateurs, tels que `new` (avec `construct`), `in` (avec `has`), `delete` (avec `deleteProperty`), etc.\n\nMais il n'y a aucun moyen d'intercepter un test d'égalité strict pour les objets. Un objet est strictement égal à lui-même uniquement, et aucune autre valeur.\n\nAinsi, toutes les opérations et les classes intégrées qui comparent les objets pour l'égalité feront la différence entre l'objet et le proxy. Pas de remplacement transparent ici.\n```\n\n## Proxies révocables\n\nUn proxy *révocable* est un proxy qui peut être désactivé.\n\nDisons que nous avons une ressource et que nous aimerions en fermer l'accès à tout moment.\n\nCe que nous pouvons faire, c'est de l'envelopper dans un proxy révocable, sans aucun piège. Un tel proxy transmettra les opérations à l'objet, et nous pouvons le désactiver à tout moment.\n\nLa syntaxe est:\n\n```js\nlet {proxy, revoke} = Proxy.revocable(target, handler)\n```\n\nL'appel renvoie un objet avec la fonction `proxy` et `revoke` pour le désactiver.\n\nVoici un exemple:\n\n```js run\nlet object = {\n  data: \"Valuable data\"\n};\n\nlet {proxy, revoke} = Proxy.revocable(object, {});\n\n// passer le proxy quelque part au lieu de l'objet...\nalert(proxy.data); // Valuable data\n\n// plus tard dans le code\nrevoke();\n\n// le proxy ne fonctionne plus (révoqué)\nalert(proxy.data); // Erreur\n```\n\nUn appel à `revoke()` supprime toutes les références internes à l'objet cible du proxy, de sorte qu'ils ne sont plus connectés.\n\nInitialement, `revoke` est séparé de `proxy`, de sorte que nous pouvons passer `proxy` tout en laissant `revoke` dans la portée actuelle.\n\nNous pouvons également lier la méthode `revoke` au proxy en définissant `proxy.revoke = revoke`.\n\nUne autre option est de créer une `WeakMap` qui a `proxy` comme clé et la valeur `revoke` correspondante, qui permet de trouver facilement `revoke` pour un proxy :\n\n```js run\n*!*\nlet revokes = new WeakMap();\n*/!*\n\nlet object = {\n  data: \"Valuable data\"\n};\n\nlet {proxy, revoke} = Proxy.revocable(object, {});\n\nrevokes.set(proxy, revoke);\n\n// ..plus tard dans notre code..\nrevoke = revokes.get(proxy);\nrevoke();\n\nalert(proxy.data); // Erreur (révoqué)\n```\n\nNous utilisons ici `WeakMap` au lieu de `Map` car cela ne bloquera pas le \"garbage collection\". Si un objet proxy devient \"inaccessible\" (par exemple si plus aucune variable ne le référence), `WeakMap` permet de l'effacer de la mémoire en même temps que `revoke` dont nous n'aurons plus besoin.\n\n## Références\n\n- spécification: [Proxy](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots).\n- MDN: [Proxy](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Proxy).\n\n## Résumé\n\nLe `proxy` est un wrapper autour d'un objet, qui transfère des opérations sur celui-ci à l'objet, éventuellement en piégeant certains d'entre eux.\n\nIl peut envelopper n'importe quel type d'objet, y compris les classes et les fonctions.\n\nLa syntaxe est:\n\n```js\nlet proxy = new Proxy(target, {\n  /* traps */\n});\n```\n\n... Ensuite, nous devrions utiliser le `proxy` partout au lieu de `target`. Un proxy n'a pas ses propres propriétés ou méthodes. Il intercepte une opération si l'interruption est fournie, sinon la transmet à `target`.\n\nNous pouvons piéger :\n- Lecture (`get`), écriture (`set`), suppression (`deleteProperty`) d'une propriété (même inexistante).\n- Appeler une fonction (piège `apply`).\n- L'opérateur `new` (piège `construct`).\n- De nombreuses autres opérations (la liste complète se trouve au début de l'article et dans la [documentation](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Proxy)).\n\nCela nous permet de créer des propriétés et des méthodes \"virtuelles\", d'implémenter des valeurs par défaut, des objets observables, des décorateurs de fonctions et bien plus encore.\n\nNous pouvons également envelopper un objet plusieurs fois dans différents proxys, en le décorant avec divers aspects de la fonctionnalité.\n\nL'API de [Reflect](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Reflect) est conçu pour compléter [Proxy](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Proxy). Pour tout piège `proxy`, il existe un appel `Reflect` avec les mêmes arguments. Nous devons les utiliser pour transférer des appels vers des objets cibles\n\nLes proxy ont certaines limites:\n\n- Les objets intégrés ont des \"emplacements internes\", l'accès à ceux-ci ne peut pas être proxy. Voir la solution de contournement ci-dessus.\n- Il en va de même pour les champs de classe privés, car ils sont implémentés en interne à l'aide de slots. Les appels de méthode proxy doivent donc avoir l'objet cible comme `this` pour y accéder\n- Les tests d'égalité strics `===` ne peuvent pas être interceptés\n- Performances: les benchmarks dépendent d'un moteur, mais généralement accéder à une propriété à l'aide d'un proxy simple prend un peu plus de temps. En pratique, cela n'a d'importance que pour certains objets \"bottleneck\".\n"
  },
  {
    "path": "1-js/99-js-misc/02-eval/1-eval-calculator/solution.md",
    "content": "Utilisons `eval` pour calculer l'expression mathématique :\n\n```js demo run\nlet expr = prompt(\"Type an arithmetic expression?\", '2*3+2');\n\nalert( eval(expr) );\n```\n\nL'utilisateur peut envoyer n'importe quel texte ou code cependant.\n\nPour rendre les choses sûres, et les limiter à de l'arithmétique, nous pouvons vérifier `expr` en utilisant une [expression régulière](info:regular-expressions), pour qu'elle ne contienne que des nombres et des opérateurs.\n"
  },
  {
    "path": "1-js/99-js-misc/02-eval/1-eval-calculator/task.md",
    "content": "importance: 4\n\n---\n\n# Calculatrice-eval\n\nCréez une calculatrice qui demande une expression arithmétique et retourne son résultat.\n\nIl n'y a pas besoin de vérifier l'exactitude de l'expression. Évaluez simplement la et retournez le résultat.\n\n[demo]\n"
  },
  {
    "path": "1-js/99-js-misc/02-eval/article.md",
    "content": "# Eval : exécution d'un texte code\n\nLa fonction intégrée `eval` permet d'exécuter une chaîne de caractère comprenant du code.\n\nLa syntaxe est :\n\n```js\nlet result = eval(code);\n```\n\nPar exemple:\n\n```js run\nlet code = 'alert(\"Hello\")';\neval(code); // Hello\n```\n\nUne chaîne de code peut être long, contenir des sauts à la ligne, des déclarations de fonctions, de variables et autres.\n\nLe résultat de `eval` est le résultat de la dernière instruction.\n\nFor example:\n```js run\nlet value = eval('1+1');\nalert(value); // 2\n```\n\n```js run\nlet value = eval('let i = 0; ++i');\nalert(value); // 1\n```\n\nLe code évalué est exécuté dans l'environnement lexical actuel, il peut donc voir les variables extérieures :\n\n```js run no-beautify\nlet a = 1;\n\nfunction f() {\n  let a = 2;\n\n*!*\n  eval('alert(a)'); // 2\n*/!*\n}\n\nf();\n```\n\nIl peut également changer les variables extérieures :\n\n```js untrusted refresh run\nlet x = 5;\neval(\"x = 10\");\nalert(x); // 10, valeur modifiée\n```\n\nEn mode strict, `eval` a son propre environnement lexical. Donc les fonctions et variables, déclarées au sein de l'eval, ne sont pas visibles en dehors :\n\n```js untrusted refresh run\n// rappel : 'use strict' est actif par défaut dans les exemples exécutables\n\neval(\"let x = 5; function f() {}\");\n\nalert(typeof x); // undefined (pas de telle variable)\n// la fonction f n'est pas visible non plus\n```\n\nSans `use strict`, `eval` n'a pas d'environnement lexical propre, donc nous verrions `x` et `f` en dehors.\n\n## Utiliser \"eval\"\n\nDans la programmation moderne, `eval` est utilisé avec beaucoup de parcimonie. Il est souvent dit que \"eval est le mal\".\n\nLa raison est simple : il y a très, très longtemps, JavaScript était un langage beaucoup plus faible, beaucoup de choses ne pouvaient être faites que par l'intermédiaire de `eval`. Mais ce temps est passé depuis une décennie.\n\nÀ l'heure actuelle, il n'y a presque aucune raison `eval`. Si quelqu'un l'utilise, il y a de fortes chances pour qu'il/elle puisse le remplacer par une construction plus moderne ou un [Module JavaScript](info:modules).\n\nVeuillez noter que son accessibilité aux variables extérieures a des effets secondaires.\n\nLes minifacteurs de code (outils utilisés avant que JS parte en production, pour le compresser) renomment les variables locales avec des noms plus courts (comme `a`, `b` etc) pour rendre le code plus court. C'est souvent sûr, mais pas si `eval` est utilisé, étant donné que les variables locales peuvent être accessibles par le code évalué. Donc les minifacteurs ne font pas ce travail pour toutes les variables potentiellement visibles par le `eval`. Cela affecte négativement le ratio de compression.\n\nUtiliser les variables locales extérieures dans un `eval` est de plus considéré comme une mauvaise pratique, étant donné que cela réduit la maintenabilité du code.\n\nIl y a deux moyens pour être entièrement sûrs d'éviter de tels problèmes.\n\n**Si le code évalué n'a pas besoin des variables extérieures, appelez `eval` via `window.eval(...)`:**\n\nDe ce fait, le code est exécuté dans la portée globale:\n\n```js untrusted refresh run\nlet x = 1;\n{\n  let x = 5;\n  window.eval('alert(x)'); // 1 (variable globale)\n}\n```\n\n**Si le code évalué a besoin des variables extérieures, changez `eval` par `new Function` et passez-le comme argument :**\n\n```js run\nlet f = new Function('a', 'alert(a)');\n\nf(5); // 5\n```\n\nLa construction `new Function` est expliquée dans le chapitre <info:new-function>. Elle crée une fonction à partir d'une chaîne de caractères dans la portée globale. Elle ne peut donc pas voir les variables locales. Mais c'est beaucoup plus clair de les passer explicitement comme arguments, comme dans l'exemple ci-dessus.\n\n## Résumé\n\nUn appel de `eval(code)` exécute la chaîne de code et retourne le résultat de la dernière instruction.\n- Rarement utilisé dans le JavaScript moderne, puisque souvent inutile.\n- Peut accéder aux variables locales extérieures. C'est considéré comme une mauvaise pratique.\n- À la place, pour `eval` le code dans la portée globale, utilisez `window.eval(code)`.\n- Sinon, si votre code a besoin de données de la portée extérieure, utilisez `new Function` et passez-le comme argument.\n"
  },
  {
    "path": "1-js/99-js-misc/03-currying-partials/article.md",
    "content": "libs:\n  - lodash\n\n---\n\n# Curryfication\n\nLa [curryfication](https://fr.wikipedia.org/wiki/Curryfication) est une technique avancée de travail avec les fonctions. Ce n'est pas seulement utilisé avec JavaScript, mais dans d'autres langages également.\n\nLa curryfication est la transformation de fonction qui traduit une fonction de la forme `f(a, b, c)` en une fonction de la forme `f(a)(b)(c)`.\n\nLa curryfication n'appelle pas une fonction ; elle la transforme simplement.\n\nVoyons d'abord un exemple pour mieux comprendre de quoi nous parlons, et mettons ensuite en pratique.\n\nNous allons créer une fonction d'aide `curry(f)` qui curryfie une fonction `f` à deux arguments. En d'autres mots, `curry(f)` sur une fonction `f(a, b)` la traduit en une fonction qui s'appelle par `f(a)(b)` :\n\n```js run\n*!*\nfunction curry(f) { // curry(f) fait la curryfication\n  return function(a) {\n    return function(b) {\n      return f(a, b);\n    };\n  };\n}\n*/!*\n\n// usage\nfunction sum(a, b) {\n  return a + b;\n}\n\nlet curriedSum = curry(sum);\n\nalert( curriedSum(1)(2) ); // 3\n```\n\nComme vous pouvez le voir, l'implémentation est simple : il ne s'agit que de deux enveloppes.\n\n- Le résultat de `curry(func)` est une enveloppe `function(a)`.\n- Lorsqu'il est appelé comme `curriedSum(1)`, l'argument est sauvegardé dans l'environnement lexical, et une nouvelle enveloppe `function(b)` est retournée.\n- Ensuite cette enveloppe est appelée avec `2` comme argument, et passe l'appel à la fonction originelle `sum`.\n\nDes implémentations plus avancées de la curryfication, comme [_.curry](https://lodash.com/docs#curry) de la bibliothèque lodash, retournent une enveloppe qui permet à une fonction d'être à la fois appelée normalement et partiellement :\n\n```js run\nfunction sum(a, b) {\n  return a + b;\n}\n\nlet curriedSum = _.curry(sum); // usage de _.curry de la bibliothèque lodash\n\nalert( curriedSum(1, 2) ); // 3, toujours appelable normalement\nalert( curriedSum(1)(2) ); // 3, appelée partiellement\n```\n\n## La curryfication ? Pour quoi faire ?\n\nPour comprendre les bénéfices, nous avons besoin d'un exemple réel.\n\nPar exemple, nous avons la fonction de journalisation `log(date, importance, message)` qui formate et écrit l'information. Dans de réels projets de telles fonctions ont beaucoup de fonctionnalités utiles comme envoyer les journaux sur un réseau, ici nous allons juste utiliser `alert` :\n\n```js\nfunction log(date, importance, message) {\n  alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);\n}\n```\n\nCurryfions-la !\n\n```js\nlog = _.curry(log);\n```\n\nAprès ça `log` fonctionne normalement:\n\n```js\nlog(new Date(), \"DEBUG\", \"some debug\"); // log(a, b, c)\n```\n\n... mais aussi dans la forme curryfiée :\n\n```js\nlog(new Date())(\"DEBUG\")(\"some debug\"); // log(a)(b)(c)\n```\n\nNous pouvons maintenant faire une fonction pratique pour la journalisation actuelle :\n\n```js\n// logNow sera la partie partielle de log avec un premier argument fixe\nlet logNow = log(new Date());\n\n// utilisons-la\nlogNow(\"INFO\", \"message\"); // [HH:mm] INFO message\n```\n\nMaintenant `logNow` est `log` avec un premier argument fixe, en d'autres termes \"fonction partiellement appliquée\" ou \"partielle\" pour faire court.\n\nNous pouvons aller plus loin et faire une fonction pratique pour le débogage actuel :\n\n```js\nlet debugNow = logNow(\"DEBUG\");\n\ndebugNow(\"message\"); // [HH:mm] DEBUG message\n```\n\nDonc :\n1. Nous n'avons rien perdu après avoir curryfié : `log` est toujours appelable normalement.\n2. Nous pouvons aisément créer des fonctions partielles comme pour la journalisation d'aujourd'hui.\n\n## Implémentation avancée de la curryfication\n\nAu cas où vous souhaiteriez entrer dans les détails, voici l'implémentation \"avancée\" de la curryfication pour les fonctions à plusieurs arguments que nous avons pu utiliser plus haut.\n\nC'est plutôt court :\n\n```js\nfunction curry(func) {\n\n  return function curried(...args) {\n    if (args.length >= func.length) {\n      return func.apply(this, args);\n    } else {\n      return function(...args2) {\n        return curried.apply(this, args.concat(args2));\n      }\n    }\n  };\n\n}\n```\n\nExemples d'usage :\n\n```js\nfunction sum(a, b, c) {\n  return a + b + c;\n}\n\nlet curriedSum = curry(sum);\n\nalert( curriedSum(1, 2, 3) ); // 6, toujours appelable normalement\nalert( curriedSum(1)(2,3) ); // 6, curryfiée au premier argument\nalert( curriedSum(1)(2)(3) ); // 6, curryfiée totalement\n```\n\nLa nouvelle `curry` semble être compliquée, mais est assez simple à comprendre.\n\nLe résultat de `curry(func)` est l'enveloppe `curried` qui ressemble à ça :\n\n```js\n// func est la fonction à transformer\nfunction curried(...args) {\n  if (args.length >= func.length) { // (1)\n    return func.apply(this, args);\n  } else {\n    return function(...args2) { // (2)\n      return curried.apply(this, args.concat(args2));\n    }\n  }\n};\n```\n\nQuand on la lance, il y a deux branches `if` :\n\n1. Si le nombre d'`args` passé est égal ou supérieur à celui de la fonction d'origine dans sa définition (`func.length`), alors on lui passe simplement l'appel en utilisant `func.apply`.\n2. Sinon, on obtient un partiel : nous n'appelons pas encore `func`. Au lieu de cela, un autre wrapper est retourné, qui réappliquera `curried` en fournissant les arguments précédents avec les nouveaux.\n\nEnsuite, si nous l'appelons, encore une fois, nous obtiendrons soit un nouveau partiel (si pas assez d'arguments) ou, finalement, le résultat.\n\n```smart header=\"Fonctions à nombre d'arguments fixe seulement\"\nLa curryfaction requiert que la fonction ait un nombre d'arguments fixe.\n\nUne fonction qui utilise un paramètre de reste, comme `f(...args)`, ne peut pas être curryfiée de cette façon.\n```\n\n```smart header=\"Un peu plus que la curryfication\"\nPar définition, la curryfication devrait convertir `sum(a, b, c)` en `sum(a)(b)(c)`.\n\nMais la plupart des implémentations en JavaScript sont avancées, comme décrites : elles laissent la possibilité d'appeler la fonction via plusieurs arguments.\n```\n\n## Résumé\n\nLa *curryfication* est une transformation qui rend `f(a,b,c)` appelable comme `f(a)(b)(c)`. Les implémentations en JavaScript laissent généralement la possibilité d'appeler les fonctions normalement et de retourner une partielle si le nombre d'arguments n'est pas suffisant.\n\nLa curryfication nous permet d'avoir aisément des partielles. Comme nous avons pu le voir dans l'exemple de journalisation, après avoir curryfié la fonction à trois arguments, `log(date, importance, message)` nous donne une partielle quand appelée avec un argument (comme `log(date)`) ou deux arguments (comme `log(date, importance)`).\n"
  },
  {
    "path": "1-js/99-js-misc/04-reference-type/2-check-syntax/solution.md",
    "content": "**Erreur**!\n\nEssayez le :\n\n```js run\nlet user = {\n  name: \"John\",\n  go: function() { alert(this.name) }\n}\n\n(user.go)() // error!\n```\n\nLe message d'erreur dans la plupart des navigateurs ne permet pas de comprendre ce qui s'est mal passé.\n\n**L'erreur apparaît parce qu'un point-virgule est manquant après `user = {...}`.**\n\nJavaScript n'insert pas automatiquement un point-virgule avant une accolade `(user.go)()`, il lit donc le code comme ceci :\n\n```js no-beautify\nlet user = { go:... }(user.go)()\n```\n\nEnsuite, nous pouvons également voir qu'une telle expression conjointe est syntaxiquement un appel de l'objet `{ go: ... }` en tant que fonction avec l'argument `(user.go)`. Et cela se produit également sur la même ligne avec `let user`, alors que l'objet `user` n'a même pas encore été défini, d'où l'erreur.\n\nSi nous insérons le point-virgule, tout va bien :\n\n```js run\nlet user = {\n  name: \"John\",\n  go: function() { alert(this.name) }\n}*!*;*/!*\n\n(user.go)() // John\n```\n\nVeuillez noter que les parenthèses autour de `(user.go)` ne font rien ici. Habituellement, elles configurent l'ordre des opérations, mais ici le point `.` fonctionne toujours en premier de toute façon, donc aucun effet. Seul le point-virgule compte.\n\n"
  },
  {
    "path": "1-js/99-js-misc/04-reference-type/2-check-syntax/task.md",
    "content": "importance: 2\n\n---\n\n# Vérification de la syntaxe\n\nQuel est le résultat de ce code ?\n\n\n```js no-beautify\nlet user = {\n  name: \"John\",\n  go: function() { alert(this.name) }\n}\n\n(user.go)()\n```\n\nP.S. Il y a un piège :)\n"
  },
  {
    "path": "1-js/99-js-misc/04-reference-type/3-why-this/solution.md",
    "content": "\nVoici les explications.\n\n1. C’est un appel de méthode d’objet standard.\n\n2. De même, les parenthèses ne changent pas l'ordre des opérations ici, le point est le premier quand même.\n\n3. Nous avons ici un appel plus complexe `(expression)()`. L'appel fonctionne comme s'il était divisé en deux lignes :\n\n    ```js no-beautify\n    f = obj.go; // calculer l'expression\n    f();        // appeler ce que nous avons\n    ```\n\n    Ici, `f()` est exécuté en tant que fonction, sans `this`.\n\n4. La chose similaire à `(3)`, à gauche des parenthèses `()`, nous avons une expression.\n\nPour expliquer le comportement de `(3)` et `(4)`, nous devons rappeler que les accesseurs de propriétés (points ou crochets) renvoient une valeur du type de référence.\n\nToute opération sur celle-ci, à l'exception d'un appel de méthode (comme affectation `=` ou `||`), la convertit en une valeur ordinaire, qui ne porte pas les informations permettant de définir `this`.\n\n"
  },
  {
    "path": "1-js/99-js-misc/04-reference-type/3-why-this/task.md",
    "content": "importance: 3\n\n---\n\n# Expliquez la valeur de \"this\"\n\nDans le code ci-dessous, nous avons l'intention d'appeler la méthode `obj.go()` 4 fois de suite.\n\nMais les appels `(1)` et `(2)` fonctionnent différemment de `(3)` et `(4)`. Pourquoi ?\n\n```js run no-beautify\nlet obj, method;\n\nobj = {\n  go: function() { alert(this); }\n};\n\nobj.go();               // (1) [object Object]\n\n(obj.go)();             // (2) [object Object]\n\n(method = obj.go)();    // (3) undefined\n\n(obj.go || obj.stop)(); // (4) undefined\n```\n\n"
  },
  {
    "path": "1-js/99-js-misc/04-reference-type/article.md",
    "content": "\n# Type référence\n\n```warn header=\"Sujet avancé\"\nCet article couvre un sujet avancé pour mieux comprendre certains cas limites.\n\nCe n'est pas important. De nombreux développeurs expérimentés vivent bien sans le savoir. Continuez à lire si vous voulez savoir comment les choses fonctionnent sous le capot.\n```\n\nUn appel de méthode évalué dynamiquement peut perdre `this`.\n\nPar exemple:\n\n```js run\nlet user = {\n  name: \"John\",\n  hi() { alert(this.name); },\n  bye() { alert(\"Bye\"); }\n};\n\nuser.hi(); // fonctionne\n\n// essayons maintenant d'appeler user.hi ou user.by selon name\n*!*\n(user.name == \"John\" ? user.hi : user.bye)(); // Error !\n*/!*\n```\n\nSur la dernière ligne il y a un opérateur conditionnel qui choisit entre `user.hi` ou `user.bye`. Ici le résultat est `user.hi`.\n\nEnsuite la méthode est immédiatement appelée avec les parenthèses `()`. Mais cela ne fonctionne pas !\n\nComme vous pouvez le voir, l'appel se résout avec une erreur car la valeur de `\"this\"` dans l'appel devient `undefined`.\n\nCet appel fonctionne (syntaxe de notation par points):\n```js\nuser.hi();\n```\n\nCelui-là non (méthode évaluée):\n```js\n(user.name == \"John\" ? user.hi : user.bye)(); // Error !\n```\n\nPourquoi ? Si nous voulons comprendre pourquoi cela arrive, regardons comment l'appel de `obj.method()` fonctionne sous le capot.\n\n## Le type référence expliqué\n\nEn y regardant plus précisement, on peut remarquer 2 opérations dans la déclaration de `obj.method()`:\n\n1. En premier, le point `'.'` récupère la propriété `obj.method`.\n2. Puis les parenthèses `()` l'éxécute.\n\nMais comment l'information du `this` est passée de la première opération à la deuxième ?\n\nSi on sépare ces opération sur 2 lignes, alors `this` sera perdu :\n\n```js run\nlet user = {\n  name: \"John\",\n  hi() { alert(this.name); }\n};\n\n*!*\n// On sépare l'accès à la méthode et son appel en deux lignes\nlet hi = user.hi;\nhi(); // Error, car this n'est pas définit\n*/!*\n```\n\nIci `hi = user.hi` assigne la fonction à la variable, ensuite sur la dernière ligne `this` est complétement autonome et donc il n'y a pas de `this`.\n\n**Pour faire que `user.hi()` fonctionne, JavaScript utilise une astuce -- le point `'.'` ne retourne pas une fonction, mais une valeur  de [type référence](https://tc39.github.io/ecma262/#sec-reference-specification-type).**\n\nLe type référence n'est pas un \"type de spécifiation\". On ne peut l'utiliser explicitement, mais il est utilisé en interne par le langage.\n\nLa valeur de Type Référence est une combinaison de 3 valeurs `(base, name, strict)`, où :\n\n- `base` est l'objet.\n- `name` est le nom de la propriété.\n- `strict` est vrai si `use strict` est en vigueur.\n\nLe résultat de l'accès à la propriété `user.hi` n'est pas une fonction, mais une valeur de Type Référence. Pour `user.hi` en mode strict cela est :\n\n```js\n// Valeur de type référence\n(user, \"hi\", true)\n```\n\nLorsque les parenthèses `()` sont appelées sur le type de référence, elles reçoivent les informations complètes sur l'objet et sa méthode, et peuvent définir le bon `this` (`user` dans ce cas).\n\nLe type référence est un type interne \"intermédiaire\", avec comme but de passer l'information du point `.`  aux parenthèses `()`.\n\nN'importe quelle autre opération d'assignement comme `hi = user.hi` rejette le type référence, prends la valeur de `user.hi` (une fonction) et la passe. Ainsi n'importe quelle opération suivante \"perd\" `this`.\n\nIl en résulte que la valeur de `this` n'est passée correctement seulement lorsque la fonction est appelée directement en utilisant la notation par points `obj.method()` ou la notation par crochet `obj['method']()` (c'est la même chose). Il existe différentes manières de résoudre ce problème comme [func.bind()](/bind#solution-2-bind).\n\n## Résumé\n\nLe type référence est un type interne au langage.\n\nEn lisant une propriété, comme avec le point `.` dans `obj.method()`, qui ne retourne pas la valeur de la propriété mais la valeur spéciale de \"type référence\", qui garde le nom de la propriété et l'objet relié à la propriété.\n\nThat's for the subsequent method call `()` to get the object and set `this` to it.\nCela est fait pour que l'éxécution suivante, l'appel à la méthode  `()`, reçoive l'objet et lui assigne `this`.\n\nPour toutes les autres opérations, le type référence sera automatiquement la valeur de la propriété (une fonction dans notre cas).\n\nLe fonctionnement est caché de notre vision. Cela n'a d'importance que dans certains cas, comme lorsqu'une méthode est obtenue dynamiquement de l'object en utilisant une expression.\n"
  },
  {
    "path": "1-js/99-js-misc/05-bigint/article.md",
    "content": "# BigInt\n\n[recent caniuse=\"bigint\"]\n\n`BigInt` est un type numéral spécial qui fournit un support pour les entiers de taille arbitraire.\n\nUn bigint est créé en ajoutant `n` à la fin d'un entier littéral ou en appelant la fonction `BigInt` qui crée des bigints de chaînes de caractères, nombres, etc.\n\n```js\nconst bigint = 1234567890123456789012345678901234567890n;\n\nconst sameBigint = BigInt(\"1234567890123456789012345678901234567890\");\n\nconst bigintFromNumber = BigInt(10); // pareil que 10n\n```\n\n## Opérateurs mathématiques\n\n`BigInt` peut la plupart du temps être utilisé comme un nombre ordinaire, par exemple :\n\n```js run\nalert(1n + 2n); // 3\n\nalert(5n / 2n); // 2\n```\n\nNote : la division `5/2` retourne le résultat arrondi à zéro, sans la partie décimale. Toutes les opérations sur des bigints retourne des bigints.\n\nNous ne pouvons pas mélanger des bigints et des nombres ordinaires :\n\n```js run\nalert(1n + 2); // Error: Cannot mix BigInt and other types\n```\n\nNous devrions explicitement les convertir si nécessaire en utilisant `BigInt()` ou `Number()`, comme ceci :\n\n```js run\nlet bigint = 1n;\nlet number = 2;\n\n// nomber vers bigint\nalert(bigint + BigInt(number)); // 3\n\n// bigint vers nombre\nalert(Number(bigint) + number); // 3\n```\n\nLes opérations de conversion sont toujours silencieuses, ne donnent jamais d'erreur, mais si le bigint est trop grand et ne rentre pas dans le type number, alors les bits en trop seront retirés, donc nous devrions être prudents lorsque nous effectuons une telle conversion.\n\n````smart header=\"Le plus unaire n'est pas supporté sur les bigints\"\nLe plus unaire `+value` est un moyen bien connu pour convertir `value` en un nombre.\n\nAfin d'éviter toute confusion, il n'est pas pris en charge sur les bigints :\n```js run\nlet bigint = 1n;\n\nalert( +bigint ); // erreur\n```\nDonc nous devrions utiliser `Number()` pour convertir un bigint en un nombre.\n````\n\n## Comparaisons\n\nLes comparaisons, telles `<`, `>` fonctionnent très bien avec les bigints et les nombres :\n\n```js run\nalert( 2n > 1n ); // true\n\nalert( 2n > 1 ); // true\n```\n\nVeuillez cependant noter que puisque les nombres et les bigints sont deux types différents, ils peuvent être égaux `==`, mais pas strictement égaux `===`:\n\n```js run\nalert( 1 == 1n ); // true\n\nalert( 1 === 1n ); // false\n```\n\n## Opérations booléennes\n\nLorsqu'à l'intérieur d'un `if` ou toutes autres opérations booléennes, les bigints se comportent comme les nombres.\n\nPar exemple, dans un `if`, un bigint `0n` est dit \"falsy\", les autres valeurs sont dites \"truthy\" :\n\n```js run\nif (0n) {\n  // ne s'exécutera jamais\n}\n```\n\nLes opérateurs booléens, tels `||`, `&&` et autres fonctionnent également avec les bigints, similairement aux nombres :\n\n```js run\nalert( 1n || 2 ); // 1 (1n est considéré truthy)\n\nalert( 0n || 2 ); // 2 (0n est considéré falsy)\n```\n\n## Polyfills\n\nÉmuler des bigints est difficile. La raison est que beaucoup d'opérateurs de JavaScript, tels `+`, `-` et autres se comportent différemment avec les bigints comparé aux nombres ordinaires.\n\nPar exemple, la division de bigints retournera toujours un bigint (arrondi si nécessaire).\n\nPour émuler un tel comportement, un polyfill devrait analyser le code et remplacer ces opérateurs par ses fonctions. Mais faire ainsi est encombrant et coûterait beaucoup en performances.\n\nAinsi, il n'y a pas de bon polyfill connu.\n\nCependant, l'une des solutions est proposée par les développeurs de la bibliothèque [JSBI](https://github.com/GoogleChromeLabs/jsbi).\n\nCette bibliothèque implémente les nombres conséquents à l'aide de ses propres méthodes. Nous pouvons les utiliser à la place des bigints natifs :\n\n| Opération | `BigInt` natif | JSBI |\n|-----------|-----------------|------|\n| Création depuis un nombre | `a = BigInt(789)` | `a = JSBI.BigInt(789)` |\n| Addition | `c = a + b` | `c = JSBI.add(a, b)` |\n| Soustraction\t| `c = a - b` | `c = JSBI.subtract(a, b)` |\n| ... | ... | ... |\n\n...Et ensuite utiliser le polyfill (plugin Babel) pour convertir les appels JSBI en bigints natifs pour les navigateurs les supportant.\n\nEn d'autres termes, cette approche suggère d'écrire notre code avec JSBI à la place des bigints natifs. Mais JSBI travaille avec des nombres comme avec des bigints en interne, les émulant de près en suivant la spécification, le code sera ainsi compatible avec les bigints.\n\nNous pouvons utiliser du code JSBI \"tel quel\" pour les moteurs ne supportant pas les bigints et pour ceux qui les supportent - le polyfill les convertira en bigints natifs.\n\n## Références\n\n- [Documentation MDN sur le type BigInt](mdn:/JavaScript/Reference/Global_Objects/BigInt).\n- [Spécification](https://tc39.es/ecma262/#sec-bigint-objects).\n"
  },
  {
    "path": "1-js/99-js-misc/06-unicode/article.md",
    "content": "\n# Unicode et fonctionnement des chaînes de caractères\n\n```warn header=\"Connaissances avancées\"\nCette section approfondit les \"String Internals\", le fonctionnement interne des chaines de caractère. Des connaissances sur ces sujets vous seront utiles lorsque vous travaillerez avec les émojis, les caractères mathématiques ou les caractères hiéroglyphiques ou autres symboles rares.\n```\n\nComme vous le savez, les chaines de caractères JavaScript sont basées sur le Unicode [Unicode](https://fr.wikipedia.org/wiki/Unicode): chaque caractère est représenté par une séquence d'octets de 1 à 4 octet.\n\nJavaScript permet d'injecter un caractère dans une chaîne en spécifiant son code hexadécimal sous une de ces trois formes: \n\n- `\\xXX`\n\n    `XX` doit être deux chiffres hexadécimaux ayant une valeur entre `00` et `FF`, alors `\\xXX` est le caractère dont le code Unicode est `XX`.\n\n    Parce que la notation `\\xXX` ne supporte que deux chiffres hexadécimaux, elle peut être utilisée pour les 256 caractères Unicodes.\n\n    Les 256 premiers caractères incluent l'alphabet latin, les caractères les plus basiques de syntaxe, et d'autres. Par exemple, `\"\\x7A\"` correspond à `\"z\"` (Unicode `U+007A`).\n\n    ```js run\n    alert( \"\\x7A\" ); // z\n    alert( \"\\xA9\" ); // ©, le symbole de Copyright\n    ```\n\n- `\\uXXXX`\n    `XXXX` doit obligatoirement être 4 chiffres hexadécimaux ayant une valeur entre `0000` et `FFFF`, alors `\\uXXXX` est le caractère pour lequel le code Unicode est `XXXX`.\n\n    Les caractères avec des codes Unicode supérieurs à `U+FFFF` peuvent également être représenté avec cette notation, mais dans ce cas, nous devons utiliser ce que l'on appelle une paire de substitution (nous reparlerons des paires de substitutions plus tard dans ce chapitre).\n\n    ```js run\n    alert( \"\\u00A9\" ); // ©, le même que \\xA9, en utilisant la notation hexadécimale à 4 chiffres.\n    alert( \"\\u044F\" ); // я, la lettre de l'alphabet cyrillique\n    alert( \"\\u2191\" ); // ↑, symbole de flèche vers le haut\n    ```\n\n- `\\u{X…XXXXXX}`\n\n    `X…XXXXXX` doit être une valeur hexadécimale de 1 à 6 octet entre `0` et `10FFFF` (le plus haut code défini par Unicode). Cette notation nous permet de représenter facilement tous les caractères Unicode existants.\n\n    ```js run\n    alert( \"\\u{20331}\" ); // 佫, un caractère chinois rare (Unicode long)\n    alert( \"\\u{1F60D}\" ); // 😍, Le symbole d'un visage souriant (un autre Unicode long)\n    ```\n\n## Les paires de substitution\n\nTous les caractères fréquemment utilisés ont des codes de 2 octets (4 chiffres hexadécimaux). Les lettres dans les langages européens les plus courants, les nombres et les ensembles idéographiques unifiés de base (CJK -- provenant des systèmes d'écriture chinois, japonais et coréen),  ont une représentation en 2 octets.\n\nA l'origine, le JavaScript est basé sur l'encodage UTF-16 qui ne permet que 2 octets par caractère. Mais 2 octets ne permettent que 65536 combinaisons ce qui n'est pas suffisant pour tous les symboles possibles de l'Unicode.\n\nLes symboles rares qui nécessitent plus de 2 octets sont encodés à l'aide d'une paire de caractères de 2 octets appelée \"paire de substitution\".\n\nComme effet secondaire, la longueur de tels symboles est `2`:\n\n```js run\nalert( '𝒳'.length ); // 2, Le script mathématique avec un X majuscule\nalert( '😂'.length ); // 2, un visage qui pleure de rire\nalert( '𩷶'.length ); // 2, un caractère chinois rare\n```\n\nC'est parce que les paires de substitution n'existaient pas aumoment de la création de JavaScript, et ne sont donc pas correctement traitées par le langage !\n\nNous avons en réalité un seul symbole dans chacune des paires ci-dessus, mais la propriété `length` affiche une longueur de `2`.\n\nObtenir un symbole peut également être délicat, car la plupart des fonctionnalités du langage traitent les paires de substitution comme deux caractères.\n\nPar exemple, nous pouvons ici voir deux caractères impairs dans la sortie:\n\n```js run\nalert( '𝒳'[0] ); // affiche des symboles étranges...\nalert( '𝒳'[1] ); // ...des parties de la paire de substitution\n```\n\nLes parties de la paire de substitution n'ont pas de sens l'une sans l'autre. Les alertes dans l'exemple ci-dessus affichent ainsi des caractères indésirables.\n\nTechniquement, les paires de substitution sont également détectables par leurs codes: Si un caractère possède le code dans l'intervalle `0xd800..0xdbff`, alors il sera dans la première partie de la paire de substitution. Le caractère suivant (la seconde partie) doit avoir un code dans l'intervalle `0xdc00..0xdfff`. Ces intervalles sont exclusivement réservés pour les paires de substitution d'après les standards.\n\nLes méthodes [String.fromCodePoint](https://developer.mozilla.org/fr-FR/docs/Web/JavaScript/Reference/Global_Objects/String/fromCodePoint) et [str.codePointAt](https://developer.mozilla.org/fr-FR/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt) ont été ajoutés à JavaScript afin de gérer les paires de substitution.\n\nIls sont essentiellement les mêmes que [String.fromCharCode](mdn:js/String/fromCharCode) et [str.charCodeAt](mdn:js/String/charCodeAt), mais ils traitent les paires de substitution correctement.\n\nOn peut voir la différence ici:\n\n```js run\n// charCodeAt n'est pas conscient de la paire de substitution, donc il donne les codes pour la 1ère partie de 𝒳:\n\nalert( '𝒳'.charCodeAt(0).toString(16) ); // d835\n\n// codePointAt est conscient de la paire de substitution\nalert( '𝒳'.codePointAt(0).toString(16) ); // 1d4b3, lit les deux parties de la paire de substitution\n```\n\nCeci dit, si nous prenons la position 1 (ce qui est plutôt incorrect ici), alors ils retournent tous les deux uniquement la 2ème partie de la paire:\n\n```js run\nalert( '𝒳'.charCodeAt(1).toString(16) ); // dcb3\nalert( '𝒳'.codePointAt(1).toString(16) ); // dcb3\n// seconde moitié de la paire sans signification\n```\n\nVous trouverez plusieurs moyens de gérer les paires de substitution plus tard dans ce chapitre <info:iterable>. Il existe probablement des librairies spécialement conçues pour cela également, mais aucune n'est suffisamment connue pour vous la suggérer ici.\n\n````warn header=\"A retenir: Diviser une chaîne de caractère sur un point arbitraire est dangereux\"\nNous ne pouvons pas simplement séparer une chaine de caractère sur un point arbitraire, par exemple, prenez `str.slice(0, 4)` et attendez-vous à ce que ce soit une chaîne de caractère valide, par exemple :\n\n```js run\nalert( 'Salut 😂'.slice(0, 4) ); //  Salut [?]\n```\n\nIci, nous pouvons voir un caractère indésirable ( la première moitié de la paire de substitution du sourire) en sortie.\n\nSoyez simplement conscient de cela si vous avez l'intention de travailler de manière fiable avec des paires de substitution. Cela peut ne pas être un gros problème, mais vous devriez au moins comprendre ce qu'il se passe.\n````\n\n## Marques diacritiques et normalisation\n\nDans de nombreux langages, des symboles sont composés d'un caractère de base avec une marque au dessus ou en dessous.\n\nPar exemple, la lettre `a` peut être la base de ces caractères: `àáâäãåā`.\n\nLes caractères \"composites\" les plus communs ont leur propre code dans la table Unicode. Mais tous n'en ont pas en raison du trop grand nombre de possibilité de combinaison.\n\nPour supporter les compositions arbitraires, le standard Unicode nous permet d'utiliser plusieurs caractères Unicode: le caractère de base suivi d'un ou plusieurs caractères de marques qui le \"décorent\".\n\nPar exemple, si nous avons `S` suivi par le caractère spécial \"point au-dessus\" (code `\\u0307`), il est affiché comme Ṡ.\n\n```js run\nalert( 'S\\u0307' ); // Ṡ\n```\n\nSi nous avons besoin d'une marque supplémentaire au-dessus de la lettre (ou en dessous d'elle) -- pas de problème, il suffit simplement d'ajouter le caractère de marque nécessaire.\n\nPar exemple, si nous ajoutons un caractère \"point en dessous\" (code `\\u0323`), nous aurons \"S avec des points au-dessus et en dessous\": `Ṩ`.\n\nPar exemple:\n\n```js run\nalert( 'S\\u0307\\u0323' ); // Ṩ\n```\n\nCela offre une grande flexibilité, mais aussi un problème intéressant: deux caractères peuvent visuellement se ressembler, mais être représenté par différentes compositions Unicode.\n\nPar exemple:\n\n```js run\nlet s1 = 'S\\u0307\\u0323'; // Ṩ, S + point au-dessus + point en dessous\nlet s2 = 'S\\u0323\\u0307'; // Ṩ, S + point en dessous + point au-dessus\n\nalert( `s1: ${s1}, s2: ${s2}` );\n\nalert( s1 == s2 ); // faux bien que les caractères semblent identiques (?!)\n```\n\nPour résoudre ce problème, il existe une \"normalisation Unicode\", un algorithme qui convertit chaque chaîne vers sa forme \"normale\".\n\nCet algorithme est implémenté par [str.normalize()](mdn:js/String/normalize).\n\n```js run\nalert( \"S\\u0307\\u0323\".normalize() == \"S\\u0323\\u0307\".normalize() ); // true\n```\n\nIl est intéressant de noter que dans notre situation `normalize()` rassemble en réalité une séquence de 3 caractères en un seul: `\\u1e68` (S avec deux points).\n\n```js run\nalert( \"S\\u0307\\u0323\".normalize().length ); // 1\n\nalert( \"S\\u0307\\u0323\".normalize() == \"\\u1e68\" ); // true\n```\n\nEn réalité, ce n'est pas toujours le cas. Cela est dû au fait que le symbole `Ṩ` est \"assez commun\", donc les créateurs de l'Unicode l'ont inclus dans la table principale et lui ont attribué un code.\n\nSi vous souhaitez en apprendre plus sur les règles de normalisation et ses variantes -- elles sont décrites dans l'appendix du standard Unicode: [Unicode Normalization Forms](https://www.unicode.org/reports/tr15/), mais pour la plupart des besoins pratiques, les informations de cette section sont suffisantes.\n"
  },
  {
    "path": "1-js/99-js-misc/index.md",
    "content": "# Divers\n"
  },
  {
    "path": "1-js/index.md",
    "content": "# JavaScript le langage\n\nDans ce guide nous allons apprendre JavaScript, en partant de zéro jusqu'à des concepts avancés comme la POO.\n\nNous allons nous concentrer ici sur le langage lui même, avec le minimum de notes spécifiques aux environnements.\n"
  },
  {
    "path": "2-ui/1-document/01-browser-environment/article.md",
    "content": "# L'environnement du navigateur, spécifications\n\nLe langage JavaScript a été initialement créé pour les navigateurs web. Dès lors, il a évolué et est devenu un langage aux multiples utilisations et plateformes.\n\nUne plate-forme peut être un navigateur, ou un serveur Web ou un autre *hôte*, ou même une machine à café \"intelligente\" si elle peut exécuter JavaScript. Chacun d'entre eux fournit des fonctionnalités spécifiques à la plate-forme. La spécification JavaScript appelle cela un *environnement hôte*.\n\nUn environnement hôte fournit ses propres objets et fonctions en plus du noyau du langage. Les navigateurs Web permettent de contrôler les pages Web. Node.js fournit des fonctionnalités côté serveur, etc.\n\nVoici une vue globale de ce que nous avons lorsque JavaScript s'exécute dans un navigateur Web :\n\n![](windowObjects.svg)\n\nIl existe un objet \"racine\" appelé `window`. Il a 2 rôles :\n\n1. Premièrement, c'est un objet global pour le code JavaScript, comme décrit dans le chapitre <info:global-object>.\n2. Deuxièmement, il représente la \"fenêtre du navigateur\" et fournit des méthodes pour la contrôler.\n\nPar exemple, nous l'utilisons ici comme un objet global :\n\n```js run global\nfunction sayHi() {\n  alert(\"Hello\");\n}\n\n// les fonctions globales sont des méthodes de l'objet global :\nwindow.sayHi();\n```\n\nEt nous l'utilisons ici comme une fenêtre du navigateur pour voir la hauteur de la fenêtre :\n\n```js run\nalert(window.innerHeight); // hauteur de la fenêtre intérieure\n```\n\nIl y a d'autres méthodes et propriétés spécifiques à la fenêtre, nous les étudierons plus tard.\n\n## DOM (Document Object Model)\n\nDocument Object Model, ou DOM en abrégé, représente tout le contenu de la page sous forme d'objets pouvant être modifiés.\n\nL'objet `document` est le \"point d'entrée\" principal de la page. Nous pouvons changer ou créer n'importe quoi sur la page en l'utilisant.\n\nPar exemple :\n```js run\n// change la couleur de fond en rouge\ndocument.body.style.background = \"red\";\n\n// réinitialisation après 1 seconde\nsetTimeout(() => document.body.style.background = \"\", 1000);\n```\n\n```smart header=\"DOM n'est pas seulement pour les navigateurs\"\nLa spécification DOM explique la structure d'un document et fournit des objets pour le manipuler. Il existe également des instruments autres que les navigateurs qui utilisent DOM.\n\nPar exemple, les scripts côté serveur qui téléchargent des pages HTML et les traitent peuvent également utiliser le DOM. Ils peuvent cependant ne supporter qu'une partie de la spécification.\n```\n\n```smart header=\"CSSOM pour le style\"\nIl existe également une spécification distincte, [Modèle d'objet CSS (CSSOM)](https://www.w3.org/TR/cssom-1/) pour les règles CSS et les feuilles de style, qui explique comment elles sont représentées en tant qu'objets et comment les lire et les écrire.\n\nCSSOM est utilisé avec DOM lorsque nous modifions les règles de style du document. En pratique cependant, CSSOM est rarement nécessaire, car nous avons rarement besoin de modifier les règles CSS à partir de JavaScript (généralement, nous ajoutons / supprimons simplement des classes CSS, pas de modifier leurs règles CSS), mais c'est également possible.\n```\n\n## BOM (Browser Object Model)\n\nLe modèle d'objet du navigateur (BOM en anglais) contient des objets supplémentaires fournis par le navigateur (l'environnement hôte) pour travailler avec tout à l'exception du document.\n\nPar exemple :\n\n- L'objet [navigator](mdn:api/Window/navigator) fournit des informations contextuelles à propos du navigateur et du système d'exploitation. Il y a beaucoup de propriétés mais les deux plus connues sont : `navigator.userAgent` -- qui donne des informations sur le navigateur actuel, et `navigator.platform` sur la plateforme (peut permettre de faire la différence entre Windows/Linux/Mac etc).\n- L'objet [location](mdn:api/Window/location) nous permet de lire l'URL courante et peut rediriger le navigateur vers une nouvelle adresse.\n\nVoici comment l'on peut utiliser l'objet `location` :\n\n```js run\nalert(location.href); // affiche l'URL actuelle\nif (confirm(\"Go to Wikipedia?\")) {\n  location.href = \"https://wikipedia.org\"; // rediriger le navigateur vers une autre URL\n}\n```\n\nLes fonctions `alert/confirm/prompt` font aussi partie du BOM : elles ne sont pas directement liées au document, mais représentent des méthodes du navigateur de communication pure avec l'utilisateur.\n\n```smart header=\"Specifications\"\nle BOM fait partie de la [spécification HTML](https://html.spec.whatwg.org) générale.\n\nOui, vous avez bien entendu. La spécification HTML disponible à l'adresse <https://html.spec.whatwg.org> ne parle pas seulement du \"langage HTML\" (balises, attributs), mais couvre également un tas d'objets, de méthodes et d'extensions DOM spécifiques au navigateur. C'est l'\"HTML de manière générale\". En outre, certaines parties ont des spécifications supplémentaires listées ici : <https://spec.whatwg.org>.\n```\n\n## Résumé\n\nQuand on parle de normes, nous avons :\n\nLa spécification DOM\n: Décrit la structure du document, ses manipulations et événements, voir <https://dom.spec.whatwg.org>.\n\nLa spécification CSSOM\n: Décrit les feuilles de style et les règles de style, les manipulations de style les impliquant et leur liaisons aux documents, voir <https://www.w3.org/TR/cssom-1/>.\n\nSpécification HTML\n: Décrit le langage HTML (c'est à dire les balises) mais également le BOM (modèle d'objet du navigateur) -- diverses fonctions du navigateur : `setTimeout`, `alert`, `location` etc, voir <https://html.spec.whatwg.org>. Il récupère la spécification DOM et l'étend avec de nombreuses propriétés et méthodes additionnelles.\n\nDe plus, certaines classes sont décrites séparément sur <https://spec.whatwg.org/>.\n\nSouvenez vous de ces liens, il y a tellement de choses à apprendre qu'il est impossible de tout couvrir et de se souvenir de tout.\n\nLorsque vous souhaitez en apprendre plus sur une propriété ou une méthode, le manuel de Mozilla disponible sur <https://developer.mozilla.org/en-US/search> est également une bonne ressource, mais la specification correspondante peut-être meilleure dans le sens qu'elle est plus complexe et longue à lire, mais rendra vos connaissances fondamentales saines et complètes.\n\nPour trouver quelque chose, il est souvent pratique de faire une simple recherche de \"WHATWG [terme]\" ou \"MDN [terme]\", par exemple <https://google.com?q=whatwg+localstorage>, <https://google.com?q=mdn+localstorage>.\n\nNous allons maintenant nous pencher sur le DOM, car le document joue un rôle essentiel dans l'interface utilisateur (UI).\n"
  },
  {
    "path": "2-ui/1-document/02-dom-nodes/article.md",
    "content": "libs:\n  - d3\n  - domtree\n\n---\n\n# L'arbre DOM \n\nL'épine dorsale d'un document HTML est constituée de balises.\n\nSelon le modèle d'objets de document (DOM), chaque balise HTML est un objet. Les balises imbriquées sont des \"enfants\" de celle qui les entoure. Le texte à l'intérieur d'une balise est également un objet.\n\nTous ces objets sont accessibles via JavaScript, et nous pouvons les utiliser pour modifier la page.\n\nPar exemple, `document.body` est l'objet représentant la balise `<body>`.\n\nL'exécution de ce code rendra le `<body>` rouge pendant 3 secondes :\n\n```js run\ndocument.body.style.background = 'red'; // make the background red\n\nsetTimeout(() => document.body.style.background = '', 3000); // return back\n```\n\nIci, nous avons utilisé `style.background` pour changer la couleur d'arrière-plan de `document.body`, mais il existe de nombreuses autres propriétés, telles que :\n\n- `innerHTML` -- Contenu HTML du nœud.\n- `offsetWidth` -- la largeur du nœud (en pixels)\n- … etc.\n\nBientôt, nous apprendrons plus de façons de manipuler le DOM, mais nous devons d'abord connaître sa structure.\n\n## Un exemple du DOM\n\nCommençons par le simple document suivant :\n\n```html run no-beautify\n<!DOCTYPE HTML>\n<html>\n<head>\n  <title>About elk</title>\n</head>\n<body>\n  The truth about elk.\n</body>\n</html>\n```\n\nLe DOM représente le HTML comme une structure arborescente de balises. Voici à quoi ça ressemble :\n\n<div class=\"domtree\"></div>\n\n<script>\nlet node1 = {\"name\":\"HTML\",\"nodeType\":1,\"children\":[{\"name\":\"HEAD\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n  \"},{\"name\":\"TITLE\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"About elk\"}]},{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n\"}]},{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n\"},{\"name\":\"BODY\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n  The truth about elk.\\n\"}]}]}\n\ndrawHtmlTree(node1, 'div.domtree', 690, 320);\n</script>\n\n```online\nSur l'image ci-dessus, vous pouvez cliquer sur les nœuds des éléments et leurs enfants s'ouvriront/se réduiront.\n```\n\nChaque nœud de l'arbre est un objet.\n\nLes balises sont des *nœuds d'élément* (ou simplement des éléments) et forment la structure arborescente : `<html>` est à la racine, puis `<head>` et `<body>` sont ses enfants, etc.\n\nLe texte à l'intérieur des éléments forme *des nœuds texte*, étiquetés comme `#text`. Un nœud texte ne contient qu'une chaîne de caractères. Il peut ne pas avoir d'enfants et est toujours une feuille de l'arbre.\n\nPar exemple, la balise `<title>` a le texte `\"About elk\"`.\n\nVeuillez noter les caractères spéciaux dans les nœuds texte :\n\n- une nouvelle ligne : `↵` (en JavaScript appelé `\\n`)\n- un espace : `␣`\n\nLes espaces et les nouvelles lignes sont des caractères totalement valides, comme les lettres et les chiffres. Ils forment des nœuds de texte et deviennent une partie du DOM. Ainsi, par exemple, dans l'exemple ci-dessus, la balise `<head>` contient des espaces avant `<title>`, et ce texte devient un nœud `#text` (il contient une nouvelle ligne et quelques espaces uniquement).\n\nIl n'y a que deux exclusions de haut niveau :\n1. Les espaces et les nouvelles lignes avant `<head>` sont ignorés pour des raisons historiques.\n2. Si nous mettons quelque chose après `</body>`, alors cela est automatiquement déplacé à l'intérieur du `body`, à la fin, car la spécification HTML exige que tout le contenu soit à l'intérieur de `<body>`. Il ne peut donc pas y avoir d'espace après `</body>`.\n\nDans d'autres cas, tout est simple -- s'il y a des espaces (comme n'importe quel caractère) dans le document, alors ils deviennent des nœuds texte dans le DOM, et si nous les supprimons, il n'y en aura pas.\n\nVoici des nœud de texte sans espace :\n\n```html no-beautify\n<!DOCTYPE HTML>\n<html><head><title>About elk</title></head><body>The truth about elk.</body></html>\n```\n\n<div class=\"domtree\"></div>\n\n<script>\nlet node2 = {\"name\":\"HTML\",\"nodeType\":1,\"children\":[{\"name\":\"HEAD\",\"nodeType\":1,\"children\":[{\"name\":\"TITLE\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"About elk\"}]}]},{\"name\":\"BODY\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"The truth about elk.\"}]}]}\n\ndrawHtmlTree(node2, 'div.domtree', 690, 210);\n</script>\n\n```smart header=\"Les espaces au début/à la fin de la chaîne de caractères et les nœuds texte uniquement composé d'espaces sont généralement masqués dans les outils\"\nLes outils de navigation (qui seront bientôt traités) qui fonctionnent avec le DOM n'affichent généralement pas d'espaces au début/fin du texte et des nœuds texte vides (sauts de ligne) entre les balises.\n\nLes outils de développement permettent d'économiser de l'espace d'écran de cette façon.\n\nSur d'autres images DOM, nous les omettons parfois lorsqu'elles ne sont pas pertinentes. Ces espaces n'affectent généralement pas la façon dont le document est affiché.\n```\n\n## Auto-correction\n\nSi le navigateur rencontre du HTML mal formé, il le corrige automatiquement lors de la création du DOM.\n\nPar exemple, la balise la plus haute est toujours `<html>`. Même si elle n'existe pas dans le document, elle existera dans le DOM, car le navigateur la créera. Il en va de même pour `<body>`.\n\nPar exemple, si le fichier HTML est le seul mot `\"Hello\"`, le navigateur l'enroulera dans `<html>` et `<body>`, et ajoutera le `<head>` requis, et le DOM sera :\n\n\n<div class=\"domtree\"></div>\n\n<script>\nlet node3 = {\"name\":\"HTML\",\"nodeType\":1,\"children\":[{\"name\":\"HEAD\",\"nodeType\":1,\"children\":[]},{\"name\":\"BODY\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"Hello\"}]}]}\n\ndrawHtmlTree(node3, 'div.domtree', 690, 150);\n</script>\n\nLors de la génération du DOM, les navigateurs traitent automatiquement les erreurs dans le document, ferment les balises, etc.\n\nUn document avec des balises non fermées :\n\n```html no-beautify\n<p>Hello\n<li>Mom\n<li>and\n<li>Dad\n```\n\n… deviendra un DOM normal à mesure que le navigateur lit les balises et restaure les parties manquantes :\n\n<div class=\"domtree\"></div>\n\n<script>\nlet node4 = {\"name\":\"HTML\",\"nodeType\":1,\"children\":[{\"name\":\"HEAD\",\"nodeType\":1,\"children\":[]},{\"name\":\"BODY\",\"nodeType\":1,\"children\":[{\"name\":\"P\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"Hello\"}]},{\"name\":\"LI\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"Mom\"}]},{\"name\":\"LI\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"and\"}]},{\"name\":\"LI\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"Dad\"}]}]}]}\n\ndrawHtmlTree(node4, 'div.domtree', 690, 360);\n</script>\n\n````warn header=\"Les tableaux ont toujours `<tbody>`\"\nUn \"cas spécial\" intéressant est celui des tableaux. Selon la spécification DOM, ils doivent avoir un `<tbody>`, mais le texte HTML peut (officiellement) l'omettre. Ensuite, le navigateur crée automatiquement le `<tbody>` dans le DOM.\n\nPour le HTML :\n\n```html no-beautify\n<table id=\"table\"><tr><td>1</td></tr></table>\n```\n\nLa structure du DOM sera :\n<div class=\"domtree\"></div>\n\n<script>\nlet node5 = {\"name\":\"TABLE\",\"nodeType\":1,\"children\":[{\"name\":\"TBODY\",\"nodeType\":1,\"children\":[{\"name\":\"TR\",\"nodeType\":1,\"children\":[{\"name\":\"TD\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"1\"}]}]}]}]};\n\ndrawHtmlTree(node5,  'div.domtree', 600, 200);\n</script>\n\nVous voyez ? Le `<body>` est sorti de nulle part. Vous devez garder cela à l'esprit lorsque vous travaillez avec des tableaux pour éviter les surprises.\n````\n\n## Autres types de nœuds\n\nIl existe d'autres types de nœuds en plus des éléments et des nœuds de texte.\n\nPar exemple, les commentaires :\n\n```html\n<!DOCTYPE HTML>\n<html>\n<body>\n  The truth about elk.\n  <ol>\n    <li>An elk is a smart</li>\n*!*\n    <!-- comment -->\n*/!*\n    <li>...and cunning animal!</li>\n  </ol>\n</body>\n</html>\n```\n\n<div class=\"domtree\"></div>\n\n<script>\nlet node6 = {\"name\":\"HTML\",\"nodeType\":1,\"children\":[{\"name\":\"HEAD\",\"nodeType\":1,\"children\":[]},{\"name\":\"BODY\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n  The truth about elk.\\n  \"},{\"name\":\"OL\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n    \"},{\"name\":\"LI\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"An elk is a smart\"}]},{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n    \"},{\"name\":\"#comment\",\"nodeType\":8,\"content\":\"comment\"},{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n    \"},{\"name\":\"LI\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"...and cunning animal!\"}]},{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n  \"}]},{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n\\n\\n\"}]}]};\n\ndrawHtmlTree(node6, 'div.domtree', 690, 500);\n</script>\n\nNous pouvons voir ici un nouveau type de nœud de l'arbre - *nœud commentaire*, étiqueté comme `#comment`, entre deux nœuds texte.\n\nNous pouvons penser - pourquoi un commentaire est-il ajouté au DOM ? Cela n'affecte en rien la représentation visuelle. Mais il y a une règle -- si quelque chose est en HTML, alors il doit aussi être dans l'arborescence DOM.\n\n**Tout en HTML, même les commentaires, devient une partie du DOM.**\n\nMême la directive `<!DOCTYPE...>` au tout début du html est également un noeud du dom. C'est dans l'arborescence du DOM juste avant `<html>`. Peu de gens le savent. Nous n'allons pas toucher ce nœud, nous ne le dessinons même pas sur les diagrammes pour cette raison, mais il est là.\n\nL'objet `document` qui représente l'ensemble du document est également, formellement, un nœud dom.\n\nIl existe [12 types de nœuds](https://dom.spec.whatwg.org/#node). En pratique, nous travaillons généralement avec 4 d'entre eux :\n\n1. le `document` -- le \"point d'entrée\" dans le dom.\n2. les nœuds éléments -- les balises HTML, les blocs de construction de l'arborescence.\n3. les nœuds texte -- contient du texte.\n4. les commentaires -- parfois, nous pouvons y mettre des informations, elles ne seront pas affichées, mais js peut les lire depuis le dom.\n\n## Voyez par vous-même\n\nPour voir la structure dom en temps réel, essayez le [live DOM viewer](https://software.hixie.ch/utilities/js/live-dom-viewer/). Tapez simplement le document, et il apparaîtra comme un dom en un instant.\n\nUne autre façon d'explorer le dom est d'utiliser les outils de développement du navigateur. en fait, c'est ce que nous utilisons lors du développement.\n\nPour ce faire, ouvrez la page web [elk.html](elk.html), activez les outils de développement du navigateur et passez à l'onglet éléments.\n\nCela devrait ressembler à ça :\n\n![](elk.svg)\n\nVous pouvez voir le dom, cliquer sur les éléments, voir leurs détails et ainsi de suite.\n\nVeuillez noter que la structure du dom dans les outils de développement est simplifiée. Les nœuds texte sont affichés comme du texte. Et il n'y a aucun nœud texte \"vide\" (espace uniquement). C'est très bien, car la plupart du temps nous nous intéressons aux nœuds éléments.\n\nEn cliquant sur le bouton <span class=\"devtools\" style=\"background-position:-328px -124px\"></span> dans le coin supérieur gauche cela nous permet de choisir un nœud à partir de la page Web à l'aide d'une souris (ou d'autres périphériques de pointeur) et de \"l'inspecter\" (faites défiler jusqu'à l'onglet Éléments). cela fonctionne très bien lorsque nous avons une énorme page html (et un énorme dom correspondant) et que nous aimerions voir la place d'un élément particulier.\n\nUne autre façon de le faire serait simplement de cliquer avec le bouton droit sur une page Web et de sélectionner \"inspecter\" dans le menu contextuel.\n\n![](inspect.svg)\n\nDans la partie droite des outils se trouvent les sous-onglets suivants :\n- **Styles** -- nous pouvons voir le CSS appliqué à l'élément en cours, règle par règle, y compris les règles intégrées (gris). Presque tout peut être modifié sur place, y compris les dimensions/margins/paddings de la boîte ci-dessous.\n- **Computed** -- pour voir le CSS appliqué à l'élément par propriété : pour chaque propriété, nous pouvons voir une règle qui la lui donne (y compris l'héritage CSS et autres).\n- **Event Listeners** -- pour voir les écouteurs d'événements attachés aux éléments du DOM (nous les couvrirons dans la prochaine partie du tutoriel).\n- … etc.\n\nLa meilleure façon de les étudier est de cliquer dessus. La plupart des valeurs sont modifiables sur place.\n\n## Interaction avec la console\n\nComme nous travaillons le DOM, nous pouvons également vouloir lui appliquer du JavaScript. Comme : obtenir un nœud et exécuter du code pour le modifier, pour voir le résultat. Voici quelques conseils pour voyager entre l'onglet Elements et la console.\n\nPour commencer :\n\n1. Sélectionnez le premier `<li>` dans l'onglet Éléments.\n2. Appuyez sur la touche `key:Esc` -- cela ouvrira la console juste en dessous de l'onglet Éléments.\n\nMaintenant, le dernier élément sélectionné est disponible en tant que `$0`, le précédent sélectionné est `$1`, etc.\n\nNous pouvons exécuter des commandes sur eux. Par exemple, `$0.style.background = 'red'` rend l'élément de la liste sélectionné rouge, comme ceci :\n\n![](domconsole0.svg)\n\nVoilà comment obtenir un nœud à partir d'Elements dans la console.\n\nIl y a aussi un chemin de retour. S'il y a une variable référençant un nœud DOM, alors nous pouvons utiliser la commande `inspect(node)` dans la console pour la voir dans le volet Éléments.\n\nOu nous pouvons simplement sortir le nœud DOM dans la console et explorer \"sur place\", comme `document.body` ci-dessous :\n\n![](domconsole1.svg)\n\nC'est à des fins de débogage bien sûr. À partir du chapitre suivant, nous accéderons et modifierons le DOM en utilisant JavaScript.\n\nLes outils de développement du navigateur sont d'une grande aide au développement : nous pouvons explorer le DOM, essayer des choses et voir ce qui ne va pas.\n\n## Résumé\n\nUn document HTML/XML est représenté dans le navigateur sous forme d'arbre DOM.\n\n- Les balises deviennent des nœuds élément et forment la structure.\n- Le texte devient des nœuds texte.\n- … etc, tout en HTML a sa place dans le DOM, même les commentaires.\n\nNous pouvons utiliser les outils de développement pour inspecter le DOM et le modifier manuellement.\n\nIci, nous avons couvert les bases, les actions les plus utilisées et les plus importantes pour commencer. Il existe une documentation complète sur Chrome Developer Tools à l'adresse <https://developers.google.com/web/tools/chrome-devtools>. La meilleure façon d'apprendre les outils est de cliquer ici et là, lire les menus : la plupart des options sont évidentes. Plus tard, lorsque vous les connaissez en général, lisez la documentation et découvrez le reste.\n\nLes nœuds DOM ont des propriétés et des méthodes qui nous permettent de voyager entre eux, de les modifier, de se déplacer dans la page, etc. Nous y reviendrons dans les prochains chapitres.\n"
  },
  {
    "path": "2-ui/1-document/02-dom-nodes/elk.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n  The truth about elk.\n  <ol>\n    <li>An elk is a smart</li>\n    <!-- comment -->\n    <li>...and cunning animal!</li>\n  </ol>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/02-dom-nodes/head.html",
    "content": "<style>\nspan.devtools {\n  display: inline-block;\n  background-image: url(/article/dom-nodes/toolbarButtonGlyphs.svg);\n  height:16px;\n  width:16px;\n}\n</style>\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/1-dom-children/solution.md",
    "content": "Il existe de nombreuses façons, par exemple :\n\n\nLe noeud `<div>` du DOM :\n\n```js\ndocument.body.firstElementChild\n// ou\ndocument.body.children[0]\n// ou (le premier nœud est l'espace, nous prenons donc le deuxième)\ndocument.body.childNodes[1]\n```\n\nLe nœud `<ul>` du DOM :\n\n```js\ndocument.body.lastElementChild\n// ou\ndocument.body.children[1]\n```\n\nLe deuxième `<li>` (avec Pete) :\n\n```js\n// obtenir <ul>, puis obtenir son dernier élément enfant\ndocument.body.lastElementChild.lastElementChild\n```\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/1-dom-children/task.md",
    "content": "importance: 5\n\n---\n\n# Enfants DOM\n\nRegardez cette page :\n\n```html\n<html>\n<body>\n  <div>Users:</div>\n  <ul>\n    <li>John</li>\n    <li>Pete</li>\n  </ul>\n</body>\n</html>\n```\n\nPour chacun des éléments suivants, donnez au moins un moyen d’y accéder :\n- Le noeud `<div>` du DOM ?\n- Le noeud `<ul>` du DOM ?\n- Le deuxième `<li>` (avec Pete) ?\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md",
    "content": "1. Oui c'est vrai. L'élément `elem.lastChild` est toujours le dernier, il n'a pas de `nextSibling`.\n2. Non, c'est faux, car `elem.children[0]` est le premier enfant *parmi les éléments*. Mais il peut exister des nœuds non-éléments avant lui. Ainsi, `previousSibling` peut être un nœud texte.\n\nRemarque: dans les deux cas, s'il n'y a pas d'enfants, il y aura une erreur.\n\nS'il n'y a pas d'enfants, `elem.lastChild` est  `null`, nous ne pouvons donc pas accéder à `elem.lastChild.nextSibling`. Et la collection `elem.children` est vide (comme un tableau vide `[]`).\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/task.md",
    "content": "importance: 5\n\n---\n\n# La question des frères et sœurs\n\nSi `element` - est un nœud élément arbitraire du DOM ...\n\n- Est-il vrai que `elem.lastChild.nextSibling` est toujours `null` ?\n- Est-il vrai que `elem.children[0].previousSibling` est toujours `null` ?\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/solution.md",
    "content": "Nous utiliserons les propriétés `rows` et `cells` pour accéder aux cellules du tableau en diagonale.\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <style>\n    table {\n      border-collapse: collapse;\n    }\n\n    td {\n      border: 1px solid black;\n      padding: 3px 5px;\n    }\n  </style>\n</head>\n\n<body>\n  <table>\n    <tr>\n      <td>1:1</td>\n      <td>2:1</td>\n      <td>3:1</td>\n      <td>4:1</td>\n      <td>5:1</td>\n    </tr>\n    <tr>\n      <td>1:2</td>\n      <td>2:2</td>\n      <td>3:2</td>\n      <td>4:2</td>\n      <td>5:2</td>\n    </tr>\n    <tr>\n      <td>1:3</td>\n      <td>2:3</td>\n      <td>3:3</td>\n      <td>4:3</td>\n      <td>5:3</td>\n    </tr>\n    <tr>\n      <td>1:4</td>\n      <td>2:4</td>\n      <td>3:4</td>\n      <td>4:4</td>\n      <td>5:4</td>\n    </tr>\n    <tr>\n      <td>1:5</td>\n      <td>2:5</td>\n      <td>3:5</td>\n      <td>4:5</td>\n      <td>5:5</td>\n    </tr>\n  </table>\n  <script>\n    let table = document.body.firstElementChild;\n\n    for (let i = 0; i < table.rows.length; i++) {\n      let row = table.rows[i];\n      row.cells[i].style.backgroundColor = 'red';\n    }\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <style>\n    table {\n      border-collapse: collapse;\n    }\n\n    td {\n      border: 1px solid black;\n      padding: 3px 5px;\n    }\n  </style>\n</head>\n\n<body>\n  <table>\n    <tr>\n      <td>1:1</td>\n      <td>2:1</td>\n      <td>3:1</td>\n      <td>4:1</td>\n      <td>5:1</td>\n    </tr>\n    <tr>\n      <td>1:2</td>\n      <td>2:2</td>\n      <td>3:2</td>\n      <td>4:2</td>\n      <td>5:2</td>\n    </tr>\n    <tr>\n      <td>1:3</td>\n      <td>2:3</td>\n      <td>3:3</td>\n      <td>4:3</td>\n      <td>5:3</td>\n    </tr>\n    <tr>\n      <td>1:4</td>\n      <td>2:4</td>\n      <td>3:4</td>\n      <td>4:4</td>\n      <td>5:4</td>\n    </tr>\n    <tr>\n      <td>1:5</td>\n      <td>2:5</td>\n      <td>3:5</td>\n      <td>4:5</td>\n      <td>5:5</td>\n    </tr>\n  </table>\n  <script>\n    let table = document.body.firstElementChild;\n\n    // your code\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md",
    "content": "importance: 5\n\n---\n\n# Sélectionner toutes les cellules diagonales\n\nÉcrivez le code pour colorer toutes les cellules du tableau diagonal en rouge.\n\nVous devrez obtenir toutes les diagonales `<td>` de la `<table>` et les colorer en utilisant le code :\n\n```js\n// td doit être la référence à la cellule du tableau\ntd.style.backgroundColor = 'red';\n```\n\nLe résultat devrait être :\n\n[iframe src=\"solution\" height=180]\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/article.md",
    "content": "libs:\n  - d3\n  - domtree\n\n---\n\n\n# Parcourir le DOM\n\nLe DOM nous permet de faire n'importe quoi avec les éléments et leur contenu, mais nous devons d'abord atteindre l'objet DOM correspondant.\n\nToutes les opérations sur le DOM commencent par l'objet `document`. C'est le \"point d'entrée\" principal du DOM. De là, nous pouvons accéder à n'importe quel nœud.\n\nVoici une image des liens qui permettent de voyager entre les nœuds DOM :\n\n![](dom-links.svg)\n\nDiscutons-en plus en détail.\n\n## En haut : documentElement et body\n\nLes nœuds supérieurs de l'arbre sont disponibles directement en tant que propriétés de `document` :\n\n`<html>` = `document.documentElement`\n: Le nœud de document le plus haut est `document.documentElement`. C'est le noeud DOM de la balise `<html>`.\n\n`<body>` = `document.body`\n: Un autre nœud DOM largement utilisé est l'élément `<body>` -- `document.body`.\n\n`<head>` = `document.head`\n: La balise `<head>` est disponible en tant que `document.head`.\n\n````warn header=\"Il y a un hic : `document.body` peut être `null`\"\nUn script ne peut pas accéder à un élément qui n'existe pas au moment de l'exécution.\n\nEn particulier, si un script se trouve dans `<head>`, alors `document.body` n'est pas disponible, car le navigateur ne l'a pas encore lu.\n\nAinsi, dans l'exemple ci-dessous, la première `alert` affiche `null` :\n\n```html run\n<html>\n\n<head>\n  <script>\n*!*\n    alert( \"From HEAD: \" + document.body ); // null, il n'y a pas encore de <body>\n*/!*\n  </script>\n</head>\n\n<body>\n\n  <script>\n    alert( \"From BODY: \" + document.body ); // HTMLBodyElement maintenant existe\n  </script>\n\n</body>\n</html>\n```\n````\n\n```smart header=\"Dans le monde du DOM, `null` signifie \\\"n'existe pas \\\"\"\nDans le DOM, la valeur `null` signifie \"n'existe pas\" ou \"pas ce genre de nœud\".\n```\n\n## Enfants : childNodes, firstChild, lastChild\n\nNous utiliserons désormais deux termes :\n\n- **Noeuds enfants (ou enfants)** -- éléments qui sont des enfants directs. En d'autres termes, ils sont imbriqués dans celui donné. Par exemple, `<head>` et `<body>` sont des enfants de l'élément `<html>`.\n- **Descendants** -- tous les éléments imbriqués dans l'élément donné, y compris les enfants, leurs enfants, etc.\n\nPar exemple, ici `<body>` a des enfants `<div>` et `<ul>` (et quelques nœuds texte vides) :\n\n```html run\n<html>\n<body>\n  <div>Begin</div>\n\n  <ul>\n    <li>\n      <b>Information</b>\n    </li>\n  </ul>\n</body>\n</html>\n```\n\n... Et les descendants de `<body>` ne sont pas seulement des enfants directs `<div>`, `<ul>` mais aussi des éléments plus profondément imbriqués, tels que `<li>` (un enfant de `<ul>` ) et `<b>` (un enfant de `<li>`) -- le sous-arbre entier.\n\n**La collection `childNodes` répertorie tous les nœuds enfants, y compris les nœuds texte.**\n\nL'exemple ci-dessous montre des enfants de `document.body` :\n\n```html run\n<html>\n<body>\n  <div>Begin</div>\n\n  <ul>\n    <li>Information</li>\n  </ul>\n\n  <div>End</div>\n\n  <script>\n*!*\n    for (let i = 0; i < document.body.childNodes.length; i++) {\n      alert( document.body.childNodes[i] ); // Text, DIV, Text, UL, ..., SCRIPT\n    }\n*/!*\n  </script>\n  ...more stuff...\n</body>\n</html>\n```\n\nVeuillez noter un détail intéressant ici. Si nous exécutons l'exemple ci-dessus, le dernier élément affiché est `<script>`. En fait, le document contient plus de choses en dessous, mais au moment de l'exécution du script, le navigateur ne l'a pas encore lu, donc le script ne le voit pas.\n\n**Les propriétés `firstChild` et `lastChild` donnent un accès rapide aux premier et dernier enfants.**\n\nCe ne sont que des raccourcis. S'il existe des nœuds enfants, ce qui suit est toujours vrai :\n```js\nelem.childNodes[0] === elem.firstChild\nelem.childNodes[elem.childNodes.length - 1] === elem.lastChild\n```\n\nIl y a aussi une fonction spéciale `elem.hasChildNodes()` pour vérifier s'il y a des nœuds enfants.\n\n### Collections DOM\n\nComme nous pouvons le voir, `childNodes` ressemble à un tableau. Mais en réalité ce n'est pas un tableau, mais plutôt une * collection * -- un objet itérable spécial semblable à un tableau.\n\nIl y a deux conséquences importantes :\n\n1. Nous pouvons utiliser `for..of` pour itérer dessus :\n  ```js\n  for (let node of document.body.childNodes) {\n    alert(node); // shows all nodes from the collection\n  }\n  ```\n  C'est parce qu'il est itérable (fournit la propriété `Symbol.iterator`, selon les besoins).\n\n2. Les méthodes de tableau ne fonctionneront pas, car ce n'est pas un tableau :\n  ```js run\n  alert(document.body.childNodes.filter); // undefined (there's no filter method!)\n  ```\n\nLa première chose est sympa. La seconde est tolérable, car nous pouvons utiliser `Array.from` pour créer un \"vrai\" tableau à partir de la collection, si nous voulons des méthodes de tableau :\n\n  ```js run\n  alert( Array.from(document.body.childNodes).filter ); // function\n  ```\n\n```warn header=\"Les collections DOM sont en lecture seule\"\nLes collections DOM, et plus encore -- *toutes* les propriétés de navigation répertoriées dans ce chapitre sont en lecture seule.\n\nNous ne pouvons pas remplacer un enfant par autre chose en attribuant `childNodes[i] = ...`.\n\nChanger le DOM nécessite d'autres méthodes. Nous les verrons dans le prochain chapitre.\n```\n\n```warn header=\"Les collections DOM sont live\"\nPresque toutes les collections DOM avec des exceptions mineures sont *live*. En d'autres termes, elles reflètent l'état actuel du DOM.\n\nSi nous gardons une référence à `element.childNodes`, et ajoutons/supprimons des nœuds dans le DOM, alors ils apparaissent automatiquement dans la collection.\n```\n\n````warn header=\"N'utilisez pas `for..in` pour parcourir les collections\"\nLes collections sont itérables en utilisant `for..of`. Parfois, les gens essaient d'utiliser `for..in` pour cela.\n\nÀ ne pas faire. La boucle `for..in` parcourt toutes les propriétés énumérables. Et les collections ont des propriétés \"supplémentaires\" rarement utilisées que nous ne voulons généralement pas obtenir :\n\n```html run\n<body>\n<script>\n  // affiche 0, 1, length, item, values et plus encore.\n  for (let prop in document.body.childNodes) alert(prop);\n</script>\n</body>\n````\n\n## Frères, sœurs et parent\n\n*Les frères et sœurs* sont des nœuds qui sont les enfants du même parent.\n\nPar exemple, ici `<head>` et `<body>` sont des frères et sœurs :\n\n```html\n<html>\n  <head>...</head><body>...</body>\n</html>\n```\n\n- `<body>` est dit être le frère \"suivant\" ou \"droit\" de `<head>`,\n- `<head>` est dit être le frère \"précédent\" ou \"gauche\" de `<body>`.\n\nLe frère suivant est dans la propriété `nextSibling`, et le précédent - dans `previousSibling`.\n\nLe parent est disponible en tant que `parentNode`.\n\nPar exemple :\n\n```js run\n// le parent de <body> est <html>\nalert( document.body.parentNode === document.documentElement ); // true\n\n// après <head> vient <body>\nalert( document.head.nextSibling ); // HTMLBodyElement\n\n// avant <body> vient <head>\nalert( document.body.previousSibling ); // HTMLHeadElement\n```\n\n## Navigation par élément uniquement\n\nLes propriétés de navigation répertoriées ci-dessus font référence à *tous* les nœuds. Par exemple, dans `childNodes`, nous pouvons voir à la fois les nœuds texte, les nœuds élément et même les nœuds commentaire s'il en existe.\n\nMais pour de nombreuses tâches, nous ne voulons pas de nœuds texte ou commentaire. Nous voulons manipuler des nœuds élément qui représentent des balises et forment la structure de la page.\n\nVoyons donc plus de liens de navigation qui ne prennent en compte que les *nœuds élément* :\n\n![](dom-links-elements.svg)\n\nLes liens sont similaires à ceux donnés ci-dessus, juste avec le mot `Element` à l'intérieur :\n\n- `children` -- seuls les enfants qui sont des nœuds élément.\n- `firstElementChild`, `lastElementChild` -- enfants du premier et du dernier élément.\n- `previousElementSibling`, `nextElementSibling` -- éléments voisins.\n- `parentElement` -- élément parent.\n\n````smart header=\"Pourquoi `parentElement` ? Le parent peut-il ne *pas* être un élément ?\"\nLa propriété `parentElement` renvoie l'élément parent, tandis que `parentNode` retourne le parent \"peu importe le nœud\". Ces propriétés sont généralement les mêmes : elles obtiennent toutes deux le parent.\n\nÀ la seule exception de `document.documentElement` :\n\n```js run\nalert( document.documentElement.parentNode ); // document\nalert( document.documentElement.parentElement ); // null\n```\n\nLa raison en est que le nœud racine `document.documentElement` (`<html>`) a\n`document` comme parent. Mais `document` n'est pas un nœud élément, donc `parentNode` le renvoie et pas `parentElement`.\n\nCe détail peut être utile lorsque nous voulons passer d'un élément arbitraire `elem` à `<html>`, mais pas au `document` :\n\n```js\nwhile(elem = elem.parentElement) { // remonter jusqu'à <html>\n  alert( elem );\n}\n```\n````\n\nModifions l'un des exemples ci-dessus : remplaçons `childNodes` par `children`. Maintenant, il ne montre que des éléments :\n\n```html run\n<html>\n<body>\n  <div>Begin</div>\n\n  <ul>\n    <li>Information</li>\n  </ul>\n\n  <div>End</div>\n\n  <script>\n*!*\n    for (let elem of document.body.children) {\n      alert(elem); // DIV, UL, DIV, SCRIPT\n    }\n*/!*\n  </script>\n  ...\n</body>\n</html>\n```\n\n## Plus de liens : tableaux [#dom-navigation-tables]\n\nJusqu'à présent, nous avons décrit les propriétés de navigation de base.\n\nCertains types d'éléments DOM peuvent fournir des propriétés supplémentaires, spécifiques à leur type, pour plus de commodité.\n\nLes tableaux en sont un excellent exemple et représentent un cas particulièrement important :\n\n**L'élément `<table>`** supporte (en plus de ce qui précède) ces propriétés :\n- `table.rows` -- la collection d'éléments `<tr>` du tableau.\n- `table.caption/tHead/tFoot` -- références aux éléments `<caption>`, `<thead>`, `<tfoot>`.\n- `table.tBodies` -- la collection d'éléments `<tbody>` (peut être multiple selon la norme, mais il y en aura toujours au moins une - même si elle n'est pas dans le HTML source, le navigateur la mettra dans le DOM).\n\n**`<thead>`, `<tfoot>`, `<tbody>`** les éléments fournissent la propriété `rows` :\n- `tbody.rows` -- la collection de `<tr>` à l'intérieur.\n\n**`<tr>`:**\n- `tr.cells` -- la collection de cellules `<td>` et `<th>` à l'intérieur du `<tr>` donné.\n- `tr.sectionRowIndex` -- la position (index) du `<tr>` donné à l'intérieur du `<thead>/<tbody>/<tfoot>`.\n- `tr.rowIndex` -- le nombre de `<tr>` dans le tableau dans son ensemble (y compris toutes les lignes du tableau).\n\n**`<td>` et `<th>` : **\n- `td.cellIndex` -- le numéro de la cellule à l'intérieur du `<tr>` qui l'entoure.\n\nUn exemple d'utilisation :\n\n```html run height=100\n<table id=\"table\">\n  <tr>\n    <td>one</td><td>two</td>\n  </tr>\n  <tr>\n    <td>three</td><td>four</td>\n  </tr>\n</table>\n\n<script>\n  // obtenir td avec \"two\" (première ligne, deuxième colonne)\n  let td = table.*!*rows[0].cells[1]*/!*;\n  td.style.backgroundColor = \"red\"; // le mettre en valeur\n</script>\n```\n\nLa spécification : [tabular data](https://html.spec.whatwg.org/multipage/tables.html).\n\nIl existe également des propriétés de navigation supplémentaires pour les formulaires HTML. Nous les examinerons plus tard lorsque nous commencerons à travailler avec des formulaires.\n\n## Résumé\n\nÉtant donné un nœud DOM, nous pouvons aller vers ses voisins immédiats en utilisant les propriétés de navigation.\n\nIl en existe deux ensembles principaux :\n\n- Pour tous les nœuds : `parentNode`, `childNodes`, `firstChild`, `lastChild`, `previousSibling`, `nextSibling`.\n- Pour les nœuds élément uniquement : `parentElement`, `children`, `firstElementChild`, `lastElementChild`, `previousElementSibling`, `nextElementSibling`.\n\nCertains types d'éléments DOM, par exemple , fournissent des propriétés et des collections supplémentaires pour accéder à leur contenu.\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/head.html",
    "content": "<style>\n#travel-dom-comment {\n  font-style: italic;\n}\n#travel-dom-control ul {\n  margin: 6px 0;\n}\n</style>"
  },
  {
    "path": "2-ui/1-document/04-searching-elements-dom/1-find-elements/solution.md",
    "content": "Il existe plusieurs façons de faire : \nEn voici quelques unes : \n\n```js\n// 1. Le tableau avec `id=\"age-table\"`.\nlet table = document.getElementById('age-table')\n\n// 2. Tous les éléments 'label' dans le tableau\ntable.getElementsByTagName('label')\n// ou\ndocument.querySelectorAll('#age-table label')\n\n// 3. Le premier td dans ce tableau (avec le mot \"Age\")\ntable.rows[0].cells[0]\n// ou\ntable.getElementsByTagName('td')[0]\n// ou\ntable.querySelector('td')\n\n// 4. Le formulaire avec le nom \"search\"\n// en supposant qu'il n'y ait qu'un élément avec name=\"search\" dans le document.\nlet form = document.getElementsByName('search')[0]\n// ou spécifiquement un formulaire\ndocument.querySelector('form[name=\"search\"]')\n\n// 5. Le premier input dans ce formulaire\nform.getElementsByTagName('input')[0]\n// ou\nform.querySelector('input')\n\n// 6. Le dernier input dans ce formulaire\nlet inputs = form.querySelectorAll('input') // trouve tous les inputs\ninputs[inputs.length-1] // prend le dernier\n```\n"
  },
  {
    "path": "2-ui/1-document/04-searching-elements-dom/1-find-elements/table.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n  <form name=\"search\">\n    <label>Cherchez dans le site :\n      <input type=\"text\" name=\"search\">\n    </label>\n    <input type=\"submit\" value=\"Search!\">\n  </form>\n\n  <hr>\n\n  <form name=\"search-person\">\n    Recherchez les visiteurs :\n    <table id=\"age-table\">\n      <tr>\n        <td>Age:</td>\n        <td id=\"age-list\">\n          <label>\n            <input type=\"radio\" name=\"age\" value=\"young\">moins de 18</label>\n          <label>\n            <input type=\"radio\" name=\"age\" value=\"mature\">18-50</label>\n          <label>\n            <input type=\"radio\" name=\"age\" value=\"senior\">plus de 50</label>\n        </td>\n      </tr>\n\n      <tr>\n        <td>Infos complémentaires :</td>\n        <td>\n          <input type=\"text\" name=\"info[0]\">\n          <input type=\"text\" name=\"info[1]\">\n          <input type=\"text\" name=\"info[2]\">\n        </td>\n      </tr>\n\n    </table>\n\n    <input type=\"submit\" value=\"Search!\">\n  </form>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md",
    "content": "importance: 4\n\n---\n\n# Recherche d'éléments\n\nVoici le document avec le tableau et formulaire\n\nComment trouver ?...\n\n1. Le tableau avec `id=\"age-table\"`.\n2. Tous les éléments `label` dans ce tableau (il devrait y en avoir 3).\n3. Le premier `td` dans ce tableau (avec le mot \"Age\").\n4. Le `form` avec `name=\"search\"`.\n5. Le premier `input` dans ce formulaire.\n6. Le dernier `input` dans ce formulaire.\n\nOuvrez la page [table.html](table.html) dans un onglet à part et utilisez les outils du navigateur pour cela. "
  },
  {
    "path": "2-ui/1-document/04-searching-elements-dom/article.md",
    "content": "# Recherches: getElement*, querySelector*\n\nLes outils de navigation du DOM sont très pratiques quand les éléments sont proches les uns des autres. Mais s'ils ne le sont pas ? Comment atteindre un élément arbitraire de la page ?\n\nIl existe d'autres méthodes de recherche pour cela.\n\n## document.getElementById ou juste id\n\nSi un élément a l'attribut `id`, on peut atteindre cet élément en utilisant la méthode `document.getElementById(id)`, peu importe où elle se trouve.\n\nPar exemple :\n\n```html run\n<div id=\"elem\">\n  <div id=\"elem-content\">Elément</div>\n</div>\n\n<script>\n  // récupération de l'élément :\n*!*\n  let elem = document.getElementById('elem');\n*/!*\n\n  // on met son arrière-plan rouge :\n  elem.style.background = 'red';\n</script>\n```\nIl y a aussi une variable globale nommée selon l'`id` qui référence l'élément :\n\n```html run\n<div id=\"*!*elem*/!*\">\n  <div id=\"*!*elem-content*/!*\">Elément</div>\n</div>\n\n<script>\n  // elem est une référence à l'élément du DOM ayant l'id \"elem\"\n  elem.style.background = 'red';\n\n  // id=\"elem-content\" contient un tiret, donc ça ne peut pas être un nom de variable\n  // ...mais on peut y accéder en utilisant les crochets : window['elem-content']\n</script>\n```\n\n...A moins qu'on déclare une variable JavaScript avec le même nom, auquel cas celle-ci obtient la priorité :\n\n```html run untrusted height=0\n<div id=\"elem\"></div>\n\n<script>\n  let elem = 5; // maintenant elem vaut 5, ce n'est plus une référence à <div id=\"elem\">\n\n  alert(elem); // 5\n</script>\n```\n\n```warn header=\"Ne pas utiliser les variables globales nommées selon l'id pour accéder aux éléments !\"\nCe comportement est décrit [dans la spécification](https://html.spec.whatwg.org/multipage/window-object.html#named-access-on-the-window-object), mais il est pris en charge principalement pour la compatibilité .\n\nLe navigateur essaie de nous aider en mélangeant les noms de JS et du DOM. C'est bien pour des scripts simples, intégré dans du HTML, mais en général ce n'est pas bon. Il peut y avoir des conflits de noms. Aussi, quand quelqu'un lira le code JS sans avoir le HTML à côté, ce ne sera pas évident pour lui d'où vient la variable.\n\nDans ce tutoriel, on utilise `id` pour directement référencer un élément rapidement, quand il sera évident d'où il vient.\n\nDans la vraie vie, `document.getElementById` est la méthode à avantager.\n```\n\n```smart header=\"L' `id` doit être unique\"\nL'`id` doit être unique. Il ne peut y avoir qu'un élément dans le document avec un `id` donné.\n\nS'il y a de multiples éléments avec le même `id`, alors le comportement de la méthode qui l'utilise est imprévisible, par exemple `document.getElementById` pourra renvoyer n'importe lequel de ces éléments aléatoirement. Donc suivez la règle et gardez l'`id` unique.\n```\n\n```warn header=\"Seulement `document.getElementById`, pas `anyElem.getElementById`\"\nLa méthode `getElementById` ne peut être appelée que sur l'objet `document` . Elle va chercher l'`id` dans le document entier.\n```\n\n## querySelectorAll [#querySelectorAll]\n\nDe loin, la méthode la plus polyvalente, `elem.querySelectorAll(css)` renvoie tous les éléments à l'intérieur de `elem` correspondant au sélecteur CSS donné en paramètre\n\nIci, on recherche tous les éléments `<li>` qui sont les derniers enfants :\n\n```html run\n<ul>\n  <li>Le</li>\n  <li>test</li>\n</ul>\n<ul>\n  <li>a</li>\n  <li>réussi</li>\n</ul>\n<script>\n*!*\n  let elements = document.querySelectorAll('ul > li:last-child');\n*/!*\n\n  for (let elem of elements) {\n    alert(elem.innerHTML); // \"test\", \"réussi\"\n  }\n</script>\n```\n\nCette méthode est très puissante, car tous les sélecteurs CSS peuvent être utilisés.\n\n```smart header=\"On peut aussi utiliser des pseudo-classes\"\n\nLes pseudo-classes dans le sélecteur CSS comme `:hover` et `:active` sont aussi acceptés. Par exemple, `document.querySelectorAll(':hover')` renverra l'ensemble des éléments dont le curseur est au-dessus en ce moment (dans l'ordre d'imbrication : du plus extérieur `<html>` au plus imbriqué).\n```\n\n## querySelector [#querySelector]\n\nUn appel à `elem.querySelector(css)` renverra le premier élément d'un sélecteur CSS donné.\n\nEn d'autres termes, le résultat sera le même que `elem.querySelectorAll(css)[0]`, mais celui-ci cherchera *tous* les éléments et en choisira un seul, alors que `elem.querySelector` n'en cherchera qu'un. C'est donc plus rapide, et plus court à écrire.\n\n## matches\n\nLes méthodes précédentes recherchaient dans le DOM.\n\nLa commande [elem.matches(css)](http://dom.spec.whatwg.org/#dom-element-matches) ne recherche rien, elle vérifie simplement que `elem` correspond au sélecteur CSS donné. Elle renvoie `true` ou `false`.\n\nCette méthode devient utile quand on itère sur des éléments (comme dans un array par exemple) et qu'on veut filtrer ceux qui nous intéressent.\n\nPar exemple :\n\n```html run\n<a href=\"http://example.com/file.zip\">...</a>\n<a href=\"http://ya.ru\">...</a>\n\n<script>\n  // on peut mettre n'importe quel ensemble à la place de document.body.children\n  for (let elem of document.body.children) {\n*!*\n    if (elem.matches('a[href$=\"zip\"]')) {\n*/!*\n      alert(\"le lien de l'archive : \" + elem.href );\n    }\n  }\n</script>\n```\n\n## closest\n\nLes *ancêtres* d'un élément sont : le parent, le parent du parent, son propre parent etc... Les ancêtres forment une chaîne de parents depuis l'élément jusqu'au sommet.\n\nLa méthode `elem.closest(css)` cherche l'ancêtre le plus proche qui correspond au sélecteur CSS. L'élément `elem` est lui-même inclus dans la recherche.\n\nEn d'autres mots, la méthode `closest` part de l'élément et remonte en regardant chacun des parents. S'il correspond au sélecteur, la recherche s'arrête et l'ancêtre est renvoyé.\n\nPar exemple :\n\n```html run\n<h1>Contenu</h1>\n\n<div class=\"contents\">\n  <ul class=\"book\">\n    <li class=\"chapter\">Chapître 1</li>\n    <li class=\"chapter\">Chapître 2</li>\n  </ul>\n</div>\n\n<script>\n  let chapter = document.querySelector('.chapter'); // LI\n\n  alert(chapter.closest('.book')); // UL\n  alert(chapter.closest('.contents')); // DIV\n\n  alert(chapter.closest('h1')); // null (car h1 n'est pas un ancêtre)\n</script>\n```\n\n## getElementsBy*\n\nIl y a aussi d'autres méthodes pour rechercher des balises par tag, classe, etc...\n\nAujourd'hui, elles sont principalement de l'histoire ancienne, car `querySelector` est plus puissante et plus courte à écrire.\n\nDonc ici, on va surtout en parler dans le souci d'être complet, comme elles peuvent encore se retrouver dans des vieux scripts.\n\n- `elem.getElementsByTagName(tag)` cherche les éléments avec le tag donné et renvoie l'ensemble de ces éléments. Le paramètre `tag` peut aussi être une étoile `\"*\"` pour signifier n'importe quel tag.\n- `elem.getElementsByClassName(className)` renvoie les éléments qui ont la classe CSS donnée.\n- `document.getElementsByName(name)` renvoie les éléments qui ont l'attribut `name`, dans tout le document. Très rarement utilisé.\n\nPar exemple:\n```js\n// récupérer tous les divs du document.\nlet divs = document.getElementsByTagName('div');\n```\n\nTrouvons tous les tags `input` dans le tableau :\n\n```html run height=50\n<table id=\"table\">\n  <tr>\n    <td>Votre âge:</td>\n\n    <td>\n      <label>\n        <input type=\"radio\" name=\"age\" value=\"young\" checked> moins de 18\n      </label>\n      <label>\n        <input type=\"radio\" name=\"age\" value=\"mature\"> entre 18 et 50\n      </label>\n      <label>\n        <input type=\"radio\" name=\"age\" value=\"senior\"> plus de 60\n      </label>\n    </td>\n  </tr>\n</table>\n\n<script>\n*!*\n  let inputs = table.getElementsByTagName('input');\n*/!*\n\n  for (let input of inputs) {\n    alert( input.value + ': ' + input.checked );\n  }\n</script>\n```\n\n```warn header=\"N'oubliez pas la lettre `\\\"s\\\"` !\"\nLes développeurs junior oublient parfois la lettre `\"s\"`. Ils essaient donc d'appeler `getElementByTagName` au lieu de <code>getElement<b>s</b>ByTagName</code>.\n\nLa lettre `\"s\"` letter n'apparaît pas dans `getElementById`, car cette méthode renvoie un seul élément. Mais `getElementsByTagName` renvoie un ensemble d'éléments, il y a donc un `\"s\"`.\n```\n\n````warn header=\"Elle renvoie un ensemble, pas un élément !\"\nUne autre erreur répandue parmi les débutants est d'écrire :\n\n```js\n// ne fonctionne pas :\ndocument.getElementsByTagName('input').value = 5;\n```\n\nCela ne va pas marcher, parce qu'on essaie d'affecter une valeur à un ensemble d'objets plutôt qu'à un élément de cet ensemble.\nOn devrait plutôt itérer sur l'ensemble ou récupérer un élément par son index, et lui affecter la valeur, comme ceci :\n\n```js\n// doit fonctionner (s'il y a un élément 'input' )\ndocument.getElementsByTagName('input')[0].value = 5;\n```\n````\n\nRecherche des éléments `.article` :\n\n```html run height=50\n<form name=\"my-form\">\n  <div class=\"article\">Article</div>\n  <div class=\"long article\">Long article</div>\n</form>\n\n<script>\n  // recherche par attribut nom\n  let form = document.getElementsByName('my-form')[0];\n\n  // recherche par classe dans le formulaire\n  let articles = form.getElementsByClassName('article');\n  alert(articles.length); // 2 éléments trouvés avec la classe 'article'\n</script>\n```\n\n## Ensembles courants\n\nToutes les méthodes `\"getElementsBy*\"` renvoient l'ensemble *courant*. De tels ensembles montrent toujours l'état courant du document et se mettent à jour automatiquement quand celui-ci change.\n\nDans l'exemple ci-dessous, il y a deux scripts :\n\n1. Le premier crée une référence à l'ensemble des `<div>`. Maintenant, sa longueur est `1`.\n2. Le second se lance après que le navigateur aie rencontré un autre `<div>`, donc sa longueur est `2`.\n\n```html run\n<div>Premier div</div>\n\n<script>\n  let divs = document.getElementsByTagName('div');\n  alert(divs.length); // 1\n</script>\n\n<div>Second div</div>\n\n<script>\n*!*\n  alert(divs.length); // 2\n*/!*\n</script>\n```\n\nA l'opposé, `querySelectorAll` renvoie un ensemble *statique*. C'est comme un tableau fixe d'éléments\n\nSi on l'utilise, alors les deux scripts ci-dessus renvoient `1`:\n\n\n```html run\n<div>Premier div</div>\n\n<script>\n  let divs = document.querySelectorAll('div');\n  alert(divs.length); // 1\n</script>\n\n<div>Second div</div>\n\n<script>\n*!*\n  alert(divs.length); // 1\n*/!*\n</script>\n```\n\nMaintenant, on voit facilement la différence. L'ensemble statique ne s'est pas incrémenté après l'apparition d'un nouveau `div` dans le document.\n\n## Résumé\n\nIl y a 6 principales méthodes pour rechercher des balises dans le DOM :\n\n<table>\n<thead>\n<tr>\n<td>Méthode</td>\n<td>Recherches par...</td>\n<td>Peut appeler un élément ?</td>\n<td>Courant ?</td>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>querySelector</code></td>\n<td>sélecteur CSS</td>\n<td>✔</td>\n<td>-</td>\n</tr>\n<tr>\n<td><code>querySelectorAll</code></td>\n<td>sélecteur CSS</td>\n<td>✔</td>\n<td>-</td>\n</tr>\n<tr>\n<td><code>getElementById</code></td>\n<td><code>id</code></td>\n<td>-</td>\n<td>-</td>\n</tr>\n<tr>\n<td><code>getElementsByName</code></td>\n<td><code>nom</code></td>\n<td>-</td>\n<td>✔</td>\n</tr>\n<tr>\n<td><code>getElementsByTagName</code></td>\n<td>tag ou <code>'*'</code></td>\n<td>✔</td>\n<td>✔</td>\n</tr>\n<tr>\n<td><code>getElementsByClassName</code></td>\n<td>classe</td>\n<td>✔</td>\n<td>✔</td>\n</tr>\n</tbody>\n</table>\n\nDe loin, les plus utilisées sont `querySelector` et `querySelectorAll`, mais `getElement(s)By*` peut être de temps en temps utile ou rencontrée dans de vieux scripts.\n\nA part ça :\n\n- Il y a `elem.matches(css)` qui vérifie si `elem` correspond au sélecteur CSS donné.\n- Il y a `elem.closest(css)` qui va chercher l'ancêtre le plus proche qui correspond au sélecteur CSS donné.\n\nEt on peut mentionner ici une autre méthode pour vérifier la relation parent-enfant, ce qui est parfois utile :\n-  `elemA.contains(elemB)` renvoie true si `elemB` est dans `elemA` (un descendant de `elemA`) ou quand `elemA==elemB`.\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/solution.md",
    "content": "Il y a un piège ici.\n\nAu moment de l'exécution de `<script>`, le dernier nœud DOM est exactement `<script>`, car le navigateur n'a pas encore traité le reste de la page.\n\nLe résultat est donc `1` (nœud élément).\n\n```html run height=60\n<html>\n\n<body>\n  <script>\n    alert(document.body.lastChild.nodeType);\n  </script>\n</body>\n\n</html>\n```\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md",
    "content": "importance: 5\n\n---\n\n# Qu'y a-t-il dans le nodeType ?\n\nQu'affiche le script ?\n\n```html\n<html>\n\n<body>\n  <script>\n    alert(document.body.lastChild.nodeType);\n  </script>\n</body>\n\n</html>\n```\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md",
    "content": "Faisons une boucle dans le `<li>`:\n\n```js\nfor (let li of document.querySelectorAll('li')) {\n  ...\n}\n```\n\nDans la boucle, nous devons obtenir le texte à l'intérieur de chaque `li`.\n\nNous pouvons lire le texte du premier nœud enfant de `li`, c'est-à-dire le nœud texte :\n\n```js\nfor (let li of document.querySelectorAll('li')) {\n  let title = li.firstChild.data;\n\n  // title est le texte dans <li> avant tout autre noeud\n}\n```\n\nEnsuite, nous pouvons obtenir le nombre de descendants comme `li.getElementsByTagName('li').length`.\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <ul>\n    <li>Animals\n      <ul>\n        <li>Mammals\n          <ul>\n            <li>Cows</li>\n            <li>Donkeys</li>\n            <li>Dogs</li>\n            <li>Tigers</li>\n          </ul>\n        </li>\n        <li>Other\n          <ul>\n            <li>Snakes</li>\n            <li>Birds</li>\n            <li>Lizards</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n    <li>Fishes\n      <ul>\n        <li>Aquarium\n          <ul>\n            <li>Guppy</li>\n            <li>Angelfish</li>\n          </ul>\n        </li>\n        <li>Sea\n          <ul>\n            <li>Sea trout</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n  </ul>\n\n  <script>\n    for (let li of document.querySelectorAll('li')) {\n      // get the title from the text node\n      let title = li.firstChild.data;\n\n      title = title.trim(); // remove extra spaces from ends\n\n      // get the descendants count\n      let count = li.getElementsByTagName('li').length;\n\n      alert(title + ': ' + count);\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/2-tree-info/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <ul>\n    <li>Animals\n      <ul>\n        <li>Mammals\n          <ul>\n            <li>Cows</li>\n            <li>Donkeys</li>\n            <li>Dogs</li>\n            <li>Tigers</li>\n          </ul>\n        </li>\n        <li>Other\n          <ul>\n            <li>Snakes</li>\n            <li>Birds</li>\n            <li>Lizards</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n    <li>Fishes\n      <ul>\n        <li>Aquarium\n          <ul>\n            <li>Guppy</li>\n            <li>Angelfish</li>\n          </ul>\n        </li>\n        <li>Sea\n          <ul>\n            <li>Sea trout</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n  </ul>\n\n  <script>\n    // ... your code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/2-tree-info/task.md",
    "content": "importance: 5\n\n---\n\n# Compter les descendants\n\nIl y a un arbre structuré comme un `ul/li` imbriqué.\n\nÉcrivez le code qui pour que chaque `<li>` affiche :\n\n1. Quel est le texte à l'intérieur (sans le sous-arbre)\n2. Le nombre de `<li>` imbriqués - tous les descendants, y compris ceux profondément imbriqués.\n\n[demo src=\"solution\"]\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/solution.md",
    "content": "La réponse : **`BODY`**.\n\n```html run\n<script>\n  let body = document.body;\n\n  body.innerHTML = \"<!--\" + body.tagName + \"-->\";\n\n  alert( body.firstChild.data ); // BODY\n</script>\n```\n\nCe qui se passe pas à pas :\n\n1. Le contenu de `<body>` est remplacé par le commentaire. Le commentaire est `<!--BODY-->`, car `body.tagName == \"BODY\"`. Comme nous nous en souvenons, `tagName` est toujours en majuscule en HTML.\n2. Le commentaire est maintenant le seul nœud enfant, donc nous l'avons dans `body.firstChild`.\n3. La propriété `data` du commentaire est son contenu (à l'intérieur `<!--...-->`) : `\"BODY\"`.\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md",
    "content": "importance: 3\n\n---\n\n# Balise dans le commentaire\n\nQu'affice ce code ?\n\n```html\n<script>\n  let body = document.body;\n\n  body.innerHTML = \"<!--\" + body.tagName + \"-->\";\n\n  alert( body.firstChild.data ); // Qu'est ce qu'il y a ici ?\n</script>\n```\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/solution.md",
    "content": "Nous pouvons voir à quelle classe il appartient en le sortant, comme :\n\n```js run\nalert(document); // [object HTMLDocument]\n```\n\nOu :\n\n```js run\nalert(document.constructor.name); // HTMLDocument\n```\n\nAinsi, `document` est une instance de la classe `HTMLDocument`.\n\nQuelle est sa place dans la hiérarchie ?\n\nOui, nous pourrions parcourir les spécifications, mais il serait plus rapide de le déterminer manuellement.\n\nParcourons la chaîne du prototype via `__proto__`.\n\nComme nous le savons, les méthodes d'une classe sont dans le `prototype` du constructeur. Par exemple, `HTMLDocument.prototype` a des méthodes pour les documents.\n\nDe plus, il y a une référence à la fonction constructeur à l'intérieur du `prototype` :\n\n```js run\nalert(HTMLDocument.prototype.constructor === HTMLDocument); // true\n```\n\nPour obtenir le nom de la classe sous forme de chaîne de caractères, nous pouvons utiliser `constructor.name`. Faisons-le pour toute la chaîne du prototype de `document`, jusqu'à la classe `Node` :\n\n```js run\nalert(HTMLDocument.prototype.constructor.name); // HTMLDocument\nalert(HTMLDocument.prototype.__proto__.constructor.name); // Document\nalert(HTMLDocument.prototype.__proto__.__proto__.constructor.name); // Node\n```\n\nVoilà la hiérarchie.\n\nNous pourrions également examiner l'objet en utilisant `console.dir(document)` et voir ces noms en ouvrant `__proto__`. La console les prend du constructeur en interne.\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md",
    "content": "importance: 4\n\n---\n\n# Où est le \"document\" dans la hiérarchie ?\n\nÀ quelle classe appartient le `document` ?\n\nQuelle est sa place dans la hiérarchie DOM ?\n\nHérite-t-il de `Node` ou `Element`, ou peut-être de `HTMLElement` ?\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/article.md",
    "content": "# Propriétés de nœud : type, balise et contenu\n\nVoyons plus en détail les nœuds DOM.\n\nDans ce chapitre, nous verrons plus en détail ce qu'ils sont et découvrirons leurs propriétés les plus utilisées.\n\n## Classes de nœud DOM\n\nDifférents nœuds DOM peuvent avoir des propriétés différentes. Par exemple, un nœud élément correspondant à la balise `<a>` a des propriétés liées aux liens, et celui correspondant à `<input>` a des propriétés liées aux entrées, etc. Les nœuds texte ne sont pas identiques aux nœuds élément. Mais il existe également des propriétés et des méthodes communes à chacun d'entre eux, car toutes les classes de nœuds DOM forment une hiérarchie unique.\n\nChaque nœud DOM appartient à la classe intégrée correspondante.\n\nLa racine de la hiérarchie est [EventTarget](https://dom.spec.whatwg.org/#eventtarget), hérité par [Node](http://dom.spec.whatwg.org/#interface-node), et d'autres nœuds DOM en héritent.\n\nVoici l'image, les explications à suivre :\n\n![](dom-class-hierarchy.svg)\n\nLes classes sont :\n\n- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- est la classe racine \"abstraite\" pour tout.\n\n    Les objets de cette classe ne sont jamais créés. Ils servent de base, afin que tous les nœuds DOM supportent les soi-disant \"événements\", nous les étudierons plus tard.\n\n- [Node](https://dom.spec.whatwg.org/#interface-node) -- est également une classe \"abstraite\", servant de base aux nœuds DOM.\n\n    Elle fournit la fonctionnalité de l'arborescence de base : `parentNode`, `nextSibling`, `childNodes` et ainsi de suite (ce sont des getters). Les objets de la classe `Node` ne sont jamais créés. Mais il existe d'autres classes qui en héritent (et héritent donc de la fonctionnalité `Node`).\n\n- [Document](https://dom.spec.whatwg.org/#interface-document), pour des raisons historiques souvent héritées par `HTMLDocument` (bien que la dernière spécification ne le dicte pas) -- est un document dans son ensemble.\n\n    L'objet global `document` appartient exactement à cette classe. Il sert de point d'entrée au DOM.\n\n- [CharacterData](https://dom.spec.whatwg.org/#interface-characterdata) -- une classe \"abstraite\", héritée par :\n    - [Text](https://dom.spec.whatwg.org/#interface-text) -- la classe correspondant à un texte à l'intérieur des éléments, par ex. `Bonjour` dans `<p>Bonjour</p>`.\n    - [Comment](https://dom.spec.whatwg.org/#interface-comment) -- la classe pour les commentaires. Ils ne sont pas affichés, mais chaque commentaire devient un membre du DOM.\n\n- [Element](https://dom.spec.whatwg.org/#interface-element) -- est la classe de base des éléments DOM.\n\n    Elle fournit une navigation au niveau des éléments comme `nextElementSibling`, `children` et des méthodes de recherche comme `getElementsByTagName`, `querySelector`.\n\n    Un navigateur prend en charge non seulement HTML, mais également XML et SVG. Ainsi, la classe `Element` sert de base à des classes plus spécifiques : `SVGElement`, `XMLElement` (nous n'en avons pas besoin ici) et `HTMLElement`.\n\n- Enfin, [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) est la classe de base pour tous les éléments HTML. Nous travaillerons avec lui la plupart du temps.\n\n    Elle est héritée par des éléments HTML concrets :\n    - [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) -- la classe pour les éléments `<input>`,\n    - [HTMLBodyElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlbodyelement) -- la classe pour les éléments `<body>`,\n    - [HTMLAnchorElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlanchorelement) -- la classe pour les éléments `<a>`,\n    - ...etc.\n\nIl existe de nombreuses autres balises avec leurs propres classes qui peuvent avoir des propriétés et des méthodes spécifiques, tandis que certains éléments, tels que `<span>`, `<section>`, `<article>` n'ont pas de propriétés spécifiques, ce sont donc des instances de la classe `HTMLElement`.\n\nAinsi, l'ensemble complet des propriétés et des méthodes d'un nœud donné est le résultat de la chaîne de l'héritage.\n\nPar exemple, considérons l'objet DOM pour un élément `<input>`. Il appartient à la classe  [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement).\n\nIl obtient les propriétés et les méthodes en superposition de (répertoriées dans l'ordre d'héritage) :\n\n- `HTMLInputElement` -- cette classe fournit des propriétés spécifiques à l'entrée,\n- `HTMLElement` -- elle fournit des méthodes d'élément HTML communes (et des getters/setters),\n- `Element` -- fournit des méthodes d'élément génériques,\n- `Node` -- fournit des propriétés de noeud DOM communes,\n- `EventTarget` -- apporte du support aux événements (à couvrir),\n- ...et finalement il hérite de `Object`, donc les méthodes \"plain object\" comme `hasOwnProperty` sont également disponibles.\n\nPour voir le nom de la classe de noeud DOM, nous pouvons rappeler qu'un objet a généralement la propriété `constructor`. Il fait référence au constructeur de classe, et `constructor.name` est son nom :\n\n```js run\nalert( document.body.constructor.name ); // HTMLBodyElement\n```\n\n...Or we can just `toString` it:\n\n```js run\nalert( document.body ); // [object HTMLBodyElement]\n```\n\nNous pouvons également utiliser `instanceof` pour vérifier l'héritage :\n\n```js run\nalert( document.body instanceof HTMLBodyElement ); // true\nalert( document.body instanceof HTMLElement ); // true\nalert( document.body instanceof Element ); // true\nalert( document.body instanceof Node ); // true\nalert( document.body instanceof EventTarget ); // true\n```\n\nComme nous pouvons le voir, les nœuds DOM sont des objets JavaScript normaux. Ils utilisent des classes basées sur des prototypes pour l'héritage.\n\nC'est aussi facile à voir en sortant un élément avec `console.dir (elem)` dans un navigateur. Là, dans la console, vous pouvez voir `HTMLElement.prototype`, `Element.prototype` et ainsi de suite.\n\n```smart header=\"`console.dir(elem)` versus `console.log(elem)`\"\nLa plupart des navigateurs prennent en charge deux commandes dans leurs outils de développement : `console.log` et `console.dir`. Elles sortent leurs arguments dans la console. Pour les objets JavaScript, ces commandes font généralement la même chose.\n\nMais pour les éléments DOM, elles sont différents :\n\n- `console.log(elem)` affiche l'arborescence DOM de l'élément.\n- `console.dir(elem)` affiche l'élément en tant qu'objet DOM, bon pour explorer ses propriétés.\n\nEssayez les sur `document.body`.\n```\n\n````smart header=\"IDL dans la spécification\"\nDans la spécification, les classes DOM ne sont pas décrites en utilisant JavaScript, mais une [Interface description language](https://en.wikipedia.org/wiki/Interface_description_language) spéciale (IDL), qui est généralement facile à comprendre.\n\nDans IDL, toutes les propriétés sont précédées de leurs types. Par exemple, `DOMString`,  `boolean` et ainsi de suite.\n\nEn voici un extrait, avec des commentaires :\n\n```js\n// Definir HTMLInputElement\n*!*\n// Le signe deux-points \":\" signifie que HTMLInputElement hérite de HTMLElement\n*/!*\ninterface HTMLInputElement: HTMLElement {\n  // ici les propriétés et méthodes des éléments <input>\n\n*!*\n  // \"DOMString\" signifie que la valeur d'une propriété est une chaîne de caractères\n*/!*\n  attribute DOMString accept;\n  attribute DOMString alt;\n  attribute DOMString autocomplete;\n  attribute DOMString value;\n\n*!*\n  // propriété de valeur booléenne (true/false)\n  attribute boolean autofocus;\n*/!*\n  ...\n*!*\n  // maintenant la méthode : \"void\" signifie que la méthode ne renvoie aucune valeur\n*/!*\n  void select();\n  ...\n}\n```\n````\n\n## La propriété \"nodeType\"\n\nLa propriété `nodeType` fournit une autre méthode \"à l'ancienne\" pour obtenir le \"type\" d'un nœud DOM.\n\nIl a une valeur numérique :\n- `elem.nodeType == 1` pour les nœuds élément,\n- `elem.nodeType == 3` pour les nœuds texte,\n- `elem.nodeType == 9` pour l'objet document,\n- il y a peu d'autres valeurs dans [la spécification](https://dom.spec.whatwg.org/#node).\n\nPar exemple :\n\n```html run\n<body>\n  <script>\n  let elem = document.body;\n\n  // examinons ceci : quel type de nœud est dans elem ?\n  alert(elem.nodeType); // 1 => element\n\n  // et son premier enfant est...\n  alert(elem.firstChild.nodeType); // 3 => text\n\n  // pour l'objet document, le type est 9\n  alert( document.nodeType ); // 9\n  </script>\n</body>\n```\n\nDans les scripts modernes, nous pouvons utiliser `instanceof` et d'autres tests basés sur les classes pour voir le type de nœud, mais parfois `nodeType` peut être plus simple. Nous pouvons seulement lire `nodeType`, pas le changer.\n\n## Balise : nodeName et tagName\n\nÉtant donné un nœud DOM, nous pouvons lire son nom de balise dans les propriétés `nodeName` ou `tagName` :\n\nPar exemple :\n\n```js run\nalert( document.body.nodeName ); // BODY\nalert( document.body.tagName ); // BODY\n```\n\nY a-t-il une différence entre `tagName` et `nodeName` ?\n\nBien sûr, la différence se reflète dans leurs noms, mais c'est en effet un peu subtile.\n\n- La propriété `tagName` existe uniquement pour les nœuds `Element`.\n- Le `nodeName` est défini pour tout `Node` :\n    - pour les éléments, cela signifie la même chose que `tagName`.\n    - pour les autres types de nœuds (texte, commentaire, etc.), il a une chaîne de caractères avec le type de nœud.\n\n    En d'autres termes, `tagName` est uniquement pris en charge par les nœuds élément (car il provient de la classe `Element`), tandis que `nodeName` peut dire quelque chose sur d'autres types de nœuds.\n\nPar exemple, comparons `tagName` et `nodeName` pour le `document` et un nœud de commentaire :\n\n\n```html run\n<body><!-- commentaire -->\n\n  <script>\n    // pour le commentaire\n    alert( document.body.firstChild.tagName ); // undefined (pas un élément)\n    alert( document.body.firstChild.nodeName ); // #comment\n\n    // pour le document\n    alert( document.tagName ); // undefined (pas un élément)\n    alert( document.nodeName ); // #document\n  </script>\n</body>\n```\n\nSi nous ne traitons que des éléments, nous pouvons utiliser à la fois `tagName` et `nodeName` - il n'y a pas de différence.\n\n```smart header=\"Le nom de la balise est toujours en majuscule sauf en mode XML\"\nLe navigateur a deux modes de traitement des documents: HTML et XML. Habituellement, le mode HTML est utilisé pour les pages Web. Le mode XML est activé lorsque le navigateur reçoit un document XML avec l'en-tête : `Content-Type: application/xml+xhtml`.\n\nEn mode HTML, `tagName/nodeName` est toujours en majuscule : c'est `BODY` pour `<body>` ou `<BoDy>`.\n\nEn mode XML, la casse est conservée \"en l'état\". De nos jours, le mode XML est rarement utilisé.\n```\n\n\n## innerHTML: les contenus\n\nLa propriété [innerHTML](https://w3c.github.io/DOM-Parsing/#widl-Element-innerHTML) permet d'obtenir le HTML à l'intérieur de l'élément sous forme de chaîne de caractères.\n\nNous pouvons également le modifier. C'est donc l'un des moyens les plus puissants de modifier la page.\n\nL'exemple montre le contenu de `document.body` puis le remplace complètement :\n\n```html run\n<body>\n  <p>A paragraph</p>\n  <div>A div</div>\n\n  <script>\n    alert( document.body.innerHTML ); // lit le contenu actuel\n    document.body.innerHTML = 'The new BODY!'; // le remplace\n  </script>\n\n</body>\n```\n\nNous pouvons essayer d'insérer du code HTML invalide, le navigateur corrigera nos erreurs :\n\n```html run\n<body>\n\n  <script>\n    document.body.innerHTML = '<b>test'; // oublié de fermer la balise\n    alert( document.body.innerHTML ); // <b>test</b> (corrigé)\n  </script>\n\n</body>\n```\n\n```smart header=\"Les scripts ne s'exécutent pas\"\nSi `innerHTML` insère une balise `<script> `dans le document - elle devient une partie du HTML, mais ne s'exécute pas.\n```\n\n### Attention : \"innerHTML+=\" fait un écrasement complet\n\nNous pouvons ajouter du HTML à un élément en utilisant `elem.innerHTML+=\"more html\"`.\n\nComme ceci :\n\n```js\nchatDiv.innerHTML += \"<div>Hello<img src='smile.gif'/> !</div>\";\nchatDiv.innerHTML += \"How goes?\";\n```\n\nMais nous devons faire très attention à le faire, car ce qui se passe n'est *pas* un ajout, mais une réécriture complète.\n\nTechniquement, ces deux lignes font de même :\n\n```js\nelem.innerHTML += \"...\";\n// is a shorter way to write:\n*!*\nelem.innerHTML = elem.innerHTML + \"...\"\n*/!*\n```\n\nEn d'autres termes, `innerHTML+=` fait ceci :\n\n1. L'ancien contenu est supprimé.\n2. Le nouveau `innerHTML` est écrit à la place (une concaténation de l'ancien et du nouveau).\n\n**Comme le contenu est \"remis à zéro\" et réécrit à partir de zéro, toutes les images et autres ressources seront rechargées**.\n\nDans l'exemple `chatDiv` au-dessus de la ligne `chatDiv.innerHTML+=\"How goes?\"` recrée le contenu HTML et recharge `smile.gif` (espérons qu'il est mis en cache). Si `chatDiv` a beaucoup d'autres textes et images, alors le rechargement devient clairement visible.\n\nIl existe également d'autres effets secondaires. Par exemple, si le texte existant a été sélectionné avec la souris, la plupart des navigateurs supprimeront la sélection lors de la réécriture de \"innerHTML\". Et s'il y avait un `<input>` avec un texte entré par le visiteur, alors le texte sera supprimé. Etc.\n\nHeureusement, il existe d'autres façons d'ajouter du HTML en plus de `innerHTML`, et nous les étudierons bientôt.\n\n## outerHTML : HTML complet de l'élément\n\nLa propriété `outerHTML` contient le code HTML complet de l'élément. C'est comme `innerHTML` plus l'élément lui-même.\n\nVoici un exemple :\n\n```html run\n<div id=\"elem\">Hello <b>World</b></div>\n\n<script>\n  alert(elem.outerHTML); // <div id=\"elem\">Hello <b>World</b></div>\n</script>\n```\n\n**Attention : contrairement à `innerHTML`, l'écriture dans `outerHTML` ne modifie pas l'élément. Au lieu de cela, il le remplace dans le DOM.**\n\nOui, cela semble étrange, et c'est étrange, c'est pourquoi nous en faisons une note séparée ici. Jetez-y un oeil.\n\nPrenons l'exemple :\n\n```html run\n<div>Hello, world!</div>\n\n<script>\n  let div = document.querySelector('div');\n\n*!*\n  // remplace div.outerHTML avec <p>...</p>\n*/!*\n  div.outerHTML = '<p>A new element</p>'; // (*)\n\n*!*\n  // Wow! 'div' est toujours la même !\n*/!*\n  alert(div.outerHTML); // <div>Hello, world!</div> (**)\n</script>\n```\n\nÇa a l'air vraiment bizarre, non ?\n\nDans la ligne `(*)` nous avons remplacé `div` par `<p>A new element</p>`. Dans le document externe (le DOM), nous pouvons voir le nouveau contenu au lieu de `<div>`. Mais, comme nous pouvons le voir dans la ligne `(**)`, la valeur de l'ancienne variable `div` n'a pas changé !\n\nL'affectation `outerHTML` ne modifie pas l'élément DOM (l'objet référencé, dans ce cas, la variable 'div'), mais le supprime du DOM et insère le nouveau HTML à sa place.\n\nDonc, ce qui s'est passé dans `div.outerHTML=...` est :\n- `div` a été supprimé du document.\n- Un autre morceau du HTML `<p>A new element</p>` a été inséré à sa place.\n- `div` a toujours son ancienne valeur. Le nouveau HTML n'a été enregistré dans aucune variable.\n\nIl est si facile de faire une erreur ici : modifiez `div.outerHTML` puis continuez à travailler avec `div` comme s'il contenait le nouveau contenu. Mais ce n'est pas le cas. Ce genre de chose est correcte pour `innerHTML`, mais pas pour `outerHTML`.\n\nNous pouvons écrire dans `elem.outerHTML`, mais nous devons garder à l'esprit que cela ne change pas l'élément dans lequel nous écrivons ('elem'). Il place le nouveau HTML à sa place. Nous pouvons obtenir des références aux nouveaux éléments en interrogeant le DOM.\n\n## nodeValue/data : contenu du nœud texte\n\nLa propriété `innerHTML` n'est valide que pour les nœuds élément.\n\nD'autres types de nœuds, tels que les nœuds texte, ont leur contrepartie : les propriétés `nodeValue` et `data`. Ces deux sont presque les mêmes pour une utilisation pratique, il n'y a que des différences de spécifications mineures. Nous allons donc utiliser `data`, car il est plus court.\n\nUn exemple de lecture du contenu d'un nœud texte et d'un commentaire :\n\n```html run height=\"50\"\n<body>\n  Hello\n  <!-- Commentaire -->\n  <script>\n    let text = document.body.firstChild;\n*!*\n    alert(text.data); // Hello\n*/!*\n\n    let comment = text.nextSibling;\n*!*\n    alert(comment.data); // Commentaire\n*/!*\n  </script>\n</body>\n```\n\nPour les nœuds texte, nous pouvons imaginer une raison de les lire ou de les modifier, mais pourquoi des commentaires ?\n\nParfois, les développeurs incorporent des informations ou des instructions de modèle dans HTML, comme ceci :\n\n```html\n<!-- if isAdmin -->\n  <div>Welcome, Admin!</div>\n<!-- /if -->\n```\n\n...Ensuite, JavaScript peut le lire à partir de la propriété `data` et traiter les instructions intégrées.\n\n## textContent: texte pur \n\nLe `textContent` donne accès au *texte* à l'intérieur de l'élément : seulement le texte, moins tous les `<tags>`.\n\nPar exemple :\n\n```html run\n<div id=\"news\">\n  <h1>Headline!</h1>\n  <p>Martians attack people!</p>\n</div>\n\n<script>\n  // Headline! Martians attack people!\n  alert(news.textContent);\n</script>\n```\n\nComme nous pouvons le voir, seul le texte est renvoyé, comme si tous les `<tags>` étaient supprimés, mais le texte qu'ils contenaient est resté.\n\nEn pratique, la lecture d'un tel texte est rarement nécessaire.\n\n**Ecrire dans `textContent` est beaucoup plus utile, car il permet d'écrire du texte de \"manière sûre\".**\n\nDisons que nous avons une chaîne de caractères arbitraire, par exemple entrée par un utilisateur, et que nous voulons l'afficher.\n\n- Avec `innerHTML` nous allons l'insérer \"au format HTML\", avec toutes les balises HTML.\n- Avec `textContent` nous allons l'insérer \"en tant que texte\", tous les symboles sont traités littéralement.\n\nComparez les deux :\n\n```html run\n<div id=\"elem1\"></div>\n<div id=\"elem2\"></div>\n\n<script>\n  let name = prompt(\"What's your name?\", \"<b>Winnie-the-Pooh!</b>\");\n\n  elem1.innerHTML = name;\n  elem2.textContent = name;\n</script>\n```\n\n1. La première `<div>` obtient le nom \"en HTML\" : toutes les balises deviennent des balises, nous voyons donc le nom en gras.\n2. La seconde `<div>` obtient le nom \"sous forme de texte\", donc nous voyons littéralement `<b>Winnie-the-pooh!</b>`.\n\nDans la plupart des cas, nous attendons le texte d'un utilisateur et souhaitons le traiter comme du texte. Nous ne voulons pas de HTML inattendu sur notre site. Une affectation à `textContent` fait exactement cela.\n\n## La propriété \"cachée\"\n\nL'attribut \"hidden\" (caché) et la propriété DOM spécifient si l'élément est visible ou non.\n\nNous pouvons l'utiliser dans le HTML ou l'attribuer en utilisant JavaScript, comme ceci :\n\n```html run height=\"80\"\n<div>Both divs below are hidden</div>\n\n<div hidden>With the attribute \"hidden\"</div>\n\n<div id=\"elem\">JavaScript assigned the property \"hidden\"</div>\n\n<script>\n  elem.hidden = true;\n</script>\n```\n\nTechniquement, `hidden` fonctionne de la même manière que `style=\"display:none\"`. Mais c'est plus court à écrire.\n\nVoici un élément clignotant :\n\n\n```html run height=50\n<div id=\"elem\">A blinking element</div>\n\n<script>\n  setInterval(() => elem.hidden = !elem.hidden, 1000);\n</script>\n```\n\n## Plus de propriétés\n\nLes éléments DOM ont également des propriétés supplémentaires, en particulier celles qui dépendent de la classe :\n\n- `value` -- la valeur pour `<input>`, `<select>` et `<textarea>` (`HTMLInputElement`, `HTMLSelectElement`...).\n- `href` -- le \"href\" pour `<a href=\"...\">` (`HTMLAnchorElement`).\n- `id` -- la valeur de l'attribut \"id\", pour tous les éléments (`HTMLElement`).\n- ...et beaucoup plus...\n\nPar exemple :\n\n```html run height=\"80\"\n<input type=\"text\" id=\"elem\" value=\"value\">\n\n<script>\n  alert(elem.type); // \"text\"\n  alert(elem.id); // \"elem\"\n  alert(elem.value); // value\n</script>\n```\n\nLa plupart des attributs HTML standard ont la propriété DOM correspondante, et nous pouvons y accéder comme ça.\n\nSi nous voulons connaître la liste complète des propriétés prises en charge pour une classe donnée, nous pouvons les trouver dans la spécification. Par exemple, `HTMLInputElement` est documenté à <https://html.spec.whatwg.org/#htmlinputelement>.\n\nOu si nous voulons les obtenir rapidement ou encore si nous sommes intéressés par une spécification concrète de navigateur - nous pouvons toujours sortir l'élément en utilisant `console.dir(elem)` et lire les propriétés. Ou explorez les \"propriétés DOM\" dans l'onglet Éléments des outils de développement du navigateur.\n\n## Résumé\n\nChaque nœud DOM appartient à une certaine classe. Les classes forment une hiérarchie. L'ensemble complet des propriétés et des méthodes résulte de l'héritage.\n\nLes propriétés principales du nœud DOM sont :\n\n`nodeType`\n: Nous pouvons l'utiliser pour voir si un nœud est un texte ou un nœud d'élément. Il a une valeur numérique: `1` pour les éléments, `3` pour les nœuds de texte et quelques autres pour les autres types de nœuds. Lecture seulement.\n\n`nodeName/tagName`\n: Pour les éléments, nom de balise (en majuscules sauf en mode XML). Pour les nœuds non-élément, `nodeName` décrit ce que c'est. Lecture seulement.\n\n`innerHTML`\n: Le contenu HTML de l'élément. Peut être modifié.\n\n`outerHTML`\n: Le code HTML complet de l'élément. Une opération d'écriture dans `elem.outerHTML` ne touche pas `elem` lui-même. Au lieu de cela, il est remplacé par le nouveau HTML dans le contexte externe.\n\n`nodeValue/data`\n: Le contenu d'un nœud non élément (texte, commentaire). Ces deux sont presque les mêmes, nous utilisons généralement `data`. Peut être modifié.\n\n`textContent`\n: Le texte à l'intérieur de l'élément : le HTML moins tous les `<tags>`. L'écriture met le texte à l'intérieur de l'élément, avec tous les caractères spéciaux et balises traités exactement comme du texte. Peut insérer en toute sécurité du texte généré par l'utilisateur et protéger contre les insertions HTML indésirables.\n\n`hidden`\n: Lorsqu'il est défini sur `true`, fait la même chose que CSS `display:none`.\n\nLes nœuds DOM ont également d'autres propriétés en fonction de leur classe. Par exemple, les éléments `<input>` (`HTMLInputElement`) prennent en charge `value`, `type`, tandis que les éléments `<a>` (`HTMLAnchorElement`) prennent en charge `href` etc. La plupart des attributs HTML standard ont une propriété DOM correspondante.\n\nCependant, les attributs HTML et les propriétés DOM ne sont pas toujours les mêmes, comme nous le verrons dans le chapitre suivant.\n"
  },
  {
    "path": "2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/solution.md",
    "content": "\n```html run height=100\n<!DOCTYPE html>\n<html>\n<body>\n\n  <div data-widget-name=\"menu\">Choose the genre</div>\n\n  <script>\n    // getting it\n    let elem = document.querySelector('[data-widget-name]');\n\n    // reading the value\n    alert(elem.dataset.widgetName);\n    // or\n    alert(elem.getAttribute('data-widget-name'));\n  </script>\n</body>\n</html>\n```\n"
  },
  {
    "path": "2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md",
    "content": "importance: 5\n\n---\n\n# Obtenez l'attribut\n\nÉcrivez le code pour sélectionner l'élément avec l'attribut `data-widget-name` dans le document et pour lire sa valeur.\n\n```html run\n<!DOCTYPE html>\n<html>\n<body>\n\n  <div data-widget-name=\"menu\">Choose the genre</div>\n\n  <script>\n    /* your code */\n  </script>\n</body>\n</html>\n```\n"
  },
  {
    "path": "2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.md",
    "content": "\nTout d'abord, nous devons trouver toutes les références externes.\n\nIl y a deux façons.\n\nLa première consiste à trouver tous les liens à l'aide de `document.querySelectorAll('a')` puis à filtrer ce dont nous avons besoin :\n\n```js\nlet links = document.querySelectorAll('a');\n\nfor (let link of links) {\n*!*\n  let href = link.getAttribute('href');\n*/!*\n  if (!href) continue; // pas d'attribut\n\n  if (!href.includes('://')) continue; // pas de protocol\n\n  if (href.startsWith('http://internal.com')) continue; // interne\n\n  link.style.color = 'orange';\n}\n```\n\nVeuillez noter: nous utilisons `link.getAttribute('href')`. Pas `link.href`, car nous avons besoin de la valeur HTML.\n\n... Un autre moyen plus simple serait d'ajouter les contrôles au sélecteur CSS :\n\n```js\n// recherchez tous les liens qui ont :// dans href\n// mais href ne commence pas par http://internal.com\nlet selector = 'a[href*=\"://\"]:not([href^=\"http://internal.com\"])';\nlet links = document.querySelectorAll(selector);\n\nlinks.forEach(link => link.style.color = 'orange');\n```\n"
  },
  {
    "path": "2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <a name=\"list\">The list:</a>\n  <ul>\n    <li><a href=\"http://google.com\">http://google.com</a></li>\n    <li><a href=\"/tutorial\">/tutorial.html</a></li>\n    <li><a href=\"local/path\">local/path</a></li>\n    <li><a href=\"ftp://ftp.com/my.zip\">ftp://ftp.com/my.zip</a></li>\n    <li><a href=\"http://nodejs.org\">http://nodejs.org</a></li>\n    <li><a href=\"http://internal.com/test\">http://internal.com/test</a></li>\n  </ul>\n\n  <script>\n    let selector = 'a[href*=\"://\"]:not([href^=\"http://internal.com\"])';\n    let links = document.querySelectorAll(selector);\n\n    links.forEach(link => link.style.color = 'orange');\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <a name=\"list\">The list:</a>\n  <ul>\n    <li><a href=\"http://google.com\">http://google.com</a></li>\n    <li><a href=\"/tutorial\">/tutorial.html</a></li>\n    <li><a href=\"local/path\">local/path</a></li>\n    <li><a href=\"ftp://ftp.com/my.zip\">ftp://ftp.com/my.zip</a></li>\n    <li><a href=\"http://nodejs.org\">http://nodejs.org</a></li>\n    <li><a href=\"http://internal.com/test\">http://internal.com/test</a></li>\n  </ul>\n\n  <script>\n    // ...your code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/task.md",
    "content": "importance: 3\n\n---\n\n# Rendre les liens externes orange\n\nMettez tous les liens externes en orange en modifiant leur propriété `style`.\n\nUn lien est externe si :\n- Son `href` contient `://`\n- Mais ne commence pas par `http://internal.com`.\n\nExemple :\n\n```html run\n<a name=\"list\">the list</a>\n<ul>\n  <li><a href=\"http://google.com\">http://google.com</a></li>\n  <li><a href=\"/tutorial\">/tutorial.html</a></li>\n  <li><a href=\"local/path\">local/path</a></li>\n  <li><a href=\"ftp://ftp.com/my.zip\">ftp://ftp.com/my.zip</a></li>\n  <li><a href=\"http://nodejs.org\">http://nodejs.org</a></li>\n  <li><a href=\"http://internal.com/test\">http://internal.com/test</a></li>\n</ul>\n\n<script>\n  // setting style for a single link\n  let link = document.querySelector('a');\n  link.style.color = 'orange';\n</script>\n```\n\nLe résultat devrait être :\n\n[iframe border=1 height=180 src=\"solution\"]\n"
  },
  {
    "path": "2-ui/1-document/06-dom-attributes-and-properties/article.md",
    "content": "# Attributs et propriétés\n\nLorsque le navigateur charge la page, il \"lit\" (un autre mot: \"analyse\") le code HTML et en génère des objets DOM. Pour les nœuds éléments, la plupart des attributs HTML standards deviennent automatiquement des propriétés des objets DOM.\n\nPar exemple, si la balise est `<body id=\"page\">`, alors l'objet DOM a `body.id=\"page\"`.\n\nMais le mapping des propriétés d'attribut ne se fait pas un à un ! Dans ce chapitre, nous ferons attention à séparer ces deux notions, pour voir comment travailler avec elles, quand elles sont identiques et quand elles sont différentes.\n\n## Propriétés DOM\n\nNous avons déjà vu des propriétés DOM intégrées. Il y en a beaucoup. Mais techniquement, personne ne nous limite et s'il n'y en a pas assez, nous pouvons ajouter les nôtres.\n\nLes nœuds DOM sont des objets JavaScript normaux. Nous pouvons les modifier.\n\nPar exemple, créons une nouvelle propriété dans `document.body` :\n\n```js run\ndocument.body.myData = {\n  name: 'Caesar',\n  title: 'Imperator'\n};\n\nalert(document.body.myData.title); // Imperator\n```\n\nNous pouvons également ajouter une méthode :\n\n```js run\ndocument.body.sayTagName = function() {\n  alert(this.tagName);\n};\n\ndocument.body.sayTagName(); // BODY (la valeur de \"this\" dans la méthode est document.body)\n```\n\nNous pouvons également modifier des prototypes intégrés comme `Element.prototype` et ajouter de nouvelles méthodes à tous les éléments :\n\n```js run\nElement.prototype.sayHi = function() {\n  alert(`Hello, I'm ${this.tagName}`);\n};\n\ndocument.documentElement.sayHi(); // Hello, I'm HTML\ndocument.body.sayHi(); // Hello, I'm BODY\n```\n\nAinsi, les propriétés et méthodes DOM se comportent comme celles des objets JavaScript normaux :\n\n- Ils peuvent avoir n'importe quelle valeur.\n- Ils sont sensibles à la casse (écrivez `elem.nodeType`, pas `elem.NoDeTyPe`).\n\n## Attributs HTML\n\nEn HTML, les balises peuvent avoir des attributs. Lorsque le navigateur analyse le code HTML pour créer des objets DOM pour les balises, il reconnaît les attributs * standard * et crée des propriétés DOM à partir d'elles.\n\nAinsi, lorsqu'un élément a `id` ou un autre attribut *standard*, la propriété correspondante est créée. Mais cela ne se produit pas si l'attribut n'est pas standard.\n\nPar exemple :\n```html run\n<body id=\"test\" something=\"non-standard\">\n  <script>\n    alert(document.body.id); // test\n*!*\n    // l'attribut non standard ne donne pas de propriété\n    alert(document.body.something); // undefined\n*/!*\n  </script>\n</body>\n```\n\nVeuillez noter qu'un attribut standard pour un élément peut être inconnu pour un autre. Par exemple, `\"type\"` est standard pour `<input>` ([HTMLInputElement](https://html.spec.whatwg.org/#htmlinputelement)), mais pas pour `<body>` ([HTMLBodyElement](https://html.spec.whatwg.org/#htmlbodyelement)). Les attributs standard sont décrits dans la spécification de la classe d'élément correspondante.\n\nIci, nous pouvons le voir :\n```html run\n<body id=\"body\" type=\"...\">\n  <input id=\"input\" type=\"text\">\n  <script>\n    alert(input.type); // text\n*!*\n    alert(body.type); // undefined: Propriété DOM non créée, car non standard\n*/!*\n  </script>\n</body>\n```\n\nDonc, si un attribut n'est pas standard, il n'y aura pas de propriété DOM pour lui. Existe-t-il un moyen d'accéder à ces attributs ?\n\nBien sûr, tous les attributs sont accessibles en utilisant les méthodes suivantes :\n\n- `elem.hasAttribute(name)` -- vérifie l'existence.\n- `elem.getAttribute(name)` -- obtient la valeur.\n- `elem.setAttribute(name, value)` -- définit la valeur.\n- `elem.removeAttribute(name)` -- supprime l'attribut.\n\nCes méthodes fonctionnent exactement avec ce qui est écrit en HTML.\n\nOn peut aussi lire tous les attributs en utilisant `elem.attributes` : une collection d'objets qui appartiennent à une classe intégrée [Attr](https://dom.spec.whatwg.org/#attr),avec `name` et la propriété `value`.\n\nVoici une démonstration de la lecture d'une propriété non standard :\n\n```html run\n<body something=\"non-standard\">\n  <script>\n*!*\n    alert(document.body.getAttribute('something')); // non-standard\n*/!*\n  </script>\n</body>\n```\n\nLes attributs HTML ont les fonctionnalités suivantes :\n\n- Leur nom est insensible à la casse (`id` est identique à `ID`).\n- Leurs valeurs sont toujours des chaînes de caractères.\n\nVoici une démonstration détaillée de l'utilisation des attributs :\n\n```html run\n<body>\n  <div id=\"elem\" about=\"Elephant\"></div>\n\n  <script>\n    alert( elem.getAttribute('About') ); // (1) 'Elephant', lecture\n\n    elem.setAttribute('Test', 123); // (2), écriture\n\n    alert( elem.outerHTML ); // (3), voir si l'attribut est en HTML (oui)\n\n    for (let attr of elem.attributes) { // (4) lister tout\n      alert( `${attr.name} = ${attr.value}` );\n    }\n  </script>\n</body>\n```\n\nVeuillez noter :\n\n1. `getAttribute('About')` -- la première lettre est en majuscules ici, et en HTML tout est en minuscules. Mais cela n'a pas d'importance: les noms d'attribut ne sont pas sensibles à la casse.\n2. Nous pouvons assigner n'importe quoi à un attribut, mais il devient une chaîne. Nous avons donc ici `\"123\"` comme valeur.\n3. Tous les attributs, y compris ceux que nous définissons, sont visibles dans `outerHTML`.\n4. La collection `attributes` est itérable et possède tous les attributs de l'élément (standard et non standard) en tant qu'objets avec les propriétés `name` et `value`.\n\n## Synchronisation des attributs de propriété\n\nLorsqu'un attribut standard change, la propriété correspondante est automatiquement mise à jour et (à quelques exceptions près) vice versa.\n\nDans l'exemple ci-dessous, `id` est modifié en tant qu'attribut, et nous pouvons également voir la propriété modifiée. Et puis la même chose à l'envers :\n\n```html run\n<input>\n\n<script>\n  let input = document.querySelector('input');\n\n  // attribute => property\n  input.setAttribute('id', 'id');\n  alert(input.id); // id (mis à jour)\n\n  // property => attribute\n  input.id = 'newId';\n  alert(input.getAttribute('id')); // newId (mis à jour)\n</script>\n```\n\nMais il y a des exclusions, par exemple `input.value` se synchronise uniquement de l'attribut -> vers la propriété, mais pas dans l'autre sens :\n\n```html run\n<input>\n\n<script>\n  let input = document.querySelector('input');\n\n  // attribute => property\n  input.setAttribute('value', 'text');\n  alert(input.value); // text\n\n*!*\n  // NOT property => attribute\n  input.value = 'newValue';\n  alert(input.getAttribute('value')); // text (non mis à jour !)\n*/!*\n</script>\n```\n\nDans l'exemple ci-dessus :\n- La modification de l'attribut `value` met à jour la propriété.\n- Mais le changement de propriété n'affecte pas l'attribut.\n\nCette \"fonctionnalité\" peut en fait être utile, car les actions de l'utilisateur peuvent entraîner des changements de `value`, puis après cela, si nous voulons récupérer la valeur \"d'origine\" du HTML, c'est dans l'attribut.\n\n## Les propriétés DOM sont typées\n\nLes propriétés DOM ne sont pas toujours des chaînes de caractères. Par exemple, la propriété `input.checked` (pour les cases à cocher) est un booléen :\n\n```html run\n<input id=\"input\" type=\"checkbox\" checked> checkbox\n\n<script>\n  alert(input.getAttribute('checked')); // la valeur d'attribut est une chaîne de caractères vide\n  alert(input.checked); // la valeur de la propriété est : true\n</script>\n```\n\nIl y a d'autres exemples. L'attribut `style` est une chaîne de caractères, mais la propriété `style` est un objet :\n\n```html run\n<div id=\"div\" style=\"color:red;font-size:120%\">Hello</div>\n\n<script>\n  // string\n  alert(div.getAttribute('style')); // color:red;font-size:120%\n\n  // object\n  alert(div.style); // [object CSSStyleDeclaration]\n  alert(div.style.color); // red\n</script>\n```\n\nCependant la plupart des propriétés sont des chaînes de caractères.\n\nTrès rarement, même si un type de propriété DOM est une chaîne de caractères, il peut différer de l'attribut. Par exemple, la propriété DOM `href` est toujours une URL *complète*, même si l'attribut contient une URL relative ou juste un `#hash`.\n\nVoici un exemple :\n\n```html height=30 run\n<a id=\"a\" href=\"#hello\">link</a>\n<script>\n  // attribute\n  alert(a.getAttribute('href')); // #hello\n\n  // property\n  alert(a.href ); // full URL in the form http://site.com/page#hello\n</script>\n```\n\nSi nous avons besoin de la valeur de `href` ou de tout autre attribut exactement comme écrit dans le HTML, nous pouvons utiliser `getAttribute`.\n\n\n## Attributs non standard, dataset\n\nLors de l'écriture HTML, nous utilisons beaucoup d'attributs standard. Mais qu'en est-il des modèles non standard et personnalisés ? Voyons d'abord s'ils sont utiles ou non? Pourquoi ?\n\nParfois, des attributs non standard sont utilisés pour transmettre des données personnalisées de HTML à JavaScript ou pour \"marquer\" des éléments HTML pour JavaScript.\n\nComme ceci :\n\n```html run\n<!-- mark the div to show \"name\" here -->\n<div *!*show-info=\"name\"*/!*></div>\n<!-- and age here -->\n<div *!*show-info=\"age\"*/!*></div>\n\n<script>\n  // le code trouve un élément avec la marque et montre ce qui est demandé\n  let user = {\n    name: \"Pete\",\n    age: 25\n  };\n\n  for(let div of document.querySelectorAll('[show-info]')) {\n    // insert the corresponding info into the field\n    let field = div.getAttribute('show-info');\n    div.innerHTML = user[field]; // d'abord Pete dans \"name\", puis 25 dans \"age\"\n  }\n</script>\n```\n\nIls peuvent également être utilisés pour styliser un élément.\n\nPar exemple, ici pour la commande de l'état de l'attribut, `order-state` est utilisé :\n\n```html run\n<style>\n  /* les styles reposent sur l'attribut personnalisé \"order-state\" */\n  .order[order-state=\"new\"] {\n    color: green;\n  }\n\n  .order[order-state=\"pending\"] {\n    color: blue;\n  }\n\n  .order[order-state=\"canceled\"] {\n    color: red;\n  }\n</style>\n\n<div class=\"order\" order-state=\"new\">\n  A new order.\n</div>\n\n<div class=\"order\" order-state=\"pending\">\n  A pending order.\n</div>\n\n<div class=\"order\" order-state=\"canceled\">\n  A canceled order.\n</div>\n```\n\nPourquoi l'utilisation d'un attribut serait-elle préférable à des classes comme `.order-state-new`, `.order-state-pending`, `order-state-canceled` ?\n\nParce qu'un attribut est plus pratique à gérer. L'état peut être changé aussi facilement que :\n\n```js\n// un peu plus simple que de supprimer l'ancienne / ajouter une nouvelle classe\ndiv.setAttribute('order-state', 'canceled');\n```\n\nMais il peut y avoir un problème possible avec les attributs personnalisés. Que se passe-t-il si nous utilisons un attribut non standard selon nos besoins et que plus tard le standard l'introduit et lui fait faire quelque chose ? Le langage HTML est vivant, il grandit et de plus en plus d'attributs semblent répondre aux besoins des développeurs. Il peut y avoir des effets inattendus dans ce cas.\n\nPour éviter les conflits, il existe les attributs [data-*](https://html.spec.whatwg.org/#embedding-custom-non-visible-data-with-the-data-*-attributes).\n\n**Tous les attributs commençant par \"data-\" sont réservés à l'usage des programmeurs. Ils sont disponibles dans la propriété `dataset`.**\n\nPar exemple, si un `elem` a un attribut nommé `\"data-about\"`, il est disponible en tant que `elem.dataset.about`.\n\nComme ceci :\n\n```html run\n<body data-about=\"Elephants\">\n<script>\n  alert(document.body.dataset.about); // Elephants\n</script>\n```\n\nLes attributs de plusieurs mots comme `data-order-state` deviennent camel-cased : `dataset.orderState`.\n\nVoici un exemple d'\"order state\" réécrit :\n\n```html run\n<style>\n  .order[data-order-state=\"new\"] {\n    color: green;\n  }\n\n  .order[data-order-state=\"pending\"] {\n    color: blue;\n  }\n\n  .order[data-order-state=\"canceled\"] {\n    color: red;\n  }\n</style>\n\n<div id=\"order\" class=\"order\" data-order-state=\"new\">\n  A new order.\n</div>\n\n<script>\n  // read\n  alert(order.dataset.orderState); // new\n\n  // modify\n  order.dataset.orderState = \"pending\"; // (*)\n</script>\n```\n\nL'utilisation des attributs `data- *` est un moyen valide et sûr de transmettre des données personnalisées.\n\nVeuillez noter que nous pouvons non seulement lire, mais également modifier les attributs de données. Ensuite, CSS met à jour la vue en conséquence : dans l'exemple ci-dessus, la dernière ligne `(*)` change la couleur en bleu.\n\n## Résumé\n\n- Les attributs - sont ce qui est écrit en HTML.\n- Les propriétés - sont ce qui se trouve dans les objets DOM.\n\nUne petite comparaison :\n\n|      | Propriétés                                                                                    | Attributs                            |\n|------|-----------------------------------------------------------------------------------------------|--------------------------------------|\n| Type | N'importe quelle valeur, les propriétés standards ont des types décrits dans la spécification | Une chaîne de caractères             |\n| Nom  | Le nom est sensible à la casse                                                                | Le nom n'est pas sensible à la casse |\n\nLes méthodes de travail avec les attributs sont les suivantes :\n\n- `elem.hasAttribute(name)` -- pour vérifier l'existence.\n- `elem.getAttribute(name)` -- pour obtenir la valeur.\n- `elem.setAttribute(name, value)` -- pour définir la valeur.\n- `elem.removeAttribute(name)` -- pour supprimer l'attribut.\n- `elem.attributes` est une collection de tous les attributs.\n\nPour la plupart des situations, l'utilisation des propriétés DOM est préférable. Nous devons nous référer aux attributs uniquement lorsque les propriétés DOM ne nous conviennent pas, lorsque nous avons besoin exactement d'attributs, par exemple :\n\n- Nous avons besoin d'un attribut non standard. Mais s'il commence par `data-`, alors nous devrions utiliser `dataset`.\n- Nous voulons lire la valeur \"telle qu'elle est écrite\" en HTML. La valeur de la propriété DOM peut être différente, par exemple la propriété `href` est toujours une URL complète, et nous pouvons vouloir obtenir la valeur \"originale\".\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md",
    "content": "Réponse : **1 et 3**.\n\nLes deux commandes ont pour résultat d'ajouter le `texte` \"en tant que texte\" dans `elem`.\n\nVoici un exemple :\n\n```html run height=80\n<div id=\"elem1\"></div>\n<div id=\"elem2\"></div>\n<div id=\"elem3\"></div>\n<script>\n  let text = '<b>text</b>';\n\n  elem1.append(document.createTextNode(text));\n  elem2.innerHTML = text;\n  elem3.textContent = text;\n</script>\n```\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md",
    "content": "importance: 5\n\n---\n\n# createTextNode vs innerHTML vs textContent\n\nNous avons un élément DOM vide `elem` et une chaîne de caractères `text`.\n\nLesquelles de ces 3 commandes feront exactement la même chose ?\n\n1. `elem.append(document.createTextNode(text))`\n2. `elem.innerHTML = text`\n3. `elem.textContent = text`\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.md",
    "content": "Tout d'abord, créons notre HTML/CSS.\n\nChaque composante du temps aurait fière allure dans son propre `<span>` :\n\n```html\n<div id=\"clock\">\n  <span class=\"hour\">hh</span>:<span class=\"min\">mm</span>:<span class=\"sec\">ss</span>\n</div>\n```\n\nNous aurons également besoin de CSS pour les colorer.\n\nLa fonction `update` rafraîchira l'horloge, qui sera appelée par `setInterval` toutes les secondes :\n\n```js\nfunction update() {\n  let clock = document.getElementById('clock');\n*!*\n  let date = new Date(); // (*)\n*/!*\n  let hours = date.getHours();\n  if (hours < 10) hours = '0' + hours;\n  clock.children[0].innerHTML = hours;\n\n  let minutes = date.getMinutes();\n  if (minutes < 10) minutes = '0' + minutes;\n  clock.children[1].innerHTML = minutes;\n\n  let seconds = date.getSeconds();\n  if (seconds < 10) seconds = '0' + seconds;\n  clock.children[2].innerHTML = seconds;\n}\n```\n\nDans la ligne `(*)` nous vérifions à chaque fois la date actuelle. Les appels à `setInterval` ne sont pas fiables : ils peuvent survenir avec des retards.\n\nLes fonctions de gestion d'horloge :\n\n```js\nlet timerId;\n\nfunction clockStart() { // exécute l'horloge\n  if (!timerId) { // défini un nouvel intervalle uniquement si l'horloge ne fonctionne pas\n    timerId = setInterval(update, 1000);\n  }\n  update(); // (*)\n}\n\nfunction clockStop() {\n  clearInterval(timerId);\n  timerId = null; // (**)\n}\n```\n\nVeuillez noter que l'appel à `update()` est non seulement planifié dans `clockStart()`, mais s'exécute immédiatement dans la ligne `(*)`. Sinon, le visiteur devra attendre la première exécution de `setInterval`. Et l'horloge serait vide jusque-là.\n\nIl est également important de définir un nouvel intervalle dans `clockStart()` uniquement lorsque l'horloge ne fonctionne pas. Sinon, cliquer plusieurs fois sur le bouton de démarrage définirait plusieurs intervalles simultanés. Pire encore, nous ne garderions que le `timerID` du dernier intervalle, perdant ainsi les références à tous les autres. Alors nous ne pourrions plus jamais arrêter le chronomètre! Notez que nous devons effacer le `timerID` lorsque l'horloge est arrêtée dans la ligne `(**)`, afin qu'elle puisse être redémarrée en exécutant `clockStart()`.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <style>\n    .hour {\n      color: red\n    }\n\n    .min {\n      color: green\n    }\n\n    .sec {\n      color: blue\n    }\n  </style>\n</head>\n\n<body>\n\n  <div id=\"clock\">\n    <span class=\"hour\">hh</span>:<span class=\"min\">mm</span>:<span class=\"sec\">ss</span>\n  </div>\n\n  <script>\n    let timerId;\n\n    function update() {\n      let clock = document.getElementById('clock');\n      let date = new Date();\n\n      let hours = date.getHours();\n      if (hours < 10) hours = '0' + hours;\n      clock.children[0].innerHTML = hours;\n\n      let minutes = date.getMinutes();\n      if (minutes < 10) minutes = '0' + minutes;\n      clock.children[1].innerHTML = minutes;\n\n      let seconds = date.getSeconds();\n      if (seconds < 10) seconds = '0' + seconds;\n      clock.children[2].innerHTML = seconds;\n    }\n\n    function clockStart() {\n      // set a new interval only if the clock is stopped\n      // otherwise we would rewrite the timerID reference to the running interval and wouldn't be able to stop the clock ever again\n      if (!timerId) {\n        timerId = setInterval(update, 1000);\n      }\n      update(); // <--  start right now, don't wait 1 second till the first setInterval works\n    }\n\n    function clockStop() {\n      clearInterval(timerId);\n      timerId = null; // <-- clear timerID to indicate that the clock has been stopped, so that it is possible to start it again in clockStart()\n    }\n\n  </script>\n\n  <!-- click on this button calls clockStart() -->\n  <input type=\"button\" onclick=\"clockStart()\" value=\"Start\">\n\n  <!-- click on this button calls clockStop() -->\n  <input type=\"button\" onclick=\"clockStop()\" value=\"Stop\">\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/10-clock-setinterval/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <!-- click on this button calls clockStart() -->\n  <input type=\"button\" onclick=\"clockStart()\" value=\"Start\">\n\n  <!-- click on this button calls clockStop() -->\n  <input type=\"button\" onclick=\"clockStop()\" value=\"Stop\">\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md",
    "content": "importance: 4\n\n---\n\n# Horloge colorée avec setInterval\n\nCréez une horloge colorée comme ici : \n\n[iframe src=\"solution\" height=60]\n\nUtilisez HTML/CSS pour le style, JavaScript ne met à jour que le temps dans les éléments.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/11-append-to-list/solution.md",
    "content": "\nLorsque nous devons insérer un morceau de HTML quelque part, `insertAdjacentHTML` est le meilleur choix.\n  \nLa solution :\n\n```js\none.insertAdjacentHTML('afterend', '<li>2</li><li>3</li>');\n```\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/11-append-to-list/task.md",
    "content": "importance: 5\n\n---\n\n# Insérez le HTML dans la liste\n\nÉcrivez le code pour insérer `<li>2</li><li>3</li>` entre deux `<li>` ici :\n\n```html\n<ul id=\"ul\">\n  <li id=\"one\">1</li>\n  <li id=\"two\">4</li>\n</ul>\n```\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/12-sort-table/solution.md",
    "content": "La solution est courte, mais peut sembler un peu délicate, alors ici je la présente avec de nombreux commentaires :\n\n```js\nlet sortedRows = Array.from(table.tBodies[0].rows) // 1\n  .sort((rowA, rowB) => rowA.cells[0].innerHTML.localeCompare(rowB.cells[0].innerHTML));\n\ntable.tBodies[0].append(...sortedRows); // (3)\n```\n\nL'algorithme pas à pas :\n\n1. Obtenez tous les `<tr>` de `<tbody>`.\n2. Triez-les ensuite en les comparant au contenu du premier `<td>` (le champ du nom).\n3. Insérez maintenant les nœuds dans le bon ordre par `.append(...sortedRows)`.\n\nNous n'avons pas à supprimer les éléments de ligne, il suffit de les \"réinsérer\", ils quittent automatiquement l'ancien emplacement.\n\nP.S. Dans notre cas, il y a un `<tbody>` explicite dans le tableau, mais même si le tableau HTML n'a pas de `<tbody>`, la structure DOM l'a toujours.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/12-sort-table/solution.view/index.html",
    "content": "<!DOCTYPE html>\n\n<table id=\"table\">\n<thead>\n  <tr>\n    <th>Name</th><th>Surname</th><th>Age</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>John</td><td>Smith</td><td>10</td>\n  </tr>\n  <tr>\n    <td>Pete</td><td>Brown</td><td>15</td>\n  </tr>\n  <tr>\n    <td>Ann</td><td>Lee</td><td>5</td>\n  </tr>\n  <tr>\n    <td>...</td><td>...</td><td>...</td>\n  </tr>\n</tbody>\n</table>\n\n<script>\n  let sortedRows = Array.from(table.tBodies[0].rows)\n    .sort((rowA, rowB) => rowA.cells[0].innerHTML.localeCompare(rowB.cells[0].innerHTML));\n\n  table.tBodies[0].append(...sortedRows);\n</script>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/12-sort-table/source.view/index.html",
    "content": "<!DOCTYPE html>\n\n<table id=\"table\">\n<thead>\n  <tr>\n    <th>Name</th><th>Surname</th><th>Age</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>John</td><td>Smith</td><td>10</td>\n  </tr>\n  <tr>\n    <td>Pete</td><td>Brown</td><td>15</td>\n  </tr>\n  <tr>\n    <td>Ann</td><td>Lee</td><td>5</td>\n  </tr>\n  <tr>\n    <td>...</td><td>...</td><td>...</td>\n  </tr>\n</tbody>\n</table>\n\n<script>\n  // ... your code ...\n</script>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/12-sort-table/task.md",
    "content": "importance: 5\n\n---\n\n# Trier le tableau\n\nIl y a un tableau :\n\n```html run\n<table>\n<thead>\n  <tr>\n    <th>Name</th><th>Surname</th><th>Age</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>John</td><td>Smith</td><td>10</td>\n  </tr>\n  <tr>\n    <td>Pete</td><td>Brown</td><td>15</td>\n  </tr>\n  <tr>\n    <td>Ann</td><td>Lee</td><td>5</td>\n  </tr>\n  <tr>\n    <td>...</td><td>...</td><td>...</td>\n  </tr>\n</tbody>\n</table>\n```\n\nIl peut y avoir plus de lignes.\n\nÉcrivez le code pour le trier par la colonne `\"name\"`.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/4-clear-elem/solution.md",
    "content": "\nVoyons d'abord comment ne *pas* le faire :\n\n```js\nfunction clear(elem) {\n  for (let i=0; i < elem.childNodes.length; i++) {\n      elem.childNodes[i].remove();\n  }\n}\n```\n\nCela ne fonctionnera pas, car l'appel à `remove()` décale la collection `elem.childNodes`, donc les éléments commencent à partir de l'index `0` à chaque fois. Mais `i` augmente et certains éléments seront ignorés.\n\nLa boucle `for..of` fait de même.\n\nLa bonne variante pourrait être :\n\n```js\nfunction clear(elem) {\n  while (elem.firstChild) {\n    elem.firstChild.remove();\n  }\n}\n```\n\nEt il existe également un moyen plus simple de faire de même :\n\n```js\nfunction clear(elem) {\n  elem.innerHTML = '';\n}\n```\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/4-clear-elem/task.md",
    "content": "importance: 5\n\n---\n\n# Effacer l'élément\n\nCréez une fonction `clear(elem)` qui supprime tout de l'élément.\n\n```html run height=60\n<ol id=\"elem\">\n  <li>Hello</li>\n  <li>World</li>\n</ol>\n\n<script>\n  function clear(elem) { /* votre code */ }\n\n  clear(elem); // efface la liste\n</script>\n```\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/5-why-aaa/solution.md",
    "content": "Le code HTML de cette tâche est incorrect. Voilà la raison de la chose étrange.\n\nLe navigateur doit le réparer automatiquement. Mais il peut ne pas y avoir de texte à l'intérieur de la `<table>` : selon la spécification, seules les balises spécifiques à la table sont autorisées. Le navigateur ajoute donc `\"aaa\"` *avant* la `<table>`.\n\nMaintenant, il est évident que lorsque nous retirons la `table`, la chaîne de caractères reste.\n\nLa question peut être facilement répondue en explorant le DOM à l'aide des outils du navigateur. Vous verrez `\"aaa\"` avant la `<table>`.\n\nLe standard HTML spécifie en détail comment traiter un mauvais HTML, et un tel comportement du navigateur est correct.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/5-why-aaa/task.md",
    "content": "importance: 1\n\n---\n\n# Pourquoi \"aaa\" reste-t-il ?\n\nDans l'exemple ci-dessous, l'appel `table.remove()` supprime le tableau du document.\n\nmais si vous l'exécutez, vous pouvez voir que le texte `\"aaa\"` est toujours visible.\n\nPourquoi cela se produit-il ?\n\n```html height=100 run\n<table id=\"table\">\n  aaa\n  <tr>\n    <td>Test</td>\n  </tr>\n</table>\n\n<script>\n  alert(table); // la table, comme il se doit\n\n  table.remove();\n  // pourquoi y a-t-il encore \"aaa\" dans le document ?\n</script>\n```\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/6-create-list/solution.md",
    "content": "Veuillez noter l'utilisation de `textContent` pour attribuer le contenu `<li>`.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/6-create-list/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n  <h1>Create a list</h1>\n\n  <script>\n    let ul = document.createElement('ul');\n    document.body.append(ul);\n\n    while (true) {\n      let data = prompt(\"Enter the text for the list item\", \"\");\n\n      if (!data) {\n        break;\n      }\n\n      let li = document.createElement('li');\n      li.textContent = data;\n      ul.append(li);\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/6-create-list/task.md",
    "content": "importance: 4\n\n---\n\n# Créer une liste\n\nÉcrivez une interface pour créer une liste à partir des entrées utilisateur.\n\nPour chaque élément de la liste :\n\n1. Interrogez un utilisateur sur son contenu en utilisant `prompt`.\n2. Créez le `<li>` avec et ajoutez-le à `<ul>`.\n3. Continuez jusqu'à ce que l'utilisateur annule l'entrée (en appuyant sur la touche `key:Esc` ou une entrée vide).\n\nTous les éléments doivent être créés dynamiquement.\n\nSi un utilisateur tape des balises HTML, elles doivent être traitées comme un texte.\n\n[demo src=\"solution\"]\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/7-create-object-tree/build-tree-dom.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<body>\n\n  <div id=\"container\"></div>\n\n  <script>\n    let data = {\n      \"Fish\": {\n        \"trout\": {},\n        \"salmon\": {}\n      },\n\n      \"Tree\": {\n        \"Huge\": {\n          \"sequoia\": {},\n          \"oak\": {}\n        },\n        \"Flowering\": {\n          \"apple tree\": {},\n          \"magnolia\": {}\n        }\n      }\n    };\n\n    function createTree(container, obj) {\n      container.append(createTreeDom(obj));\n    }\n\n    function createTreeDom(obj) {\n      // if there's no children, then the call returns undefined\n      // and the <ul> won't be created\n      if (!Object.keys(obj).length) return;\n\n      let ul = document.createElement('ul');\n\n      for (let key in obj) {\n        let li = document.createElement('li');\n        li.innerHTML = key;\n\n        let childrenUl = createTreeDom(obj[key]);\n        if (childrenUl) {\n          li.append(childrenUl);\n        }\n\n        ul.append(li);\n      }\n\n      return ul;\n    }\n\n    let container = document.getElementById('container');\n    createTree(container, data);\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/7-create-object-tree/innerhtml.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<body>\n\n  <div id=\"container\"></div>\n\n  <script>\n    let data = {\n      \"Fish\": {\n        \"trout\": {},\n        \"salmon\": {}\n      },\n\n      \"Tree\": {\n        \"Huge\": {\n          \"sequoia\": {},\n          \"oak\": {}\n        },\n        \"Flowering\": {\n          \"apple tree\": {},\n          \"magnolia\": {}\n        }\n      }\n    };\n\n    function createTree(container, obj) {\n      container.innerHTML = createTreeText(obj);\n    }\n\n    function createTreeText(obj) { // standalone recursive function\n      let li = '';\n      let ul;\n      for (let key in obj) {\n        li += '<li>' + key + createTreeText(obj[key]) + '</li>';\n      }\n      if (li) {\n        ul = '<ul>' + li + '</ul>'\n      }\n      return ul || '';\n    }\n\n    createTree(container, data);\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/7-create-object-tree/solution.md",
    "content": "La façon la plus simple de parcourir l'objet est d'utiliser la récursivité.\n\n1. [La solution avec innerHTML](https://plnkr.co/edit/PzjPAk9yKHeKkT36?p=preview).\n2. [La solution avec DOM](https://plnkr.co/edit/e3TEVqQrm7ZqZkn6?p=preview).\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/7-create-object-tree/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <div id=\"tree\"></div>\n\n  <!-- The result should be:\n<div id=\"tree\">\n<ul>\n  <li>Fish\n    <ul>\n      <li>trout</li>\n      <li>salmon</li>\n    </ul>\n  </li>\n  <li>Trees\n    <ul>\n      <li>Huge\n        <ul>\n          <li>sequoia</li>\n          <li>oak</li>\n        </ul>\n      </li>\n      <li>Flowering\n        <ul>\n          <li>apple tree</li>\n          <li>magnolia</li>\n        </ul>\n      </li>\n    </ul>\n  </li>\n</ul>\n</div>\n-->\n\n  <script>\n    let data = {\n      \"Fish\": {\n        \"trout\": {},\n        \"salmon\": {}\n      },\n\n      \"Tree\": {\n        \"Huge\": {\n          \"sequoia\": {},\n          \"oak\": {}\n        },\n        \"Flowering\": {\n          \"apple tree\": {},\n          \"magnolia\": {}\n        }\n      }\n    };\n\n    function createTree(container, data) {\n      /* your code */\n    }\n\n    createTree(document.getElementById('tree'), data);\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/7-create-object-tree/task.md",
    "content": "importance: 5\n\n---\n\n# Créer un arbre à partir de l'objet\n\nÉcrivez une fonction `createTree` qui crée une liste imbriquée `ul/li` à partir de l'objet imbriqué.\n\nPar exemple :\n\n```js\nlet data = {\n  \"Fish\": {\n    \"trout\": {},\n    \"salmon\": {}\n  },\n\n  \"Tree\": {\n    \"Huge\": {\n      \"sequoia\": {},\n      \"oak\": {}\n    },\n    \"Flowering\": {\n      \"apple tree\": {},\n      \"magnolia\": {}\n    }\n  }\n};\n```\n\nLa syntaxe :\n\n```js\nlet container = document.getElementById('container');\n*!*\ncreateTree(container, data); // crée l'arbre dans le conteneur\n*/!*\n```\n\nLe résultat (arbre) devrait ressembler à ceci :\n\n[iframe border=1 src=\"build-tree-dom\"]\n\nChoisissez l'une des deux façons de résoudre cette tâche :\n\n1. Créez le code HTML de l'arborescence, puis attribuez-le à `container.innerHTML`.\n2. Créez des nœuds d'arbre et ajoutez-les avec les méthodes DOM.\n\nCe serait génial si vous pouviez faire les deux.\n\nP.S. L'arbre ne doit pas avoir d'éléments \"supplémentaires\" comme des `<ul></ul>` vides pour les feuilles (de l'arbre).\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/8-tree-count/solution.md",
    "content": "Pour ajouter du texte à chaque `<li>`, nous pouvons modifier le nœud texte `data`.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/8-tree-count/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <ul>\n    <li>Animals\n      <ul>\n        <li>Mammals\n          <ul>\n            <li>Cows</li>\n            <li>Donkeys</li>\n            <li>Dogs</li>\n            <li>Tigers</li>\n          </ul>\n        </li>\n        <li>Other\n          <ul>\n            <li>Snakes</li>\n            <li>Birds</li>\n            <li>Lizards</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n    <li>Fishes\n      <ul>\n        <li>Aquarium\n          <ul>\n            <li>Guppy</li>\n            <li>Angelfish</li>\n          </ul>\n        </li>\n        <li>Sea\n          <ul>\n            <li>Sea trout</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n  </ul>\n\n  <script>\n    let lis = document.getElementsByTagName('li');\n\n    for (let li of lis) {\n      // get the count of all <li> below this <li>\n      let descendantsCount = li.getElementsByTagName('li').length;\n      if (!descendantsCount) continue;\n\n      // add directly to the text node (append to the text)\n      li.firstChild.data += ' [' + descendantsCount + ']';\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/8-tree-count/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <ul>\n    <li>Animals\n      <ul>\n        <li>Mammals\n          <ul>\n            <li>Cows</li>\n            <li>Donkeys</li>\n            <li>Dogs</li>\n            <li>Tigers</li>\n          </ul>\n        </li>\n        <li>Other\n          <ul>\n            <li>Snakes</li>\n            <li>Birds</li>\n            <li>Lizards</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n    <li>Fishes\n      <ul>\n        <li>Aquarium\n          <ul>\n            <li>Guppy</li>\n            <li>Angelfish</li>\n          </ul>\n        </li>\n        <li>Sea\n          <ul>\n            <li>Sea trout</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n  </ul>\n\n  <script>\n    // ... your code ...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/8-tree-count/task.md",
    "content": "importance: 5\n\n---\n\n# Afficher les descendants dans un arbre\n\nIl y a un arbre organisé comme un `ul/li` imbriqué.\n\nÉcrivez le code qui ajoute à chaque `<li>` le nombre de ses descendants. Sautez les feuilles (nœuds sans enfants).\n\nLe resultat :\n\n[iframe border=1 src=\"solution\"]\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/9-calendar-table/solution.md",
    "content": "Nous allons créer le tableau sous forme de chaîne de caractères : `\"<table>...</table>\"`, puis l'affecter à `innerHTML`.\n\nL'algorithme :\n\n1. Créer l'en-tête du tableau avec les noms `<th>` et les jours de la semaine.\n2. Créez l'objet de date `d = new Date(year, month-1)`. C'est le premier jour de `month` (en tenant compte du fait que les mois en JavaScript commencent à `0`, pas à `1`).\n3. Les premières cellules jusqu'au premier jour du mois `d.getDay()` peuvent être vides. Remplissons-les avec `<td></td>`.\n4. Augmentez le jour en `d`: `d.setDate(d.getDate()+1)`. Si `d.getMonth()` n'est pas encore le mois suivant, alors ajoutez la nouvelle cellule `<td>` au calendrier. Si c'est un dimanche, ajoutez une nouvelle ligne <code>\"&lt;/tr&gt;&lt;tr&gt;\"</code>.\n5. Si le mois est terminé, mais que la ligne du tableau n'est pas encore pleine, ajoutez-y un `<td>` vide pour le rendre carré.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/9-calendar-table/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    table {\n      border-collapse: collapse;\n    }\n\n    td,\n    th {\n      border: 1px solid black;\n      padding: 3px;\n      text-align: center;\n    }\n\n    th {\n      font-weight: bold;\n      background-color: #E6E6E6;\n    }\n  </style>\n</head>\n\n<body>\n\n\n  <div id=\"calendar\"></div>\n\n  <script>\n    function createCalendar(elem, year, month) {\n\n      let mon = month - 1; // months in JS are 0..11, not 1..12\n      let d = new Date(year, mon);\n\n      let table = '<table><tr><th>MO</th><th>TU</th><th>WE</th><th>TH</th><th>FR</th><th>SA</th><th>SU</th></tr><tr>';\n\n      // spaces for the first row\n      // from Monday till the first day of the month\n      // * * * 1  2  3  4\n      for (let i = 0; i < getDay(d); i++) {\n        table += '<td></td>';\n      }\n\n      // <td> with actual dates\n      while (d.getMonth() == mon) {\n        table += '<td>' + d.getDate() + '</td>';\n\n        if (getDay(d) % 7 == 6) { // sunday, last day of week - newline\n          table += '</tr><tr>';\n        }\n\n        d.setDate(d.getDate() + 1);\n      }\n\n      // add spaces after last days of month for the last row\n      // 29 30 31 * * * *\n      if (getDay(d) != 0) {\n        for (let i = getDay(d); i < 7; i++) {\n          table += '<td></td>';\n        }\n      }\n\n      // close the table\n      table += '</tr></table>';\n\n      elem.innerHTML = table;\n    }\n\n    function getDay(date) { // get day number from 0 (monday) to 6 (sunday)\n      let day = date.getDay();\n      if (day == 0) day = 7; // make Sunday (0) the last day\n      return day - 1;\n    }\n\n    createCalendar(calendar, 2012, 9);\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/9-calendar-table/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    table {\n      border-collapse: collapse;\n    }\n\n    td,\n    th {\n      border: 1px solid black;\n      padding: 3px;\n      text-align: center;\n    }\n\n    th {\n      font-weight: bold;\n      background-color: #E6E6E6;\n    }\n  </style>\n</head>\n\n<body>\n\n\n  <div id=\"calendar\"></div>\n\n  <script>\n    function createCalendar(elem, year, month) {\n      // ...your code that generates the calndar in elem...\n    }\n\n    createCalendar(calendar, 2012, 9);\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/9-calendar-table/task.md",
    "content": "importance: 4\n\n---\n\n# Créer un calendrier\n\nÉcrivez une fonction `createCalendar(elem, year, month)`.\n\nL'appel doit créer un calendrier pour l'année/le mois donné et le mettre dans `elem`.\n\nLe calendrier doit être un tableau, où une semaine est un `<tr>` et un jour est un `<td>`. Le dessus du tableau doit être un `<th>` avec les noms des jours de la semaine : le premier jour doit être le lundi, et ainsi de suite jusqu'au dimanche.\n\nPar exemple, `createCalendar(cal, 2012, 9)` devrait générer dans l'élément `cal` le calendrier suivant :\n\n[iframe height=210 src=\"solution\"]\n\nP.S. Pour cette tâche, il suffit de générer le calendrier, il ne doit pas encore être cliquable.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/article.md",
    "content": "# Modification du document\n\nLa modification DOM est la clé pour créer des pages \"live\".\n\nIci, nous verrons comment créer de nouveaux éléments \"à la volée\" et modifier le contenu de la page existante.\n\n## Exemple : afficher un message\n\nDémontrons en utilisant un exemple. Nous allons ajouter un message sur la page qui est plus joli que `alert`.\n\nVoici à quoi cela ressemblera :\n\n```html autorun height=\"80\"\n<style>\n.alert {\n  padding: 15px;\n  border: 1px solid #d6e9c6;\n  border-radius: 4px;\n  color: #3c763d;\n  background-color: #dff0d8;\n}\n</style>\n\n*!*\n<div class=\"alert\">\n  <strong>Hi there!</strong> You've read an important message.\n</div>\n*/!*\n```\n\nC'était un exemple HTML. Créons maintenant la même `div` avec JavaScript (en supposant que les styles sont déjà dans le HTML ou un fichier CSS externe).\n\n## Création d'un élément\n\nPour créer des nœuds DOM, il existe deux méthodes :\n\n`document.createElement(tag)`\n: Crée un nouveau *noeud élément* avec la balise donnée :\n\n    ```js\n    let div = document.createElement('div');\n    ```\n\n`document.createTextNode(text)`\n: Crée un nouveau *nœud texte* avec le texte donné :\n\n    ```js\n    let textNode = document.createTextNode('Here I am');\n    ```\n\nLa plupart du temps, nous devons créer des nœuds d'élément, tels que le `div` pour le message.\n\n### Création du message\n\nLa création du message div prend 3 étapes :\n\n```js\n// 1. Create <div> element\nlet div = document.createElement('div');\n\n// 2. Set its class to \"alert\"\ndiv.className = \"alert\";\n\n// 3. Fill it with the content\ndiv.innerHTML = \"<strong>Hi there!</strong> You've read an important message.\";\n```\n\nNous avons créé l'élément. Mais pour le moment, ce n'est que dans une variable nommée `div`, pas encore dans la page. Nous ne pouvons donc pas le voir.\n\n## Méthodes d'insertion\n\nPour faire apparaître la `div`, nous devons l'insérer quelque part dans `document`. Par exemple, dans l'élément `<body>`, référencé par `document.body`.\n\nIl existe une méthode spéciale `append` pour cela : `document.body.append(div)`.\n\nVoici le code complet :\n\n```html run height=\"80\"\n<style>\n.alert {\n  padding: 15px;\n  border: 1px solid #d6e9c6;\n  border-radius: 4px;\n  color: #3c763d;\n  background-color: #dff0d8;\n}\n</style>\n\n<script>\n  let div = document.createElement('div');\n  div.className = \"alert\";\n  div.innerHTML = \"<strong>Hi there!</strong> You've read an important message.\";\n\n*!*\n  document.body.append(div);\n*/!*\n</script>\n```\n\nIci, nous avons appelé `append` sur `document.body`, mais nous pouvons appeler la méthode `append` sur n'importe quel autre élément, pour y mettre un autre élément. Par exemple, nous pouvons ajouter quelque chose à `<div>` en appelant `div.append(anotherElement)`.\n\nVoici plus de méthodes d'insertion, elles spécifient différents endroits où insérer :\n\n- `node.append(...nodes or strings)` -- ajouter des nœuds ou des chaînes de caractères *à la fin* de `node`,\n- `node.prepend(...nodes or strings)` -- insérer des nœuds ou des chaînes de caractères *au début* de `node`,\n- `node.before(...nodes or strings)` –- insérer des nœuds ou des chaînes de caractères *avant* `node`,\n- `node.after(...nodes or strings)` –- insérer des nœuds ou des chaînes de caractères *après* `node`,\n- `node.replaceWith(...nodes or strings)` –- remplace `node` avec les nœuds ou chaînes de caractères donnés.\n\nLes arguments de ces méthodes sont une liste arbitraire de nœuds DOM à insérer ou des chaînes de texte (qui deviennent automatiquement des nœuds de texte).\n\nVoyons-les en action.\n\nVoici un exemple d'utilisation de ces méthodes pour ajouter des éléments à une liste et le texte avant/après :\n\n```html autorun\n<ol id=\"ol\">\n  <li>0</li>\n  <li>1</li>\n  <li>2</li>\n</ol>\n\n<script>\n  ol.before('before'); // insère la chaîne de caractères \"before\" avant <ol>\n  ol.after('after'); // insère la chaîne de caractères \"after\" après <ol>\n\n  let liFirst = document.createElement('li');\n  liFirst.innerHTML = 'prepend';\n  ol.prepend(liFirst); // insère liFirst au début de <ol>\n\n  let liLast = document.createElement('li');\n  liLast.innerHTML = 'append';\n  ol.append(liLast); // insère liLast à la fin de <ol>\n</script>\n```\n\nVoici une image visuelle de ce que font les méthodes :\n\n![](before-prepend-append-after.svg)\n\nLa liste finale sera donc :\n\n```html\nbefore\n<ol id=\"ol\">\n  <li>prepend</li>\n  <li>0</li>\n  <li>1</li>\n  <li>2</li>\n  <li>append</li>\n</ol>\nafter\n```\n\nComme indiqué, ces méthodes peuvent insérer plusieurs nœuds et morceaux de texte en un seul appel.\n\nPar exemple, ici une chaîne de caractères et un élément sont insérés :\n\n```html run\n<div id=\"div\"></div>\n<script>\n  div.before('<p>Hello</p>', document.createElement('hr'));\n</script>\n```\n\nRemarque: le texte est inséré \"en tant que texte\", pas \"en tant que HTML\", avec un échappement approprié des caractères tels que `<`, `>`.\n\nLe HTML final est donc :\n\n```html run\n*!*\n&lt;p&gt;Hello&lt;/p&gt;\n*/!*\n<hr>\n<div id=\"div\"></div>\n```\n\nEn d'autres termes, les chaînes de caractères sont insérées de manière sûre, comme le fait `elem.textContent`.\n\nAinsi, ces méthodes ne peuvent être utilisées que pour insérer des nœuds DOM ou des morceaux de texte.\n\nMais que se passe-t-il si nous voulons insérer du HTML \"en tant que html\", avec toutes les balises et les trucs qui fonctionnent, comme `elem.innerHTML` le fait ?\n\n## insertAdjacentHTML/Text/Element\n\nPour cela, nous pouvons utiliser une autre méthode assez polyvalente : `elem.insertAdjacentHTML(where, html)`.\n\nLe premier paramètre est un mot de code, spécifiant où insérer par rapport à `elem`. Doit être l'un des suivants :\n\n- `\"beforebegin\"` -- insère `html` immédiatement avant `elem`,\n- `\"afterbegin\"` -- insère `html` dans `elem`, au début,\n- `\"beforeend\"` -- insère `html` dans `elem`, à la fin,\n- `\"afterend\"` -- insère `html` immédiatement après `elem`.\n\nLe second paramètre est une chaîne HTML insérée \"au format HTML\".\n\nPar exemple :\n\n```html run\n<div id=\"div\"></div>\n<script>\n  div.insertAdjacentHTML('beforebegin', '<p>Hello</p>');\n  div.insertAdjacentHTML('afterend', '<p>Bye</p>');\n</script>\n```\n\n...Conduirait à :\n\n```html run\n<p>Hello</p>\n<div id=\"div\"></div>\n<p>Bye</p>\n```\n\nVoilà comment nous pouvons ajouter du code HTML arbitraire à la page.\n\nVoici l'image des variantes d'insertion :\n\n![](insert-adjacent.svg)\n\nNous pouvons facilement remarquer des similitudes entre cette image et l'image précédente. Les points d'insertion sont en fait les mêmes, mais cette méthode insère du HTML.\n\nLa méthode a deux sœurs :\n\n- `elem.insertAdjacentText(where, text)` -- la même syntaxe, mais une chaîne de caractères `text` est insérée `en tant que texte` au lieu de HTML,\n- `elem.insertAdjacentElement(where, elem)` -- la même syntaxe, mais insère un élément.\n\nElles existent principalement pour rendre la syntaxe \"uniforme\". En pratique, seule `insertAdjacentHTML` est utilisée la plupart du temps. Parce que pour les éléments et le texte, nous avons des méthodes `append/prepend/before/after` -- elles sont plus courtes à écrire et peuvent insérer des nœuds/morceaux de texte.\n\nVoici donc une variante alternative pour afficher un message :\n\n```html run\n<style>\n.alert {\n  padding: 15px;\n  border: 1px solid #d6e9c6;\n  border-radius: 4px;\n  color: #3c763d;\n  background-color: #dff0d8;\n}\n</style>\n\n<script>\n  document.body.insertAdjacentHTML(\"afterbegin\", `<div class=\"alert\">\n    <strong>Hi there!</strong> You've read an important message.\n  </div>`);\n</script>\n```\n\n## Suppression de noeuds\n\nPour supprimer un nœud, il existe une méthode `node.remove()`.\n\nFaisons disparaître notre message après une seconde :\n\n```html run untrusted\n<style>\n.alert {\n  padding: 15px;\n  border: 1px solid #d6e9c6;\n  border-radius: px;\n  color: #3c763d;\n  background-color: #dff0d8;\n}\n</style>\n\n<script>\n  let div = document.createElement('div');\n  div.className = \"alert\";\n  div.innerHTML = \"<strong>Hi there!</strong> You've read an important message.\";\n\n  document.body.append(div);\n*!*\n  setTimeout(() => div.remove(), 1000);\n*/!*\n</script>\n```\n\nVeuillez noter : si nous voulons *déplacer* un élément vers un autre endroit -- il n'est pas nécessaire de le supprimer de l'ancien.\n\n**Toutes les méthodes d'insertion suppriment automatiquement le nœud de l'ancien emplacement.**\n\nPar exemple, permutons les éléments :\n\n```html run height=50\n<div id=\"first\">First</div>\n<div id=\"second\">Second</div>\n<script>\n  // pas besoin d'appeler remove\n  second.after(first); // prend #second et après insère #first\n</script>\n```\n\n## Clonage de Noeuds : cloneNode\n\nComment insérer un autre message similaire ?\n\nNous pourrions créer une fonction et y mettre le code. Mais l'alternative serait de *cloner* la `div` existant et de modifier le texte à l'intérieur (si nécessaire).\n\nParfois, lorsque nous avons un gros élément, cela peut être plus rapide et plus simple.\n\n- L'appel `elem.cloneNode(true)` crée un clone \"profond\" de l'élément -- avec tous les attributs et sous-éléments. Si nous appelons `elem.cloneNode(false)`, alors le clone est fait sans éléments enfants.\n\nUn exemple de copie du message :\n\n```html run height=\"120\"\n<style>\n.alert {\n  padding: 15px;\n  border: 1px solid #d6e9c6;\n  border-radius: 4px;\n  color: #3c763d;\n  background-color: #dff0d8;\n}\n</style>\n\n<div class=\"alert\" id=\"div\">\n  <strong>Hi there!</strong> You've read an important message.\n</div>\n\n<script>\n*!*\n  let div2 = div.cloneNode(true); // clone the message\n  div2.querySelector('strong').innerHTML = 'Bye there!'; // change le clone\n\n  div.after(div2); // affiche le clone après le div existant\n*/!*\n</script>\n```\n\n## DocumentFragment [#document-fragment]\n\n`DocumentFragment` est un nœud DOM spécial qui sert de wrapper pour passer autour des listes de nœuds.\n\nNous pouvons y ajouter d'autres nœuds, mais lorsque nous l'insérons quelque part, son contenu est inséré à la place.\n\nPar exemple, `getListContent` ci-dessous génère un fragment avec des éléments `<li>`, qui sont ensuite insérés dans `<ul>` :\n\n```html run\n<ul id=\"ul\"></ul>\n\n<script>\nfunction getListContent() {\n  let fragment = new DocumentFragment();\n\n  for(let i=1; i<=3; i++) {\n    let li = document.createElement('li');\n    li.append(i);\n    fragment.append(li);\n  }\n\n  return fragment;\n}\n\n*!*\nul.append(getListContent()); // (*)\n*/!*\n</script>\n```\n\nVeuillez noter qu'à la dernière ligne `(*)` nous ajoutons `DocumentFragment`, mais il \"s'adapte\", donc la structure résultante sera :\n\n```html\n<ul>\n  <li>1</li>\n  <li>2</li>\n  <li>3</li>\n</ul>\n```\n\n`DocumentFragment` est rarement utilisé explicitement. Pourquoi ajouter à un type spécial de nœud, si nous pouvons renvoyer un tableau de nœuds à la place ? Exemple réécrit :\n\n```html run\n<ul id=\"ul\"></ul>\n\n<script>\nfunction getListContent() {\n  let result = [];\n\n  for(let i=1; i<=3; i++) {\n    let li = document.createElement('li');\n    li.append(i);\n    result.push(li);\n  }\n\n  return result;\n}\n\n*!*\nul.append(...getListContent()); // append + \"...\" operator = friends!\n*/!*\n</script>\n```\n\nNous mentionnons `DocumentFragment` principalement parce qu'il y a quelques concepts dessus, comme l'élément [template](info:template-element), que nous couvrirons beaucoup plus tard.\n\n## Méthodes d'insertion/suppression à l'ancienne\n\n[old]\n\nIl existe également des méthodes de manipulation du DOM \"à l'ancienne\", qui existent pour des raisons historiques.\n\nCes méthodes viennent d'une époque très ancienne. De nos jours, il n'y a aucune raison de les utiliser, depuis qu'il existe des méthodes modernes, telles que `append`, `prepend`, `before`, `after`, `remove`, `replaceWith`, qui sont plus flexibles.\n\nLa seule raison pour laquelle nous listons ces méthodes ici est que vous pouvez les trouver dans de nombreux anciens scripts :\n\n`parentElem.appendChild(node)`\n: Appends `node` as the last child of `parentElem`.\n\n    The following example adds a new `<li>` to the end of `<ol>`:\n\n    ```html run height=100\n    <ol id=\"list\">\n      <li>0</li>\n      <li>1</li>\n      <li>2</li>\n    </ol>\n\n    <script>\n      let newLi = document.createElement('li');\n      newLi.innerHTML = 'Hello, world!';\n\n      list.appendChild(newLi);\n    </script>\n    ```\n\n`parentElem.insertBefore(node, nextSibling)`\n: Insère `node` avant `nextSibling` dans `parentElem`.\n\n    Le code suivant insère un nouvel élément de liste avant le second `<li>` :\n\n    ```html run height=100\n    <ol id=\"list\">\n      <li>0</li>\n      <li>1</li>\n      <li>2</li>\n    </ol>\n    <script>\n      let newLi = document.createElement('li');\n      newLi.innerHTML = 'Hello, world!';\n\n    *!*\n      list.insertBefore(newLi, list.children[1]);\n    */!*\n    </script>\n    ```\n    Pour insérer `newLi` comme premier élément, nous pouvons le faire comme ceci :\n\n    ```js\n    list.insertBefore(newLi, list.firstChild);\n    ```\n\n`parentElem.replaceChild(node, oldChild)`\n: Remplace `oldChild` avec `node` chez les enfants de `parentElem`.\n\n`parentElem.removeChild(node)`\n: Supprime `node` de `parentElem` (en supposant que `node` est son enfant).\n\n    L'exemple suivant supprime d'abord `<li>` de `<ol>` :\n\n    ```html run height=100\n    <ol id=\"list\">\n      <li>0</li>\n      <li>1</li>\n      <li>2</li>\n    </ol>\n\n    <script>\n      let li = list.firstElementChild;\n      list.removeChild(li);\n    </script>\n    ```\n\nToutes ces méthodes renvoient le nœud inséré/supprimé. En d'autres termes, `parentElem.appendChild(node)` renvoie `node`. Mais généralement, la valeur retournée n'est pas utilisée, nous exécutons simplement la méthode.\n\n## Un mot sur \"document.write\"\n\nIl existe une autre méthode très ancienne pour ajouter quelque chose à une page Web : `document.write`.\n\nLa syntaxe :\n\n```html run\n<p>Somewhere in the page...</p>\n*!*\n<script>\n  document.write('<b>Hello from JS</b>');\n</script>\n*/!*\n<p>The end</p>\n```\n\nL'appel à `document.write(html)` écrit le `html` dans la page \"ici et maintenant\". La chaîne de caractères `html` peut être générée dynamiquement, donc c'est un peu flexible. Nous pouvons utiliser JavaScript pour créer une page Web à part entière et l'écrire.\n\nLa méthode vient de l'époque où il n'y avait pas de DOM, pas de standards ... Des temps vraiment anciens. Il vit toujours, car il existe des scripts qui l'utilisent.\n\nDans les scripts modernes, nous le voyons rarement, en raison de la limitation importante suivante :\n\n**L'appel à `document.write` ne fonctionne que pendant le chargement de la page.**\n\nSi nous l'appelons ensuite, le contenu du document existant est effacé.\n\nPar exemple :\n\n```html run\n<p>After one second the contents of this page will be replaced...</p>\n*!*\n<script>\n  // document.write après 1 seconde\n  // cela après que la page soit chargée, donc il efface le contenu existant\n  setTimeout(() => document.write('<b>...By this.</b>'), 1000);\n</script>\n*/!*\n```\n\nC'est donc un peu inutilisable au stade \"post chargement\", contrairement aux autres méthodes DOM que nous avons couvertes ci-dessus.\n\nVoilà l'inconvénient.\n\nIl y a aussi un avantage. Techniquement, lorsque `document.write` est appelé pendant que le navigateur lit (\"analyse\") le HTML entrant, et qu'il écrit quelque chose, le navigateur le consomme comme s'il était initialement là, dans le texte HTML.\n\nCela fonctionne donc très rapidement, car il n'y a *aucune modification du DOM* impliquée. Il écrit directement dans le texte de la page, tandis que le DOM n'est pas encore construit.\n\nDonc, si nous devons ajouter beaucoup de texte en HTML de manière dynamique, et que nous sommes en phase de chargement de page, et que la vitesse compte, cela peut aider. Mais dans la pratique, ces exigences se rencontrent rarement. Et généralement, nous pouvons voir cette méthode dans les scripts simplement parce qu'ils sont anciens.\n\n## Résumé\n\n- Méthodes pour créer de nouveaux nœuds :\n    - `document.createElement(tag)` -- crée un élément avec la balise donnée,\n    - `document.createTextNode(value)` -- crée un nœud texte (rarement utilisé),\n    - `elem.cloneNode(deep)` -- clone l'élément, si `deep==true` tous les descendants viennent avec.\n\n- Insertion et suppression :\n    - `node.append(...nodes or strings)` -- insère dans `node`, à la fin,\n    - `node.prepend(...nodes or strings)` -- insère dans `node`, au début,\n    - `node.before(...nodes or strings)` –- insère juste avant `node`,\n    - `node.after(...nodes or strings)` –- insère juste après `node`,\n    - `node.replaceWith(...nodes or strings)` –- remplace `node`.\n    - `node.remove()` –- supprime le `node`.\n\n    Les chaînes de caractères texte sont insérées \"sous forme de texte\".\n\n- Il existe également des méthodes \"old school\" :\n    - `parent.appendChild(node)`\n    - `parent.insertBefore(node, nextSibling)`\n    - `parent.removeChild(node)`\n    - `parent.replaceChild(newElem, node)`\n\n    Toutes ces méthodes retournent le `node`.\n\n- Étant donné un peu de HTML dans `html`, `elem.insertAdjacentHTML(where, html)` l'insère en fonction de la valeur de `where` :\n    - `\"beforebegin\"` -- insère `html` juste avant `elem`,\n    - `\"afterbegin\"` -- insère `html` dans `elem`, au début,\n    - `\"beforeend\"` -- insère `html` dans `elem`, à la fin,\n    - `\"afterend\"` -- insère `html` juste après `elem`.\n\n    Il existe également des méthodes similaires, `elem.insertAdjacentText` et `elem.insertAdjacentElement`, qui insèrent des chaînes de caractères texte et des éléments, mais ils sont rarement utilisés.\n\n- Pour ajouter du HTML à la page avant la fin du chargement :\n    - `document.write(html)`\n\n    Une fois la page chargée, un tel appel efface le document. Surtout vu dans les anciens scripts.\n"
  },
  {
    "path": "2-ui/1-document/08-styles-and-classes/2-create-notification/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/1-document/08-styles-and-classes/2-create-notification/solution.view/index.css",
    "content": ".notification {\n  position: fixed;\n  z-index: 1000;\n  padding: 5px;\n  border: 1px solid black;\n  font-size: 20px;\n  background: white;\n  text-align: center;\n}\n\n.welcome {\n  background: #b80000;\n  color: yellow;\n}\n"
  },
  {
    "path": "2-ui/1-document/08-styles-and-classes/2-create-notification/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <link rel=\"stylesheet\" href=\"index.css\">\n</head>\n\n<body>\n\n  <h2>Notification is on the right</h2>\n\n  <p>\n    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum aspernatur quam ex eaque inventore quod voluptatem adipisci omnis nemo nulla fugit iste numquam ducimus cumque minima porro ea quidem maxime necessitatibus beatae labore soluta voluptatum\n    magnam consequatur sit laboriosam velit excepturi laborum sequi eos placeat et quia deleniti? Corrupti velit impedit autem et obcaecati fuga debitis nemo ratione iste veniam amet dicta hic ipsam unde cupiditate incidunt aut iure ipsum officiis soluta\n    temporibus. Tempore dicta ullam delectus numquam consectetur quisquam explicabo culpa excepturi placeat quo sequi molestias reprehenderit hic at nemo cumque voluptates quidem repellendus maiores unde earum molestiae ad.\n  </p>\n\n  <script>\n    function showNotification({top = 0, right = 0, className, html}) {\n\n      let notification = document.createElement('div');\n      notification.className = \"notification\";\n      if (className) {\n        notification.classList.add(className);\n      }\n\n      notification.style.top = top + 'px';\n      notification.style.right = right + 'px';\n\n      notification.innerHTML = html;\n      document.body.append(notification);\n\n      setTimeout(() => notification.remove(), 1500);\n    }\n\n    // test it\n    let i = 1;\n    setInterval(() => {\n      showNotification({\n        top: 10,\n        right: 10,\n        html: 'Hello ' + i++,\n        className: \"welcome\"\n      });\n    }, 2000);\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/08-styles-and-classes/2-create-notification/source.view/index.css",
    "content": ".notification {\n  position: fixed;\n  z-index: 1000;\n  padding: 5px;\n  border: 1px solid black;\n  font-size: 20px;\n  background: white;\n  text-align: center;\n}\n\n.welcome {\n  background: #b80000;\n  color: yellow;\n}\n"
  },
  {
    "path": "2-ui/1-document/08-styles-and-classes/2-create-notification/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <link rel=\"stylesheet\" href=\"index.css\">\n</head>\n\n<body>\n\n  <h2>Notification is on the right</h2>\n\n  <p>\n    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum aspernatur quam ex eaque inventore quod voluptatem adipisci omnis nemo nulla fugit iste numquam ducimus cumque minima porro ea quidem maxime necessitatibus beatae labore soluta voluptatum\n    magnam consequatur sit laboriosam velit excepturi laborum sequi eos placeat et quia deleniti? Corrupti velit impedit autem et obcaecati fuga debitis nemo ratione iste veniam amet dicta hic ipsam unde cupiditate incidunt aut iure ipsum officiis soluta\n    temporibus. Tempore dicta ullam delectus numquam consectetur quisquam explicabo culpa excepturi placeat quo sequi molestias reprehenderit hic at nemo cumque voluptates quidem repellendus maiores unde earum molestiae ad.\n  </p>\n\n  <script>\n    function showNotification({top = 0, right = 0, className, html}) {\n      /* your code */\n    }\n\n    // test it\n    let i = 1;\n    setInterval(() => {\n      showNotification({\n        top: 10,\n        right: 10,\n        html: 'Hello ' + i++,\n        className: \"welcome\"\n      });\n    }, 2000);\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/08-styles-and-classes/2-create-notification/task.md",
    "content": "importance: 5\n\n---\n\n# Create a notification\n\nWrite a function `showNotification(options)` that creates a notification: `<div class=\"notification\">` with the given content. The notification should automatically disappear after 1.5 seconds.\n\nThe options are:\n\n```js\n// shows an element with the text \"Hello\" near the right-top of the window\nshowNotification({\n  top: 10, // 10px from the top of the window (by default 0px)\n  right: 10, // 10px from the right edge of the window (by default 0px)\n  html: \"Hello!\", // the HTML of notification\n  className: \"welcome\" // an additional class for the div (optional)\n});\n```\n\n[demo src=\"solution\"]\n\n\nUse CSS positioning to show the element at given top/right coordinates. The source document has the necessary styles.\n"
  },
  {
    "path": "2-ui/1-document/08-styles-and-classes/article.md",
    "content": "# Styles et classes\n\nAvant d'entrer dans les méthodes que JavaScript utilise pour traiter les styles et classes, voici une règle importante. Elle devrait être assez évidente, mais nous devons encore la mentionner.\n\nIl y a, en général, deux façons de styliser un élément:\n\n1. Créer une classe dans CSS et l'ajouter: `<div class=\"...\">`\n2. Écrire les propriétés directement dans `style`: `<div style=\"...\">`.\n\nJavaScript peut modifier les classes et les propriétés de `style`.\n\nNous devons toujours favoriser l'utilisation des classes CSS plutôt que `style`. Ce dernier devrait seulement être utilisé si les classes sont incapables d'effectuer la tâche requise.\n\nPar exemple, `style` est acceptable si nous calculons les coordonnées d'un élément dynamiquement et souhaitons les définir à partir de JavaScript, comme ceci:\n\n```js\nlet top = /* calculs complexes */;\nlet left = /* calculs complexes */;\n\nelem.style.left = left; // par ex. '123px', calculé lors de l'exécution\nelem.style.top = top; // par ex. '456px'\n```\n\nPour les autres cas, comme rendre le texte rouge, ajouter une icône d'arrière-plan -- décrivez cela dans CSS et ensuite ajoutez la classe (JavaScript peut effectuer ceci). C'est plus flexible et plus facile à gérer.\n\n## className et classList\n\nChanger une classe est l'une des actions les plus utilisées dans les scripts.\n\nAutrefois, il existait une limitation dans JavaScript: un mot réservé comme `\"class\"` ne pouvait pas être une propriété d'un object. Cette limitation n'existe plus maintenant, mais à l'époque, il était impossible d'avoir une propriété de `\"class\"`, comme `elem.class`.\n\nAlors pour les classes, une propriété similaire, `\"className\"`, a été introduite: `elem.className` correspond à l'attribut `\"class\"`.\n\nPrenons, par example:\n\n```html run\n<body class=\"page d'accueil\">\n  <script>\n    alert(document.body.className); // page d'accueil\n  </script>\n</body>\n```\n\nSi nous attribuons quelque chose à `elem.className`, elle remplace la chaîne entière de classes. Parfois c'est ce que nous avons besoin, mais souvent, nous voulons seulement ajouter ou enlever une classe.\n\nIl y a une autre propriété pour ce besoin: `elem.classList`.\n\n`elem.classList` est un objet spécial avec des méthodes pour `add/remove/toggle` une seule classe.\n\nPrenons, par exemple:\n\n```html run\n<body class=\"page d'accueil\">\n  <script>\n*!*\n    // ajouter une classe\n    document.body.classList.add('article');\n*/!*\n\n    alert(document.body.className); // l'article de la page d'accueil\n  </script>\n</body>\n```\n\nAlors nous pouvons opérer avec la chaîne de toutes les classes en utilisant `className` ou avec les classes individuelles en utilisant `classList`. Ce que nous choisissons dépend de nos besoins.\n\nMéthodes de `classList`:\n\n- `elem.classList.add/remove(\"class\")` -- ajoute ou enlève la classe.\n- `elem.classList.toggle(\"class\")` -- ajoute la classe si elle n'existe pas, sinon enlève-la.\n- `elem.classList.contains(\"class\")` -- vérifie pour la classe donnée, renvoie `true/false`.\n\nEn outre, `classList` est itérable, alors nous pouvons lister toutes les classes avec `for..of`, comme ceci:\n\n```html run\n<body class=\"page d'accueil\">\n  <script>\n    for (let name of document.body.classList) {\n      alert(name); // page d'accueil, ensuite page\n    }\n  </script>\n</body>\n```\n\n## Style de l'élément\n\nLa propriété `elem.style` est un objet qui correspond à ce qui est écrit dans l'attribut `\"style\"`. Attribuant `elem.style.width=\"100px\"` fonctionne de la même façon qu'un attribut `style` ayant une chaîne `width:100px`.\n\nPour une propriété ayant plusieurs mots, camelCase est utilisé:\n\n```js no-beautify\nbackground-color  => elem.style.backgroundColor\nz-index           => elem.style.zIndex\nborder-left-width => elem.style.borderLeftWidth\n```\n\nPrenons, par exemple:\n\n```js run\ndocument.body.style.backgroundColor = prompt('background color?', 'green');\n```\n\n````smart header=\"Propriétés prédétérminées\"\nLes propriétés prédéterminées par le navigateur comme `-moz-border-radius`, `-webkit-border-radius` suivent la même règle: un tiret signifie une majuscule.\n\nPrenons, par exemple:\n\n```js\nbutton.style.MozBorderRadius = '5px';\nbutton.style.WebkitBorderRadius = '5px';\n```\n````\n\n## Réinitialiser la propriété de style\n\nParfois nous voulons attribuer une propriété de style, et ensuite la retirer.\n\nPar exemple, pour cacher un élément, nous pouvons définir `elem.style.display = \"none\"`.\n\nPlus tard, nous voulons peut-être enlever `style.display` comme si cette propriété n'était définie. Au lieu de `delete elem.style.display`, nous devons attribuer une chaîne vide à la propriété de style: `elem.style.display = \"\"`.\n\n```js run\n// si nous exécutons cette code, <body> clignotera\ndocument.body.style.display = \"none\"; // cacher\n\nsetTimeout(() => document.body.style.display = \"\", 1000); // retour à la normale\n```\n\nSi nous attribuons `style.display` à une chaîne vide, le navigateur applique les classes CSS et ses styles intégrés normalement, comme s'il n'y avait pas de propriété `style.display`.\n\nIl existe également une méthode spéciale pour cela, `elem.style.removeProperty('style property')`. Ainsi, nous pouvons supprimer une propriété comme celle-ci :\n\n```js run\ndocument.body.style.background = 'red'; //configure le background à rouge\n\nsetTimeout(() => document.body.style.removeProperty('background'), 1000); // supprime l'arrière-plan après 1 seconde\n```\n\n````smart header=\"Réécriture complète avec `style.cssText`\"\nNormalement, nous utilisons `style.*` pour attribuer des propriétés de style individuelles. Nous ne pouvons pas attribuer le style complet comme `div.style=\"color: red; width: 100px\"`, parce que `div.style` est un object, et il est en lecture seulement.\n\nPour définir un style complet comme une chaîne, il y a une propriété spéciale `style.cssText`:\n\n```html run\n<div id=\"div\">Bouton</div>\n\n<script>\n  // nous pouvons attribuer des drapeaux de style spéciaux comme \"important\" ici\n  div.style.cssText=`color: red !important;\n    background-color: yellow;\n    width: 100px;\n    text-align: center;\n  `;\n\n  alert(div.style.cssText);\n</script>\n```\n\nCette propriété est rarement utilisée parce qu'une telle affectation enlève tous les styles pré-existants: au lieu d'être ajoutée, elle les remplace. Peut occasionnellement effacer quelque chose de nécessaire. Par contre, nous pouvons l'utiliser sans risque pour des nouveaux éléments -- nous savons que nous n'éffacerons pas un style pré-existant.\n\nLa même chose peut être accomplie en définissant un attribut: `div.setAttribute('style', 'color: red...')`.\n````\n\n## Faites attention aux unités\n\nN'oubliez pas d'ajouter des unités de CSS aux valeurs.\n\nPar exemple, nous ne devrions pas attribuer `elem.style.top` à `10`, mais plutôt à `10px`. Sinon ça ne fonctionnera pas:\n\n```html run height=100\n<body>\n  <script>\n  *!*\n    // ne fonctionne pas!\n    document.body.style.margin = 20;\n    alert(document.body.style.margin); // '' (chaîne vide, l'affectation est ignorée)\n  */!*\n\n    // maintenant ajoutez l'unité de CSS (px) - et ça fonctionne\n    document.body.style.margin = '20px';\n    alert(document.body.style.margin); // 20px\n\n    alert(document.body.style.marginTop); // 20px\n    alert(document.body.style.marginLeft); // 20px\n  </script>\n</body>\n```\n\nIl est à noter: le navigateur \"décortique\"  la propriété `style.margin` dans les dernières lignes et déduit `style.marginLeft` et `style.marginTop` à partir de ceci.\n\n## Styles calculés: getComputedStyle\n\nAlors, modifier un style est facile. Mais comment pouvons-nous le *lire*?\n\nPar exemple, nous voulons savoir la taille, les marges et la couleur d'un élément. Comment faire?\n\n**La propriété `style` opère seulement sur la valeur de l'attribut `\"style\"`, sans aucune cascade CSS.**\n\nAlors nous ne pouvons rien lire des classes CSS en utilisant `elem.style`.\n\nPar exemple, ici, `style` ne reconnaît pas la marge:\n\n```html run height=60 no-beautify\n<head>\n  <style> body { color: red; margin: 5px } </style>\n</head>\n<body>\n\n  Le texte rouge\n  <script>\n*!*\n    alert(document.body.style.color); // vide\n    alert(document.body.style.marginTop); // vide\n*/!*\n  </script>\n</body>\n```\n\n...Mais, si nous voulons, par exemple, augmenter la marge par `20px`? Nous en voudrions la valeur actuelle.\n\nIl y a une autre méthode pour cela: `getComputedStyle`.\n\nLa syntaxe est:\n\n```js\ngetComputedStyle(element, [pseudo])\n```\n\nelement\n: Élément pour lire la valeur de.\n\npseudo\n: Un pseudo-élément si nécessaire, par exemple `::before`. Une chaîne vide ou aucun argument signifie l'élément lui-même.\n\nLe résultat est un objet avec des styles, comme `elem.style`, mais maintenant par rapport à toutes les classes CSS.\n\nPrenons, par exemple:\n\n```html run height=100\n<head>\n  <style> body { color: red; margin: 5px } </style>\n</head>\n<body>\n\n  <script>\n    let computedStyle = getComputedStyle(document.body);\n\n    // maintenant nous pouvons en lire la marge et la couleur\n\n    alert( computedStyle.marginTop ); // 5px\n    alert( computedStyle.color ); // rgb(255, 0, 0)\n  </script>\n\n</body>\n```\n\n```smart header=\"Valeurs calculées et résolues\"\nIl y a deux concepts dans [CSS](https://drafts.csswg.org/cssom/#resolved-values):\n\n1. Une valeur de style \"calculée\" est le résultat d'une cascade CSS, après que tous les règles et héritage CSS sont appliqués. Elle peut ressembler comme `height:1em` ou `font-size:125%`.\n2. Une valeur de style \"résolue\" est celle qui est finalement appliquée à l'élément. Les valeurs comme `1em` ou `125%` sont relatives. Le navigateur prend la valeur calculée et fait que toutes les unités sont fixes et absolues; par exemple: `height:20px` ou `font-size:16px`. Pour les propriétés géométriques, les valeurs résolues peuvent avoir une virgule flottante, comme `width:50.5px`.\n\nIl y a longtemps, `getComputedStyle` a été créé pour extraire les valeurs calculées, mais il s'est avéré que les valeurs résolues étaient beaucoup plus pratiques, alors la norme a changé.\n\nMaintenant, `getComputedStyle` renvoie la valeur résolue de la propriété, habituellement en `px` pour géométrie.\n```\n\n````warn header=\"`getComputedStyle` exige le nom complet de la propriété\"\nNous devons toujours demander pour la propriété exacte requise, comme `paddingLeft` ou `marginTop` ou `borderTopWidth`. Sinon, recevoir le bon résultat n'est pas garanti.\n\nPar exemple, s'il existe des propriétés `paddingLeft/paddingTop`, que devrions-nous obtenir pour `getComputedStyle(elem).padding` ? Rien, ou peut-être une valeur \"générée\" à partir de paddings connus ? Il n'y a pas de règle standard ici.\n````\n\n```smart header=\"Styles appliqués aux liens `:visited` sont cachés!\"\nLes liens visités peuvent être coloriés en utilisant la pseudo-classe CSS `:visited`.\n\nCependant, `getComputedStyle` ne donne pas accès à cette couleur, parce qu'autrement une page arbitraire pourrait savoir si l'utilisateur aurait visité un lien en créant un lien sur la page et vérifier les styles.\n\nJavaScript ne pourrait pas voir les styles appliqués par `:visited`. De plus, il y a une limitation avec CSS qui interdit l'application de styles qui changent la géométrie dans `:visited`. C'est pour garantir qu'il n'y a aucun moyen pour une page malfaisante de tester si un lien a été visité, qui porterait atteinte à la vie privée.\n```\n\n## Résumé\n\nPour gérer les classes, il y a deux propriétés DOM:\n\n- `className` -- la valeur de chaîne, utile pour gérer l'ensemble complet des classes.\n- `classList` -- l'object avec les méthodes `add/remove/toggle/contains`, utile pour les classes individuelles.\n\nPour changer les styles:\n\n- La propriété `style` est un objet avec les styles en camelCase. Lire et y écrire a le même sens que de modifier les propriétés individuelles dans l'attribut `\"style\"`. Pour savoir comment appliquer `important` et autres trucs rares -- il y a une liste de méthodes à [MDN](mdn:api/CSSStyleDeclaration).\n\n- La propriété `style.cssText` correspond à l'attribut entier de `\"style\"`, la chaîne complète des styles.\n\nPour lire les styles résolus (par rapport à toutes les classes, après que tout le CSS est appliqué et que les valeurs finales sont calculées):\n\n- `getComputedStyle(elem, [pseudo])` renvoie un objet de style avec eux. Lecture seulement.\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/1-get-scroll-height-bottom/solution.md",
    "content": "La solution est :\n\n```js\nlet scrollBottom = elem.scrollHeight - elem.scrollTop - elem.clientHeight;\n```\n\nEn d'autres termes : (pleine hauteur) moins (partie supérieure déroulée) moins (partie visible) -- c'est exactement la partie inférieure déroulée.\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/1-get-scroll-height-bottom/task.md",
    "content": "importance: 5\n\n---\n\n# Quel est le défilement à partir du bas ?\n\nLa propriété `elem.scrollTop` est la taille de la partie déroulante à partir du haut. Comment obtenir la taille du défilement inférieur (appelons-le `scrollBottom`) ?\n\nÉcrivez le code qui fonctionne pour un `element` arbitraire.\n\nP.S. Veuillez vérifier votre code: s'il n'y a pas de défilement ou que l'élément est entièrement défilé vers le bas, alors il devrait retourner `0`.\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/2-scrollbar-width/solution.md",
    "content": "Pour obtenir la largeur de la barre de défilement, nous pouvons créer un élément avec le défilement, mais sans bordures ni paddings.\n\nEnsuite, la différence entre sa largeur totale `offsetWidth` et la largeur de la zone de contenu interne `clientWidth` sera exactement la barre de défilement :\n\n```js run\n// créer une div avec le défilement\nlet div = document.createElement('div');\n\ndiv.style.overflowY = 'scroll';\ndiv.style.width = '50px';\ndiv.style.height = '50px';\n\n// doit le mettre dans le document, sinon les tailles seront 0\ndocument.body.append(div);\nlet scrollWidth = div.offsetWidth - div.clientWidth;\n\ndiv.remove();\n\nalert(scrollWidth);\n```\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/2-scrollbar-width/task.md",
    "content": "importance: 3\n\n---\n\n# Quelle est la largeur de la barre de défilement ?\n\nÉcrivez le code qui renvoie la largeur d'une barre de défilement standard.\n\nPour Windows, il varie généralement entre `12px` et `20px`. Si le navigateur ne lui réserve pas d'espace (la barre de défilement est à moitié translucide sur le texte, cela arrive également), alors il peut s'agir de `0px`.\n\nP.S. Le code devrait fonctionner pour tout document HTML, ne dépend pas de son contenu.\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/ball-half/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    #field {\n      width: 200px;\n      border: 10px groove black;\n      background-color: #00FF00;\n      position: relative;\n    }\n\n    #ball {\n      position: absolute;\n    }\n  </style>\n</head>\n\n<body>\n\n\n  <div id=\"field\">\n    <img src=\"https://js.cx/clipart/ball.svg\" height=\"40\" width=\"40\" id=\"ball\"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n  </div>\n\n\n  <script>\n    let ball = document.getElementById('ball')\n    let field = document.getElementById('field')\n\n    // ball.offsetWidth=0 before image loaded!\n    // to fix: set width\n    ball.style.left = Math.round(field.clientWidth / 2) + 'px'\n    ball.style.top = Math.round(field.clientHeight / 2) + 'px'\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.md",
    "content": "La balle a une `position:absolue`. Cela signifie que ses coordonnées `gauche/haut` sont mesurées à partir de l'élément positionné le plus proche, c'est-à-dire `#field` (car il a `position:relative`).\n\nLes coordonnées commencent à partir du coin supérieur gauche intérieur du champ :\n\n![](field.svg)\n\nLa largeur/hauteur du champ intérieur est `clientWidth/clientHeight`. Le centre de terrain a donc les coordonnées `(clientWidth/2, clientHeight/2)`.\n\n...Mais si nous définissons `ball.style.left/top` à de telles valeurs, alors pas la balle dans son ensemble, mais le bord supérieur gauche de la balle serait au centre :\n\n```js\nball.style.left = Math.round(field.clientWidth / 2) + 'px';\nball.style.top = Math.round(field.clientHeight / 2) + 'px';\n```\n\nVoici à quoi ça ressemble :\n\n[iframe height=180 src=\"ball-half\"]\n\nPour aligner le centre de la balle avec le centre du terrain, nous devons déplacer la balle sur la moitié de sa largeur à gauche et sur la moitié de sa hauteur vers le haut :\n\n```js\nball.style.left = Math.round(field.clientWidth / 2 - ball.offsetWidth / 2) + 'px';\nball.style.top = Math.round(field.clientHeight / 2 - ball.offsetHeight / 2) + 'px';\n```\n\nMaintenant, la balle est enfin centrée.\n\n````warn header=\"Attention : l'écueil !\"\n\nLe code ne fonctionnera pas de manière fiable tant que `<img>` n'a pas de largeur/hauteur :\n\n```html\n<img src=\"ball.png\" id=\"ball\">\n```\n````\n\nLorsque le navigateur ne connaît pas la largeur/hauteur d'une image (à partir des attributs de balise ou CSS), il suppose qu'ils sont égaux à `0` jusqu'à ce que le chargement de l'image soit terminé.\n\nAinsi, la valeur de `ball.offsetWidth` sera `0` jusqu'à ce que l'image se charge. Cela conduit à de mauvaises coordonnées dans le code ci-dessus.\n\nAprès le premier chargement, le navigateur met généralement l'image en cache et lors des rechargements, elle aura immédiatement la taille. Mais lors du premier chargement, la valeur de `ball.offsetWidth` est `0`.\n\nNous devons corriger cela en ajoutant `width/height` à `<img>` :\n\n```html\n<img src=\"ball.png\" *!*width=\"40\" height=\"40\"*/!* id=\"ball\">\n```\n\n...Ou indiquez la taille en CSS :\n\n```css\n#ball {\n  width: 40px;\n  height: 40px;\n}\n```\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    #field {\n      width: 200px;\n      border: 10px groove black;\n      background-color: #00FF00;\n      position: relative;\n    }\n\n    #ball {\n      position: absolute;\n    }\n  </style>\n</head>\n\n<body>\n\n\n  <div id=\"field\">\n    <img src=\"https://en.js.cx/clipart/ball.svg\" width=\"40\" height=\"40\" id=\"ball\"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n  </div>\n\n\n  <script>\n    // ball.offsetWidth=0 before image loaded!\n    // to fix: set width\n    ball.style.left = Math.round(field.clientWidth / 2 - ball.offsetWidth / 2) + 'px'\n    ball.style.top = Math.round(field.clientHeight / 2 - ball.offsetHeight / 2) + 'px'\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    #field {\n      width: 200px;\n      border: 10px groove black;\n      background-color: #00FF00;\n      position: relative;\n    }\n\n    #ball {\n      position: absolute;\n    }\n  </style>\n</head>\n\n<body>\n\n\n  <div id=\"field\">\n    <img src=\"https://en.js.cx/clipart/ball.svg\" id=\"ball\"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n  </div>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/task.md",
    "content": "importance: 5\n\n---\n\n# Placer la balle au centre du terrain\n\nVoici à quoi ressemble le document source :\n\n[iframe src=\"source\" edit link height=180]\n\nQuelles sont les coordonnées du centre de terrain ?\n\nCalculez et utilisez-les pour placer la balle au centre du champ vert :\n\n[iframe src=\"solution\" height=180]\n\n- L'élément doit être déplacé par JavaScript, pas CSS.\n- Le code doit fonctionner avec n'importe quelle taille de boule (`10`, `20`, `30` pixels) et n'importe quelle taille de champ, ne pas être lié aux valeurs données.\n\nP.S. Bien sûr, le centrage pourrait être effectué avec CSS, mais ici, nous voulons exactement JavaScript. De plus, nous rencontrerons d'autres sujets et des situations plus complexes lorsque JavaScript doit être utilisé. Ici, nous faisons un \"échauffement\".\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/solution.md",
    "content": "Différences :\n\n1. `clientWidth` est numérique, tandis que `getComputedStyle(elem).width` renvoie une chaîne de caractères avec `px` à la fin.\n2. `getComputedStyle` peut renvoyer une largeur non numérique comme `\"auto\"` pour un élément inline.\n3. `clientWidth` est la zone de contenu interne de l'élément plus les paddings, tandis que la largeur CSS (avec le `box-sizing` standard ) est la zone de contenu interne *sans les paddings*.\n4. S'il y a une barre de défilement et que le navigateur lui réserve de l'espace, certains navigateurs soustraient cet espace de la largeur CSS (car il n'est plus disponible pour le contenu), et d'autres non. La propriété `clientWidth` est toujours la même : la taille de la barre de défilement est soustraite si elle est réservée.\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/task.md",
    "content": "importance: 5\n\n---\n\n# La différence: largeur CSS vs clientWidth\n\nQuelle est la différence entre `getComputedStyle(elem).width` et `elem.clientWidth` ?\n\nDonnez au moins 3 différences. Plus c'est mieux.\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/article.md",
    "content": "# Taille des éléments et défilement\n\nIl existe de nombreuses propriétés JavaScript qui nous permettent de lire des informations sur la largeur, la hauteur des éléments et d'autres caractéristiques géométriques.\n\nNous en avons souvent besoin lors du déplacement ou du positionnement d'éléments en JavaScript.\n\n## Exemple d'élément\n\nComme exemple d'élément pour démontrer les propriétés, nous utiliserons celui donné ci-dessous :\n\n```html no-beautify\n<div id=\"example\">\n  ...Text...\n</div>\n<style>\n  #example {\n    width: 300px;\n    height: 200px;\n    border: 25px solid #E8C48F;\n    padding: 20px;\n    overflow: auto;\n  }\n</style>\n```\n\nIl a la bordure, le padding et le défilement. L'ensemble complet de fonctionnalités. Il n'y a pas de marges, car elles ne font pas partie de l'élément lui-même et il n'y a pas de propriétés spéciales pour elles.\n\nL'élément ressemble à ceci :\n\n![](metric-css.svg)\n\nVous pouvez [ouvrir le document dans la sandbox](sandbox:metric).\n\n```smart header=\"Attention à la barre de défilement\"\nL'image ci-dessus illustre le cas le plus complexe lorsque l'élément a une barre de défilement. Certains navigateurs (pas tous) lui réservent de l'espace en le prenant dans le contenu (étiqueté comme \"largeur de contenu\" ci-dessus).\n\nAinsi, sans barre de défilement, la largeur du contenu serait de `300px`, mais si la barre de défilement est large de 16 pixels (la largeur peut varier entre les appareils et les navigateurs), il ne reste que `300 - 16 = 284 pixels`, et nous devons en tenir compte. . C'est pourquoi les exemples de ce chapitre supposent qu'il y a une barre de défilement. Sans cela, certains calculs sont plus simples.\n```\n\n```smart header=\"La zone `padding-bottom` peut être remplie de texte\"\nHabituellement, les paddings sont vides sur nos illustrations, mais s'il y a beaucoup de texte dans l'élément et qu'il déborde, alors les navigateurs affichent le texte \"débordant\" dans `padding-bottom`, c'est normal.\n```\n\n## Géométrie\n\nVoici l'image globale avec les propriétés de la géométrie :\n\n![](metric-all.svg)\n\nLes valeurs de ces propriétés sont techniquement des nombres, mais ces nombres sont \"de pixels\", donc ce sont des mesures de pixels.\n\nCommençons à explorer les propriétés à partir de l'extérieur de l'élément.\n\n## offsetParent, offsetLeft/Top\n\nCes propriétés sont rarement nécessaires, mais ce sont toujours les propriétés de géométrie \"les plus extérieures\", nous allons donc commencer par elles.\n\nLe `offsetParent` est l'ancêtre le plus proche que le navigateur utilise pour calculer les coordonnées pendant le rendu.\n\nC'est l'ancêtre le plus proche qui est l'un des suivants :\n\n1. CSS positionné (`position` is `absolute`, `relative`, `fixed` or `sticky`),  ou\n2. `<td>`, `<th>`, ou `<table>`,  ou\n3. `<body>`.\n\nLes propriétés `offsetLeft/offsetTop` fournissent des coordonnées x/y par rapport au coin supérieur gauche de `offsetParent`.\n\nDans l'exemple ci-dessous, la `<div>` intérieure a `<main>` comme `offsetParent` et `offsetLeft/offsetTop` décale de son coin supérieur gauche (`180`) :\n\n```html run height=10\n<main style=\"position: relative\" id=\"main\">\n  <article>\n    <div id=\"example\" style=\"position: absolute; left: 180px; top: 180px\">...</div>\n  </article>\n</main>\n<script>\n  alert(example.offsetParent.id); // main\n  alert(example.offsetLeft); // 180 (note : un nombre, pas une chaîne de caractères \"180px\")\n  alert(example.offsetTop); // 180\n</script>\n```\n\n![](metric-offset-parent.svg)\n\nIl y a plusieurs occasions où `offsetParent` est `null` :\n\n1. Pour les éléments non affichés (`display:none` ou pas dans le document).\n2. Pour `<body>` et `<html>`.\n3. Pour les éléments avec `position:fixed`.\n\n## offsetWidth/Height\n\nPassons maintenant à l'élément lui-même.\n\nCes deux propriétés sont les plus simples. Elles fournissent la largeur/hauteur \"extérieure\" de l'élément. Ou, en d'autres termes, sa taille complète, y compris les bordures.\n\n![](metric-offset-width-height.svg)\n\nPour notre exemple d'élément :\n\n- `offsetWidth = 390` -- la largeur extérieure, peut être calculée comme la largeur CSS intérieure (`300px`) plus les paddings (`2 * 20px`) et les bordures (`2 * 25px`).\n- `offsetHeight = 290` -- la hauteur extérieure.\n\n````smart header=\"Les propriétés de géométrie sont zéro/nulles pour les éléments qui ne sont pas affichés\"\nLes propriétés de géométrie sont calculées uniquement pour les éléments affichés.\n\nSi un élément (ou l'un de ses ancêtres) a `display:none` ou n'est pas dans le document, alors toutes les propriétés géométriques sont zéro (ou `null` pour `offsetParent`).\n\nPar exemple, `offsetParent` est `null`, et `offsetWidth`, `offsetHeight` sont `0` lorsque nous avons créé un élément, mais ne l'avons pas encore inséré dans le document, ou il (ou son ancêtre) a `display:none`.\n\nNous pouvons l'utiliser pour vérifier si un élément est caché, comme ceci :\n\n```js\nfunction isHidden(elem) {\n  return !elem.offsetWidth && !elem.offsetHeight;\n}\n```\n\nVeuillez noter qu'un `isHidden` renvoie `true` pour les éléments qui sont à l'écran, mais qui ont des tailles nulles (comme une `<div>` vide).\n````\n\n## clientTop/Left\n\nÀ l'intérieur de l'élément, nous avons les bordures.\n\nPour les mesurer, il existe des propriétés `clientTop` et `clientLeft`.\n\nDans notre exemple :\n\n- `clientLeft = 25` -- largeur de bordure gauche\n- `clientTop = 25` -- largeur de bordure supérieure\n\n![](metric-client-left-top.svg)\n\n... Mais pour être précis - ces propriétés ne sont pas la largeur/hauteur de la bordure, mais plutôt les coordonnées relatives du côté intérieur par rapport au côté extérieur.\n\nQuelle est la différence ?\n\nIl devient visible lorsque le document est de droite à gauche (le système d'exploitation est en arabe ou en hébreu). La barre de défilement n'est alors pas à droite, mais à gauche, puis `clientLeft` inclut également la largeur de la barre de défilement.\n\nDans ce cas, `clientLeft` ne serait pas `25`, mais avec la largeur de la barre de défilement `25 + 16 = 41`.\n\nVoici l'exemple en hébreu :\n\n![](metric-client-left-top-rtl.svg)\n\n## clientWidth/Height\n\nCes propriétés fournissent la taille de la zone à l'intérieur des bordures des éléments.\n\nIls incluent la largeur du contenu ainsi que les paddings, mais sans la barre de défilement :\n\n![](metric-client-width-height.svg)\n\nSur l'image ci-dessus, considérons d'abord `clientHeight`.\n\nIl n'y a pas de barre de défilement horizontale, c'est donc exactement la somme de ce qui se trouve à l'intérieur des bordures : hauteur CSS `200px` plus paddings supérieur et inférieur (`2 * 20px`) total `240px`.\n\nMaintenant `clientWidth` - ici la largeur du contenu n'est pas `300px`, mais `284px`, car `16px` sont occupés par la barre de défilement. Ainsi, la somme est `284px` plus les paddings gauche et droit, total `324px`.\n\n**S'il n'y a pas de paddings, alors `clientWidth/Height` est exactement la zone de contenu, à l'intérieur des bordures et de la barre de défilement (le cas échéant).**\n\n![](metric-client-width-nopadding.svg)\n\nDonc, quand il n'y a pas de padding, nous pouvons utiliser `clientWidth/clientHeight` pour obtenir la taille de la zone de contenu.\n\n## scrollWidth/Height\n\nCes propriétés sont comme `clientWidth/clientHeight`, mais elles incluent également les parties déroulantes (cachées) :\n\n![](metric-scroll-width-height.svg)\n\nSur l'image ci-dessus :\n\n- `scrollHeight = 723` -- est la pleine hauteur intérieure de la zone de contenu, y compris des parties déroulantes.\n- `scrollWidth = 324` -- est la largeur intérieure complète, ici nous n'avons pas de défilement horizontal, donc il est égal à `clientWidth`.\n\nNous pouvons utiliser ces propriétés pour agrandir l'élément à sa pleine largeur/hauteur.\n\nComme ceci :\n\n```js\n// expand the element to the full content height\nelement.style.height = `${element.scrollHeight}px`;\n```\n\n```online\nCliquez sur le bouton pour développer l'élément :\n\n<div id=\"element\" style=\"width:300px;height:200px; padding: 0;overflow: auto; border:1px solid black;\">text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text</div>\n\n<button style=\"padding:0\" onclick=\"element.style.height = `${element.scrollHeight}px`\">element.style.height = `${element.scrollHeight}px`</button>\n```\n\n## scrollLeft/scrollTop\n\nLes propriétés `scrollLeft/scrollTop` sont la largeur/hauteur de la partie cachée et déroulée de l'élément.\n\nSur l'image ci-dessous, nous pouvons voir `scrollHeight` et `scrollTop` pour un bloc avec un défilement vertical.\n\n![](metric-scroll-top.svg)\n\nEn d'autres termes, `scrollTop` est \"combien est déroulé\".\n\n````smart header=\"`scrollLeft/scrollTop` peut être modifié\"\nLa plupart des propriétés de géométrie ici sont en lecture seule, mais `scrollLeft/scrollTop` peut être modifié, et le navigateur fera défiler l'élément.\n\n```online\nSi vous cliquez sur l'élément ci-dessous, le code `elem.scrollTop + = 10` s'exécute. Cela fait défiler le contenu de l'élément `10px` vers le bas.\n\n<div onclick=\"this.scrollTop+=10\" style=\"cursor:pointer;border:1px solid black;width:100px;height:80px;overflow:auto\">Click<br>Me<br>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9</div>\n```\n\nDéfinir `scrollTop` sur `0` ou une grande valeur, telle que `1e9`, fera défiler l'élément vers le haut/bas respectivement.\n````\n\n## Ne prenez pas la largeur/hauteur du CSS\n\nNous venons de couvrir les propriétés géométriques des éléments DOM, qui peuvent être utilisées pour obtenir des largeurs, des hauteurs et calculer des distances.\n\nMais comme nous le savons du chapitre <info:styles-and-classes>, nous pouvons lire hauteur et largeur CSS en utilisant `getComputedStyle`.\n\nAlors pourquoi ne pas lire la largeur d'un élément avec `getComputedStyle`, comme ceci ?\n\n```js run\nlet elem = document.body;\n\nalert( getComputedStyle(elem).width ); // affiche la largeur CSS pour elem\n```\n\nPourquoi devrions-nous plutôt utiliser des propriétés géométriques ? Il y a deux raisons :\n\n1. Tout d'abord, la `largeur/hauteur` en CSS dépend d'une autre propriété : le `box-sizing` qui définit \"ce qui est\" la largeur et la hauteur en CSS. Un changement de `box-sizing` à des fins CSS peut casser un tel JavaScript.\n2. Deuxièmement, la `largeur/hauteur` en CSS peut être `auto`, par exemple pour un élément en ligne :\n\n    ```html run\n    <span id=\"elem\">Hello!</span>\n\n    <script>\n    *!*\n      alert( getComputedStyle(elem).width ); // auto\n    */!*\n    </script>\n    ```\n\n    Du point de vue CSS, `width:auto` est parfaitement normal, mais en JavaScript, nous avons besoin d'une taille exacte en `px` que nous pouvons utiliser dans les calculs. Donc ici, la largeur CSS est inutile.\n\nEt il y a une autre raison : une barre de défilement. Parfois, le code qui fonctionne correctement sans barre de défilement devient bogué, car une barre de défilement prend de l'espace dans le contenu de certains navigateurs. La largeur réelle disponible pour le contenu est donc *inférieure* à la largeur CSS. Et `clientWidth/clientHeight` en tient compte.\n\n...Mais avec `getComputedStyle(elem).width` la situation est différente. Certains navigateurs (par exemple Chrome) renvoient la largeur intérieure réelle, moins la barre de défilement, et certains d'entre eux (par exemple Firefox) -- largeur CSS (ignorent la barre de défilement). De telles différences entre navigateurs sont la raison de ne pas utiliser `getComputedStyle`, mais plutôt de s'appuyer sur les propriétés géométriques.\n\n```online\nSi votre navigateur réserve l'espace pour une barre de défilement (la plupart des navigateurs pour Windows le font), vous pouvez le tester ci-dessous.\n\n[iframe src=\"cssWidthScroll\" link border=1]\n\nL'élément avec du texte a comme CSS `width:300px`.\n\nSur un OS de bureau Windows, Firefox, Chrome, Edge réservent tous l'espace pour la barre de défilement. Mais Firefox affiche `300 pixels`, tandis que Chrome et Edge affichent moins. En effet, Firefox renvoie la largeur CSS et les autres navigateurs renvoient la largeur \"réelle\".\n```\n\nVeuillez noter que la différence décrite concerne uniquement la lecture de `getComputedStyle(...).width` à partir de JavaScript, visuellement tout est correct.\n\n## Résumé\n\nLes éléments ont les propriétés géométriques suivantes :\n\n- `offsetParent` -- est l'ancêtre le plus proche ou `td`, `th`, `table`, `body`.\n- `offsetLeft/offsetTop` -- coordonnées par rapport au bord supérieur gauche de `offsetParent`.\n- `offsetWidth/offsetHeight` -- largeur/hauteur \"extérieure\" d'un élément, bordures comprises.\n- `clientLeft/clientTop` -- les distances entre le coin extérieur supérieur gauche et le coin intérieur supérieur gauche (contenu + padding). Pour le système d'exploitation de gauche à droite, ce sont toujours les largeurs des bordures gauche/supérieure. Pour le système d'exploitation de droite à gauche, la barre de défilement verticale est à gauche, donc `clientLeft` inclut également sa largeur.\n- `clientWidth/clientHeight` -- la largeur/hauteur du contenu, y compris les paddings, mais sans la barre de défilement.\n- `scrollWidth/scrollHeight` -- la largeur/hauteur du contenu, tout comme `clientWidth/clientHeight`, mais inclut également la partie invisible et déroulante de l'élément.\n- `scrollLeft/scrollTop` -- largeur/hauteur de la partie supérieure déroulante de l'élément, à partir de son coin supérieur gauche.\n\nToutes les propriétés sont en lecture seule sauf `scrollLeft/scrollTop` qui font défiler le navigateur l'élément s'il est modifié.\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/cssWidthScroll.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n  <div id=\"elem\" style=\"overflow-y:scroll;width:300px;height:200px;border:1px solid black\">\n    text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text\n    text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text\n    text text text text text text text text text text text text text text text text text text text text text text text text text text\n  </div>\n\n  The element has <code>style=\"width:300px\"</code>\n  <br>\n\n  <button onclick=\"alert( getComputedStyle(elem).width )\">alert( getComputedStyle(elem).width )</button>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/metric.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style type=\"text/css\">\n    * {\n      margin: 0;\n      padding: 0;\n    }\n\n    #example {\n      width: 300px;\n      height: 200px;\n      overflow: auto;\n      border: 25px solid #E8C48F;\n      padding: 20px;\n    }\n\n    .key {\n      cursor: pointer;\n      text-decoration: underline;\n    }\n  </style>\n\n</head>\n\n<body>\n\n\n  <div id=\"example\">\n    <h3>Introduction</h3>\n    <p>This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company's Navigator 2.0 browser.\n      It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the\n      Ecma General Assembly of June 1997.</p>\n\n    <p>That Ecma Standard was submitted to ISO/IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep\n      it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.</p>\n\n    <p>The third edition of the Standard introduced powerful regular expressions, better string handling, new control statements, try/catch exception handling, tighter definition of errors, formatting for numeric output and minor changes in anticipation\n      of forthcoming internationalisation facilities and future language growth. The third edition of the ECMAScript standard was adopted by the Ecma General Assembly of December 1999 and published as ISO/IEC 16262:2002 in June 2002.</p>\n\n  </div>\n\n\n  <div id=\"mouse-wrap\">Mouse coordinates: <span id=\"mouse\">...</span></div>\n  <div id=\"info\"></div>\n\n\n  <script>\n    let props = {\n      geometry: ['clientLeft', 'clientTop', 'clientWidth', 'clientHeight', 'offsetWidth', 'offsetHeight', 'scrollWidth', 'scrollHeight'],\n      scroll: ['scrollLeft', 'scrollTop'],\n      offsetParent: ['offsetParent', 'offsetLeft', 'offsetTop']\n    };\n\n    info.innerHTML = '<h3>Click to see the value:</h3>';\n    for (let k in props) {\n      info.innerHTML += `<h4>${k}</h4>`;\n      let prop = props[k];\n      for (let i = 0; i < prop.length; i++) {\n        info.innerHTML += \"<span class='key'>\" + prop[i] + '</span>: <span id=\"' + prop[i] + '\">&nbsp;</span>' + \" \"\n        i++;\n        if (i < prop.length) {\n          info.innerHTML += \"<span class='key'>\" + prop[i] + '</span>: <span id=\"' + prop[i] + '\">&nbsp;</span>';\n        }\n        info.innerHTML += \"<br/>\";\n\n      }\n    }\n\n    document.onclick = function(event) {\n      let target = event.target;\n      if (!target.classList.contains('key')) return;\n\n      let prop = target.innerHTML;\n      let value = example[prop];\n      value = value.tagName || value;\n      document.getElementById(prop).innerHTML = value;\n    };\n\n\n    document.onmousemove = function(e) {\n      document.getElementById('mouse').innerHTML = Math.round(e.clientX) + ':' + Math.round(e.clientY);\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/10-size-and-scroll-window/article.md",
    "content": "# Tailles des fenêtres et défilement\n\nComment trouver la largeur et la hauteur de la fenêtre du navigateur ? Comment obtenir la largeur et la hauteur complètes du document, y compris la partie déroulante ? Comment faire défiler la page en utilisant JavaScript ?\n\nPour ce type d'informations, nous pouvons utiliser l'élément de document racine `document.documentElement`, qui correspond à la balise `<html>`. Mais il existe des méthodes et des particularités supplémentaires suffisamment importantes pour être prises en compte.\n\n## Largeur/hauteur de la fenêtre\n\nPour obtenir la largeur et la hauteur de la fenêtre, nous pouvons utiliser `clientWidth/clientHeight` de `document.documentElement` :\n\n![](document-client-width-height.svg)\n\n```online\nPar exemple, ce bouton affiche la hauteur de votre fenêtre :\n\n<button onclick=\"alert(document.documentElement.clientHeight)\">alert(document.documentElement.clientHeight)</button>\n```\n\n````warn header=\"Pas `window.innerWidth/Height`\"\nLes navigateurs prennent également en charge les propriétés `window.innerWidth/innerHeight`. Ils ressemblent à ce que nous voulons, alors pourquoi ne pas les utiliser à la place ?\n\nS'il existe une barre de défilement et qu'elle occupe de l'espace, `clientWidth/clientHeight` fournit la largeur/hauteur sans elle (cela la soustrait). En d'autres termes, elles renvoient la largeur/hauteur de la partie visible du document, disponible pour le contenu.\n\n… Et `window.innerWidth/innerHeight` inclut la barre de défilement.\n\nS'il y a une barre de défilement et qu'elle occupe de l'espace, ces deux lignes affichent des valeurs différentes :\n```js run\nalert( window.innerWidth ); // pleine largeur de fenêtre\nalert( document.documentElement.clientWidth ); // largeur de la fenêtre moins la barre de défilement\n```\n\nDans la plupart des cas, nous avons besoin de la largeur de fenêtre *disponible* : pour dessiner ou positionner quelque chose. C'est-à-dire : à l'intérieur des barres de défilement s'il y en a. Nous devons donc utiliser `documentElement.clientHeight/Width`.\n````\n\n```warn header=\"`Le DOCTYPE` est important\"\nRemarque: les propriétés de géométrie de niveau supérieur peuvent fonctionner un peu différemment lorsqu'il n'y a pas de `<!DOCTYPE HTML>` dans HTML. Des choses étranges sont possibles.\n\nDans le HTML moderne, nous devons toujours écrire le `DOCTYPE`.\n```\n\n## Largeur/hauteur du document\n\nThéoriquement, comme l'élément de document racine est `document.documentElement` et qu'il contient tout le contenu, nous pourrions mesurer le document en taille réelle comme `document.documentElement.scrollWidth/scrollHeight`.\n\nMais sur cet élément, pour la page entière, ces propriétés ne fonctionnent pas comme prévu. Dans Chrome/Safari/Opera s'il n'y a pas de défilement, alors `documentElement.scrollHeight` peut être encore moins que `documentElement.clientHeight` ! Cela ressemble à un non-sens, bizarre, non ?\n\nPour obtenir de manière fiable la pleine hauteur du document, nous devons prendre le maximum de ces propriétés :\n\n```js run\nlet scrollHeight = Math.max(\n  document.body.scrollHeight, document.documentElement.scrollHeight,\n  document.body.offsetHeight, document.documentElement.offsetHeight,\n  document.body.clientHeight, document.documentElement.clientHeight\n);\n\nalert('Full document height, with scrolled out part: ' + scrollHeight);\n```\n\nPourquoi ? Mieux vaut ne pas demander. Ces incohérences viennent des temps anciens, pas d'une logique \"intelligente\".\n\n## Obtenez le défilement actuel [#page-scroll]\n\nLes éléments DOM ont leur état de défilement actuel dans leurs propriétés `scrollLeft/scrollTop`.\n\nPour le défilement de document, `document.documentElement.scrollLeft/Top` fonctionne dans la plupart des navigateurs, à l'exception des plus anciens basés sur WebKit, comme Safari (bug [5991](https://bugs.webkit.org/show_bug.cgi?id=5991)), où nous devrions utiliser `document.body` au lieu de `document.documentElement`.\n\nHeureusement, nous n'avons pas du tout à nous souvenir de ces particularités, car le défilement est disponible dans les propriétés spéciales `window.pageXOffset/pageYOffset` :\n\n```js run\nalert('Current scroll from the top: ' + window.pageYOffset);\nalert('Current scroll from the left: ' + window.pageXOffset);\n```\n\nCes propriétés sont en lecture seule.\n\n```smart header=\"Également disponible en tant que propriétés `window` `scrollX` et `scrollY`\"\nPour des raisons historiques, les deux propriétés existent, mais elles sont identiques :\n- `window.pageXOffset` est un alias de `window.scrollX`.\n- `window.pageYOffset` est un alias de `window.scrollY`.\n```\n## Défilement : scrollTo, scrollBy, scrollIntoView [#window-scroll]\n\n\n```warn\nPour faire défiler la page avec JavaScript, son DOM doit être entièrement construit.\n\nPar exemple, si nous essayons de faire défiler la page à partir du script dans `<head>`, cela ne fonctionnera pas.\n```\n\nLes éléments réguliers peuvent défiler en changeant `scrollTop/scrollLeft`.\n\nNous pouvons faire de même pour la page utilisant `document.documentElement.scrollTop/Left` (sauf Safari, où `document.body.scrollTop/Left` devrait être utilisé à la place).\n\nAlternativement, il existe une solution plus simple et universelle: des méthodes spéciales [window.scrollBy(x,y)](mdn:api/Window/scrollBy) et [window.scrollTo(pageX,pageY)](mdn:api/Window/scrollTo).\n\n- La méthode `scrollBy(x, y)` fait défiler la page *par rapport à sa position actuelle*. Par exemple, `scrollBy(0,10)` fait défiler la page `10px` vers le bas.\n\n    ```online\n    Le bouton ci-dessous illustre cela:\n\n    <button onclick=\"window.scrollBy(0,10)\">window.scrollBy(0,10)</button>\n    ```\n- La méthode `scrollTo(pageX,pageY)` fait défiler la page *jusqu'aux coordonnées absolues*, de sorte que le coin supérieur gauche de la partie visible ait les coordonnées `(pageX, pageY)` par rapport au coin supérieur gauche du document. C'est comme définir `scrollLeft/scrollTop`.\n\n    Pour faire défiler jusqu'au tout début, nous pouvons utiliser `scrollTo(0,0)`.\n\n    ```online\n    <button onclick=\"window.scrollTo(0,0)\">window.scrollTo(0,0)</button>\n    ```\n\nCes méthodes fonctionnent de la même manière pour tous les navigateurs.\n\n## scrollIntoView\n\nPour être complet, couvrons une autre méthode : [elem.scrollIntoView(top)](mdn:api/Element/scrollIntoView).\n\nL'appel à `elem.scrollIntoView(top)` fait défiler la page pour rendre `elem` visible. Il a un argument :\n\n- Si `top=true` (c'est la valeur par défaut), alors la page défilera pour faire apparaître `elem` en haut de la fenêtre. Le bord supérieur de l'élément est aligné avec le haut de la fenêtre.\n- Si `top=false`, alors la page défile pour faire apparaître `elem` en bas. Le bord inférieur de l'élément est aligné avec le bas de la fenêtre.\n\n```online\nLe bouton ci-dessous fait défiler la page pour aligner l'élément en haut de la fenêtre :\n\n<button onclick=\"this.scrollIntoView()\">this.scrollIntoView()</button>\n\nEt ce bouton fait défiler la page pour l'aligner en bas :\n\n<button onclick=\"this.scrollIntoView(false)\">this.scrollIntoView(false)</button>\n```\n\n## Interdire le défilement\n\nParfois, nous devons rendre le document \"non-défilable\". Par exemple, lorsque nous devons le couvrir d'un gros message nécessitant une attention immédiate et que nous voulons que le visiteur interagisse avec ce message, pas avec le document.\n\nPour rendre le document impossible à faire défiler, il suffit de définir `document.body.style.overflow = \"hidden\"`. La page se fige sur son défilement actuel.\n\n```online\nEssayez-le :\n\n<button onclick=\"document.body.style.overflow = 'hidden'\">document.body.style.overflow = 'hidden'</button>\n\n<button onclick=\"document.body.style.overflow = ''\">document.body.style.overflow = ''</button>\n\nLe premier bouton fige le défilement, le second le reprend.\n```\n\nNous pouvons utiliser la même technique pour \"figer\" le défilement pour d'autres éléments, pas seulement pour `document.body`.\n\nL'inconvénient de la méthode est que la barre de défilement disparaît. S'il occupait de l'espace, cet espace est désormais libre et le contenu \"saute\" pour le remplir.\n\nCela semble un peu étrange, mais peut être contourné si nous comparons `clientWidth` avant et après le gel, et s'il a augmenté (la barre de défilement a disparu), puis ajoutez un `padding` à `document.body` à la place de la barre de défilement, pour conservez la même largeur de contenu.\n\n## Résumé\n\nGéométrie :\n\n- Largeur/hauteur de la partie visible du document (largeur/hauteur de la zone de contenu) : `document.documentElement.clientWidth/Height`\n- Largeur/hauteur de l'ensemble du document, avec la partie déroulante :\n\n    ```js\n    let scrollHeight = Math.max(\n      document.body.scrollHeight, document.documentElement.scrollHeight,\n      document.body.offsetHeight, document.documentElement.offsetHeight,\n      document.body.clientHeight, document.documentElement.clientHeight\n    );\n    ```\n\nDéfilement :\n\n- Lire le défilement actuel : `window.pageYOffset/pageXOffset`.\n- Modifiez le défilement actuel :\n\n    - `window.scrollTo(pageX,pageY)` -- coordonnées absolues,\n    - `window.scrollBy(x,y)` -- défilement par rapport à l'endroit actuel,\n    - `elem.scrollIntoView(top)` -- défilement pour rendre `elem` visible (alignement avec le haut/bas de la fenêtre).\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/1-find-point-coordinates/solution.md",
    "content": "# Coins extérieurs\n\nLes coins extérieurs sont essentiellement ce que nous obtenons de [elem.getBoundingClientRect()](https://developer.mozilla.org/en-US/docs/DOM/element.getBoundingClientRect).\n\nLes coordonnées du coin supérieur gauche `answer1` et du coin inférieur droit `answer2` :\n\n```js\nlet coords = elem.getBoundingClientRect();\n\nlet answer1 = [coords.left, coords.top];\nlet answer2 = [coords.right, coords.bottom];\n```\n\n# Coin intérieur supérieur gauche\n\nCela diffère du coin extérieur par la largeur de la bordure. Un moyen fiable pour obtenir la distance est `clientLeft/clientTop` :\n\n```js\nlet answer3 = [coords.left + field.clientLeft, coords.top + field.clientTop];\n```\n\n# Coin intérieur en bas à droite\n\nDans notre cas, nous devons soustraire la taille de la bordure des coordonnées extérieures.\n\nNous pourrions utiliser la manière CSS :\n\n```js\nlet answer4 = [\n  coords.right - parseInt(getComputedStyle(field).borderRightWidth),\n  coords.bottom - parseInt(getComputedStyle(field).borderBottomWidth)\n];\n```\n\nUne autre façon serait d'ajouter `clientWidth/clientHeight` aux coordonnées du coin supérieur gauche. C'est probablement encore mieux :\n\n```js\nlet answer4 = [\n  coords.left + elem.clientLeft + elem.clientWidth,\n  coords.top + elem.clientTop + elem.clientHeight\n];\n```\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/1-find-point-coordinates/solution.view/index.css",
    "content": "body {\n  padding: 20px 0 0 20px;\n  cursor: pointer;\n}\n\n#field {\n  overflow: hidden;\n  width: 200px;\n  height: 150px;\n  border-top: 10px solid black;\n  border-right: 10px solid gray;\n  border-bottom: 10px solid gray;\n  border-left: 10px solid black;\n  background-color: #00FF00;\n  font: 10px/1.2 monospace;\n}\n\n.triangle-right {\n  position: relative;\n  width: 0;\n  height: 0;\n  border-top: 6px solid transparent;\n  border-bottom: 6px solid transparent;\n  border-left: 20px solid red;\n  text-indent: -20px;\n  font: 12px/1 monospace;\n}"
  },
  {
    "path": "2-ui/1-document/11-coordinates/1-find-point-coordinates/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"index.css\">\n  <script>\n    document.onclick = function(e) { // shows click coordinates\n      coords.innerHTML = e.clientX + ':' + e.clientY;\n    };\n  </script>\n</head>\n\n<body>\n\n  Click anywhere to get window coordinates.\n  <br> That's for testing, to check the result you get by JavaScript.\n  <br>\n  <div id=\"coords\">(click coordinates show up here)</div>\n\n\n  <div id=\"field\">\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n  </div>\n\n\n  <div class=\"triangle-right\" style=\"left:-20px;top:-176px\">1</div>\n  <div class=\"triangle-right\" style=\"left:-10px;top:-178px\">3</div>\n  <div class=\"triangle-right\" style=\"left:190px;top:-40px\">4</div>\n  <div class=\"triangle-right\" style=\"left:200px;top:-42px\">2</div>\n\n  <script>\n    let fieldCoords = field.getBoundingClientRect();\n\n    let answer = [\n      [ // 1\n        fieldCoords.left,\n        fieldCoords.top\n      ],\n      [ // 2\n        fieldCoords.right,\n        fieldCoords.bottom\n      ],\n      [ // 3\n        fieldCoords.left + field.clientLeft,\n        fieldCoords.top + field.clientTop\n      ],\n      [ // 4\n        fieldCoords.left + field.clientLeft + field.clientWidth,\n        fieldCoords.top + field.clientTop + field.clientHeight\n      ]\n    ];\n\n    alert(answer.join('  '));\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/1-find-point-coordinates/source.view/index.css",
    "content": "body {\n  padding: 20px 0 0 20px;\n  cursor: pointer;\n}\n\n#field {\n  overflow: hidden;\n  width: 200px;\n  height: 150px;\n  border-top: 10px solid black;\n  border-right: 10px solid gray;\n  border-bottom: 10px solid gray;\n  border-left: 10px solid black;\n  background-color: #00FF00;\n  font: 10px/1.2 monospace;\n}\n\n.triangle-right {\n  position: relative;\n  width: 0;\n  height: 0;\n  border-top: 6px solid transparent;\n  border-bottom: 6px solid transparent;\n  border-left: 20px solid red;\n  text-indent: -20px;\n  font: 12px/1 monospace;\n}\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/1-find-point-coordinates/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"index.css\">\n  <script>\n    document.onclick = function(e) { // shows click coordinates\n      coords.innerHTML = e.clientX + ':' + e.clientY;\n    };\n  </script>\n</head>\n\n<body>\n\n  Click anywhere to get window coordinates.\n  <br> That's for testing, to check the result you get by JavaScript.\n  <br>\n  <div id=\"coords\">(click coordinates show up here)</div>\n\n\n  <div id=\"field\">\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n  </div>\n\n\n  <div class=\"triangle-right\" style=\"left:-20px;top:-176px\">1</div>\n  <div class=\"triangle-right\" style=\"left:-10px;top:-178px\">3</div>\n  <div class=\"triangle-right\" style=\"left:190px;top:-40px\">4</div>\n  <div class=\"triangle-right\" style=\"left:200px;top:-42px\">2</div>\n\n\n  <script>\n    // ...your code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/1-find-point-coordinates/task.md",
    "content": "importance: 5\n\n---\n\n# Trouver les coordonnées de la fenêtre du champ\n\nDans l'iframe ci-dessous, vous pouvez voir un document avec le \"champ\" vert.\n\nUtilisez JavaScript pour trouver les coordonnées de la fenêtre des coins pointés par des flèches.\n\nIl y a une petite fonctionnalité implémentée dans le document pour plus de commodité. Un clic à n'importe quel endroit montre les coordonnées là-bas.\n\n[iframe border=1 height=360 src=\"source\" link edit]\n\nVotre code doit utiliser DOM pour obtenir les coordonnées de la fenêtre de :\n\n1. Coin extérieur supérieur gauche (c'est simple).\n2. En bas à droite, coin extérieur (simple aussi).\n3. Coin intérieur supérieur gauche (un peu plus dur).\n4. En bas à droite, coin intérieur (il y a plusieurs façons, choisissez-en une).\n\nLes coordonnées que vous calculez doivent être les mêmes que celles renvoyées par le clic de souris.\n\nP.S. Le code devrait également fonctionner si l'élément a une autre taille ou bordure, qui n'est lié à aucune valeur fixe.\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/2-position-at/solution.md",
    "content": "Dans cet exercice, il suffit de calculer avec précision les coordonnées. Voir le code pour plus de détails.\n\nVeuillez noter : les éléments doivent être dans le document pour lire `offsetHeight` et d'autres propriétés.\nUn élément caché (`display:none`) ou hors du document n'a pas de taille.\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/2-position-at/solution.view/index.css",
    "content": ".note {\n  position: fixed;\n  z-index: 1000;\n  padding: 5px;\n  border: 1px solid black;\n  background: white;\n  text-align: center;\n  font: italic 14px serif;\n}\n\nblockquote {\n  background: #f9f9f9;\n  border-left: 10px solid #ccc;\n  margin: 0 0 0 100px;\n  padding: .5em 10px;\n  quotes: \"\\201C\"\"\\201D\"\"\\2018\"\"\\2019\";\n  display: inline-block;\n  white-space: pre;\n}\n\nblockquote:before {\n  color: #ccc;\n  content: open-quote;\n  font-size: 4em;\n  line-height: .1em;\n  margin-right: .25em;\n  vertical-align: -.4em;\n}\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/2-position-at/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"index.css\">\n</head>\n\n<body>\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n  <blockquote>\n    Teacher: Why are you late?\n    Student: There was a man who lost a hundred dollar bill.\n    Teacher: That's nice. Were you helping him look for it?\n    Student: No. I was standing on it.\n  </blockquote>\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n\n  <script>\n    /**\n     * Positions elem relative to anchor as said in position.\n     *\n     * @param {Node} anchor     Anchor element for positioning\n     * @param {string} position One of: top/right/bottom\n     * @param {Node} elem       Element to position\n     *\n     * Both elements: elem and anchor must be in the document\n     */\n    function positionAt(anchor, position, elem) {\n\n      let anchorCoords = anchor.getBoundingClientRect();\n\n      switch (position) {\n        case \"top\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top - elem.offsetHeight + \"px\";\n          break;\n\n        case \"right\":\n          elem.style.left = anchorCoords.left + anchor.offsetWidth + \"px\";\n          elem.style.top = anchorCoords.top + \"px\";\n          break;\n\n        case \"bottom\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top + anchor.offsetHeight + \"px\";\n          break;\n      }\n\n    }\n\n    /**\n     * Shows a note with the given html at the given position\n     * relative to the anchor element.\n     */\n    function showNote(anchor, position, html) {\n\n      let note = document.createElement('div');\n      note.className = \"note\";\n      note.innerHTML = html;\n      document.body.append(note);\n\n      positionAt(anchor, position, note);\n    }\n\n    // test it\n    let blockquote = document.querySelector('blockquote');\n\n    showNote(blockquote, \"top\", \"note above\");\n    showNote(blockquote, \"right\", \"note at the right\");\n    showNote(blockquote, \"bottom\", \"note below\");\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/2-position-at/source.view/index.css",
    "content": ".note {\n  position: fixed;\n  z-index: 1000;\n  padding: 5px;\n  border: 1px solid black;\n  background: white;\n  text-align: center;\n  font: italic 14px serif;\n}\n\nblockquote {\n  background: #f9f9f9;\n  border-left: 10px solid #ccc;\n  margin: 0 0 0 100px;\n  padding: .5em 10px;\n  quotes: \"\\201C\"\"\\201D\"\"\\2018\"\"\\2019\";\n  display: inline-block;\n  white-space: pre;\n}\n\nblockquote:before {\n  color: #ccc;\n  content: open-quote;\n  font-size: 4em;\n  line-height: .1em;\n  margin-right: .25em;\n  vertical-align: -.4em;\n}\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/2-position-at/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"index.css\">\n</head>\n\n<body>\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n  <blockquote>\n    Teacher: Why are you late?\n    Student: There was a man who lost a hundred dollar bill.\n    Teacher: That's nice. Were you helping him look for it?\n    Student: No. I was standing on it.\n  </blockquote>\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n\n  <script>\n    /**\n     * Positions elem relative to anchor as said in position.\n     *\n     * @param {Node} anchor     Anchor element for positioning\n     * @param {string} position One of: top/right/bottom\n     * @param {Node} elem       Element to position\n     *\n     * Both elements: elem and anchor must be in the document\n     */\n    function positionAt(anchor, position, elem) {\n      // ... your code ...\n    }\n\n    /**\n     * Shows a note with the given html at the given position\n     * relative to the anchor element.\n     */\n    function showNote(anchor, position, html) {\n\n      let note = document.createElement('div');\n      note.className = \"note\";\n      note.innerHTML = html;\n      document.body.append(note);\n\n      positionAt(anchor, position, note);\n    }\n\n    // test it\n    let blockquote = document.querySelector('blockquote');\n\n    showNote(blockquote, \"top\", \"note above\");\n    showNote(blockquote, \"right\", \"note at the right\");\n    showNote(blockquote, \"bottom\", \"note below\");\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/2-position-at/task.md",
    "content": "importance: 5\n\n---\n\n# Afficher une note près de l'élément\n\nCréez une fonction `positionAt(anchor, position, elem)` qui positionne `elem`, en fonction de `position` près de l'élément `anchor`.\n\nLa `position` doit être une chaîne de caractères avec l'une des 3 valeurs :\n- `\"top\"` - position `elem` juste au dessus de `anchor`\n- `\"right\"` - position `elem` immédiatement à droite de `anchor`\n- `\"bottom\"` - position `elem` juste en dessous `anchor`\n\nIl est utilisé à l'intérieur de la fonction `showNote(anchor, position, html)`, fournie dans le code source de la tâche, qui crée un élément \"note\" avec `html` donné et l'affiche à la `position` donnée près de `anchor`.\n\nVoici la démo des notes :\n\n[iframe src=\"solution\" height=\"350\" border=\"1\" link]\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/3-position-at-absolute/solution.md",
    "content": "La solution est en fait assez simple :\n\n- Utilisez `position:absolute` dans le CSS au lieu de `position:fixed` pour `.note`.\n- Utilisez la fonction [getCoords()](info:coordinates#getCoords) du chapitre <info:coordinates> pour obtenir les coordonnées relatives au document.\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/3-position-at-absolute/solution.view/index.css",
    "content": ".note {\n  position: absolute;\n  z-index: 1000;\n  padding: 5px;\n  border: 1px solid black;\n  background: white;\n  text-align: center;\n  font: italic 14px serif;\n}\n\nblockquote {\n  background: #f9f9f9;\n  border-left: 10px solid #ccc;\n  margin: 0 0 0 100px;\n  padding: .5em 10px;\n  quotes: \"\\201C\"\"\\201D\"\"\\2018\"\"\\2019\";\n  display: inline-block;\n  white-space: pre;\n}\n\nblockquote:before {\n  color: #ccc;\n  content: open-quote;\n  font-size: 4em;\n  line-height: .1em;\n  margin-right: .25em;\n  vertical-align: -.4em;\n}\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/3-position-at-absolute/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"index.css\">\n</head>\n\n<body style=\"height: 2000px\">\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n  <blockquote>\n    Teacher: Why are you late?\n    Student: There was a man who lost a hundred dollar bill.\n    Teacher: That's nice. Were you helping him look for it?\n    Student: No. I was standing on it.\n  </blockquote>\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n\n  <script>\n\n    function getCoords(elem) {\n      let box = elem.getBoundingClientRect();\n\n      return {\n        top: box.top + window.pageYOffset,\n        left: box.left + window.pageXOffset\n      };\n    }\n\n    function positionAt(anchor, position, elem) {\n\n      let anchorCoords = getCoords(anchor);\n\n      switch (position) {\n        case \"top\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top - elem.offsetHeight + \"px\";\n          break;\n\n        case \"right\":\n          elem.style.left = anchorCoords.left + anchor.offsetWidth + \"px\";\n          elem.style.top = anchorCoords.top + \"px\";\n          break;\n\n        case \"bottom\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top + anchor.offsetHeight + \"px\";\n          break;\n      }\n\n    }\n\n    function showNote(anchor, position, html) {\n\n      let note = document.createElement('div');\n      note.className = \"note\";\n      note.innerHTML = html;\n      document.body.append(note);\n\n      positionAt(anchor, position, note);\n    }\n\n    // test it\n    let blockquote = document.querySelector('blockquote');\n\n    showNote(blockquote, \"top\", \"note above\");\n    showNote(blockquote, \"right\", \"note at the right\");\n    showNote(blockquote, \"bottom\", \"note below\");\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/3-position-at-absolute/task.md",
    "content": "importance: 5\n\n---\n\n# Afficher une note près de l'élément (absolute)\n\nModifier la solution du [précédent exercice](info:task/position-at) de sorte que la note utilise `position:absolute` au lieu de `position:fixed`.\n\nCela empêchera son \"éloignement\" de l'élément lorsque la page défile.\n\nPrenez la solution de cet exercice comme point de départ. Pour tester le défilement, ajoutez le style `<body style=\"height: 2000px\">`.\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.view/index.css",
    "content": ".note {\n  position: absolute;\n  z-index: 1000;\n  padding: 5px;\n  border: 1px solid black;\n  background: white;\n  text-align: center;\n  font: italic 14px serif;\n  opacity: .8;\n}\n\nblockquote {\n  background: #f9f9f9;\n  border-left: 10px solid #ccc;\n  margin: 0 0 0 100px;\n  padding: .5em 10px;\n  quotes: \"\\201C\"\"\\201D\"\"\\2018\"\"\\2019\";\n  display: inline-block;\n  white-space: pre;\n}\n\nblockquote:before {\n  color: #ccc;\n  content: open-quote;\n  font-size: 4em;\n  line-height: .1em;\n  margin-right: .25em;\n  vertical-align: -.4em;\n}\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"index.css\">\n</head>\n\n<body style=\"height: 2000px\">\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n  <blockquote>\n    Teacher: Why are you late?\n    Student: There was a man who lost a hundred dollar bill.\n    Teacher: That's nice. Were you helping him look for it?\n    Student: No. I was standing on it.\n  </blockquote>\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n  <script>\n    function getCoords(elem) {\n      let box = elem.getBoundingClientRect();\n\n      return {\n        top: box.top + window.pageYOffset,\n        left: box.left + window.pageXOffset\n      };\n    }\n\n    function showNote(anchor, position, html) {\n\n      let note = document.createElement('div');\n      note.className = \"note\";\n      note.innerHTML = html;\n      document.body.append(note);\n\n      positionAt(anchor, position, note);\n    }\n\n    function positionAt(anchor, position, elem) {\n\n      let anchorCoords = getCoords(anchor);\n\n      switch (position) {\n        case \"top-out\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top - elem.offsetHeight + \"px\";\n          break;\n\n        case \"right-out\":\n          elem.style.left = anchorCoords.left + anchor.offsetWidth + \"px\";\n          elem.style.top = anchorCoords.top + \"px\";\n          break;\n\n        case \"bottom-out\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top + anchor.offsetHeight + \"px\";\n          break;\n\n        case \"top-in\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top + \"px\";\n          break;\n\n        case \"right-in\":\n          elem.style.width = '150px';\n          elem.style.left = anchorCoords.left + anchor.offsetWidth - elem.offsetWidth + \"px\";\n          elem.style.top = anchorCoords.top + \"px\";\n          break;\n\n        case \"bottom-in\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top + anchor.offsetHeight - elem.offsetHeight + \"px\";\n          break;\n      }\n\n    }\n\n\n    let blockquote = document.querySelector('blockquote');\n\n    showNote(blockquote, \"top-in\", \"note top-in\");\n    showNote(blockquote, \"top-out\", \"note top-out\");\n    showNote(blockquote, \"right-out\", \"note right-out\");\n    showNote(blockquote, \"bottom-in\", \"note bottom-in\");\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/4-position-inside-absolute/task.md",
    "content": "importance: 5\n\n---\n\n# Positionnez la note à l'intérieur (absolute)\n\nÉtendre l'exercice précédent <info:task/position-at-absolute> : enseigner la fonction `positionAt(anchor, position, elem)` pour inserer `elem` dans le `anchor`.\n\nNouvelles valeurs pour `position` :\n\n- `top-out`, `right-out`, `bottom-out` -- fonctionnent de la même manière qu'avant, ils insèrent le `elem` au-dessus/à droite/sous le `anchor`.\n- `top-in`, `right-in`, `bottom-in` -- insèrent `elem` à l'intérieur de `anchor` : collez-le sur le bord supérieur/droit/inférieur.\n\nPar exemple :\n\n```js\n// affiche la note au dessus de blockquote\npositionAt(blockquote, \"top-out\", note);\n\n// affiche la note à l'intérieur de blockquote, en haut\npositionAt(blockquote, \"top-in\", note);\n```\n\nLe resultat :\n\n[iframe src=\"solution\" height=\"310\" border=\"1\" link]\n\nEn tant que code source, prenez la solution de l'exercice <info:task/position-at-absolute>.\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/article.md",
    "content": "# Coordonnées\n\nPour déplacer des éléments, nous devons être familiers avec les coordonnées.\n\nLa plupart des méthodes JavaScript traitent de l'un des deux systèmes de coordonnées :\n\n1. **Par rapport à la fenêtre** - similaire à `position:fixed`, calculé à partir du bord supérieur/gauche de la fenêtre.\n    - nous désignerons ces coordonnées comme `clientX/clientY`, le raisonnement pour un tel nom deviendra clair plus tard, lorsque nous étudierons les propriétés de l'événement.\n2. **Par rapport au document** - similaire à `position:absolue` à la racine du document, calculé à partir du bord supérieur/gauche du document.\n    - nous les dénotons `pageX/pageY`.\n\nLorsque la page défile jusqu'au tout début, de sorte que le coin supérieur/gauche de la fenêtre soit exactement le coin supérieur/gauche du document, ces coordonnées sont égales. Mais après le déplacement du document, les coordonnées relatives à la fenêtre des éléments changent, à mesure que les éléments se déplacent à travers la fenêtre, tandis que les coordonnées relatives au document restent les mêmes.\n\nSur cette image, nous prenons un point dans le document et démontrons ses coordonnées avant le défilement (à gauche) et après (à droite) :\n\n![](document-and-window-coordinates-scrolled.svg)\n\nLorsque le document défile :\n- `pageY` - La coordonnée relative au document est restée la même, elle est comptée à partir du haut du document (maintenant défilée).\n- `clientY` - la coordonnée relative à la fenêtre a changé (la flèche est devenue plus courte), car le même point s'est rapproché du haut de la fenêtre.\n\n## Coordonnées des éléments : getBoundingClientRect\n\nLa méthode `elem.getBoundingClientRect()` renvoie les coordonnées de la fenêtre pour un rectangle minimal qui entoure `elem` en tant qu'objet de la classe intégrée [DOMRect](https://www.w3.org/TR/geometry-1/#domrect).\n\nPropriétés principales de `DOMRect` :\n\n- `x/y` -- Coordonnées X/Y de l'origine du rectangle par rapport à la fenêtre,\n- `width/height` -- largeur/hauteur du rectangle (peut être négatif).\n\nDe plus, il existe des propriétés dérivées :\n\n- `top/bottom` -- Coordonnée Y pour le bord supérieur/inférieur du rectangle,\n- `left/right` -- Coordonnée X pour le bord du rectangle gauche/droit.\n\n```online\nPar exemple, cliquez sur ce bouton pour voir les coordonnées de sa fenêtre :\n\n<p><input id=\"brTest\" type=\"button\" style=\"max-width: 90vw;\" value=\"Get coordinates using button.getBoundingClientRect() for this button\" onclick='showRect(this)'/></p>\n\n<script>\nfunction showRect(elem) {\n  let r = elem.getBoundingClientRect();\n  alert(`x:${r.x}\ny:${r.y}\nwidth:${r.width}\nheight:${r.height}\ntop:${r.top}\nbottom:${r.bottom}\nleft:${r.left}\nright:${r.right}\n`);\n}\n</script>\n\nSi vous faites défiler la page et répétez, vous remarquerez que lorsque la position du bouton relatif à la fenêtre change, ses coordonnées de fenêtre (`y/top/bottom` si vous faites défiler verticalement) changent également.\n```\n\nVoici l'image de la sortie de `elem.getBoundingClientRect()` :\n\n![](coordinates.svg)\n\nComme vous pouvez le voir, `x/y` et `width/height` décrivent entièrement le rectangle. Les propriétés dérivées peuvent être facilement calculées à partir d'eux :\n\n- `left = x`\n- `top = y`\n- `right = x + width`\n- `bottom = y + height`\n\nVeuillez noter :\n\n- Les coordonnées peuvent être des fractions décimales, telles que `10.5`. C'est normal, le navigateur utilise en interne des fractions dans les calculs. Nous n'avons pas à les arrondir lors de la définition de `style.left/top`.\n- Les coordonnées peuvent être négatives. Par exemple, si la page défile de sorte que `elem` se trouve maintenant au-dessus de la fenêtre, alors `elem.getBoundingClientRect().top` est négatif.\n\n```smart header=\"Pourquoi des propriétés dérivées sont nécessaires ? Pourquoi `top/left` existent-ils s'il y a `x/y` ?\"\nMathématiquement, un rectangle est défini de façon unique avec son point de départ `(x,y)` et le vecteur de direction `(width,height)`. Les propriétés dérivées supplémentaires sont donc pour plus de commodité.\n\nTechniquement, il est possible que `width/height` soit négatif, ce qui permet un rectangle \"dirigé\", par exemple pour représenter la sélection de la souris avec un début et une fin correctement marqués.\n\nLes valeurs négatives de `width/height` signifient que le rectangle commence à son coin inférieur droit puis \"grandit\" de gauche à droite.\n\nVoici un rectangle avec une `width` et `height` (par exemple `width=-200`, `height=-100`) :\n\n![](coordinates-negative.svg)\n\nComme vous pouvez le voir, `left/top` n'est pas égal à `x/y` dans ce cas.\n\nEn pratique cependant, `elem.getBoundingClientRect()` retourne toujours une largeur/hauteur positive, ici nous mentionnons une largeur/hauteur négative uniquement pour que vous compreniez pourquoi ces propriétés apparemment en double ne sont pas en fait des doublons.\n```\n\n```warn header=\"Internet Explorer : pas de support pour `x/y`\"\nInternet Explorer ne prend pas en charge les propriétés `x/y` pour des raisons historiques.\n\nNous pouvons donc soit faire un polyfill (ajouter des getters dans `DomRect.prototype`), soit simplement utiliser `top/left`, car ils sont toujours les mêmes que `x/y` pour un \n`width/height` positif, en particulier dans le résultat de `elem.getBoundingClientRect()`.\n```\n\n```warn header=\"Les coordonnées droite / inférieure sont différentes des propriétés de position CSS\"\nIl existe des similitudes évidentes entre les coordonnées relatives à la fenêtre et CSS `position:fixed`.\n\nMais dans le positionnement CSS, la propriété `right` signifie la distance par rapport au bord droit, et la propriété `bottom` signifie la distance par rapport au bord inférieur.\n\nSi nous regardons simplement l'image ci-dessus, nous pouvons voir qu'en JavaScript, ce n'est pas le cas. Toutes les coordonnées de la fenêtre sont comptées à partir du coin supérieur gauche, y compris celles-ci.\n```\n\n## elementFromPoint(x, y) [#elementFromPoint]\n\nL'appel à `document.elementFromPoint(x,y)` renvoie l'élément le plus imbriqué aux coordonnées de la fenêtre `(x,y)`.\n\nLa syntaxe est :\n\n```js\nlet elem = document.elementFromPoint(x, y);\n```\n\nPar exemple, le code ci-dessous met en évidence et génère la balise de l'élément qui se trouve maintenant au milieu de la fenêtre :\n\n```js run\nlet centerX = document.documentElement.clientWidth / 2;\nlet centerY = document.documentElement.clientHeight / 2;\n\nlet elem = document.elementFromPoint(centerX, centerY);\n\nelem.style.background = \"red\";\nalert(elem.tagName);\n```\n\nComme il utilise les coordonnées de la fenêtre, l'élément peut être différent selon la position de défilement actuelle.\n\n````warn header=\"Pour les coordonnées hors fenêtre, `elementFromPoint` renvoie `null`\"\nLa méthode `document.elementFromPoint(x,y)` ne fonctionne que si `(x,y)` se trouve à l'intérieur de la zone visible.\n\nSi l'une des coordonnées est négative ou dépasse la largeur/hauteur de la fenêtre, elle renvoie alors `null`.\n\nVoici une erreur typique qui peut se produire si nous ne la vérifions pas :\n\n```js\nlet elem = document.elementFromPoint(x, y);\n// si les coordonnées se trouvent hors de la fenêtre, alors elem = null\n*!*\nelem.style.background = ''; // Error!\n*/!*\n```\n````\n\n## Utilisation pour un positionnement \"fixe\"\n\nLa plupart du temps, nous avons besoin de coordonnées pour positionner quelque chose.\n\nPour afficher quelque chose près d'un élément, nous pouvons utiliser `getBoundingClientRect` pour obtenir ses coordonnées, puis `position` CSS avec `left/top` (ou `right/bottom`).\n\nPar exemple, la fonction `createMessageUnder(elem, html)` ci-dessous affiche le message sous `elem` :\n\n```js\nlet elem = document.getElementById(\"coords-show-mark\");\n\nfunction createMessageUnder(elem, html) {\n  // créer un élément de message\n  let message = document.createElement('div');\n  // mieux utiliser une classe css pour le style ici\n  message.style.cssText = \"position:fixed; color: red\";\n\n*!*\n  // attribuez des coordonnées, n'oubliez pas \"px\" !\n  let coords = elem.getBoundingClientRect();\n\n  message.style.left = coords.left + \"px\";\n  message.style.top = coords.bottom + \"px\";\n*/!*\n\n  message.innerHTML = html;\n\n  return message;\n}\n\n// Utilisation :\n// l'ajouter pendant 5 secondes dans le document\nlet message = createMessageUnder(elem, 'Hello, world!');\ndocument.body.append(message);\nsetTimeout(() => message.remove(), 5000);\n```\n\n```online\nCliquez sur le bouton pour l'exécuter :\n\n<button id=\"coords-show-mark\">Button with id=\"coords-show-mark\", the message will appear under it</button>\n```\n\nLe code peut être modifié pour afficher le message à gauche, à droite, en dessous, appliquer des animations CSS pour un \"fondu\" et ainsi de suite. C'est facile, car nous avons toutes les coordonnées et tailles de l'élément.\n\nMais notez le détail important : lorsque la page défile, le message s'éloigne du bouton.\n\nLa raison en est évidente: l'élément de message repose sur `position:fixed`, il reste donc au même endroit de la fenêtre pendant que la page défile.\n\nPour changer cela, nous devons utiliser des coordonnées basées sur des documents et `position:absolute`.\n\n## Coordonnées du document [#getCoords]\n\nLes coordonnées relatives au document commencent dans le coin supérieur gauche du document, pas dans la fenêtre.\n\nEn CSS, les coordonnées de la fenêtre correspondent à `position:fixed`, tandis que les coordonnées du document sont similaires à `position:absolue` en haut.\n\nNous pouvons utiliser `position:absolu` et `top/left` pour placer quelque chose à un certain endroit du document, afin qu'il y reste pendant un défilement de page. Mais nous avons d'abord besoin des bonnes coordonnées.\n\nIl n'y a pas de méthode standard pour obtenir les coordonnées du document d'un élément. Mais c'est facile de l'écrire.\n\nLes deux systèmes de coordonnées sont reliés par la formule :\n- `pageY` = `clientY` + hauteur de la partie verticale déroulée du document.\n- `pageX` = `clientX` + largeur de la partie horizontale déroulée du document.\n\nLa fonction `getCoords(elem)` prendra les coordonnées de la fenêtre de `elem.getBoundingClientRect()` et leur ajouter le défilement actuel :\n\n```js\n// obtenir les coordonnées du document de l'élément\nfunction getCoords(elem) {\n  let box = elem.getBoundingClientRect();\n\n  return {\n    top: box.top + window.pageYOffset,\n    right: box.right + window.pageXOffset,\n    bottom: box.bottom + window.pageYOffset,\n    left: box.left + window.pageXOffset\n  };\n}\n```\n\nSi dans l'exemple ci-dessus, nous l'avons utilisé avec `position:absolue`, le message resterait près de l'élément en défilement.\n\nLa fonction `createMessageUnder` modifiée :\n\n```js\nfunction createMessageUnder(elem, html) {\n  let message = document.createElement('div');\n  message.style.cssText = \"*!*position:absolute*/!*; color: red\";\n\n  let coords = *!*getCoords(elem);*/!*\n\n  message.style.left = coords.left + \"px\";\n  message.style.top = coords.bottom + \"px\";\n\n  message.innerHTML = html;\n\n  return message;\n}\n```\n\n## Résumé\n\nN'importe quel point de la page a des coordonnées :\n\n1. Par rapport à la fenêtre -- `elem.getBoundingClientRect()`.\n2. Par rapport au document -- `elem.getBoundingClientRect()` plus le défilement de la page actuelle.\n\nLes coordonnées de la fenêtre sont excellentes à utiliser avec `position:fixed`, et les coordonnées du document fonctionnent bien avec `position:absolue`.\n\nLes deux systèmes de coordonnées ont leurs avantages et leurs inconvénients; il y a des moments où nous avons besoin de l'un ou de l'autre, tout comme `position` CSS `absolute` et `fixed`.\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/head.html",
    "content": "<script>\ndocument.addEventListener('DOMContentLoaded', function() {\n\n  let elem = document.getElementById('coords-show-mark');\n\n  // no elem in ebook (pdf/epub) mode\n  if (elem) {\n    elem.onclick = function() {\n\n      function createMessageUnder(elem, text) {\n        let coords = elem.getBoundingClientRect();\n        let message = document.createElement('div');\n        message.style.cssText = \"position:fixed; color: red\";\n\n        message.style.left = coords.left + \"px\";\n        message.style.top = coords.bottom + \"px\";\n\n        message.innerHTML = text;\n\n        return message;\n      }\n\n      let message = createMessageUnder(elem, 'Hello, world!');\n      document.body.append(message);\n      setTimeout(() => message.remove(), 5000);\n    }\n  }\n\n});\n\n</script>\n"
  },
  {
    "path": "2-ui/1-document/index.md",
    "content": "# Document\n\nHere we'll learn to manipulate a web-page using JavaScript.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/01-hide-other/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/01-hide-other/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <input type=\"button\" id=\"hider\" value=\"Click to hide the text\" />\n\n  <div id=\"text\">Text</div>\n\n  <script>\n    // Here it doesn't matter how we hide the text,\n    // could also use style.display:\n    document.getElementById('hider').onclick = function() {\n      document.getElementById('text').hidden = true;\n    }\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/01-hide-other/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <input type=\"button\" id=\"hider\" value=\"Click to hide the text\" />\n\n  <div id=\"text\">Text</div>\n\n  <script>\n    /* your code */\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/01-hide-other/task.md",
    "content": "importance: 5\n\n---\n\n# Hide on click\n\nAdd JavaScript to the `button` to make `<div id=\"text\">` disappear when we click it.\n\nThe demo:\n\n[iframe border=1 src=\"solution\" height=80]\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/solution.md",
    "content": "Can use `this` in the handler to reference \"the element itself\" here:\n\n```html run height=50\n<input type=\"button\" onclick=\"this.hidden=true\" value=\"Click to hide\">\n```\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/task.md",
    "content": "importance: 5\n\n---\n\n# Hide self\n\nCreate a button that hides itself on click.\n\n```online\nLike this:\n<input type=\"button\" onclick=\"this.hidden=true\" value=\"Click to hide\">\n```\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md",
    "content": "The answer: `1` and `2`.\n\nThe first handler triggers, because it's not removed by `removeEventListener`. To remove the handler we need to pass exactly the function that was assigned. And in the code a new function is passed, that looks the same, but is still another function.\n\nTo remove a function object, we need to store a reference to it, like this:\n\n```js\nfunction handler() {\n  alert(1);\n}\n\nbutton.addEventListener(\"click\", handler);\nbutton.removeEventListener(\"click\", handler);\n```\n\nThe handler `button.onclick` works independently and in addition to `addEventListener`.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/task.md",
    "content": "importance: 5\n\n---\n\n# Which handlers run?\n\nThere's a button in the variable. There are no handlers on it.\n\nWhich handlers run on click after the following code? Which alerts show up?\n\n```js no-beautify\nbutton.addEventListener(\"click\", () => alert(\"1\"));\n\nbutton.removeEventListener(\"click\", () => alert(\"1\"));\n\nbutton.onclick = () => alert(2);\n```\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md",
    "content": "\nFirst we need to choose a method of positioning the ball.\n\nWe can't use `position:fixed` for it, because scrolling the page would move the ball from the field.\n\nSo we should use `position:absolute` and, to make the positioning really solid, make `field` itself positioned.\n\nThen the ball will be positioned relatively to the field:\n\n```css\n#field {\n  width: 200px;\n  height: 150px;\n  position: relative;\n}\n\n#ball {\n  position: absolute;\n  left: 0; /* relative to the closest positioned ancestor (field) */\n  top: 0;\n  transition: 1s all; /* CSS animation for left/top makes the ball fly */\n}\n```\n\nNext we need to assign the correct `ball.style.left/top`. They contain field-relative coordinates now.\n\nHere's the picture:\n\n![](move-ball-coords.svg)\n\nWe have `event.clientX/clientY` -- window-relative coordinates of the click.\n\nTo get field-relative `left` coordinate of the click, we can substract the field left edge and the border width:\n\n```js\nlet left = event.clientX - fieldCoords.left - field.clientLeft;\n```\n\nNormally, `ball.style.left` means the \"left edge of the element\" (the ball). So if we assign that `left`, then the ball edge, not center, would be under the mouse cursor.\n\nWe need to move the ball half-width left and half-height up to make it center.\n\nSo the final `left` would be:\n\n```js\nlet left = event.clientX - fieldCoords.left - field.clientLeft - ball.offsetWidth/2;\n```\n\nThe vertical coordinate is calculated using the same logic.\n\nPlease note that the ball width/height must be known at the time we access `ball.offsetWidth`. Should be specified in HTML or CSS.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    #field {\n      width: 200px;\n      height: 150px;\n      border: 10px solid black;\n      background-color: #00FF00;\n      position: relative;\n      overflow: hidden;\n      cursor: pointer;\n    }\n\n    #ball {\n      position: absolute;\n      left: 0;\n      top: 0;\n      width: 40px;\n      height: 40px;\n      transition: all 1s;\n    }\n  </style>\n</head>\n\n<body style=\"height:2000px\">\n\n  Click on a field to move the ball there.\n  <br>\n\n\n  <div id=\"field\">\n    <img src=\"https://en.js.cx/clipart/ball.svg\" id=\"ball\"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n  </div>\n\n  <script>\n    field.onclick = function(event) {\n\n      // window-relative field coordinates\n      let fieldCoords = this.getBoundingClientRect();\n\n      // the ball has position:absolute, the field: position:relative\n      // so ball coordinates are relative to the field inner left-upper corner\n      let ballCoords = {\n        top: event.clientY - fieldCoords.top - field.clientTop - ball.clientHeight / 2,\n        left: event.clientX - fieldCoords.left - field.clientLeft - ball.clientWidth / 2\n      };\n\n      // prevent crossing the top field boundary\n      if (ballCoords.top < 0) ballCoords.top = 0;\n\n      // prevent crossing the left field boundary\n      if (ballCoords.left < 0) ballCoords.left = 0;\n\n\n      // // prevent crossing the right field boundary\n      if (ballCoords.left + ball.clientWidth > field.clientWidth) {\n        ballCoords.left = field.clientWidth - ball.clientWidth;\n      }\n\n      // prevent crossing the bottom field boundary\n      if (ballCoords.top + ball.clientHeight > field.clientHeight) {\n        ballCoords.top = field.clientHeight - ball.clientHeight;\n      }\n\n      ball.style.left = ballCoords.left + 'px';\n      ball.style.top = ballCoords.top + 'px';\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/04-move-ball-field/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    #field {\n      width: 200px;\n      height: 150px;\n      border: 10px solid black;\n      background-color: #00FF00;\n      overflow: hidden;\n    }\n  </style>\n</head>\n\n<body style=\"height:2000px\">\n\n  Click on a field to move the ball there.\n  <br> The ball should never leave the field.\n\n\n  <div id=\"field\">\n    <img src=\"https://en.js.cx/clipart/ball.svg\" id=\"ball\"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n  </div>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/04-move-ball-field/task.md",
    "content": "importance: 5\n\n---\n\n# Move the ball across the field\n\nMove the ball across the field to a click. Like this:\n\n[iframe src=\"solution\" height=\"260\" link]\n\nRequirements:\n\n- The ball center should come exactly under the pointer on click (if possible without crossing the field edge).\n- CSS-animation is welcome.\n- The ball must not cross field boundaries.\n- When the page is scrolled, nothing should break.\n\nNotes:\n\n- The code should also work with different ball and field sizes, not be bound to any fixed values.\n- Use properties `event.clientX/event.clientY` for click coordinates.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.md",
    "content": "\n# HTML/CSS\nFirst let's create HTML/CSS.\n\nA menu is a standalone graphical component on the page, so it's better to put it into a single DOM element.\n\nA list of menu items can be laid out as a list `ul/li`.\n\nHere's the example structure:\n\n```html\n<div class=\"menu\">\n  <span class=\"title\">Sweeties (click me)!</span>\n  <ul>\n    <li>Cake</li>\n    <li>Donut</li>\n    <li>Honey</li>\n  </ul>\n</div>\n```\n\nWe use `<span>` for the title, because `<div>` has an implicit `display:block` on it, and it will occupy 100% of the horizontal width.\n\nLike this:\n\n```html autorun height=50\n<div style=\"border: solid red 1px\" onclick=\"alert(1)\">Sweeties (click me)!</div>\n```\n\nSo if we set `onclick` on it, then it will catch clicks to the right of the text.\n\nAs `<span>` has an implicit `display: inline`, it occupies exactly enough place to fit all the text:\n\n```html autorun height=50\n<span style=\"border: solid red 1px\" onclick=\"alert(1)\">Sweeties (click me)!</span>\n```\n\n# Toggling the menu\n\nToggling the menu should change the arrow and show/hide the menu list.\n\nAll these changes are perfectly handled by CSS. In JavaScript we should label the current state of the menu by adding/removing the class `.open`.\n\nWithout it, the menu will be closed:\n\n```css\n.menu ul {\n  margin: 0;\n  list-style: none;\n  padding-left: 20px;\n  display: none;\n}\n\n.menu .title::before {\n  content: '▶ ';\n  font-size: 80%;\n  color: green;\n}\n```\n\n...And with `.open` the arrow changes and the list shows up:\n\n```css\n.menu.open .title::before {\n  content: '▼ ';\n}\n\n.menu.open ul {\n  display: block;\n}\n```\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    .menu ul {\n      margin: 0;\n      list-style: none;\n      padding-left: 20px;\n      display: none;\n    }\n\n    .menu .title {\n      font-size: 18px;\n      cursor: pointer;\n    }\n\n    .menu .title::before {\n      content: '▶ ';\n      font-size: 80%;\n      color: green;\n    }\n\n    .menu.open .title::before {\n      content: '▼ ';\n    }\n\n    .menu.open ul {\n      display: block;\n    }\n  </style>\n</head>\n\n<body>\n\n  <div id=\"sweeties\" class=\"menu\">\n    <span class=\"title\">Sweeties (click me)!</span>\n    <ul>\n      <li>Cake</li>\n      <li>Donut</li>\n      <li>Honey</li>\n    </ul>\n\n  </div>\n\n  <script>\n    let menuElem = document.getElementById('sweeties');\n    let titleElem = menuElem.querySelector('.title');\n\n    titleElem.onclick = function() {\n      menuElem.classList.toggle('open');\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/05-sliding-menu/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n\n  ▶ ▼ Sweeties (click me)!\n  <ul>\n    <li>Cake</li>\n    <li>Donut</li>\n    <li>Honey</li>\n  </ul>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/05-sliding-menu/task.md",
    "content": "importance: 5\n\n---\n\n# Create a sliding menu\n\nCreate a menu that opens/collapses on click:\n\n[iframe border=1 height=100 src=\"solution\"]\n\nP.S. HTML/CSS of the source document is to be modified.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.md",
    "content": "\nTo add the button we can use either `position:absolute` (and make the pane `position:relative`) or `float:right`. The `float:right` has the benefit that the button never overlaps the text, but `position:absolute` gives more freedom. So the choice is yours.\n\nThen for each pane the code can be like:\n```js\npane.insertAdjacentHTML(\"afterbegin\", '<button class=\"remove-button\">[x]</button>');\n```\n\nThen the `<button>` becomes `pane.firstChild`, so we can add a handler to it like this:\n\n```js\npane.firstChild.onclick = () => pane.remove();\n```\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <link rel=\"stylesheet\" href=\"messages.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <div>\n    <div class=\"pane\">\n      <h3>Horse</h3>\n      <p>The horse is one of two extant subspecies of Equus ferus. It is an odd-toed ungulate mammal belonging to the taxonomic family Equidae. The horse has evolved over the past 45 to 55 million years from a small multi-toed creature, Eohippus, into the large, single-toed animal of today.</p>\n    </div>\n    <div class=\"pane\">\n      <h3>Donkey</h3>\n      <p>The donkey or ass (Equus africanus asinus) is a domesticated member of the horse family, Equidae. The wild ancestor of the donkey is the African wild ass, E. africanus. The donkey has been used as a working animal for at least 5000 years.</p>\n    </div>\n    <div class=\"pane\">\n      <h3>Cat</h3>\n      <p>The domestic cat (Latin: Felis catus) is a small, typically furry, carnivorous mammal. They are often called house cats when kept as indoor pets or simply cats when there is no need to distinguish them from other felids and felines. Cats are often valued by humans for companionship and for their ability to hunt vermin.\n      </p>\n    </div>\n  </div>\n\n\n  <script>\n    let panes = document.querySelectorAll('.pane');\n\n    for(let pane of panes) {\n      pane.insertAdjacentHTML(\"afterbegin\", '<button class=\"remove-button\">[x]</button>');\n      // button becomes the first child of pane\n      pane.firstChild.onclick = () => pane.remove();\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.view/messages.css",
    "content": "body {\n  margin: 10px auto;\n  width: 470px;\n}\n\nh3 {\n  margin: 0;\n  padding-bottom: .3em;\n  padding-right: 20px;\n  font-size: 1.1em;\n}\n\np {\n  margin: 0;\n  padding: 0 0 .5em;\n}\n\n.pane {\n  background: #edf5e1;\n  padding: 10px 20px 10px;\n  border-top: solid 2px #c4df9b;\n  position: relative;\n}\n\n.remove-button {\n  position: absolute;\n  font-size: 110%;\n  top: 0;\n  color: darkred;\n  right: 10px;\n  display: block;\n  width: 24px;\n  height: 24px;\n  border: none;\n  background: transparent;\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/06-hide-message/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"messages.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  The button code (may need to adjust CSS):\n  <button class=\"remove-button\">[x]</button>\n\n  <div>\n    <div class=\"pane\">\n      <h3>Horse</h3>\n      <p>The horse is one of two extant subspecies of Equus ferus. It is an odd-toed ungulate mammal belonging to the taxonomic family Equidae. The horse has evolved over the past 45 to 55 million years from a small multi-toed creature, Eohippus, into the large, single-toed animal of today.</p>\n    </div>\n    <div class=\"pane\">\n      <h3>Donkey</h3>\n      <p>The donkey or ass (Equus africanus asinus) is a domesticated member of the horse family, Equidae. The wild ancestor of the donkey is the African wild ass, E. africanus. The donkey has been used as a working animal for at least 5000 years.</p>\n    </div>\n    <div class=\"pane\">\n      <h3>Cat</h3>\n      <p>The domestic cat (Latin: Felis catus) is a small, typically furry, carnivorous mammal. They are often called house cats when kept as indoor pets or simply cats when there is no need to distinguish them from other felids and felines. Cats are often valued by humans for companionship and for their ability to hunt vermin.\n      </p>\n    </div>\n  </div>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/06-hide-message/source.view/messages.css",
    "content": "body {\n  margin: 10px auto;\n  width: 470px;\n}\n\nh3 {\n  margin: 0;\n  padding-bottom: .3em;\n  font-size: 1.1em;\n}\n\np {\n  margin: 0;\n  padding: 0 0 .5em;\n}\n\n.pane {\n  background: #edf5e1;\n  padding: 10px 20px 10px;\n  border-top: solid 2px #c4df9b;\n}\n\n.remove-button {\n  font-size: 110%;\n  color: darkred;\n  right: 10px;\n  width: 24px;\n  height: 24px;\n  border: none;\n  background: transparent;\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/06-hide-message/task.md",
    "content": "importance: 5\n\n---\n\n# Add a closing button\n\nThere's a list of messages.\n\nUse JavaScript to add a closing button to the right-upper corner of each message.\n\nThe result should look like this:\n\n[iframe src=\"solution\" height=450]\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/07-carousel/solution.md",
    "content": "The images ribbon can be represented as `ul/li` list of images `<img>`.\n\nNormally, such a ribbon is wide, but we put a fixed-size `<div>` around to \"cut\" it, so that only a part of the ribbon is visible:\n\n![](carousel1.svg)\n\nTo make the list show horizontally we need to apply correct CSS properties for `<li>`, like `display: inline-block`.\n\nFor `<img>` we should also adjust `display`, because by default it's `inline`. There's extra space reserved under `inline` elements for \"letter tails\", so we can use `display:block` to remove it.\n\nTo do the scrolling, we can shift `<ul>`. There are many ways to do it, for instance by changing `margin-left` or (better performance) use `transform: translateX()`:\n\n![](carousel2.svg)\n\nThe outer `<div>` has a fixed width, so \"extra\" images are cut.\n\nThe whole carousel is a self-contained \"graphical component\" on the page, so we'd better wrap it into a single `<div class=\"carousel\">` and style things inside it.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/index.html",
    "content": "<!DOCTYPE html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"carousel\" class=\"carousel\">\n    <button class=\"arrow prev\">⇦</button>\n    <div class=\"gallery\">\n      <ul>\n        <li><img src=\"https://en.js.cx/carousel/1.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/2.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/3.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/4.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/5.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/6.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/7.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/8.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/9.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/10.png\"></li>\n      </ul>\n    </div>\n    <button class=\"arrow next\">⇨</button>\n  </div>\n\n  <script>\n    /* label the images, just for convenience, to visually track them */\n    let i = 1;\n    for(let li of carousel.querySelectorAll('li')) {\n      li.style.position = 'relative';\n      li.insertAdjacentHTML('beforeend', `<span style=\"position:absolute;left:0;top:0\">${i}</span>`);\n      i++;\n    }\n\n    /* configuration */\n    let width = 130; // image width\n    let count = 3; // visible images count\n\n    let list = carousel.querySelector('ul');\n    let listElems = carousel.querySelectorAll('li');\n\n    let position = 0; // ribbon scroll position\n\n    carousel.querySelector('.prev').onclick = function() {\n      // shift left\n      position += width * count;\n      // can't move to the left too much, end of images\n      position = Math.min(position, 0)\n      list.style.marginLeft = position + 'px';\n    };\n\n    carousel.querySelector('.next').onclick = function() {\n      // shift right\n      position -= width * count;\n      // can only shift the ribbbon for (total ribbon length - visible count) images\n      position = Math.max(position, -width * (listElems.length - count));\n      list.style.marginLeft = position + 'px';\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/style.css",
    "content": "body {\n  padding: 10px;\n}\n\n.carousel {\n  position: relative;\n  width: 398px;\n  padding: 10px 40px;\n  border: 1px solid #CCC;\n  border-radius: 15px;\n  background: #eee;\n}\n\n.carousel img {\n  width: 130px;\n  height: 130px;\n  /* make it block to remove space around images */\n  display: block;\n}\n\n.arrow {\n  position: absolute;\n  top: 60px;\n  padding: 0;\n  background: #ddd;\n  border-radius: 15px;\n  border: 1px solid gray;\n  font-size: 24px;\n  line-height: 24px;\n  color: #444;\n  display: block;\n}\n\n.arrow:focus {\n  outline: none;\n}\n\n.arrow:hover {\n  background: #ccc;\n  cursor: pointer;\n}\n\n.prev {\n  left: 7px;\n}\n\n.next {\n  right: 7px;\n}\n\n.gallery {\n  width: 390px;\n  overflow: hidden;\n}\n\n.gallery ul {\n  height: 130px;\n  width: 9999px;\n  margin: 0;\n  padding: 0;\n  list-style: none;\n  transition: margin-left 250ms;\n  /* remove white-space between inline-block'ed li */\n  /* http://davidwalsh.name/remove-whitespace-inline-block */\n  font-size: 0;\n}\n\n.gallery li {\n  display: inline-block;\n}\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/07-carousel/source.view/index.html",
    "content": "<!DOCTYPE html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <!-- create your markup and styles -->\n\n  <button class=\"arrow\">⇦</button>\n  <button class=\"arrow\">⇨</button>\n\n\n  <ul>\n    <li><img src=\"https://en.js.cx/carousel/1.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/2.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/3.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/4.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/5.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/6.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/7.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/8.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/9.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/10.png\"></li>\n  </ul>\n\n\n  <script>\n    // label the images to visually track them, just for convenience,\n    // this code can be removed\n    let i = 1;\n    for(let li of carousel.querySelectorAll('li')) {\n      li.style.position = 'relative';\n      li.insertAdjacentHTML('beforeend', `<span style=\"position:absolute;left:0;top:0\">${i}</span>`);\n      i++;\n    }\n\n    // ...your code to make carousel alive!\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/07-carousel/source.view/style.css",
    "content": ".arrow {\n  padding: 0;\n  background: #ddd;\n  border-radius: 15px;\n  border: 1px solid gray;\n  font-size: 24px;\n  line-height: 24px;\n  color: #444;\n  display: block;\n}\n\n.arrow:focus {\n  outline: none;\n}\n\n.arrow:hover {\n  background: #ccc;\n  cursor: pointer;\n}\n\nul {\n  height: 130px;\n  width: 9999px;\n  margin: 0;\n  padding: 0;\n  list-style: none;\n  font-size: 0; \n}\n\nul img {\n  width: 130px;\n  height: 130px;\n  display: block; /* removes extra space near images */\n}\n\nul li {\n  display: inline-block; /* removes extra space between list items\n}\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/07-carousel/task.md",
    "content": "importance: 4\n\n---\n\n# Carousel\n\nCreate a \"carousel\" -- a ribbon of images that can be scrolled by clicking on arrows.\n\n[iframe height=200 src=\"solution\"]\n\nLater we can add more features to it: infinite scrolling, dynamic loading etc.\n\nP.S. For this task HTML/CSS structure is actually 90% of the solution.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/article.md",
    "content": "# Introduction to browser events\n\n*An event* is a signal that something has happened. All DOM nodes generate such signals (but events are not limited to DOM).\n\nHere's a list of the most useful DOM events, just to take a look at:\n\n**Mouse events:**\n- `click` -- when the mouse clicks on an element (touchscreen devices generate it on a tap).\n- `contextmenu` -- when the mouse right-clicks on an element.\n- `mouseover` / `mouseout` -- when the mouse cursor comes over / leaves an element.\n- `mousedown` / `mouseup` -- when the mouse button is pressed / released over an element.\n- `mousemove` -- when the mouse is moved.\n\n**Keyboard events:**\n- `keydown` and `keyup` -- when a keyboard key is pressed and released.\n\n**Form element events:**\n- `submit` -- when the visitor submits a `<form>`.\n- `focus` --  when the visitor focuses on an element, e.g. on an `<input>`.\n\n**Document events:**\n- `DOMContentLoaded` -- when the HTML is loaded and processed, DOM is fully built.\n\n**CSS events:**\n- `transitionend` -- when a CSS-animation finishes.\n\nThere are many other events. We'll get into more details of particular events in upcoming chapters.\n\n## Event handlers\n\nTo react on events we can assign a *handler* -- a function that runs in case of an event.\n\nHandlers are a way to run JavaScript code in case of user actions.\n\nThere are several ways to assign a handler. Let's see them, starting from the simplest one.\n\n### HTML-attribute\n\nA handler can be set in HTML with an attribute named `on<event>`.\n\nFor instance, to assign a `click` handler for an `input`, we can use `onclick`, like here:\n\n```html run\n<input value=\"Click me\" *!*onclick=\"alert('Click!')\"*/!* type=\"button\">\n```\n\nOn mouse click, the code inside `onclick` runs.\n\nPlease note that inside `onclick` we use single quotes, because the attribute itself is in double quotes. If we forget that the code is inside the attribute and use double quotes inside, like this:  `onclick=\"alert(\"Click!\")\"`, then it won't work right.\n\nAn HTML-attribute is not a convenient place to write a lot of code, so we'd better create a JavaScript function and call it there.\n\nHere a click runs the function `countRabbits()`:\n\n```html autorun height=50\n<script>\n  function countRabbits() {\n    for(let i=1; i<=3; i++) {\n      alert(\"Rabbit number \" + i);\n    }\n  }\n</script>\n\n<input type=\"button\" *!*onclick=\"countRabbits()\"*/!* value=\"Count rabbits!\">\n```\n\nAs we know, HTML attribute names are not case-sensitive, so `ONCLICK` works as well as `onClick` and `onCLICK`... But usually attributes are lowercased: `onclick`.\n\n### DOM property\n\nWe can assign a handler using a DOM property `on<event>`.\n\nFor instance, `elem.onclick`:\n\n```html autorun\n<input id=\"elem\" type=\"button\" value=\"Click me\">\n<script>\n*!*\n  elem.onclick = function() {\n    alert('Thank you');\n  };\n*/!*\n</script>\n```\n\nIf the handler is assigned using an HTML-attribute then the browser reads it, creates a new function from the attribute content and writes it to the DOM property.\n\nSo this way is actually the same as the previous one.\n\nThese two code pieces work the same:\n\n1. Only HTML:\n\n    ```html autorun height=50\n    <input type=\"button\" *!*onclick=\"alert('Click!')\"*/!* value=\"Button\">\n    ```\n2. HTML + JS:\n\n    ```html autorun height=50\n    <input type=\"button\" id=\"button\" value=\"Button\">\n    <script>\n    *!*\n      button.onclick = function() {\n        alert('Click!');\n      };\n    */!*\n    </script>\n    ```\n\nIn the first example, the HTML attribute is used to initialize the `button.onclick`, while in the second example -- the script, that's all the difference.\n\n**As there's only one `onclick` property, we can't assign more than one event handler.**\n\nIn the example below adding a handler with JavaScript overwrites the existing handler:\n\n```html run height=50 autorun\n<input type=\"button\" id=\"elem\" onclick=\"alert('Before')\" value=\"Click me\">\n<script>\n*!*\n  elem.onclick = function() { // overwrites the existing handler\n    alert('After'); // only this will be shown\n  };\n*/!*\n</script>\n```\n\nTo remove a handler -- assign `elem.onclick = null`.\n\n## Accessing the element: this\n\nThe value of `this` inside a handler is the element. The one which has the handler on it.\n\nIn the code below `button` shows its contents using `this.innerHTML`:\n\n```html height=50 autorun\n<button onclick=\"alert(this.innerHTML)\">Click me</button>\n```\n\n## Possible mistakes\n\nIf you're starting to work with events -- please note some subtleties.\n\nWe can set an existing function as a handler:\n\n```js\nfunction sayThanks() {\n  alert('Thanks!');\n}\n\nelem.onclick = sayThanks;\n```\n\nBut be careful: the function should be assigned as `sayThanks`, not `sayThanks()`.\n\n```js\n// right\nbutton.onclick = sayThanks;\n\n// wrong\nbutton.onclick = sayThanks();\n```\n\nIf we add parentheses, then `sayThanks()` becomes a function call. So the last line actually takes the *result* of the function execution, that is `undefined` (as the function returns nothing), and assigns it to `onclick`. That doesn't work.\n\n...On the other hand, in the markup we do need the parentheses:\n\n```html\n<input type=\"button\" id=\"button\" onclick=\"sayThanks()\">\n```\n\nThe difference is easy to explain. When the browser reads the attribute, it creates a handler function with body from the attribute content.\n\nSo the markup generates this property:\n```js\nbutton.onclick = function() {\n*!*\n  sayThanks(); // <-- the attribute content goes here\n*/!*\n};\n```\n\n**Don't use `setAttribute` for handlers.**\n\nSuch a call won't work:\n\n```js run no-beautify\n// a click on <body> will generate errors,\n// because attributes are always strings, function becomes a string\ndocument.body.setAttribute('onclick', function() { alert(1) });\n```\n\n**DOM-property case matters.**\n\nAssign a handler to `elem.onclick`, not `elem.ONCLICK`, because DOM properties are case-sensitive.\n\n## addEventListener\n\nThe fundamental problem of the aforementioned ways to assign handlers is that we *can't assign multiple handlers to one event*.\n\nLet's say, one part of our code wants to highlight a button on click, and another one wants to show a message on the same click.\n\nWe'd like to assign two event handlers for that. But a new DOM property will overwrite the existing one:\n\n```js no-beautify\ninput.onclick = function() { alert(1); }\n// ...\ninput.onclick = function() { alert(2); } // replaces the previous handler\n```\n\nDevelopers of web standards understood that long ago and suggested an alternative way of managing handlers using the special methods `addEventListener` and `removeEventListener` which aren't bound by such constraint.\n\nThe syntax to add a handler:\n\n```js\nelement.addEventListener(event, handler, [options]);\n```\n\n`event`\n: Event name, e.g. `\"click\"`.\n\n`handler`\n: The handler function.\n\n`options`\n: An additional optional object with properties:\n    - `once`: if `true`, then the listener is automatically removed after it triggers.\n    - `capture`: the phase where to handle the event, to be covered later in the chapter <info:bubbling-and-capturing>. For historical reasons, `options` can also be `false/true`, that's the same as `{capture: false/true}`.\n    - `passive`: if `true`, then the handler will not call `preventDefault()`, we'll explain that later in <info:default-browser-action>.\n\nTo remove the handler, use `removeEventListener`:\n\n```js\nelement.removeEventListener(event, handler, [options]);\n```\n\n````warn header=\"Removal requires the same function\"\nTo remove a handler we should pass exactly the same function as was assigned.\n\nThis doesn't work:\n\n```js no-beautify\nelem.addEventListener( \"click\" , () => alert('Thanks!'));\n// ....\nelem.removeEventListener( \"click\", () => alert('Thanks!'));\n```\n\nThe handler won't be removed, because `removeEventListener` gets another function -- with the same code, but that doesn't matter, as it's a different function object.\n\nHere's the right way:\n\n```js\nfunction handler() {\n  alert( 'Thanks!' );\n}\n\ninput.addEventListener(\"click\", handler);\n// ....\ninput.removeEventListener(\"click\", handler);\n```\n\nPlease note -- if we don't store the function in a variable, then we can't remove it. There's no way to \"read back\" handlers assigned by `addEventListener`.\n````\n\nMultiple calls to `addEventListener` allow it to add multiple handlers, like this:\n\n```html run no-beautify\n<input id=\"elem\" type=\"button\" value=\"Click me\"/>\n\n<script>\n  function handler1() {\n    alert('Thanks!');\n  };\n\n  function handler2() {\n    alert('Thanks again!');\n  }\n\n*!*\n  elem.onclick = () => alert(\"Hello\");\n  elem.addEventListener(\"click\", handler1); // Thanks!\n  elem.addEventListener(\"click\", handler2); // Thanks again!\n*/!*\n</script>\n```\n\nAs we can see in the example above, we can set handlers *both* using a DOM-property and `addEventListener`. But generally we use only one of these ways.\n\n````warn header=\"For some events, handlers only work with `addEventListener`\"\nThere exist events that can't be assigned via a DOM-property. Only with `addEventListener`.\n\nFor instance, the `DOMContentLoaded` event, that triggers when the document is loaded and the DOM has been built.\n\n```js\n// will never run\ndocument.onDOMContentLoaded = function() {\n  alert(\"DOM built\");\n};\n```\n\n```js\n// this way it works\ndocument.addEventListener(\"DOMContentLoaded\", function() {\n  alert(\"DOM built\");\n});\n```\nSo `addEventListener` is more universal. Although, such events are an exception rather than the rule.\n````\n\n## Event object\n\nTo properly handle an event we'd want to know more about what's happened. Not just a \"click\" or a \"keydown\", but what were the pointer coordinates? Which key was pressed? And so on.\n\nWhen an event happens, the browser creates an *event object*, puts details into it and passes it as an argument to the handler.\n\nHere's an example of getting pointer coordinates from the event object:\n\n```html run\n<input type=\"button\" value=\"Click me\" id=\"elem\">\n\n<script>\n  elem.onclick = function(*!*event*/!*) {\n    // show event type, element and coordinates of the click\n    alert(event.type + \" at \" + event.currentTarget);\n    alert(\"Coordinates: \" + event.clientX + \":\" + event.clientY);\n  };\n</script>\n```\n\nSome properties of `event` object:\n\n`event.type`\n: Event type, here it's `\"click\"`.\n\n`event.currentTarget`\n: Element that handled the event. That's exactly the same as `this`, unless the handler is an arrow function, or its `this` is bound to something else, then we can get the element from  `event.currentTarget`.\n\n`event.clientX` / `event.clientY`\n: Window-relative coordinates of the cursor, for pointer events.\n\nThere are more properties. Many of them depend on the event type: keyboard events have one set of properties, pointer events - another one, we'll study them later when as we move on to the details of different events.\n\n````smart header=\"The event object is also available in HTML handlers\"\nIf we assign a handler in HTML, we can also use the `event` object, like this:\n\n```html autorun height=60\n<input type=\"button\" onclick=\"*!*alert(event.type)*/!*\" value=\"Event type\">\n```\n\nThat's possible because when the browser reads the attribute, it creates a handler like this:  `function(event) { alert(event.type) }`. That is: its first argument is called `\"event\"`, and the body is taken from the attribute.\n````\n\n\n## Object handlers: handleEvent\n\nWe can assign not just a function, but an object as an event handler using `addEventListener`. When an event occurs, its `handleEvent` method is called.\n\nFor instance:\n\n\n```html run\n<button id=\"elem\">Click me</button>\n\n<script>\n  let obj = {\n    handleEvent(event) {\n      alert(event.type + \" at \" + event.currentTarget);\n    }\n  };\n\n  elem.addEventListener('click', obj);\n</script>\n```\n\nAs we can see, when `addEventListener` receives an object as the handler, it calls `obj.handleEvent(event)` in case of an event.\n\nWe could also use objects of a custom class, like this:\n\n\n```html run\n<button id=\"elem\">Click me</button>\n\n<script>\n  class Menu {\n    handleEvent(event) {\n      switch(event.type) {\n        case 'mousedown':\n          elem.innerHTML = \"Mouse button pressed\";\n          break;\n        case 'mouseup':\n          elem.innerHTML += \"...and released.\";\n          break;\n      }\n    }\n  }\n\n*!*\n  let menu = new Menu();\n\n  elem.addEventListener('mousedown', menu);\n  elem.addEventListener('mouseup', menu);\n*/!*\n</script>\n```\n\nHere the same object handles both events. Please note that we need to explicitly setup the events to listen using `addEventListener`. The `menu` object only gets `mousedown` and `mouseup` here, not any other types of events.\n\nThe method `handleEvent` does not have to do all the job by itself. It can call other event-specific methods instead, like this:\n\n```html run\n<button id=\"elem\">Click me</button>\n\n<script>\n  class Menu {\n    handleEvent(event) {\n      // mousedown -> onMousedown\n      let method = 'on' + event.type[0].toUpperCase() + event.type.slice(1);\n      this[method](event);\n    }\n\n    onMousedown() {\n      elem.innerHTML = \"Mouse button pressed\";\n    }\n\n    onMouseup() {\n      elem.innerHTML += \"...and released.\";\n    }\n  }\n\n  let menu = new Menu();\n  elem.addEventListener('mousedown', menu);\n  elem.addEventListener('mouseup', menu);\n</script>\n```\n\nNow event handlers are clearly separated, that may be easier to support.\n\n## Summary\n\nThere are 3 ways to assign event handlers:\n\n1. HTML attribute: `onclick=\"...\"`.\n2. DOM property: `elem.onclick = function`.\n3. Methods: `elem.addEventListener(event, handler[, phase])` to add, `removeEventListener` to remove.\n\nHTML attributes are used sparingly, because JavaScript in the middle of an HTML tag looks a little bit odd and alien. Also can't write lots of code in there.\n\nDOM properties are ok to use, but we can't assign more than one handler of the particular event. In many cases that limitation is not pressing.\n\nThe last way is the most flexible, but it is also the longest to write. There are few events that only work with it, for instance `transitionend` and `DOMContentLoaded` (to be covered). Also `addEventListener` supports objects as event handlers. In that case the method `handleEvent` is called in case of the event.\n\nNo matter how you assign the handler -- it gets an event object as the first argument. That object contains the details about what's happened.\n\nWe'll learn more about events in general and about different types of events in the next chapters.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/head.html",
    "content": "<style>\n/*\n.d0 { text-align:center;margin:auto; }\n.d1 p { margin: 0 }\n.d1 {\nmargin:2em;\nbackground-color:green;\nwidth:13em;\nheight:13em;\ntext-align:center;\n}\n.d1 .number {\n  line-height: 2em;\n}\n.d2 {\ntext-align:center;\nmargin:auto;\nbackground-color:blue;\nwidth:9em;\nheight:9em;\n}\n.d1 .d2 ,number {\n  line-height: 2em;\n}\n.d3 {\ntext-align:center;\nmargin:auto;\nbackground-color:red;\nwidth:5em;\nheight:5em;\n}\n.d1 .d2 .d3 .number {\n  line-height: 5em;\n}\n.d1 .d2 .d2a {\n  color:white;\n  line-height: 2em;\n}\n*/\n</style>\n<script>\n/*\nfunction highlightMe(elem) {\n    elem.style.backgroundColor='yellow'\n    alert(elem.className)\n    elem.style.backgroundColor = ''\n}\n\nfunction highlightMe2(e) {\n    highlightMe(e.currentTarget);\n}\n*/\n</script>\n"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/article.md",
    "content": "# Bubbling and capturing\n\nLet's start with an example.\n\nThis handler is assigned to `<div>`, but also runs if you click any nested tag like `<em>` or `<code>`:\n\n```html autorun height=60\n<div onclick=\"alert('The handler!')\">\n  <em>If you click on <code>EM</code>, the handler on <code>DIV</code> runs.</em>\n</div>\n```\n\nIsn't it a bit strange? Why does the handler on `<div>` run if the actual click was on `<em>`?\n\n## Bubbling\n\nThe bubbling principle is simple.\n\n**When an event happens on an element, it first runs the handlers on it, then on its parent, then all the way up on other ancestors.**\n\nLet's say we have 3 nested elements `FORM > DIV > P` with a handler on each of them:\n\n```html run autorun\n<style>\n  body * {\n    margin: 10px;\n    border: 1px solid blue;\n  }\n</style>\n\n<form onclick=\"alert('form')\">FORM\n  <div onclick=\"alert('div')\">DIV\n    <p onclick=\"alert('p')\">P</p>\n  </div>\n</form>\n```\n\nA click on the inner `<p>` first runs `onclick`:\n1. On that `<p>`.\n2. Then on the outer `<div>`.\n3. Then on the outer `<form>`.\n4. And so on upwards till the `document` object.\n\n![](event-order-bubbling.svg)\n\nSo if we click on `<p>`, then we'll see 3 alerts: `p` -> `div` -> `form`.\n\nThe process is called \"bubbling\", because events \"bubble\" from the inner element up through parents like a bubble in the water.\n\n```warn header=\"*Almost* all events bubble.\"\nThe key word in this phrase is \"almost\".\n\nFor instance, a `focus` event does not bubble. There are other examples too, we'll meet them. But still it's an exception, rather than a rule, most events do bubble.\n```\n\n## event.target\n\nA handler on a parent element can always get the details about where it actually happened.\n\n**The most deeply nested element that caused the event is called a *target* element, accessible as `event.target`.**\n\nNote the differences from `this` (=`event.currentTarget`):\n\n- `event.target` -- is the \"target\" element that initiated the event, it doesn't change through the bubbling process.\n- `this` -- is the \"current\" element, the one that has a currently running handler on it.\n\nFor instance, if we have a single handler `form.onclick`, then it can \"catch\" all clicks inside the form. No matter where the click happened, it bubbles up to `<form>` and runs the handler.\n\nIn `form.onclick` handler:\n\n- `this` (=`event.currentTarget`) is the `<form>` element, because the handler runs on it.\n- `event.target` is the actual element inside the form that was clicked.\n\nCheck it out:\n\n[codetabs height=220 src=\"bubble-target\"]\n\nIt's possible that `event.target` could equal `this` -- it happens when the click is made directly on the `<form>` element.\n\n## Stopping bubbling\n\nA bubbling event goes from the target element straight up. Normally it goes upwards till `<html>`, and then to `document` object, and some events even reach `window`, calling all handlers on the path.\n\nBut any handler may decide that the event has been fully processed and stop the bubbling.\n\nThe method for it is `event.stopPropagation()`.\n\nFor instance, here `body.onclick` doesn't work if you click on `<button>`:\n\n```html run autorun height=60\n<body onclick=\"alert(`the bubbling doesn't reach here`)\">\n  <button onclick=\"event.stopPropagation()\">Click me</button>\n</body>\n```\n\n```smart header=\"event.stopImmediatePropagation()\"\nIf an element has multiple event handlers on a single event, then even if one of them stops the bubbling, the other ones still execute.\n\nIn other words, `event.stopPropagation()` stops the move upwards, but on the current element all other handlers will run.\n\nTo stop the bubbling and prevent handlers on the current element from running, there's a method `event.stopImmediatePropagation()`. After it no other handlers execute.\n```\n\n```warn header=\"Don't stop bubbling without a need!\"\nBubbling is convenient. Don't stop it without a real need: obvious and architecturally well thought out.\n\nSometimes `event.stopPropagation()` creates hidden pitfalls that later may become problems.\n\nFor instance:\n\n1. We create a nested menu. Each submenu handles clicks on its elements and calls `stopPropagation` so that the outer menu won't trigger.\n2. Later we decide to catch clicks on the whole window, to track users' behavior (where people click). Some analytic systems do that. Usually the code uses `document.addEventListener('click'…)` to catch all clicks.\n3. Our analytic won't work over the area where clicks are stopped by `stopPropagation`. Sadly, we've got a \"dead zone\".\n\nThere's usually no real need to prevent the bubbling. A task that seemingly requires that may be solved by other means. One of them is to use custom events, we'll cover them later. Also we can write our data into the `event` object in one handler and read it in another one, so we can pass to handlers on parents information about the processing below.\n```\n\n\n## Capturing\n\nThere's another phase of event processing called \"capturing\". It is rarely used in real code, but sometimes can be useful.\n\nThe standard [DOM Events](https://www.w3.org/TR/DOM-Level-3-Events/) describes 3 phases of event propagation:\n\n1. Capturing phase -- the event goes down to the element.\n2. Target phase -- the event reached the target element.\n3. Bubbling phase -- the event bubbles up from the element.\n\nHere's the picture, taken from the specification, of the capturing `(1)`, target `(2)` and bubbling `(3)` phases for a click event on a `<td>` inside a table:\n\n![](eventflow.svg)\n\nThat is: for a click on `<td>` the event first goes through the ancestors chain down to the element (capturing phase), then it reaches the target and triggers there (target phase), and then it goes up (bubbling phase), calling handlers on its way.\n\nUntil now, we only talked about bubbling, because the capturing phase is rarely used.\n\nIn fact, the capturing phase was invisible for us, because handlers added using `on<event>`-property or using HTML attributes or using two-argument `addEventListener(event, handler)` don't know anything about capturing, they only run on the 2nd and 3rd phases.\n\nTo catch an event on the capturing phase, we need to set the handler `capture` option to `true`:\n\n```js\nelem.addEventListener(..., {capture: true})\n\n// or, just \"true\" is an alias to {capture: true}\nelem.addEventListener(..., true)\n```\n\nThere are two possible values of the `capture` option:\n\n- If it's `false` (default), then the handler is set on the bubbling phase.\n- If it's `true`, then the handler is set on the capturing phase.\n\n\nNote that while formally there are 3 phases, the 2nd phase (\"target phase\": the event reached the element) is not handled separately: handlers on both capturing and bubbling phases trigger at that phase.\n\nLet's see both capturing and bubbling in action:\n\n```html run autorun height=140 edit\n<style>\n  body * {\n    margin: 10px;\n    border: 1px solid blue;\n  }\n</style>\n\n<form>FORM\n  <div>DIV\n    <p>P</p>\n  </div>\n</form>\n\n<script>\n  for(let elem of document.querySelectorAll('*')) {\n    elem.addEventListener(\"click\", e => alert(`Capturing: ${elem.tagName}`), true);\n    elem.addEventListener(\"click\", e => alert(`Bubbling: ${elem.tagName}`));\n  }\n</script>\n```\n\nThe code sets click handlers on *every* element in the document to see which ones are working.\n\nIf you click on `<p>`, then the sequence is:\n\n1. `HTML` -> `BODY` -> `FORM` -> `DIV -> P` (capturing phase, the first listener):\n2. `P` -> `DIV` -> `FORM` -> `BODY` -> `HTML` (bubbling phase, the second listener).\n\nPlease note, the `P` shows up twice, because we've set two listeners: capturing and bubbling. The target triggers at the end of the first and at the beginning of the second phase.\n\nThere's a property `event.eventPhase` that tells us the number of the phase on which the event was caught. But it's rarely used, because we usually know it in the handler.\n\n```smart header=\"To remove the handler, `removeEventListener` needs the same phase\"\nIf we `addEventListener(..., true)`, then we should mention the same phase in `removeEventListener(..., true)` to correctly remove the handler.\n```\n\n````smart header=\"Listeners on the same element and same phase run in their set order\"\nIf we have multiple event handlers on the same phase, assigned to the same element with `addEventListener`, they run in the same order as they are created:\n\n```js\nelem.addEventListener(\"click\", e => alert(1)); // guaranteed to trigger first\nelem.addEventListener(\"click\", e => alert(2));\n```\n````\n\n```smart header=\"The `event.stopPropagation()` during the capturing also prevents the bubbling\"\nThe `event.stopPropagation()` method and its sibling `event.stopImmediatePropagation()` can also be called on the capturing phase. Then not only the futher capturing is stopped, but the bubbling as well.\n\nIn other words, normally the event goes first down (\"capturing\") and then up (\"bubbling\"). But if `event.stopPropagation()` is called during the capturing phase, then the event travel stops, no bubbling will occur.\n```\n\n\n## Summary\n\nWhen an event happens -- the most nested element where it happens gets labeled as the \"target element\" (`event.target`).\n\n- Then the event moves down from the document root to `event.target`, calling handlers assigned with `addEventListener(..., true)` on the way (`true` is a shorthand for `{capture: true}`).\n- Then handlers are called on the target element itself.\n- Then the event bubbles up from `event.target` to the root, calling handlers assigned using `on<event>`, HTML attributes and `addEventListener` without the 3rd argument or with the 3rd argument `false/{capture:false}`.\n\nEach handler can access `event` object properties:\n\n- `event.target` -- the deepest element that originated the event.\n- `event.currentTarget` (=`this`) -- the current element that handles the event (the one that has the handler on it)\n- `event.eventPhase` -- the current phase (capturing=1, target=2, bubbling=3).\n\nAny event handler can stop the event by calling `event.stopPropagation()`, but that's not recommended, because we can't really be sure we won't need it above, maybe for completely different things.\n\nThe capturing phase is used very rarely, usually we handle events on bubbling. And there's a logical explanation for that.\n\nIn real world, when an accident happens, local authorities react first. They know best the area where it happened. Then higher-level authorities if needed.\n\nThe same for event handlers. The code that set the handler on a particular element knows maximum details about the element and what it does. A handler on a particular `<td>` may be suited for that exactly `<td>`, it knows everything about it, so it should get the chance first. Then its immediate parent also knows about the context, but a little bit less, and so on till the very top element that handles general concepts and runs the last one.\n\nBubbling and capturing lay the foundation for \"event delegation\" -- an extremely powerful event handling pattern that we study in the next chapter.\n"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/both.view/example.css",
    "content": "form {\n  background-color: green;\n  position: relative;\n  width: 150px;\n  height: 150px;\n  text-align: center;\n  cursor: pointer;\n}\n\ndiv {\n  background-color: blue;\n  position: absolute;\n  top: 25px;\n  left: 25px;\n  width: 100px;\n  height: 100px;\n}\n\np {\n  background-color: red;\n  position: absolute;\n  top: 25px;\n  left: 25px;\n  width: 50px;\n  height: 50px;\n  line-height: 50px;\n  margin: 0;\n}\n\nbody {\n  line-height: 25px;\n  font-size: 16px;\n}"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/both.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"example.css\">\n</head>\n\n<body>\n\n  <form>FORM\n    <div>DIV\n      <p>P</p>\n    </div>\n  </form>\n\n  <script src=\"script.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/both.view/script.js",
    "content": "let elems = document.querySelectorAll('form,div,p');\n\nfor (let i = 0; i < elems.length; i++) {\n  elems[i].addEventListener(\"click\", highlightThis, true);\n  elems[i].addEventListener(\"click\", highlightThis, false);\n}\n\nfunction highlightThis() {\n  this.style.backgroundColor = 'yellow';\n  alert(this.tagName);\n  this.style.backgroundColor = '';\n}"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/example.css",
    "content": "form {\n  background-color: green;\n  position: relative;\n  width: 150px;\n  height: 150px;\n  text-align: center;\n  cursor: pointer;\n}\n\ndiv {\n  background-color: blue;\n  position: absolute;\n  top: 25px;\n  left: 25px;\n  width: 100px;\n  height: 100px;\n}\n\np {\n  background-color: red;\n  position: absolute;\n  top: 25px;\n  left: 25px;\n  width: 50px;\n  height: 50px;\n  line-height: 50px;\n  margin: 0;\n}\n\nbody {\n  line-height: 25px;\n  font-size: 16px;\n}"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"example.css\">\n</head>\n\n<body>\n  A click shows both <code>event.target</code> and <code>this</code> to compare:\n\n  <form id=\"form\">FORM\n    <div>DIV\n      <p>P</p>\n    </div>\n  </form>\n\n  <script src=\"script.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/script.js",
    "content": "\nform.onclick = function(event) {\n  event.target.style.backgroundColor = 'yellow';\n\n  // chrome needs some time to paint yellow\n  setTimeout(() => {\n    alert(\"target = \" + event.target.tagName + \", this=\" + this.tagName);\n    event.target.style.backgroundColor = ''\n  }, 0);\n};\n"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/capture.view/example.css",
    "content": "form {\n  background-color: green;\n  position: relative;\n  width: 150px;\n  height: 150px;\n  text-align: center;\n  cursor: pointer;\n}\n\ndiv {\n  background-color: blue;\n  position: absolute;\n  top: 25px;\n  left: 25px;\n  width: 100px;\n  height: 100px;\n}\n\np {\n  background-color: red;\n  position: absolute;\n  top: 25px;\n  left: 25px;\n  width: 50px;\n  height: 50px;\n  line-height: 50px;\n  margin: 0;\n}\n\nbody {\n  line-height: 25px;\n  font-size: 16px;\n}"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/capture.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<body>\n  <link type=\"text/css\" rel=\"stylesheet\" href=\"example.css\">\n\n  <form>FORM\n    <div>DIV\n      <p>P</p>\n    </div>\n  </form>\n\n  <script src=\"script.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/capture.view/script.js",
    "content": "let elems = document.querySelectorAll('form,div,p');\n\nfor (let i = 0; i < elems.length; i++) {\n  elems[i].addEventListener(\"click\", highlightThis, true);\n}\n\nfunction highlightThis() {\n  this.style.backgroundColor = 'yellow';\n  alert(this.tagName);\n  this.style.backgroundColor = '';\n}"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/1-hide-message-delegate/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/2-events/03-event-delegation/1-hide-message-delegate/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <link rel=\"stylesheet\" href=\"messages.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <div id=\"container\">\n    <div class=\"pane\">\n      <h3>Horse</h3>\n      <p>The horse is one of two extant subspecies of Equus ferus. It is an odd-toed ungulate mammal belonging to the taxonomic family Equidae. The horse has evolved over the past 45 to 55 million years from a small multi-toed creature, Eohippus, into the large, single-toed animal of today.</p>\n      <button class=\"remove-button\">[x]</button>\n    </div>\n    <div class=\"pane\">\n      <h3>Donkey</h3>\n      <p>The donkey or ass (Equus africanus asinus) is a domesticated member of the horse family, Equidae. The wild ancestor of the donkey is the African wild ass, E. africanus. The donkey has been used as a working animal for at least 5000 years.</p>\n      <button class=\"remove-button\">[x]</button>\n    </div>\n    <div class=\"pane\">\n      <h3>Cat</h3>\n      <p>The domestic cat (Latin: Felis catus) is a small, typically furry, carnivorous mammal. They are often called house cats when kept as indoor pets or simply cats when there is no need to distinguish them from other felids and felines. Cats are often valued by humans for companionship and for their ability to hunt vermin.\n      </p>\n      <button class=\"remove-button\">[x]</button>\n    </div>\n  </div>\n\n  <script>\n    container.onclick = function(event) {\n      if (event.target.className != 'remove-button') return;\n\n      let pane = event.target.closest('.pane');\n      pane.remove();\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/1-hide-message-delegate/solution.view/messages.css",
    "content": "body {\n  margin: 10px auto;\n  width: 470px;\n}\n\nh3 {\n  margin: 0;\n  padding-bottom: .3em;\n  padding-right: 20px;\n  font-size: 1.1em;\n}\n\np {\n  margin: 0;\n  padding: 0 0 .5em;\n}\n\n.pane {\n  background: #edf5e1;\n  padding: 10px 20px 10px;\n  border-top: solid 2px #c4df9b;\n  position: relative;\n}\n\n.remove-button {\n  position: absolute;\n  font-size: 110%;\n  top: 0;\n  color: darkred;\n  right: 10px;\n  display: block;\n  width: 24px;\n  height: 24px;\n  border: none;\n  background: transparent;\n  cursor: pointer;\n}"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/1-hide-message-delegate/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <link rel=\"stylesheet\" href=\"messages.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <div id=\"container\">\n    <div class=\"pane\">\n      <h3>Horse</h3>\n      <p>The horse is one of two extant subspecies of Equus ferus. It is an odd-toed ungulate mammal belonging to the taxonomic family Equidae. The horse has evolved over the past 45 to 55 million years from a small multi-toed creature, Eohippus, into the large, single-toed animal of today.</p>\n      <button class=\"remove-button\">[x]</button>\n    </div>\n    <div class=\"pane\">\n      <h3>Donkey</h3>\n      <p>The donkey or ass (Equus africanus asinus) is a domesticated member of the horse family, Equidae. The wild ancestor of the donkey is the African wild ass, E. africanus. The donkey has been used as a working animal for at least 5000 years.</p>\n      <button class=\"remove-button\">[x]</button>\n    </div>\n    <div class=\"pane\">\n      <h3>Cat</h3>\n      <p>The domestic cat (Latin: Felis catus) is a small, typically furry, carnivorous mammal. They are often called house cats when kept as indoor pets or simply cats when there is no need to distinguish them from other felids and felines. Cats are often valued by humans for companionship and for their ability to hunt vermin.\n      </p>\n      <button class=\"remove-button\">[x]</button>\n    </div>\n  </div>\n\n  <script>\n    // ...your code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/1-hide-message-delegate/source.view/messages.css",
    "content": "body {\n  margin: 10px auto;\n  width: 470px;\n}\n\nh3 {\n  margin: 0;\n  padding-bottom: .3em;\n  padding-right: 20px;\n  font-size: 1.1em;\n}\n\np {\n  margin: 0;\n  padding: 0 0 .5em;\n}\n\n.pane {\n  background: #edf5e1;\n  padding: 10px 20px 10px;\n  border-top: solid 2px #c4df9b;\n  position: relative;\n}\n\n.remove-button {\n  position: absolute;\n  font-size: 110%;\n  top: 0;\n  color: darkred;\n  right: 10px;\n  display: block;\n  width: 24px;\n  height: 24px;\n  border: none;\n  background: transparent;\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/1-hide-message-delegate/task.md",
    "content": "importance: 5\n\n---\n\n# Masquer les messages avec délégation d'événement\n\nIl y a une liste de messages avec des boutons de suppression \"[x]\". Faites fonctionner les boutons.\n\nComme ceci:\n\n[iframe src=\"solution\" height=420]\n\nP.S. Il devrait y avoir qu'un seul écouteur d'événement sur le conteneur, utiliser la délégation d'événement.\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/2-sliding-tree/solution.md",
    "content": "La solution comporte deux parties.\n\n1. Enveloppez chaque titre de nœud d'arbre dans `<span>`. Ensuite, nous pouvons ajouter du CSS sur `:hover` et gérer les clics exactement sur le texte, car la largeur de `<span>` est exactement la largeur du texte (contrairement à sans elle).\n2. Définissez un gestionnaire sur le nœud racine `tree` et gérez les clics sur ces `<span>` titres.\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/2-sliding-tree/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    .tree span:hover {\n      font-weight: bold;\n    }\n\n    .tree span {\n      cursor: pointer;\n    }\n  </style>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <ul class=\"tree\" id=\"tree\">\n    <li>Animals\n      <ul>\n        <li>Mammals\n          <ul>\n            <li>Cows</li>\n            <li>Donkeys</li>\n            <li>Dogs</li>\n            <li>Tigers</li>\n          </ul>\n        </li>\n        <li>Other\n          <ul>\n            <li>Snakes</li>\n            <li>Birds</li>\n            <li>Lizards</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n    <li>Fishes\n      <ul>\n        <li>Aquarium\n          <ul>\n            <li>Guppy</li>\n            <li>Angelfish</li>\n          </ul>\n        </li>\n        <li>Sea\n          <ul>\n            <li>Sea trout</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n  </ul>\n\n  <script>\n    // move all text into <span>\n    // they occupy exactly the place necessary for the text,\n    for (let li of tree.querySelectorAll('li')) {\n      let span = document.createElement('span');\n      li.prepend(span);\n      span.append(span.nextSibling); // move the text node into span\n    }\n\n    // catch clicks on whole tree\n    tree.onclick = function(event) {\n\n      if (event.target.tagName != 'SPAN') {\n        return;\n      }\n\n      let childrenContainer = event.target.parentNode.querySelector('ul');\n      if (!childrenContainer) return; // no children\n\n      childrenContainer.hidden = !childrenContainer.hidden;\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/2-sliding-tree/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n\n  <ul class=\"tree\" id=\"tree\">\n    <li>Animals\n      <ul>\n        <li>Mammals\n          <ul>\n            <li>Cows</li>\n            <li>Donkeys</li>\n            <li>Dogs</li>\n            <li>Tigers</li>\n          </ul>\n        </li>\n        <li>Other\n          <ul>\n            <li>Snakes</li>\n            <li>Birds</li>\n            <li>Lizards</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n    <li>Fishes\n      <ul>\n        <li>Aquarium\n          <ul>\n            <li>Guppy</li>\n            <li>Angelfish</li>\n          </ul>\n        </li>\n        <li>Sea\n          <ul>\n            <li>Sea trout</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n  </ul>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/2-sliding-tree/task.md",
    "content": "importance: 5\n\n---\n\n# Menu arborescent\n\nCréez une arborescence qui affiche / masque les enfants des nœuds au clic:\n\n[iframe border=1 src=\"solution\"]\n\nConditions:\n\n- Un seul gestionnaire d'événements (utiliser la délégation d'événement)\n- Un clic en dehors du titre du nœud (sur un espace vide) ne devrait rien faire.\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/3-sortable-table/solution.md",
    "content": "\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/3-sortable-table/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    table {\n       border-collapse: collapse;\n     }\n     th, td {\n       border: 1px solid black;\n       padding: 4px;\n     }\n     th {\n       cursor: pointer;\n     }\n     th:hover {\n       background: yellow;\n     }\n  </style>\n</head>\n\n<body>\n\n  <table id=\"grid\">\n    <thead>\n      <tr>\n        <th data-type=\"number\">Age</th>\n        <th data-type=\"string\">Name</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>5</td>\n        <td>John</td>\n      </tr>\n      <tr>\n        <td>2</td>\n        <td>Pete</td>\n      </tr>\n      <tr>\n        <td>12</td>\n        <td>Ann</td>\n      </tr>\n      <tr>\n        <td>9</td>\n        <td>Eugene</td>\n      </tr>\n      <tr>\n        <td>1</td>\n        <td>Ilya</td>\n      </tr>\n    </tbody>\n  </table>\n\n  <script>\n\n    grid.onclick = function(e) {\n      if (e.target.tagName != 'TH') return;\n\n      let th = e.target;\n      // if TH, then sort\n      // cellIndex is the number of th:\n      //   0 for the first column\n      //   1 for the second column, etc\n      sortGrid(th.cellIndex, th.dataset.type);\n    };\n\n    function sortGrid(colNum, type) {\n      let tbody = grid.querySelector('tbody');\n\n      let rowsArray = Array.from(tbody.rows);\n\n      // compare(a, b) compares two rows, need for sorting\n      let compare;\n\n      switch (type) {\n        case 'number':\n          compare = function(rowA, rowB) {\n            return rowA.cells[colNum].innerHTML - rowB.cells[colNum].innerHTML;\n          };\n          break;\n        case 'string':\n          compare = function(rowA, rowB) {\n            return rowA.cells[colNum].innerHTML > rowB.cells[colNum].innerHTML ? 1 : -1;\n          };\n          break;\n      }\n\n      // sort\n      rowsArray.sort(compare);\n\n      tbody.append(...rowsArray);\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/3-sortable-table/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    table {\n       border-collapse: collapse;\n     }\n     th, td {\n       border: 1px solid black;\n       padding: 4px;\n     }\n     th {\n       cursor: pointer;\n     }\n     th:hover {\n       background: yellow;\n     }\n  </style>\n</head>\n\n<body>\n\n  <table id=\"grid\">\n    <thead>\n      <tr>\n        <th data-type=\"number\">Age</th>\n        <th data-type=\"string\">Name</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>5</td>\n        <td>John</td>\n      </tr>\n      <tr>\n        <td>2</td>\n        <td>Pete</td>\n      </tr>\n      <tr>\n        <td>12</td>\n        <td>Ann</td>\n      </tr>\n      <tr>\n        <td>9</td>\n        <td>Eugene</td>\n      </tr>\n      <tr>\n        <td>1</td>\n        <td>Ilya</td>\n      </tr>\n    </tbody>\n  </table>\n\n  <script>\n    // ...your code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/3-sortable-table/task.md",
    "content": "importance: 4\n\n---\n\n# Table triable\n\nRendre le tableau triable: les clics sur les éléments `<th>` doivent le trier par colonne correspondante.\n\nChaque `<th>` a le type dans l'attribut, comme ceci:\n\n```html\n<table id=\"grid\">\n  <thead>\n    <tr>\n*!*\n      <th data-type=\"number\">Age</th>\n      <th data-type=\"string\">Name</th>\n*/!*\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>5</td>\n      <td>John</td>\n    </tr>\n    <tr>\n      <td>10</td>\n      <td>Ann</td>\n    </tr>\n    ...\n  </tbody>\n</table>\n```\n\nDans l'exemple ci-dessus, la première colonne contient des nombres et la seconde -- des chaînes. La fonction de tri doit gérer le tri en fonction du type.\n\nSeuls les types `\"string\"` et `\"number\"` doivent être pris en charge.\n\nL'exemple de travail:\n\n[iframe border=1 src=\"solution\" height=190]\n\nP.S. La table peut être grande, avec n'importe quel nombre de lignes et de colonnes.\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/4-behavior-tooltip/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/2-events/03-event-delegation/4-behavior-tooltip/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    body {\n      height: 2000px;\n      /* make body scrollable, the tooltip should work after the scroll */\n    }\n\n    .tooltip {\n      position: fixed;\n      padding: 10px 20px;\n      border: 1px solid #b3c9ce;\n      border-radius: 4px;\n      text-align: center;\n      font: italic 14px/1.3 sans-serif;\n      color: #333;\n      background: #fff;\n      box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);\n    }\n  </style>\n</head>\n\n<body>\n\n  <p>LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa</p>\n  <p>LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa</p>\n\n  <button data-tooltip=\"the tooltip is longer than the element\">Short button</button>\n  <button data-tooltip=\"HTML<br>tooltip\">One more button</button>\n\n  <p>Scroll the page to make buttons appear on the top, check if the tooltips show up correctly.</p>\n\n\n  <script>\n    let tooltipElem;\n\n    document.onmouseover = function(event) {\n      let target = event.target;\n\n      // if we have tooltip HTML...\n      let tooltipHtml = target.dataset.tooltip;\n      if (!tooltipHtml) return;\n\n      // ...create the tooltip element\n\n      tooltipElem = document.createElement('div');\n      tooltipElem.className = 'tooltip';\n      tooltipElem.innerHTML = tooltipHtml;\n      document.body.append(tooltipElem);\n\n      // position it above the annotated element (top-center)\n      let coords = target.getBoundingClientRect();\n\n      let left = coords.left + (target.offsetWidth - tooltipElem.offsetWidth) / 2;\n      if (left < 0) left = 0; // don't cross the left window edge\n\n      let top = coords.top - tooltipElem.offsetHeight - 5;\n      if (top < 0) { // if crossing the top window edge, show below instead\n        top = coords.top + target.offsetHeight + 5;\n      }\n\n      tooltipElem.style.left = left + 'px';\n      tooltipElem.style.top = top + 'px';\n    };\n\n    document.onmouseout = function(e) {\n\n      if (tooltipElem) {\n        tooltipElem.remove();\n        tooltipElem = null;\n      }\n\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/4-behavior-tooltip/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    body {\n      height: 2000px;\n      /* make body scrollable, the tooltip should work after the scroll */\n    }\n\n    .tooltip {\n      /* some styles for the tooltip, you can use your own instead */\n      position: fixed;\n      padding: 10px 20px;\n      border: 1px solid #b3c9ce;\n      border-radius: 4px;\n      text-align: center;\n      font: italic 14px/1.3 sans-serif;\n      color: #333;\n      background: #fff;\n      box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);\n    }\n  </style>\n</head>\n\n<body>\n\n  <p>LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa</p>\n  <p>LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa</p>\n\n  <button data-tooltip=\"the tooltip is longer than the element\">Short button</button>\n  <button data-tooltip=\"HTML<br>tooltip\">One more button</button>\n\n  <p>Scroll the page to make buttons appear on the top, check if the tooltips show up correctly.</p>\n\n\n  <script>\n    // ...your code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/4-behavior-tooltip/task.md",
    "content": "importance: 5\n\n---\n\n# Comportement info-bulle\n\nCréez du code JS pour le comportement de l'info-bulle.\n\nLorsqu'une souris survole un élément avec `data-tooltip`, l'info-bulle devrait apparaître dessus, et quand elle est partie, se cacher.\n\nUn exemple de HTML annoté:\n\n```html\n<button data-tooltip=\"the tooltip is longer than the element\">Short button</button>\n<button data-tooltip=\"HTML<br>tooltip\">One more button</button>\n```\n\nDevrait fonctionner comme ceci:\n\n[iframe src=\"solution\" height=200 border=1]\n\nDans cette tâche, nous supposons que tous les éléments avec `data-tooltip` ne contiennent que du texte. Aucune balise imbriquée (pour le moment).\n\nDétails:\n\n- La distance entre l'élément et l'info-bulle doit être de `5px`.\n- L'info-bulle doit être centrée par rapport à l'élément, si possible.\n- L'info-bulle ne doit pas traverser les bords de la fenêtre. Normalement, il devrait être au-dessus de l'élément, mais si l'élément est en haut de la page et qu'il n'y a pas d'espace pour l'info-bulle, alors en dessous.\n- Le contenu de l'info-bulle est donné dans l'attribut `data-tooltip`. Cela peut être du HTML arbitraire.\n\nVous aurez besoin de deux événements ici:\n- `mouseover` se déclenche lorsqu'un pointeur survole un élément.\n- `mouseout` se déclenche lorsqu'un pointeur quitte un élément.\n\nVeuillez utiliser la délégation d'événements: configurez deux gestionnaires sur `document` pour suivre tous les \"survolage\" et \"sorties\" des éléments avec `data-tooltip` et gérer les info-bulles à partir de là.\n\nUne fois le comportement implémenté, même les personnes non familiarisées avec JavaScript peuvent ajouter des éléments annotés.\n\nP.S. Une seule info-bulle peut apparaître à la fois.\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/article.md",
    "content": "\n# Délégation d'événement\n\nLa capture et le bouillonement (\"bubbling\") nous permettent d'implémenter l'un des modèles de gestion d'événements les plus puissants appelés *Délégation d'événement*.\n\nL'idée est que si nous avons beaucoup d'éléments traités de la même manière, au lieu d'assigner un gestionnaire à chacun d'eux -- nous mettons un seul gestionnaire sur leur ancêtre commun.\n\nDans le gestionnaire, nous obtenons `event.target` pour voir où l'événement s'est réellement produit et le gérer.\n\nVoyons un exemple -- le [diagramme Ba-Gua](http://en.wikipedia.org/wiki/Ba_gua) reflétant l'ancienne philosophie chinoise.\n\nLe voici:\n\n[iframe height=350 src=\"bagua\" edit link]\n\nLe code HTML est comme ceci:\n\n```html\n<table>\n  <tr>\n    <th colspan=\"3\"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>\n  </tr>\n  <tr>\n    <td class=\"nw\"><strong>Northwest</strong><br>Metal<br>Silver<br>Elders</td>\n    <td class=\"n\">...</td>\n    <td class=\"ne\">...</td>\n  </tr>\n  <tr>...2 more lines of this kind...</tr>\n  <tr>...2 more lines of this kind...</tr>\n</table>\n```\n\nLe tableau a 9 cellules, mais il pourrait y en avoir 99 ou 9999, peu importe.\n\n**Notre tâche est de surligner une cellule `<td>` en cliquant dessus.**\n\nAu lieu d'assigner un gestionnaire `onclick` à chaque `<td>` -- nous allons configurer le gestionnaire \"foure-tout\" sur l'élément `<table>`.\n\nIl utilisera `event.target` pour obtenir l'élément cliqué et le surligner.\n\nLe code:\n\n```js\nlet selectedTd;\n\n*!*\ntable.onclick = function(event) {\n  let target = event.target; // où était le clic?\n\n  if (target.tagName != 'TD') return; // pas sur TD? Alors nous ne sommes pas intéressés\n\n  highlight(target); // surligner\n};\n*/!*\n\nfunction highlight(td) {\n  if (selectedTd) { // supprimer le surlignage le cas échéant\n    selectedTd.classList.remove('highlight');\n  }\n  selectedTd = td;\n  selectedTd.classList.add('highlight'); // surligner le nouveau td\n}\n```\n\nUn tel code ne se soucie pas du nombre de cellules dans le tableau. Nous pouvons ajouter / supprimer `<td>` dynamiquement à tout moment et le surlignage fonctionnera toujours.\n\nPourtant, il y a un inconvénient.\n\nLe clic peut se produire non pas sur le `<td>`, mais à l'intérieur.\n\nDans notre cas, si nous jetons un œil à l'intérieur du HTML, nous pouvons voir des balises imbriquées à l'intérieur de `<td>`, comme `<strong>`:\n\n```html\n<td>\n*!*\n  <strong>Northwest</strong>\n*/!*\n  ...\n</td>\n```\n\nNaturellement, si un clic se produit sur la balise `<strong>`, elle devient la valeur de `event.target`.\n\n![](bagua-bubble.svg)\n\nDans le gestionnaire `table.onclick`, nous devrions prendre `event.target` et découvrir si le clic était à l'intérieur de `<td>` ou non.\n\nVoici le code amélioré:\n\n```js\ntable.onclick = function(event) {\n  let td = event.target.closest('td'); // (1)\n\n  if (!td) return; // (2)\n\n  if (!table.contains(td)) return; // (3)\n\n  highlight(td); // (4)\n};\n```\n\nExplications:\n1. La méthode `elem.closest(selector)` retourne l'ancêtre le plus proche qui correspond au sélecteur. Dans notre cas, nous recherchons `<td>` en remontant de l'élément source.\n2. Si `event.target` n'est pas à l'intérieur d'un `<td>`, alors l'appel renvoie immédiatement, car il n'y a rien à faire.\n3. Dans le cas de tableaux imbriquées, `event.target` peut être un `<td>`, mais se trouvant en dehors du tableau actuel. Nous vérifions donc si c'est réellement la balise `<td>` de *notre tableau*.\n4. Si c'est le cas, surligner le.\n\nEn conséquence, nous avons un code de surlignage rapide et efficace, qui ne se soucie pas du nombre total de `<td>` dans le tableau.\n\n## Exemple de délégation: actions dans le balisage\n\nIl existe d'autres utilisations de la délégation d'événements.\n\nDisons que nous voulons créer un menu avec les boutons \"Save\", \"Load\", \"Search\" et ainsi de suite. Et il y a un objet avec les méthodes `save`, `load`, `search`... Comment les faire correspondre?\n\nLa première idée peut être d'assigner un gestionnaire distinct à chaque bouton. Mais il existe une solution plus élégante. Nous pouvons ajouter un gestionnaire pour tout le menu et des attributs `data-action` pour les boutons qui ont la méthode à appeler:\n\n```html\n<button *!*data-action=\"save\"*/!*>Click to Save</button>\n```\n\nLe gestionnaire lit l'attribut et exécute la méthode. Jetez un œil à l'exemple suivant:\n\n```html autorun height=60 run untrusted\n<div id=\"menu\">\n  <button data-action=\"save\">Save</button>\n  <button data-action=\"load\">Load</button>\n  <button data-action=\"search\">Search</button>\n</div>\n\n<script>\n  class Menu {\n    constructor(elem) {\n      this._elem = elem;\n      elem.onclick = this.onClick.bind(this); // (*)\n    }\n\n    save() {\n      alert('saving');\n    }\n\n    load() {\n      alert('loading');\n    }\n\n    search() {\n      alert('searching');\n    }\n\n    onClick(event) {\n*!*\n      let action = event.target.dataset.action;\n      if (action) {\n        this[action]();\n      }\n*/!*\n    };\n  }\n\n  new Menu(menu);\n</script>\n```\n\nVeuillez noter qu'à la ligne `(*)`, `this.onClick` est lié à `this`. C'est important, car sinon `this` à l'intérieur ferait référence à l'élément DOM (`elem`), pas à l'objet `Menu`, et `this[action]` ne serait pas ce dont nous avons besoin.\n\nAlors, quels avantages la délégation nous donne-t-elle ici?\n\n```compare\n+ Nous n'avons pas besoin d'écrire le code pour affecter un gestionnaire à chaque bouton. Il suffit de faire une méthode et le mettre dans le balisage.\n+ La structure HTML est flexible, nous pouvons ajouter / supprimer des boutons à tout moment.\n```\n\nNous pourrions également utiliser les classes `.action-save`, `.action-load`, mais un attribut `data-action` est mieux sémantiquement. Et nous pouvons également l'utiliser dans les règles CSS.\n\n## Le patron \"comportement\"\n\nNous pouvons également utiliser la délégation d'événements pour ajouter des \"comportements\" aux éléments *déclarativement*, avec des attributs et des classes spéciaux.\n\nLe patron comporte deux parties:\n1. Nous ajoutons un attribut personnalisé à un élément qui décrit son comportement.\n2. Un gestionnaire à l'échelle du document suit les événements et, si un événement se produit sur un élément attribué -- exécute l'action.\n\n### Comportement: Compteur\n\nPar exemple, ici l'attribut `data-counter` ajoute un comportement: \"augmenter la valeur en cliquant\" aux boutons:\n\n```html run autorun height=60\nCounter: <input type=\"button\" value=\"1\" data-counter>\nOne more counter: <input type=\"button\" value=\"2\" data-counter>\n\n<script>\n  document.addEventListener('click', function(event) {\n\n    if (event.target.dataset.counter != undefined) { // si l'attribut existe...\n      event.target.value++;\n    }\n\n  });\n</script>\n```\n\nSi nous cliquons sur un bouton - sa valeur est augmentée. Pas de boutons, mais l'approche générale est importante ici.\n\nIl peut y avoir autant d'attributs avec `data-counter` que nous le voulons. Nous pouvons à tout moment en ajouter de nouveaux au HTML. En utilisant la délégation d'événements, nous avons \"étendu\" le HTML, ajouté un attribut qui décrit un nouveau comportement.\n\n```warn header=\"Pour les gestionnaires de niveau document -- toujours `addEventListener`\"\nLorsque nous affectons un gestionnaire d'événements à l'objet `document`, nous devrions toujours utiliser `addEventListener`, pas `document.on<event>`, car ce dernier provoquera des conflits: les nouveaux gestionnaires écrasent les anciens.\n\nPour de vrais projets, il est normal qu'il y ait de nombreux gestionnaires sur `document` définis par différentes parties du code.\n```\n\n### Comportement: Toggler\n\nEncore un exemple de comportement. Un clic sur un élément avec l'attribut `data-toggle-id` affichera/masquera l'élément avec le `id` donné:\n\n```html autorun run height=60\n<button *!*data-toggle-id=\"subscribe-mail\"*/!*>\n  Show the subscription form\n</button>\n\n<form id=\"subscribe-mail\" hidden>\n  Your mail: <input type=\"email\">\n</form>\n\n<script>\n*!*\n  document.addEventListener('click', function(event) {\n    let id = event.target.dataset.toggleId;\n    if (!id) return;\n\n    let elem = document.getElementById(id);\n\n    elem.hidden = !elem.hidden;\n  });\n*/!*\n</script>\n```\n\nNotons encore une fois ce que nous avons fait. Maintenant, pour ajouter une fonctionnalité de basculement à un élément -- il n'est pas nécessaire de connaître JavaScript, utilisez simplement l'attribut `data-toggle-id`.\n\nCela peut devenir très pratique -- pas besoin d'écrire du JavaScript pour chacun de ces éléments. Utilisez simplement le comportement. Le gestionnaire au niveau du document le fait fonctionner pour n'importe quel élément de la page.\n\nNous pouvons également combiner plusieurs comportements sur un seul élément.\n\nLe patron \"comportement\" peut être une alternative aux mini-fragments de JavaScript.\n\n## Résumé\n\nLa délégation d'événement est vraiment sympa! C'est l'un des patrons les plus utiles pour les événements DOM.\n\nIl est souvent utilisé pour ajouter la même manipulation pour de nombreux éléments similaires, mais pas seulement pour cela.\n\nL'algorithme:\n\n1. Placez un seul gestionnaire sur le conteneur.\n2. Dans le gestionnaire -- vérifiez l'élément source `event.target`.\n3. Si l'événement s'est produit à l'intérieur d'un élément qui nous intéresse, gérez l'événement.\n\nAvantages:\n\n```compare\n+ Simplifie l'initialisation et économise de la mémoire: pas besoin d'ajouter de nombreux gestionnaires.\n+ Moins de code: lors de l'ajout ou de la suppression d'éléments, pas besoin d'ajouter/supprimer des gestionnaires.\n+ Modifications DOM: nous pouvons ajouter/supprimer en masse des éléments avec `innerHTML` et autres.\n```\n\nLa délégation a bien sûr ses limites:\n\n```compare\n- Premièrement, l'événement doit bouillonner. Certains événements ne bouillonnent pas. De plus, les gestionnaires de bas niveau ne devraient pas utiliser `event.stopPropagation()`.\n- Deuxièmement, la délégation peut ajouter une charge au processeur, car le gestionnaire au niveau du conteneur réagit aux événements à n'importe quel endroit du conteneur, qu'ils nous intéressent ou non. Mais généralement, la charge est négligeable, nous ne la prenons donc pas en compte.\n```\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/bagua.view/bagua.css",
    "content": "#bagua-table th {\n  text-align: center;\n  font-weight: bold;\n}\n\n#bagua-table td {\n  width: 150px;\n  white-space: nowrap;\n  text-align: center;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 12px;\n}\n\n#bagua-table .nw {\n  background: #999;\n}\n\n#bagua-table .n {\n  background: #03f;\n  color: #fff;\n}\n\n#bagua-table .ne {\n  background: #ff6;\n}\n\n#bagua-table .w {\n  background: #ff0;\n}\n\n#bagua-table .c {\n  background: #60c;\n  color: #fff;\n}\n\n#bagua-table .e {\n  background: #09f;\n  color: #fff;\n}\n\n#bagua-table .sw {\n  background: #963;\n  color: #fff;\n}\n\n#bagua-table .s {\n  background: #f60;\n  color: #fff;\n}\n\n#bagua-table .se {\n  background: #0c3;\n  color: #fff;\n}\n\n#bagua-table .highlight {\n  background: red;\n}"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/bagua.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<body>\n  <link type=\"text/css\" rel=\"stylesheet\" href=\"bagua.css\">\n\n\n  <table id=\"bagua-table\">\n    <tr>\n      <th colspan=\"3\"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>\n    </tr>\n    <tr>\n      <td class=\"nw\"><strong>Northwest</strong>\n        <br>Metal\n        <br>Silver\n        <br>Elders\n      </td>\n      <td class=\"n\"><strong>North</strong>\n        <br>Water\n        <br>Blue\n        <br>Change\n      </td>\n      <td class=\"ne\"><strong>Northeast</strong>\n        <br>Earth\n        <br>Yellow\n        <br>Direction\n      </td>\n    </tr>\n    <tr>\n      <td class=\"w\"><strong>West</strong>\n        <br>Metal\n        <br>Gold\n        <br>Youth\n      </td>\n      <td class=\"c\"><strong>Center</strong>\n        <br>All\n        <br>Purple\n        <br>Harmony\n      </td>\n      <td class=\"e\"><strong>East</strong>\n        <br>Wood\n        <br>Blue\n        <br>Future\n      </td>\n    </tr>\n    <tr>\n      <td class=\"sw\"><strong>Southwest</strong>\n        <br>Earth\n        <br>Brown\n        <br>Tranquility\n      </td>\n      <td class=\"s\"><strong>South</strong>\n        <br>Fire\n        <br>Orange\n        <br>Fame\n      </td>\n      <td class=\"se\"><strong>Southeast</strong>\n        <br>Wood\n        <br>Green\n        <br>Romance\n      </td>\n    </tr>\n\n  </table>\n\n  <script>\n    let table = document.getElementById('bagua-table');\n\n    let selectedTd;\n\n    table.onclick = function(event) {\n      let target = event.target;\n\n      while (target != this) {\n        if (target.tagName == 'TD') {\n          highlight(target);\n          return;\n        }\n        target = target.parentNode;\n      }\n    }\n\n    function highlight(node) {\n      if (selectedTd) {\n        selectedTd.classList.remove('highlight');\n      }\n      selectedTd = node;\n      selectedTd.classList.add('highlight');\n    }\n  </script>\n\n</body>\n\n</html>"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/1-why-return-false-fails/solution.md",
    "content": "Lorsque le navigateur lit l'attribut `on*`, comme `onclick`, il créer le gestionnaire depuis son contenu.\n\nPour `onclick=\"handler()\"` la fonction sera:\n\n```js\nfunction(event) {\n  handler() // le contenu de onclick\n}\n```\n\nMaintenant nous pouvons voir que la valeur retournée par `handler()` n'est pas utilisée et n'affecte pas le résultat.\n\nLa correction est simple:\n\n```html run\n<script>\n  function handler() {\n    alert(\"...\");\n    return false;\n  }\n</script>\n\n<a href=\"https://w3.org\" onclick=\"*!*return handler()*/!*\">w3.org</a>\n```\n\nNous pouvons aussi utiliser `event.preventDefault()`, comme ceci:\n\n```html run\n<script>\n*!*\n  function handler(event) {\n    alert(\"...\");\n    event.preventDefault();\n  }\n*/!*\n</script>\n\n<a href=\"https://w3.org\" onclick=\"*!*handler(event)*/!*\">w3.org</a>\n```\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/1-why-return-false-fails/task.md",
    "content": "importance: 3\n\n---\n\n# Pourquoi \"return false\" ne fonctionne pas?\n\nPourquoi dans le code ci-dessous `return false` ne fonctionne pas?\n\n```html autorun run\n<script>\n  function handler() {\n    alert( \"...\" );\n    return false;\n  }\n</script>\n\n<a href=\"https://w3.org\" onclick=\"handler()\">le navigateur va aller sur w3.org</a>\n```\n\nLe navigateur suit le lien lors du clic, mais nous ne voulons pas ça.\n\nComment réparer ce problème?\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.md",
    "content": "C'est une bonne utilisation de la délégation d'évènement.\n\nDans la vie réelle au lieu de demander nous pouvons envoyer une requête de \"logging\" au serveur pour sauvegarder les informations sur où l'utilisateur a quitté. Ou nous pouvons charger le contenu et l'afficher directement dans la page (si permis).\n\nTout ce que nous avons à faire est de capturer le `contents.onclick` et utiliser `confirm` pour demander à l'utilisateur. Une bonne idée serait d'utiliser `link.getAttribute('href')` plutôt que `link.href` pour l'URL. Regardez la solution pour plus de détails.\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    #contents {\n      padding: 5px;\n      border: 1px green solid;\n    }\n  </style>\n</head>\n\n<body>\n\n  <fieldset id=\"contents\">\n    <legend>#contents</legend>\n    <p>\n      How about to read <a href=\"https://wikipedia.org\">Wikipedia</a> or visit <a href=\"https://w3.org\"><i>W3.org</i></a> and learn about modern standards?\n    </p>\n  </fieldset>\n\n  <script>\n    contents.onclick = function(event) {\n\n      function handleLink(href) {\n        let isLeaving = confirm(`Quitter pour ${href}?`);\n        if (!isLeaving) return false;\n      }\n\n      let target = event.target.closest('a');\n\n      if (target && contents.contains(target)) {\n        return handleLink(target.getAttribute('href'));\n      }\n    };\n  </script>\n  \n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/2-catch-link-navigation/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    #contents {\n      padding: 5px;\n      border: 1px green solid;\n    }\n  </style>\n</head>\n\n<body>\n\n  <fieldset id=\"contents\">\n    <legend>#contents</legend>\n    <p>\n      How about to read <a href=\"https://wikipedia.org\">Wikipedia</a> or visit <a href=\"https://w3.org\"><i>W3.org</i></a> and learn about modern standards?\n    </p>\n  </fieldset>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/2-catch-link-navigation/task.md",
    "content": "importance: 5\n\n---\n\n# Capturer des liens dans l'élément\n\nFaire en sorte que tous les liens dans l'élément avec `id=\"contents\"` demande à l'utilisateur s'il veut vraiment partir. Et s'il ne veut pas ne suivez pas le lien.\n\nCommce ceci:\n\n[iframe height=100 border=1 src=\"solution\"]\n\nDétails:\n\n- Le HTML à l'intérieur de l'élément peut être chargé ou regénéré dynamiquement à n'importe quel moment, donc nous ne pouvons pas trouver tous les liens et mettre des gestionnaires dessus. Utilisez la délégation d'évènement.\n- Le contenu peut avoir des éléments imbriqués. À l'intérieur des liens aussi, comme ceci `<a href=\"..\"><i>...</i></a>`.\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/3-image-gallery/solution.md",
    "content": "La solution est d'assigner le gestionnaire au conteneur et suivre les clics. Si un clic est sur le lien `<a>`, alors changer `src` de `#largeImg` pour le `href` de la miniature.\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css",
    "content": "body {\n  margin: 0;\n  padding: 0;\n  font: 75%/120% sans-serif;\n}\n\n#largeImg {\n  border: solid 1px #ccc;\n  width: 550px;\n  height: 400px;\n  padding: 5px;\n}\n\n#thumbs a {\n  border: solid 1px #ccc;\n  width: 100px;\n  height: 100px;\n  padding: 3px;\n  margin: 2px;\n  float: left;\n}\n\n#thumbs a:hover {\n  border-color: #FF9900;\n}\n\n#thumbs li {\n  list-style: none;\n}\n\n#thumbs {\n  margin: 0;\n  padding: 0;\n}"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <title>Gallery</title>\n  <link rel=\"stylesheet\" href=\"gallery.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <p><img id=\"largeImg\" src=\"https://en.js.cx/gallery/img1-lg.jpg\" alt=\"Large image\"></p>\n\n  <ul id=\"thumbs\">\n    <!-- le navigateur affiche une petite infobulle lors du survol avec le texte de l'attribut \"title\" -->\n    <li>\n      <a href=\"https://en.js.cx/gallery/img2-lg.jpg\" title=\"Image 2\"><img src=\"https://en.js.cx/gallery/img2-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img3-lg.jpg\" title=\"Image 3\"><img src=\"https://en.js.cx/gallery/img3-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img4-lg.jpg\" title=\"Image 4\"><img src=\"https://en.js.cx/gallery/img4-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img5-lg.jpg\" title=\"Image 5\"><img src=\"https://en.js.cx/gallery/img5-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img6-lg.jpg\" title=\"Image 6\"><img src=\"https://en.js.cx/gallery/img6-thumb.jpg\"></a>\n    </li>\n  </ul>\n\n  <script>\n    thumbs.onclick = function(event) {\n      let thumbnail = event.target.closest('a');\n\n      if (!thumbnail) return;\n      showThumbnail(thumbnail.href, thumbnail.title);\n      event.preventDefault();\n    }\n\n    function showThumbnail(href, title) {\n      largeImg.src = href;\n      largeImg.alt = title;\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css",
    "content": "body {\n  margin: 0;\n  padding: 0;\n  font: 75%/120% sans-serif;\n}\n\n#largeImg {\n  border: solid 1px #ccc;\n  width: 550px;\n  height: 400px;\n  padding: 5px;\n}\n\n#thumbs a {\n  border: solid 1px #ccc;\n  width: 100px;\n  height: 100px;\n  padding: 3px;\n  margin: 2px;\n  float: left;\n}\n\n#thumbs a:hover {\n  border-color: #FF9900;\n}\n\n#thumbs li {\n  list-style: none;\n}\n\n#thumbs {\n  margin: 0;\n  padding: 0;\n}"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <title>Gallery</title>\n  <link rel=\"stylesheet\" href=\"gallery.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <p><img id=\"largeImg\" src=\"https://en.js.cx/gallery/img1-lg.jpg\" alt=\"Large image\"></p>\n\n  <ul id=\"thumbs\">\n    <!-- le navigateur affiche une petite infobulle lors du survol avec le texte de l'attribut \"title\" -->\n    <li>\n      <a href=\"https://en.js.cx/gallery/img2-lg.jpg\" title=\"Image 2\"><img src=\"https://en.js.cx/gallery/img2-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img3-lg.jpg\" title=\"Image 3\"><img src=\"https://en.js.cx/gallery/img3-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img4-lg.jpg\" title=\"Image 4\"><img src=\"https://en.js.cx/gallery/img4-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img5-lg.jpg\" title=\"Image 5\"><img src=\"https://en.js.cx/gallery/img5-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img6-lg.jpg\" title=\"Image 6\"><img src=\"https://en.js.cx/gallery/img6-thumb.jpg\"></a>\n    </li>\n  </ul>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/3-image-gallery/task.md",
    "content": "importance: 5\n\n---\n\n# Galerie d'images\n\nCréer une galerie d'images dans laquelle l'image principale change lors d'un clic sur une miniature.\n\nComme ceci:\n\n[iframe src=\"solution\" height=600]\n\nP.S. Utilisez la délégation d'évènement.\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/article.md",
    "content": "# Actions par défaut du navigateur\n\nBeaucoup d'évènements mènent à l'exécution d'actions par le navigateur.\n\nPar exemple:\n\n- Un clic sur un lien -- initie la navigation vers son URL.\n- Un clic sur un bouton d'envoi de formulaire -- initie son envoie vers le serveur.\n- Appuyer sur un bouton de la souris au-dessus d'un texte et la déplacer -- sélectionne le texte.\n\nSi nous gérons un évènement avec JavaScript, nous pouvons ne pas avoir envie de déclencher l'action de navigateur associée, et déclencher un autre comportement à la place.\n\n## Empêcher les actions du navigateur\n\nIl y a deux manières de dire au navigateur que nous ne souhaitons pas qu'il agisse:\n\n- La manière principale est d'utiliser l'objet `event`. Il y a une méthode `event.preventDefault()`.\n- Si le gestionnaire d'évènement a été assigné en utilisant `on<event>` (pas par `addEventListener`), alors renvoyer `false` fonctionne de la même manière.\n\nDans cet HTML un clic sur un lien n'entraine pas une navigation, le navigateur ne fait rien :\n\n```html autorun height=60 no-beautify\n<a href=\"/\" onclick=\"return false\">Cliquez ici</a>\nou\n<a href=\"/\" onclick=\"event.preventDefault()\">ici</a>\n```\n\nDans le prochain exemple nous allons utiliser cette technique pour créer un menu avec JavaScript.\n\n```warn header=\"Renvoyer `false` depuis un gestionnaire d'évènement est une exception\"\nLa valeur renvoyée par un gestionnaire d'évènement est généralement ignorée.\n\nLa seule exception est `return false` depuis un gestionnaire assigné en utilisant `on<event>`.\n\nDans tous les autres cas, la valeur de `return` est ignorée. Il n'y a aucune raison de renvoyer `true`.\n```\n\n### Exemple: le menu\n\nConsidérez le menu d'un site, comme ceci:\n\n```html\n<ul id=\"menu\" class=\"menu\">\n  <li><a href=\"/html\">HTML</a></li>\n  <li><a href=\"/javascript\">JavaScript</a></li>\n  <li><a href=\"/css\">CSS</a></li>\n</ul>\n```\n\nVoici à quoi il ressemble avec un peu de CSS:\n\n[iframe height=70 src=\"menu\" link edit]\n\nLes objets du menu sont implémentés comme des liens HTML `<a>`, pas des boutons `<button>`. Il y a plusieurs raisons pour ceci, par exemple:\n\n- Beaucoup de gens aiment utiliser \"clic droit\" -- \"ouvrir dans une nouvelle fenêtre\". Si nous utilisons `<button>` ou `<span>`, cela ne fonctionne pas.\n- Les moteurs de recherche suivent les liens `<a href=\"...\">` lors de l'indexation.\n\nDonc nous utilisons `<a>`. Mais normalement nous avons l'intention de gérer les clics avec JavaScript. Donc nous devrions empêcher les actions par défaut du navigateur.\n\nComme ici:\n\n```js\nmenu.onclick = function(event) {\n  if (event.target.nodeName != 'A') return;\n\n  let href = event.target.getAttribute('href');\n  alert( href ); // ...peut être en chargement depuis le serveur, génération d'UI etc\n\n*!*\n  return false; // empêche l'action du navigateur (ne va pas sur l'URL)\n*/!*\n};\n```\n\nSi nous enlevons `return false`, alors après l'exécution de notre code le navigateur fera son \"action par défaut\" -- naviguer vers l'URL dans `href`. Et nous n'avons pas besoin de ça ici comme nous gérons nous-mêmes les clics.\n\nAu passage, utiliser la délégation d'évènement ici rend notre menu très flexible. Nous pouvons ajouter des listes imbriquées et les styliser en utilisant CSS pour \"les faire glisser\".\n\n````smart header=\"Les évènements suivis\"\nCertains évènements se suivent les uns après les autres. Si nous empêchons le premier évènement, il n'y aura pas de second.\n\nPar exemple, `mousedown` sur un champ `<input>` entraine un focus dessus, et l'évènement `focus`. Si nous empêchons l'évènement `mousedown`, il n'y a pas de focus.\n\nEssayez de cliquer sur le premier `<input>` ci-dessous -- l'évènement `focus` se produit. Mais si vous cliquez sur le second, il n'y a pas de focus.\n\n```html run autorun\n<input value=\"Focus fonctionne\" onfocus=\"this.value=''\">\n<input *!*onmousedown=\"return false\"*/!* onfocus=\"this.value=''\" value=\"Cliquez sur moi\">\n```\n\nC'est parce que l'action du navigateur est annulée lors de `mousedown`. Le focus est toujours possible si nous utilisons une autre manière d'entrer dans le champ de saisie. Par exemple, la touche `Tab` pour passer du premier champ au deuxième. Mais plus avec le clic de la souris.\n````\n\n## L'option de gestionnaire \"passive\"\n\nL'option facultative `passive: true` de `addEventListener` signale au navigateur que ce gestionnaire n'appellera pas `preventDefault()`.\n\nPourquoi cela pourrait-il être nécessaire ?\n\nIl y a certains évènements comme `touchmove` sur les appareils mobile (lorsque l'utilisateur déplace ses doigts sur l'écran), qui entrainent un défilement par défaut, mais ce défilement peut être empêché en utilisant `preventDefault()` dans le gestionnaire.\n\nDonc lorsque le navigateur detecte un tel évènement, il doit d'abord traiter tous les gestionnaires, et si `preventDefault` n'est appelé nulle part, il peut continuer avec le défilement. Cela peut causer des délais et \"tremblements\" inutile dans l'UI.\n\nL'option `passive: true` communique au navigateur que le gestionnaire n'annulera pas le défilement. Alors le navigateur défile immédiatement, fournissant ainsi une expérience fluide au maximum, et l'évènement est géré au passage.\n\nPour certains navigateurs (Firefox, Chrome), `passive` est `true` par défaut pour les évènements `touchstart` et `touchmove`.\n\n\n## event.defaultPrevented\n\nLa propriété `event.defaultPrevented` est `true` si l'action par défaut a été empêchée, et `false` dans les autres cas.\n\nIl y a un cas d'utilisation intéressant avec ceci.\n\nDans le chapitre <info:bubbling-and-capturing> nous avons parlé de `event.stopPropagation()` et pourquoi arrêter le bubbling est mauvais?\n\nParfois nous pouvons utiliser  `event.defaultPrevented` à la place, pour signaler aux autres gestionnaires d'évènement que l'évènement a été géré.\n\nVoyons un exemple pratique.\n\nPar défaut le navigateur affiche un menu contextuel avec des options standards lors de l'évènement `contextmenu` (clic droit). Nous pouvons empêcher cela et afficher le nôtre comme ceci:\n\n```html autorun height=50 no-beautify run\n<button>Clic droit affiche le menu contextuel du navigateur</button>\n\n<button *!*oncontextmenu=\"alert('Affiche notre menu'); return false\"*/!*>\n  Clic droit affiche notre menu contextuel\n</button>\n```\n\nMaintenant, en plus de ce menu contextuel nous voulons implémenter un menu contextuel sur tout le document.\n\nAprès un clic droit, le menu contextuel le plus proche devrait s'afficher.\n\n```html autorun height=80 no-beautify run\n<p>Clic droit ici pour le menu contextuel du document</p>\n<button id=\"elem\">Clic droit ici pour le menu contextuel du bouton</button>\n\n<script>\n  elem.oncontextmenu = function(event) {\n    event.preventDefault();\n    alert(\"Menu contextuel du bouton\");\n  };\n\n  document.oncontextmenu = function(event) {\n    event.preventDefault();\n    alert(\"Menu contextuel du document\");\n  };\n</script>\n```\n\nLe problème est que lorsque l'on clique sur `elem`, nous obtenons deux menus: le menu du bouton et (l'évènement remonte) le menu du document.\n\nComment régler cela? Une des solutions est de penser: \"Quand nous gérons le clic droit dans le gestionnaire du bouton, arrêtons sa propagation\" et utilisons `event.stopPropagation()`:\n\n```html autorun height=80 no-beautify run\n<p>Clic droit ici pour le menu contextuel du document</p>\n<button id=\"elem\">Clic droit ici pour le menu contextuel du bouton (réparé avec event.stopPropagation)</button>\n\n<script>\n  elem.oncontextmenu = function(event) {\n    event.preventDefault();\n*!*\n    event.stopPropagation();\n*/!*\n    alert(\"Menu contextuel du bouton\");\n  };\n\n  document.oncontextmenu = function(event) {\n    event.preventDefault();\n    alert(\"Menu contextuel du document\");\n  };\n</script>\n```\n\nMaintenant le bouton contextuel du menu fonctionne comme voulu. Mais le prix est elevé. Nous bloquons pour toujours l'accès aux informations sur les clics droits au code extérieur, comme les compteurs qui récoltent des statistiques. Ce n'est pas sage.\n\nUne solution alternative est de vérifier dans le gestionnaire du `document` si l'action par défaut a été empêchée? Si c'est le cas, alors l'évènement a été géré, et nous n'avons pas besoin de réagir.\n\n\n```html autorun height=80 no-beautify run\n<p>Clic droit ici pour le menu contextuel du document (une vérification a été ajoutée avec event.defaultPrevented)</p>\n<button id=\"elem\">Clic droit ici pour le menu contextuel du bouton</button>\n\n<script>\n  elem.oncontextmenu = function(event) {\n    event.preventDefault();\n    alert(\"Menu contextuel du bouton\");\n  };\n\n  document.oncontextmenu = function(event) {\n*!*\n    if (event.defaultPrevented) return;\n*/!*\n\n    event.preventDefault();\n    alert(\"Menu contextuel du document\");\n  };\n</script>\n```\n\nMaintenant tout fonctionne correctement. Si nous avons des éléments imbriqués, et que chacun d'eux possède son propre menu contextuel, cela fonctionnerait aussi. Assurez-vous juste de vérifier `event.defaultPrevented` dans chaque gestionnaire `contextmenu`.\n\n```smart header=\"event.stopPropagation() et event.preventDefault()\"\nComme nous pouvons le voir, `event.stopPropagation()` et `event.preventDefault()` (aussi connu comme `return false`) sont deux choses différentes. Ils n'ont pas liens.\n```\n\n```smart header=\"Architecture de menus contextuels imbriqués\"\nIl y a aussi des manières alternatives d'implémenter des menus contextuels imbriqués. Une d'elle est d'avoir un seul objet global avec un gestionnaire pour `document.oncontextmenu`, et d'autre méthodes pour nous permettre de stocker d'autres gestionnaires dedans.\n\nL'objet captura tous les clics droit, et lancera le gestionnaire approprié parmi les gestionnaires stockés.\n\nMais chaque partie du code qui nécessite un menu contextuel devra avoir connaissance de cet objet et l'utiliser plutôt que le gestionnaire `contextmenu`.\n```\n\n## Résumé\n\nIl y a plusieurs actions de navigateur par défaut:\n\n- `mousedown` -- débute la sélection (déplacer la souris pour sélectionner).\n- `click` sur `<input type=\"checkbox\">` -- coche/décoche le `input`.\n- `submit` -- ciquer sur un `<input type=\"submit\">` oou appuyer sur `key:Enter` à l'intérieur d'un formulaire entraine le lancement de cet évènement, et le navigateur envoie le formulaire après.\n- `keydown` -- appuyer sur une touche peut amener à ajouter un caractère dans un champ, ou d'autres actions.\n- `contextmenu` -- l'évènement se déclenche lors d'un clic droit, l'action est d'afficher le menu contextuel du navigateur.\n- ...il y en a plus...\n\nToutes les actions par défaut peuvent être empêchées si nous voulons gérer l'évènement exclusivement avec JavaScript.\n\nPour empêcher une action par défaut -- utilisez soit `event.preventDefault()`, soit `return false`. La seconde méthode ne fonctionne que pour les gestionnaires assignés avec `on<event>`.\n\nL'option `passive: true` de `addEventListener` dis au navigateur que l'action ne va pas être empêchée. C'est utile pour certains évènements de mobile, comme `touchstart` et `touchmove`, pour dire au navigateur qu'il ne devrait pas attendre que tous les gestionnaires soient terminés pour défiler la page.\n\nSi l'action par défaut a été empêchée, la valeur de `event.defaultPrevented` devient `true`, sinon `false`.\n\n```warn header=\"Restez sémantique, n'abusez pas\"\nTechniquement, en empêchant les actions par défaut et en ajoutant du JavaScript nous pouvons personnaliser le comportement de n'importe quel élément. Par exemple, nous pouvons faire fonctionner un lien `<a>` comme un bouton, et un bouton `<button>` se comporter comme un lien (rediriger vers une autre URL ou autre).\n\nMais nous devrions généralement garder la signification sémantique des éléments HTML. Par exemple `<a>` devrait entrainer une navigation, pas un bouton.\n\nCe n'est pas \"juste une bonne chose\", cela rend votre HTML meilleur en terme d'accessibilité.\n\nAussi, si nous prenons l'exemple avec `<a>`, veuillez noter: un navigateur nous permet d'ouvrir de tels liens dans une nouvelle fenêtre (en faisant un clic droit dessus par exemple). Et les gens aiment ça. Mais si nous faisons un bouton qui se comporte comme un lien en utilisant JavaScript et qui ressemble à un lien en utilisant CSS, les fonctionnalités de navigateur spécifiques à `<a>` ne fonctionneront toujours pas.\n```\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/menu.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"menu.css\" />\n</head>\n\n<body>\n\n  <ul id=\"menu\" class=\"menu\">\n    <li><a href=\"/html\">HTML</a></li>\n    <li><a href=\"/javascript\">JavaScript</a></li>\n    <li><a href=\"/css\">CSS</a></li>\n  </ul>\n\n  <script src=\"menu.js\"></script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/menu.view/menu.css",
    "content": ".menu li {\n  display: inline-block;\n  margin: 0;\n}\n\n.menu > li a {\n  display: inline-block;\n  margin: 0 2px;\n  outline: none;\n  text-align: center;\n  text-decoration: none;\n  font: 14px/100% sans-serif;\n  padding: .5em 2em .55em;\n  text-shadow: 0 1px 1px rgba(0, 0, 0, .3);\n  border-radius: .5em;\n  box-shadow: 0 1px 2px rgba(0, 0, 0, .2);\n  color: #d9eef7;\n  border: solid 1px #0076a3;\n  background: #0095cd;\n}\n\n.menu > li:hover a {\n  text-decoration: none;\n  background: #007ead;\n}"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/menu.view/menu.js",
    "content": "menu.onclick = function(event) {\n  if (event.target.nodeName != 'A') return;\n\n  let href = event.target.getAttribute('href');\n  alert(href);\n\n  return false; // empêcher le changement d'URL\n};\n"
  },
  {
    "path": "2-ui/2-events/05-dispatch-events/article.md",
    "content": "# Distribution d'événements personnalisés\n\nNous pouvons non seulement affecter des gestionnaires, mais également générer des événements à partir de JavaScript.\n\nLes événements personnalisés peuvent être utilisés pour créer des \"composants graphiques\". Par exemple, un élément racine de notre propre menu basé sur JS peut déclencher des événements indiquant ce qui se passe avec le menu: `open` (menu ouvert), `select` (un élément est sélectionné) et ainsi de suite. Un autre code peut écouter les événements et observer ce qui se passe avec le menu.\n\nNous pouvons générer non seulement des événements complètement nouveaux, que nous inventons pour nos propres besoins, mais aussi des événements intégrés, tels que `click`, `mousedown`, etc. Cela peut être utile pour les tests automatisés.\n\n## Constructeur d'événements\n\nLes classes d'événements intégrées forment une hiérarchie, similaire aux classes d'éléments DOM. La racine est la classe intégrée [Event](https://www.w3.org/TR/dom/#event).\n\nNous pouvons créer des objets `Event` comme ceci:\n\n```js\nlet event = new Event(type[, options]);\n```\n\nArguments:\n\n- *type* -- type d'événement, soit une chaîne comme `\"click\"` ou la nôtre comme `\"my-event\"`.\n- *options* -- l'objet avec deux propriétés facultatives:\n  - `bubbles: true/false` -- si `true`, alors l'événement bouillonne.\n  - `cancelable: true/false` -- si `true`, alors \"l'action par défaut\" peut être empêchée. Plus tard, nous verrons ce que cela signifie pour les événements personnalisés.\n\n  Par défaut, les deux sont \"false\": `{bubbles: false, cancelable: false}`.\n\n## dispatchEvent\n\nAprès la création d'un objet événement, nous devons le \"lancer\" sur un élément en utilisant l'appel `elem.dispatchEvent(event)`.\n\nEnsuite, les gestionnaires réagissent comme s'il s'agissait d'un événement de navigateur normal. Si l'événement a été créé avec le marqueur `bubbles`, alors il bouillonne.\n\nDans l'exemple ci-dessous l'événement `click` est initié avec JavaScript. Le gestionnaire fonctionne de la même manière que si le bouton était cliqué:\n\n```html run no-beautify\n<button id=\"elem\" onclick=\"alert('Click!');\">Autoclick</button>\n\n<script>\n  let event = new Event(\"click\");\n  elem.dispatchEvent(event);\n</script>\n```\n\n```smart header=\"event.isTrusted\"\nIl existe un moyen de distinguer un événement utilisateur \"réel\" d'un événement généré par script.\n\nLa propriété `event.isTrusted` est `true` pour les événements qui proviennent d'actions réelles de l'utilisateur et `false` pour les événements générés par un script.\n```\n\n## Exemple de bouillonnement\n\nNous pouvons créer un événement qui bouillonne avec le nom `\"hello\"` et l'attraper sur `document`.\n\nTout ce dont nous avons besoin de faire est de définir `bulles` en tant que `true`:\n\n```html run no-beautify\n<h1 id=\"elem\">Hello from the script!</h1>\n\n<script>\n  // attraper sur document...\n  document.addEventListener(\"hello\", function(event) { // (1)\n    alert(\"Hello from \" + event.target.tagName); // Hello de H1\n  });\n\n  // ...distribué sur elem!\n  let event = new Event(\"hello\", {bubbles: true}); // (2)\n  elem.dispatchEvent(event);\n\n  // le gestionnaire sur le document activera et affichera le message.\n\n</script>\n```\n\n\nRemarques:\n\n1. Nous devrions utiliser `addEventListener` pour nos événements personnalisés, car `on<event>` n'existe que pour les événements intégrés, `document.onhello` ne fonctionne pas.\n2. Il est essentiel de définir `bubbles: true`, sinon l'événement ne bouillonnera pas.\n\nLe mécanisme de bouillonnement est le même pour les événements intégrés (`click`) et personnalisés (`hello`). Il existe également des étapes de capture et de bouillonnement.\n\n## MouseEvent, KeyboardEvent et autres\n\nVoici une courte liste de classes pour les événements UI de la [spécification](https://www.w3.org/TR/uievents):\n\n- `UIEvent`\n- `FocusEvent`\n- `MouseEvent`\n- `WheelEvent`\n- `KeyboardEvent`\n- ...\n\nNous devrions les utiliser au lieu de `new Event` si nous voulons créer de tels événements. Par exemple, `new MouseEvent(\"click\")`.\n\nLe bon constructeur permet de spécifier des propriétés standard pour ce type d'événement.\n\nComme `clientX/clientY` pour un événement de souris:\n\n```js run\nlet event = new MouseEvent(\"click\", {\n  bubbles: true,\n  cancelable: true,\n  clientX: 100,\n  clientY: 100\n});\n\n*!*\nalert(event.clientX); // 100\n*/!*\n```\n\nRemarque: le constructeur générique `Event` ne le permet pas.\n\nEssayons:\n\n```js run\nlet event = new Event(\"click\", {\n  bubbles: true, // bouillonne uniquement et annulable\n  cancelable: true, // travail dans le constructeur de l'événement\n  clientX: 100,\n  clientY: 100\n});\n\n*!*\nalert(event.clientX); // indéfini, la propriété inconnue est ignorée!\n*/!*\n```\n\nTechniquement, nous pouvons contourner cela en attribuant directement `event.clientX=100` après la création. C'est donc une question de commodité et de respect des règles. Les événements générés par le navigateur ont toujours le bon type.\n\nLa liste complète des propriétés des différents événements UI se trouve dans la spécification, par exemple, [MouseEvent](https://www.w3.org/TR/uievents/#mouseevent).\n\n## Événements personnalisés\n\nPour nos propres types d'événements comme `\"hello\"`, nous devrions utiliser `new CustomEvent`. Techniquement, [CustomEvent](https://dom.spec.whatwg.org/#customevent) est identique à `Event`, à une exception près.\n\nDans le deuxième argument (objet), nous pouvons ajouter une propriété supplémentaire `detail` pour toute information personnalisée que nous voulons transmettre avec l'événement.\n\nPar exemple:\n\n```html run refresh\n<h1 id=\"elem\">Hello for John!</h1>\n\n<script>\n  // des détails supplémentaires sont fournis avec l'événement au gestionnaire\n  elem.addEventListener(\"hello\", function(event) {\n    alert(*!*event.detail.name*/!*);\n  });\n\n  elem.dispatchEvent(new CustomEvent(\"hello\", {\n*!*\n    detail: { name: \"John\" }\n*/!*\n  }));\n</script>\n```\n\nLa propriété `detail` peut avoir n'importe quelle donnée. Techniquement, nous pourrions vivre sans, car nous pouvons attribuer n'importe quelle propriété à un objet `new Event` normal après sa création. Mais `CustomEvent` fournit le champ spécial `detail` pour éviter les conflits avec d'autres propriétés d'événement.\n\nDe plus, la classe event décrit \"quel genre d'événement\" il s'agit, et si l'événement est personnalisé, alors nous devrions utiliser `CustomEvent` juste pour être clair sur ce que c'est.\n\n## event.preventDefault()\n\nDe nombreux événements de navigateur ont une \"action par défaut\", telle que la navigation vers un lien, le démarrage d'une sélection, etc.\n\nPour les nouveaux événements personnalisés, il n'y a certainement pas d'actions de navigateur par défaut, mais un code qui distribue un tel événement peut avoir ses propres plans pour ce qu'il faut faire après le déclenchement de l'événement.\n\nEn appelant `event.preventDefault()`, un gestionnaire d'événements peut envoyer un signal indiquant que ces actions doivent être annulées.\n\nDans ce cas, l'appel à `elem.dispatchEvent(event)` retourne `false`. Et le code qui l'a envoyé sait qu'il ne devrait pas continuer.\n\nVoyons un exemple pratique - un lapin qui se cache (peut être un menu de clôture ou autre chose).\n\nCi-dessous, vous pouvez voir une fonction `#rabbit` et `hide()` qui la distribue l'événement `\"hide\"`, pour faire savoir à toutes les parties intéressées que le lapin va se cacher.\n\nN'importe quel gestionnaire peut écouter cet événement avec `rabbit.addEventListener('hide',...)` et, si nécessaire, annuler l'action en utilisant `event.preventDefault()`. Alors le lapin ne disparaîtra pas:\n\n```html run refresh autorun\n<pre id=\"rabbit\">\n  |\\   /|\n   \\|_|/\n   /. .\\\n  =\\_Y_/=\n   {>o<}\n</pre>\n<button onclick=\"hide()\">Hide()</button>\n\n<script>\n  function hide() {\n    let event = new CustomEvent(\"hide\", {\n      cancelable: true // sans ce marqueur, preventDefault ne fonctionne pas\n    });\n    if (!rabbit.dispatchEvent(event)) {\n      alert('The action was prevented by a handler');\n    } else {\n      rabbit.hidden = true;\n    }\n  }\n\n  rabbit.addEventListener('hide', function(event) {\n    if (confirm(\"Call preventDefault?\")) {\n      event.preventDefault();\n    }\n  });\n</script>\n```\n\nRemarque: l'événement doit avoir le marqueur `cancelable: true`, sinon l'appel `event.preventDefault()` est ignoré.\n\n## Les événements imbriqués sont synchrones\n\n\nLes événements sont généralement traités dans une file d'attente. C'est-à-dire : si le navigateur traite `onclick` et qu'un nouvel événement se produit, par exemple la souris a bougé, sa gestion est mise en file d'attente, les gestionnaires `mousemove` correspondants seront appelés après la fin du traitement `onclick`.\n\n\nL'exception notable est lorsqu'un événement est déclenché à partir d'un autre, par exemple en utilisant `dispatchEvent`. Ces événements sont traités immédiatement: les nouveaux gestionnaires d'événements sont appelés, puis la gestion des événements en cours est reprise.\n\nPar exemple, dans le code ci-dessous, l'événement `menu-open` est déclenché pendant `onclick`.\n\n\nIl est traité immédiatement, sans attendre la fin du gestionnaire `onclick` :\n\n\n\n```html run autorun\n<button id=\"menu\">Menu (click me)</button>\n\n<script>\n  menu.onclick = function() {\n    alert(1);\n\n    menu.dispatchEvent(new CustomEvent(\"menu-open\", {\n      bubbles: true\n    }));\n\n    alert(2);\n  };\n\n  // se déclenche entre 1 et 2\n  document.addEventListener('menu-open', () => alert('nested'));\n</script>\n```\n\nL'ordre de sortie est: 1 -> imbriqué -> 2.\n\nVeuillez noter que l'événement imbriqué `menu-open` est intercepté sur le `document`. La propagation et la gestion de l'événement imbriqué sont terminées avant que le traitement ne revienne au code externe (`onclick`).\n\n\nIl ne s'agit pas seulement de `dispatchEvent`, il y a d'autres cas. Si un gestionnaire d'événements appelle des méthodes qui déclenchent d'autres événements -- ils sont également traités de manière synchrone, de manière imbriquée.\n\n\nDisons que nous n'aimons pas ça. Nous voudrions que `onclick` soit entièrement traité en premier, indépendamment de `menu-open` ou de tout autre événement imbriqué.\n\nAlors, nous pouvons soit mettre le `dispatchEvent` (ou un autre appel déclencheur d'événement) à la fin de `onclick` ou, peut-être mieux, l'envelopper dans `setTimeout`:\n\n```html run\n<button id=\"menu\">Menu (click me)</button>\n\n<script>\n  menu.onclick = function() {\n    alert(1);\n\n    setTimeout(() => menu.dispatchEvent(new CustomEvent(\"menu-open\", {\n      bubbles: true\n    })));\n\n    alert(2);\n  };\n\n  document.addEventListener('menu-open', () => alert('nested'));\n</script>\n```\n\nDésormais, `dispatchEvent` s'exécute de manière asynchrone une fois l'exécution du code en cours terminée, y compris `mouse.onclick`, les gestionnaires d'événements sont donc totalement séparés.\n\nL'ordre de sortie devient: 1 -> 2 -> imbriqué.\n\n## Résumé\n\nPour générer un événement à partir du code, nous devons d'abord créer un objet événement.\n\nLe constructeur générique `Event(name, options)` accepte un nom d'événement arbitraire et l'objet `options` avec deux propriétés:\n- `bubbles: true` si l'événement doit bouillonner.\n- `cancelable: true` si `event.preventDefault()` doit fonctionner.\n\nD'autres constructeurs d'événements natifs tels que `MouseEvent`, `KeyboardEvent` et ainsi de suite acceptent des propriétés spécifiques à ce type d'événement. Par exemple, `clientX` pour les événements de souris.\n\nPour les événements personnalisés, nous devons utiliser le constructeur `CustomEvent`. Il a une option supplémentaire nommée `detail`, nous devons lui attribuer les données spécifiques à l'événement. Ensuite, tous les gestionnaires peuvent y accéder en tant que `event.detail`.\n\n\nMalgré la possibilité technique de générer des événements de navigateur comme  `click` ou `keydown`, nous devons les utiliser avec le plus grand soin.\n\nNous ne devrions pas générer d'événements de navigateur car c'est une manière pirate d'exécuter des gestionnaires. C'est une mauvaise architecture la plupart du temps.\n\n\nDes événements natifs peuvent être générés:\n\n- En tant que bidouille pour faire fonctionner les bibliothèques tierces de la manière nécessaire, si elles ne fournissent pas d'autres moyens d'interaction.\n- Pour les tests automatisés, pour \"cliquer sur le bouton\" dans le script et voir si l'interface réagit correctement.\n\nLes événements personnalisés avec nos propres noms sont souvent générés à des fins architecturales, pour signaler ce qui se passe dans nos menus, curseurs, carrousels, etc.\n"
  },
  {
    "path": "2-ui/2-events/index.md",
    "content": "# Introduction to Events\n\nAn introduction to browser events, event properties and handling patterns.\n"
  },
  {
    "path": "2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    .selected {\n      background: #0f0;\n    }\n\n    li {\n      cursor: pointer;\n    }\n  </style>\n</head>\n\n<body>\n\n  Cliquez sur un élément de la liste pour le sélectionner.\n  <br>\n\n  <ul id=\"ul\">\n    <li>Christopher Robin</li>\n    <li>Winnie-the-Pooh</li>\n    <li>Tigger</li>\n    <li>Kanga</li>\n    <li>Rabbit. Just rabbit.</li>\n  </ul>\n\n  <script>\n    ul.onclick = function(event) {\n      if (event.target.tagName != \"LI\") return;\n\n      if (event.ctrlKey || event.metaKey) {\n        toggleSelect(event.target);\n      } else {\n        singleSelect(event.target);\n      }\n\n    }\n\n    // empêcher la sélection inutile des éléments de la liste clickés\n    ul.onmousedown = function() {\n      return false;\n    };\n\n    function toggleSelect(li) {\n      li.classList.toggle('selected');\n    }\n\n    function singleSelect(li) {\n      let selected = ul.querySelectorAll('.selected');\n      for(let elem of selected) {\n        elem.classList.remove('selected');\n      }\n      li.classList.add('selected');\n    }\n\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    .selected {\n      background: #0f0;\n    }\n\n    li {\n      cursor: pointer;\n    }\n  </style>\n</head>\n\n<body>\n\n  Cliquez sur un élément de la liste pour le sélectionner.\n  <br>\n\n  <ul id=\"ul\">\n    <li>Christopher Robin</li>\n    <li>Winnie-the-Pooh</li>\n    <li>Tigger</li>\n    <li>Kanga</li>\n    <li>Rabbit. Just rabbit.</li>\n  </ul>\n\n  <script>\n    // ...votre code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/task.md",
    "content": "importance: 5\n\n---\n\n# Liste sélectionnable\n\nCréer une liste dont les éléments sont sélectionnables,comme dans les gestionnaire de fichiers\n\n- Un click sur un élément de la liste sélectionne seulement cet élément(ajoute la classe `.selected`), désélectionne tous les autres.\n- Si un click est effectué avec `key:Ctrl` (`key:Cmd` sur Mac), alors la sélection est inversée sur l'élément, mais les autres éléments ne sont pas modifiés\n\nLa demo:\n\n[iframe border=\"1\" src=\"solution\" height=180]\n\nP.S. Pour cette tâche on peut assumer que les éléments de cette liste sont uniquement du texte.Pas de tags imbriqués.\n\nP.P.S. Empêcher la séléction des textes  déclenchée par défaut par le navigateur lors d'un click.\n"
  },
  {
    "path": "2-ui/3-event-details/1-mouse-events-basics/article.md",
    "content": "# Evenements de la souris\n\nDans ce chapitre, nous verrons plus en détails les événements de la souris et leurs propriétés.\n\nRemarque: Ces événements peuvent provenir non seulement de \"périphériques de souris\", mais également de périphériques, tels que les téléphones et les tablettes, où ils sont émulés pour des raisons de compatibilité.\n\n## Les types d'évènements de Souris\n\nNous avons déjà vu certains de ces événements :\n\n`mousedown/mouseup`\n: Le bouton de la souris est appuyé puis relâché sur un élément.\n\n`mouseover/mouseout`\n: Le pointeur de la souris entre  ou sort d'un élément.\n\n`mousemove`\n: Chaque déplacement de la souris sur un élément déclenche cet évènement.\n\n`click`\n: est déclenché après un évènement `mousedown` et suite à un  `mouseup`  sur le même élément, si le bouton gauche de la souris a été utilisé\n\n`dblclick`\n: Se déclenche après deux clics sur le même élément dans un court laps de temps. Rarement utilisé de nos jours.\n\n`contextmenu`\n: Se déclenche lorsque le bouton droit de la souris est enfoncé. Il existe d'autres façons d'ouvrir un menu contextuel, par ex. en utilisant une touche spéciale du clavier, il se déclenche dans ce cas également, donc ce n'est pas exactement l'événement de la souris.\n\n... Il y a aussi plusieurs autres événements, nous les couvrirons plus tard.\n\n## Ordre des événements\n\nComme vous pouvez le voir dans la liste ci-dessus, une action utilisateur peut déclencher plusieurs événements.\n\nPar exemple, un clic gauche déclenche d'abord `mousedown`, lorsque le bouton est enfoncé, puis `mouseup` et `click` lorsqu'il est relâché.\n\nAu cas où une action unique initialise plusieurs évènements, leur ordre est fixé. C'est-à-dire que les gestionnaires sont appelés dans l'ordre `mousedown` -> `mouseup` -> `click`.\n\n```online\nClique sur le bouton en bas et vous verrez les évènements. Essayez l’évènement double clic aussi. Dans TestStand en bas tous les évènements de la souris sont enregistrés, et si il y a plus d’une seconde de retard entre eux, ils sont alors séparés par une ligne horizontale.\n\nSur le banc de test ci-dessous, tous les événements de souris sont enregistrés et s'il y a un délai de plus d'une seconde entre eux, ils sont séparés par une règle horizontale.\n\nNous pouvons également voir la propriété `button` qui permet de détecter le bouton de la souris, c'est expliqué ci-dessous.\n\n<input onmousedown=\"return logMouse(event)\" onmouseup=\"return logMouse(event)\" onclick=\"return logMouse(event)\" oncontextmenu=\"return logMouse(event)\" ondblclick=\"return logMouse(event)\" value=\"Click me with the right or the left mouse button\" type=\"button\"> <input onclick=\"logClear('test')\" value=\"Clear\" type=\"button\"> <form id=\"testform\" name=\"testform\"> <textarea style=\"font-size:12px;height:150px;width:360px;\"></textarea></form>\n```\n\n## Bouton de la souris\n\nLes événements liés aux clics ont toujours la propriété `button`, qui permet d'obtenir le bouton exact de la souris.\n\nNous ne l'utilisons généralement pas pour les événements `click` et `contextmenu`, car le premier se produit uniquement lors d'un clic gauche, et le second - uniquement lors d'un clic droit.\n\nD'un autre côté, les gestionnaires `mousedown` et `mouseup` peuvent avoir besoin de `event.button`, car ces événements se déclenchent sur n'importe quel bouton, donc `button` permet de faire la distinction entre \"right-mousedown\" et \"left-mousedown\".\n\nLes valeurs possibles de `event.button` sont :\n\n| État du bouton              | `event.button` |\n|-----------------------------|----------------|\n| Bouton gauche (principal)   | 0              |\n| Bouton central (auxiliaire) | 1              |\n| Bouton droit (secondaire)   | 2              |\n| X1 bouton (arrière)         | 3              |\n| X2 bouton (avant)          | 4              |\n\nLa plupart des souris n'ont que les boutons gauche et droit, donc les valeurs possibles sont `0` ou `2`. Les appareils tactiles génèrent également des événements similaires lorsque l'on appuie dessus.\n\nIl existe également la propriété `event.buttons` qui a tous les boutons actuellement pressés sous forme d'entier, un bit par bouton. En pratique cette propriété est très rarement utilisée, vous pouvez trouver des détails sur [MDN](mdn:/api/MouseEvent/buttons) si jamais vous en avez besoin.\n\n```warn header=\"Le `event.which` obsolète\"\nL'ancien code peut utiliser la propriété `event.which` qui est une ancienne manière non standard d'obtenir un bouton, avec des valeurs possibles :\n\n- `event.which == 1` – bouton gauche,\n- `event.which == 2` – bouton du milieu,\n- `event.which == 3` – bouton de droite.\n\nDorénavant, `event.which` est obsolète, nous ne devrions pas l'utiliser.\n```\n\n## Les Touches de Modifications: shift, alt, ctrl and meta\n\nTous les évènements de la souris contiennent des informations à propos des touches de modifications qui sont appuyées.\n\nPropriétés d'événement :\n\n- `shiftKey`: `key:Shift`\n- `altKey`: `key:Alt` (or `key:Opt` for Mac)\n- `ctrlKey`: `key:Ctrl`\n- `metaKey`: `key:Cmd` for Mac\n\nIls sont `true` si la touche correspondante fut appuyée durant l'évènement.\n\nPar exemple le bouton en bas fonctionne seulement avec `key:Alt+Shift`+click:\n\n```html autorun height=60\n<button id=\"button\">Alt+Shift+Click on me!</button>\n\n<script>\n  button.onclick = function(event) {\n*!*\n    if (event.altKey && event.shiftKey) {\n*/!*\n      alert('Hooray!');\n    }\n  };\n</script>\n```\n```warn header=\"Attention : Sur Mac c’est souvent `Cmd` au lieu de `Ctrl`\"\n\nSous Windows et Linux, il y a des touches modificatrices `key:Alt`, `key:Shift` et `key:Ctrl`. Sur Mac, il y en a une en plus : `key:Cmd`, correspondant à la propriété `metaKey`.\n\nDans la plupart des applications, lorsque Windows / Linux utilise `key:Ctrl`, sur Mac `key:Cmd` est utilisée.\n\nC'est-à-dire : lorsqu'un utilisateur Windows appuie sur `key:Ctrl+Enter` ou `key:Ctrl+A`, un utilisateur Mac presserait sur `key:Cmd+Enter` ou `key:Cmd+A`, etc.\n\nDonc, si nous voulons supporter des combinaisons comme `key:Ctrl`+click, alors pour Mac, il est logique d'utiliser `key:Cmd`+click. C'est plus confortable pour les utilisateurs de Mac.\n\nMême si nous aimerions forcer les utilisateurs de Mac à `key:Ctrl`+click -- c'est un peu difficile. Le problème est: un clic gauche avec `key:Ctrl` est interprété comme un *clic droit* sur MacOS, et il génère l'évènement `menu contextuel`, et non un `click` comme sur Windows/Linux.\n\nDonc, si nous voulons que les utilisateurs de tous les systèmes d'exploitation se sentent à l'aise, alors avec `ctrlKey` nous devrions vérifier la `metaKey`.\n\nPour JS-code cela signifie que nous devons contrôler si `if (event.ctrlKey || event.metaKey)`.\n```\n\n```warn header=\"Il y a aussi les appareils mobiles\"\n\nLes combinaisons de clavier sont un bon complément au flux de travail. Donc, si le visiteur utilise un clavier -- ils fonctionnent.\n\nMais si leur appareil n'en a pas, il devrait y avoir un moyen de vivre sans touches de modification.\n```\n\n## Cordonnées: clientX/Y, pageX/Y\n\nTous les événements de souris fournissent des coordonnées de deux manières :\n\nNous avons déjà couvert la différence entre eux dans le chapitre <info:coordinates>.\n\nEn résumé, les coordonnées relatives au document `pageX/Y` sont comptées à partir du coin supérieur gauche du document, et ne changent pas lorsque la page défile, tandis que `clientX/Y` sont comptées à partir du coin supérieur gauche de la fenêtre actuelle . Lorsque la page défile, ils changent.\n\nPar exemple, si nous avons une fenêtre de taille 500x500 et que la souris est dans le coin supérieur gauche, alors `clientX` et `clientY` sont `0`, peu importe comment la page est défilée.\n\nEt si la souris est au centre, alors `clientX` et `clientY` sont `250`, quelle que soit la place dans le document. Ils sont similaires à `position:fixed` dans cet aspect.\n\n````online\nDéplacez la souris sur le champ de saisie pour voir `clientX/clientY` (l'exemple est dans l `iframe`, ainsi les cordonnées sont relatives à cet `iframe`) :\n\n```html autorun height=50\n<input onmousemove=\"this.value=event.clientX+':'+event.clientY\" value=\"Mouse over me\">\n```\n````\n\nLes coordonnées relatives au document `pageX`, `pageY` sont comptées à partir du coin supérieur gauche du document, pas de la fenêtre. Vous pouvez en savoir plus sur les coordonnées dans le chapitre <info:coordinates>.\n\n## Empêcher la sélection sur le mousedown\n\nLe double clic de souris a un effet secondaire qui peut être dérangeant dans certaines interfaces: il sélectionne du texte.\n\npar exemple, double-cliquer sur le texte ci-dessous le sélectionne en plus de notre gestionnaire :\n\n```html autorun height=50\n<span ondblclick=\"alert('dblclick')\">Double-click me</span>\n```\n\nSi on appuie sur le bouton gauche de la souris et, sans le relâcher, on déplace la souris, la sélection devient alors souvent indésirable.\n\nIl existe plusieurs façons d’empêcher la sélection, que vous pouvez lire dans le chapitre <info:selection-range>.\n\nDans ce cas particulier, le moyen le plus raisonnable consiste à empêcher l'action du navigateur lors du `mousedown`. Il empêche ces deux sélections :\n\n```html autorun height=50\nAvant...\n<b ondblclick=\"alert('Click!')\" *!*onmousedown=\"return false\"*/!*>\n   Double-click sur moi\n</b>\n...Apres\n```\n\nDésormais, l'élément en gras n'est pas sélectionné lors d'un double-clic, et si vous appuyez sur le bouton gauche de la souris, la sélection ne sera pas lancée.\n\nRemarque: le texte à l'intérieur est toujours sélectionnable. Cependant, la sélection ne doit pas commencer sur le texte lui-même, mais avant ou après. Cela convient généralement aux utilisateurs.\n\n````smart header=\"Prévenir la copie\"\nSi nous voulons désactiver la sélection pour protéger le contenu de notre page du copier-coller, nous pouvons utiliser un autre événement : `oncopy`.\n\n```html autorun height=80 no-beautify\n<div *!*oncopy=\"alert('Copying forbidden!');return false\"*/!*>\n\nCher Utilisateur\n  Il vous est interdit de faire du copier-coller.\nSi vous connaissez JS ou HTML, alors vous pouvez tout obtenir à partir de la page source néanmoins.\n</div>\n```\nSi vous essayer de copier une partie de texte dans un `<div>`, cela ne va pas fonctionner, parce que l’action par défaut `oncopy`  est empêchée.\n\nCertes, l'utilisateur a accès à la source HTML de la page et peut en extraire le contenu, mais tout le monde ne sait pas comment le faire.\n````\n\n## Résumé\n\nLes évènements de souris ont les propriétés suivantes :\n\n- Bouton: `button`.\n- Touches de modification (`true` si pressées): `altKey`, `ctrlKey`, `shiftKey` et `metaKey` (Mac).\n  - Si vous voulez gérer `key:Ctrl`, alors n'oubliez pas les utilisateurs de Mac, ils utilisent généralement `key:Cmd`, il est donc préférable de vérifier `if (e.metaKey || e.ctrlKey)`.\n\n- Coordonnées relatives à la fenêtre : `clientX/clientY`.\n- Coordonnées relatives au document : `pageX/pageY`.\n\nL'action par défaut du navigateur de `mousedown` est la sélection de texte. Si ce n'est pas bon pour l'interface, alors il faut l'empêcher.\n\nDans le chapitre suivant, nous verrons plus en détails les événements qui suivent le mouvement du pointeur et  comment suivre les changements d’élément sous ce dernier.\n"
  },
  {
    "path": "2-ui/3-event-details/1-mouse-events-basics/head.html",
    "content": "<script>\n{\n  let timer = 0;\n\n  function showmesg(t, form) {\n\n     if (timer == 0) {\n       timer = new Date();\n     }\n\n     let tm = new Date();\n\n     if (tm - timer > 300) {\n       t = '------------------------------\\n' + t;\n     }\n\n     let area = document.forms[form + 'form'].getElementsByTagName('textarea')[0];\n\n     area.value += t + '\\n';\n     area.scrollTop = area.scrollHeight;\n\n     timer = tm;\n  }\n\n  function logMouse(e) {\n     let evt = e.type;\n     while (evt.length < 11) evt += ' ';\n     showmesg(evt + \" button=\" + e.button, 'test')\n     return false;\n  }\n\n  function keyval(n) {\n     if (n == null) return 'undefined';\n     let s = '' + n;\n     if (n >= 32 && n < 127) s += ' ' + String.fromCharCode(n);\n     while (s.length < 6) s += ' ';\n     return s;\n  }\n\n\n  function logClear(form) {\n  \ttimer = 0;\n  \tdocument.forms[form+'form'].getElementsByTagName('textarea')[0].value ='';\n  \tlines = 0;\n  }\n\n  window.logClear = logClear;\n  window.logMouse = logMouse;\n}\n</script>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    body {\n      height: 2000px;\n    /* L'info-bulle doit fonctionner après un défilement de la page aussi*/\n    }\n\n    .tooltip {\n      position: fixed;\n      z-index: 100;\n\n      padding: 10px 20px;\n\n      border: 1px solid #b3c9ce;\n      border-radius: 4px;\n      text-align: center;\n      font: italic 14px/1.3 sans-serif;\n      color: #333;\n      background: #fff;\n      box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);\n    }\n\n    #house {\n      margin-top: 50px;\n      width: 400px;\n      border: 1px solid brown;\n    }\n\n    #roof {\n      width: 0;\n      height: 0;\n      border-left: 200px solid transparent;\n      border-right: 200px solid transparent;\n      border-bottom: 20px solid brown;\n      margin-top: -20px;\n    }\n\n    p {\n      text-align: justify;\n      margin: 10px 3px;\n    }\n  </style>\n</head>\n\n<body>\n\n\n <div data-tooltip=\" Voici l'interieure de la maison\" id=\"house\">\n    <div data-tooltip=\"Voici le toit\" id=\"roof\"></div>\n\n    <p> Il était une fois, une maman cochon qui avait trois petits cochons.</p>\n\n    <p>Les trois petits cochons devinrent tellement grands que leur maman leur dit, \"Vous êtes trop grands pour habiter ici d'avantage. Vous devez partir et construire vos maisons par vous-mêmes. Mais prenez gardes à ce que le loup ne vous attrape pas\"\n\n    <p>Les trois petits cochons partirent. \"Nous allons faire attention à ce que le loup ne nous attrape point,\" dirent ils.</p>\n\n    <p>Bientôt ils rencontrèrent un homme. <a href=\"https://en.wikipedia.org/wiki/The_Three_Little_Pigs\" data-tooltip=\"Read on…\">Survolez moi</a></p>\n\n  </div>\n\n  <script>\n    let tooltip;\n\n    document.onmouseover = function(event) {\n      // important: une souris qui se deplace rapidement peut sauter  \"sauter\" juste sur un élément enfant d'un noeud annote, sautant le parent\n      // Donc l'évènement mouseover peut survenir sur un élément enfant.\n\n      let anchorElem = event.target.closest('[data-tooltip]');\n\n      if (!anchorElem) return;\n\n      // monte l'info-bulle et s'en rappelle\n      tooltip = showTooltip(anchorElem, anchorElem.dataset.tooltip);\n    }\n\n    document.onmouseout = function() {\n      // Il est possible que l'évènement  mouseout se déclenche, mais nous sommes toujours dans l'élément\n      // (sa cible était à l'intérieur et l'évènement a remonté)\n      // Mais dans ce cas nous aurons un évènement  mouseover immédiatement,\n      // donc l'info-bulle sera détruit et remontre\n      //\n      // Heureusement, le \"clignotement\"  ne sera pas visible,\n      // Comme les deux évènements se déroulent presque au même moment\n      if (tooltip) {\n        tooltip.remove();\n        tooltip = false;\n      }\n\n    }\n\n\n    function showTooltip(anchorElem, html) {\n      let tooltipElem = document.createElement('div');\n      tooltipElem.className = 'tooltip';\n      tooltipElem.innerHTML = html;\n      document.body.append(tooltipElem);\n\n      let coords = anchorElem.getBoundingClientRect();\n\n      // positionne l’info-bulle au centre de l’ element\n      let left = coords.left + (anchorElem.offsetWidth - tooltipElem.offsetWidth) / 2;\n      if (left < 0) left = 0;\n\n      let top = coords.top - tooltipElem.offsetHeight - 5;\n      if (top < 0) {\n        top = coords.top + anchorElem.offsetHeight + 5;\n      }\n\n      tooltipElem.style.left = left + 'px';\n      tooltipElem.style.top = top + 'px';\n\n      return tooltipElem;\n    }\n\n\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    body {\n      height: 2000px;\n      /* L'info-bulle doit fonctionner après un défilement de la page aussi*/\n    }\n\n    .tooltip {\n      position: fixed;\n      z-index: 100;\n\n      padding: 10px 20px;\n\n      border: 1px solid #b3c9ce;\n      border-radius: 4px;\n      text-align: center;\n      font: italic 14px/1.3 sans-serif;\n      color: #333;\n      background: #fff;\n      box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);\n    }\n\n    #house {\n      margin-top: 50px;\n      width: 400px;\n      border: 1px solid brown;\n    }\n\n    #roof {\n      width: 0;\n      height: 0;\n      border-left: 200px solid transparent;\n      border-right: 200px solid transparent;\n      border-bottom: 20px solid brown;\n      margin-top: -20px;\n    }\n\n    p {\n      text-align: justify;\n      margin: 10px 3px;\n    }\n  </style>\n</head>\n\n<body>\n\n\n  <div data-tooltip=\"Here is the house interior\" id=\"house\">\n    <div data-tooltip=\"Here is the roof\" id=\"roof\"></div>\n\n    <p>Once upon a time there was a mother pig who had three little pigs.</p>\n\n    <p>The three little pigs grew so big that their mother said to them, \"You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you.\"</p>\n\n    <p>The three little pigs set off. \"We will take care that the wolf does not catch us,\" they said.</p>\n\n    <p>Soon they met a man. <a href=\"https://en.wikipedia.org/wiki/The_Three_Little_Pigs\" data-tooltip=\"Read on…\">Hover over me</a></p>\n\n  </div>\n\n  <script>\n    // ...your code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/task.md",
    "content": "importance: 5\n\n---\n\n# Improved tooltip behavior\n\nWrite JavaScript that shows a tooltip over an element with the attribute `data-tooltip`. The value of this attribute should become the tooltip text.\n\nThat's like the task <info:task/behavior-tooltip>, but here the annotated elements can be nested. The most deeply nested tooltip is shown.\n\nOnly one tooltip may show up at the same time.\n\nFor instance:\n\n```html\n<div data-tooltip=\"Here – is the house interior\" id=\"house\">\n  <div data-tooltip=\"Here – is the roof\" id=\"roof\"></div>\n  ...\n  <a href=\"https://en.wikipedia.org/wiki/The_Three_Little_Pigs\" data-tooltip=\"Read on…\">Hover over me</a>\n</div>\n```\n\nThe result in iframe:\n\n[iframe src=\"solution\" height=300 border=1]\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.md",
    "content": "\nThe algorithm looks simple:\n1. Put `onmouseover/out` handlers on the element. Also can use `onmouseenter/leave` here, but they are less universal, won't work if we introduce delegation.\n2. When a mouse cursor entered the element, start measuring the speed on `mousemove`.\n3. If the speed is slow, then run `over`.\n4. When we're going out of the element, and `over` was executed, run `out`.\n\nBut how to measure the speed?\n\nThe first idea can be: run a function every `100ms` and measure the distance between previous and new coordinates. If it's small, then the speed is small.\n\nUnfortunately, there's no way to get \"current mouse coordinates\" in JavaScript. There's no function like `getCurrentMouseCoordinates()`.\n\nThe only way to get coordinates is to listen for mouse events, like `mousemove`, and take coordinates from the event object.\n\nSo let's set a handler on `mousemove` to track coordinates and remember them. And then compare them, once per `100ms`.\n\nP.S. Please note: the solution tests use `dispatchEvent` to see if the tooltip works right.\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js",
    "content": "'use strict';\n\nclass HoverIntent {\n\n  constructor({\n    sensitivity = 0.1, //  la vitesse est moins de  0.1px/ms signifie \"survoler un element\"\n    interval = 100,    // mesure la vitesse de la souris une fois par 100ms\n    elem,\n    over,\n    out\n  }) {\n    this.sensitivity = sensitivity;\n    this.interval = interval;\n    this.elem = elem;\n    this.over = over;\n    this.out = out;\n\n    // assurez vous que \"this\" est l'objet dans les gestionaires des evenements.\n    this.onMouseMove = this.onMouseMove.bind(this);\n    this.onMouseOver = this.onMouseOver.bind(this);\n    this.onMouseOut = this.onMouseOut.bind(this);\n\n    // et dans la function mesurant le temps  (appele dans  setInterval)\n    this.trackSpeed = this.trackSpeed.bind(this);\n\n    elem.addEventListener(\"mouseover\", this.onMouseOver);\n\n    elem.addEventListener(\"mouseout\", this.onMouseOut);\n\n  }\n\n  onMouseOver(event) {\n\n    if (this.isOverElement) {\n      // si nous sommes sur un élément, alors ignore l'évènement\n      // Nous sommes déjà entrain de mesurer la vitesse\n      return;\n    }\n\n    this.isOverElement = true;\n\n    // Apres chaque évènement mousemove nous allons contrôler encore la distance\n    // entre les coordonnées actuelles de la souris et celles précédents\n    // si c’est moins de la sensibilité, alors la vitesse est lente\n    this.prevX = event.pageX;\n    this.prevY = event.pageY;\n    this.prevTime = Date.now();\n\n    elem.addEventListener('mousemove', this.onMouseMove);\n    this.checkSpeedInterval = setInterval(this.trackSpeed, this.interval);\n  }\n\n  onMouseOut(event) {\n    // if left the element\n    if (!event.relatedTarget || !elem.contains(event.relatedTarget)) {\n      this.isOverElement = false;\n      this.elem.removeEventListener('mousemove', this.onMouseMove);\n      clearInterval(this.checkSpeedInterval);\n      if (this.isHover) {\n        // S'il y  avait un arrêt sur l'élément\n        this.out.call(this.elem, event);\n        this.isHover = false;\n      }\n    }\n  }\n\n  onMouseMove(event) {\n    this.lastX = event.pageX;\n    this.lastY = event.pageY;\n    this.lastTime = Date.now();\n  }\n\n  trackSpeed() {\n\n    let speed;\n\n    if (!this.lastTime || this.lastTime == this.prevTime) {\n      // cursor didn't move\n      speed = 0;\n    } else {\n      speed = Math.sqrt(\n        Math.pow(this.prevX - this.lastX, 2) +\n        Math.pow(this.prevY - this.lastY, 2)\n      ) / (this.lastTime - this.prevTime);\n    }\n\n    if (speed < this.sensitivity) {\n      clearInterval(this.checkSpeedInterval);\n      this.isHover = true;\n      this.over.call(this.elem);\n    } else {\n      // vitesse rapide, se rappeler des coordonnées comme celles précédentes\n      this.prevX = this.lastX;\n      this.prevY = this.lastY;\n      this.prevTime = this.lastTime;\n    }\n  }\n\n  destroy() {\n    elem.removeEventListener('mousemove', this.onMouseMove);\n    elem.removeEventListener('mouseover', this.onMouseOver);\n    elem.removeEventListener('mouseout', this.onMouseOut);\n  }\n\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"hoverIntent.js\"></script>\n  <script src=\"https://en.js.cx/test/libs.js\"></script>\n  <script src=\"test.js\"></script>\n</head>\n\n<body>\n\n  <div id=\"elem\" class=\"clock\">\n    <span class=\"hours\">12</span> :\n    <span class=\"minutes\">30</span> :\n    <span class=\"seconds\">00</span>\n  </div>\n\n  <div id=\"tooltip\" hidden>Tooltip</div>\n\n  <script>\n    new HoverIntent({\n      elem,\n      over() {\n        tooltip.style.left = elem.getBoundingClientRect().left + 5 + 'px';\n        tooltip.style.top = elem.getBoundingClientRect().bottom + 5 + 'px';\n        tooltip.hidden = false;\n      },\n      out() {\n        tooltip.hidden = true;\n      }\n    });\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/style.css",
    "content": ".hours {\n  color: red;\n}\n\nbody {\n  margin: 0;\n}\n\n.minutes {\n  color: green;\n}\n\n.seconds {\n  color: blue;\n}\n\n.clock {\n  border: 1px dashed black;\n  padding: 5px;\n  display: inline-block;\n  background: yellow;\n  position: absolute;\n  left: 0;\n  top: 0;\n}\n\n#tooltip {\n  position: absolute;\n  padding: 10px 20px;\n  border: 1px solid #b3c9ce;\n  border-radius: 4px;\n  text-align: center;\n  font: italic 14px/1.3 sans-serif;\n  color: #333;\n  background: #fff;\n  z-index: 100000;\n  box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/test.js",
    "content": "'use strict';\n\ndescribe(\"hoverIntent\", function() {\n\n  function mouse(eventType, x, y, options) {\n    let eventOptions = Object.assign({\n      bubbles: true,\n      clientX: x,\n      clientY: y,\n      pageX: x,\n      pageY: y,\n      target: elem\n    }, options || {});\n\n    elem.dispatchEvent(new MouseEvent(eventType, eventOptions));\n  }\n\n\n  let isOver;\n  let hoverIntent;\n\n\n  before(function() {\n    this.clock = sinon.useFakeTimers();\n  });\n\n  after(function() {\n    this.clock.restore();\n  });\n\n\n  beforeEach(function() {\n    isOver = false;\n\n    hoverIntent = new HoverIntent({\n      elem: elem,\n      over: function() {\n        isOver = true;\n      },\n      out: function() {\n        isOver = false;\n      }\n    });\n  })\n\n  afterEach(function() {\n    if (hoverIntent) {\n      hoverIntent.destroy();\n    }\n  })\n\n  it(\"mouseover -> when the pointer just arrived, no tooltip\", function() {\n    mouse('mouseover', 10, 10);\n    assert.isFalse(isOver);\n  });\n\n  it(\"mouseover -> after a delay, the tooltip shows up\", function() {\n    mouse('mouseover', 10, 10);\n    this.clock.tick(100);\n    assert.isTrue(isOver);\n  });\n\n  it(\"mouseover -> followed by fast mouseout leads doesn't show tooltip\", function() {\n    mouse('mouseover', 10, 10);\n    setTimeout(\n      () => mouse('mouseout', 300, 300, { relatedTarget: document.body}),\n      30\n    );\n    this.clock.tick(100);\n    assert.isFalse(isOver);\n  });\n\n\n  it(\"mouseover -> slow move -> tooltips\", function() {\n    mouse('mouseover', 10, 10);\n    for(let i=10; i<200; i+= 10) {\n      setTimeout(\n        () => mouse('mousemove', i/5, 10),\n        i\n      );\n    }\n    this.clock.tick(200);\n    assert.isTrue(isOver);\n  });\n\n  it(\"mouseover -> fast move -> no tooltip\", function() {\n    mouse('mouseover', 10, 10);\n    for(let i=10; i<200; i+= 10) {\n      setTimeout(\n        () => mouse('mousemove', i, 10),\n        i\n      );\n    }\n    this.clock.tick(200);\n    assert.isFalse(isOver);\n  });\n\n});\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js",
    "content": "\"use strict\";\n\n// Voici une ébauche rapide de la classe\n// avec les chose dont vous aurez besoin de toute manière\nclass HoverIntent {\n  constructor({\n    sensitivity = 0.1, // si la vitesse est de moins de 0.1px/ms cela signifie \"survolez un élément\"\n    interval = 100, // mesurer  la vitesse de la souris  une fois  par 100ms: calculer les distance entre les points précédents et suivants\n    elem,\n    over,\n    out\n  }) {\n    this.sensitivity = sensitivity;\n    this.interval = interval;\n    this.elem = elem;\n    this.over = over;\n    this.out = out;\n\n    // s’assurer que  \"this\" est l’objet dans les gestionnaires des évènements.\n    this.onMouseMove = this.onMouseMove.bind(this);\n    this.onMouseOver = this.onMouseOver.bind(this);\n    this.onMouseOut = this.onMouseOut.bind(this);\n\n    // assigner les gestionnaires d’évènements\n    elem.addEventListener(\"mouseover\", this.onMouseOver);\n    elem.addEventListener(\"mouseout\", this.onMouseOut);\n\n    // continuer apartir d’ici\n  }\n\n  onMouseOver(event) {\n    /* ... */\n  }\n\n  onMouseOut(event) {\n    /* ... */\n  }\n\n  onMouseMove(event) {\n    /* ... */\n  }\n\n  destroy() {\n    /* votre code pour  \"désactiver\" la fonctionnalité,  enlever tous les gestionnaires d’évènements*/\n    /* il est nécessaire que les tests fonctionnent */\n  }\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"hoverIntent.js\"></script>\n  <script src=\"https://en.js.cx/test/libs.js\"></script>\n  <script src=\"test.js\"></script>\n</head>\n\n<body>\n\n  <div id=\"elem\" class=\"clock\">\n    <span class=\"hours\">12</span> :\n    <span class=\"minutes\">30</span> :\n    <span class=\"seconds\">00</span>\n  </div>\n\n  <div id=\"tooltip\" hidden>Tooltip</div>\n\n  <script>\n    new HoverIntent({\n      elem,\n      over() {\n        tooltip.style.left = elem.getBoundingClientRect().left + 5 + 'px';\n        tooltip.style.top = elem.getBoundingClientRect().bottom + 5 + 'px';\n        tooltip.hidden = false;\n      },\n      out() {\n        tooltip.hidden = true;\n      }\n    });\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/style.css",
    "content": ".hours {\n  color: red;\n}\n\nbody {\n  margin: 0;\n}\n\n.minutes {\n  color: green;\n}\n\n.seconds {\n  color: blue;\n}\n\n.clock {\n  border: 1px dashed black;\n  padding: 5px;\n  display: inline-block;\n  background: yellow;\n  position: absolute;\n  left: 0;\n  top: 0;\n}\n\n#tooltip {\n  position: absolute;\n  padding: 10px 20px;\n  border: 1px solid #b3c9ce;\n  border-radius: 4px;\n  text-align: center;\n  font: italic 14px/1.3 sans-serif;\n  color: #333;\n  background: #fff;\n  z-index: 100000;\n  box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/test.js",
    "content": "'use strict';\n\ndescribe(\"hoverIntent\", function() {\n\n  function mouse(eventType, x, y, options) {\n    let eventOptions = Object.assign({\n      bubbles: true,\n      clientX: x,\n      clientY: y,\n      pageX: x,\n      pageY: y,\n      target: elem\n    }, options || {});\n\n    elem.dispatchEvent(new MouseEvent(eventType, eventOptions));\n  }\n\n\n  let isOver;\n  let hoverIntent;\n\n\n  before(function() {\n    this.clock = sinon.useFakeTimers();\n  });\n\n  after(function() {\n    this.clock.restore();\n  });\n\n\n  beforeEach(function() {\n    isOver = false;\n\n    hoverIntent = new HoverIntent({\n      elem: elem,\n      over: function() {\n        isOver = true;\n      },\n      out: function() {\n        isOver = false;\n      }\n    });\n  })\n\n  afterEach(function() {\n    if (hoverIntent) {\n      hoverIntent.destroy();\n    }\n  })\n\n  it(\"mouseover -> when the pointer just arrived, no tooltip\", function() {\n    mouse('mouseover', 10, 10);\n    assert.isFalse(isOver);\n  });\n\n  it(\"mouseover -> after a delay, the tooltip shows up\", function() {\n    mouse('mouseover', 10, 10);\n    this.clock.tick(100);\n    assert.isTrue(isOver);\n  });\n\n  it(\"mouseover -> followed by fast mouseout leads doesn't show tooltip\", function() {\n    mouse('mouseover', 10, 10);\n    setTimeout(\n      () => mouse('mouseout', 300, 300, { relatedTarget: document.body}),\n      30\n    );\n    this.clock.tick(100);\n    assert.isFalse(isOver);\n  });\n\n\n  it(\"mouseover -> slow move -> tooltips\", function() {\n    mouse('mouseover', 10, 10);\n    for(let i=10; i<200; i+= 10) {\n      setTimeout(\n        () => mouse('mousemove', i/5, 10),\n        i\n      );\n    }\n    this.clock.tick(200);\n    assert.isTrue(isOver);\n  });\n\n  it(\"mouseover -> fast move -> no tooltip\", function() {\n    mouse('mouseover', 10, 10);\n    for(let i=10; i<200; i+= 10) {\n      setTimeout(\n        () => mouse('mousemove', i, 10),\n        i\n      );\n    }\n    this.clock.tick(200);\n    assert.isFalse(isOver);\n  });\n\n});\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/task.md",
    "content": "importance: 5\n\n---\n\n# Info-bulle \"Intelligente\"\n\nEcrivez une fonction qui montre une info-bulle  sur un élément seulement si le visiteur déplace la souris *sur celui-ci*, et non pas *en le traversant*.\n\nEn d'autres termes, si l'usager déplace la souris sur l'élément et s'arrête dessus -- afficher l'info bulle. Et si le visiteur déplace seulement la souris en traversant rapidement l'élément, pas besoin de le faire, qui a besoin d'un clignotement supplémentaire ?\n\nTechniquement, nous pouvons mesurer la vitesse de la souris sur un élément, et si elle est lente alors nous pouvons assumer qu'elle  arrive \"sur l'élément\" et monter l'info-bulle, si elle est rapide -- alors on l'ignore.\n\nCréer un objet universel `new HoverIntent(options)` pour cela. \n\nSes `options` :\n\n- `elem` -- l'élément à surveiller.\n- `over` -- une fonction à appeler si la souris arrive sur l’élément : c’est-à-dire qu’elle se déplace lentement ou s’arrête dessus.\n- `out` -- une fonction à appeler quand la souris quitte l'élément (si `over` était appelé).\n\nUn exemple d'usage d'un tel objet pour l'info-bulle:\n\n```js\n// un example d'info-bulle\nlet tooltip = document.createElement('div');\ntooltip.className = \"tooltip\";\ntooltip.innerHTML = \"Tooltip\";\n\n// L’objet va  suivre la souris et appeler les fonctions over/out\nnew HoverIntent({\n  elem,\n  over() {\n    tooltip.style.left = elem.getBoundingClientRect().left + 'px';\n    tooltip.style.top = elem.getBoundingClientRect().bottom + 5 + 'px';\n    document.body.append(tooltip);\n  },\n  out() {\n    tooltip.remove();\n  }\n});\n```\n\nLa demo:\n\n[iframe src=\"solution\" height=140]\n\nSi vous déplacez la souris rapidement sur la \"montre\" alors rien ne se passe, et si vous le faites lentement  ou que vous vous arrêtez, alors il y aura une info-bulle.\n\nNotez bien: l'info-bulle ne \"clignote\" pas lorsque le curseur se déplace entre la montre et les sous éléments.\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md",
    "content": "# Déplacer la souris : mouseover/out, mouseenter/leave\n\nPlongeont dans plus de détails sur les événements qui se produisent lorsque la souris se déplace entre les éléments.\n\n## Events mouseover/mouseout, relatedTarget\n\nL'évènement  `mouseover`  est exécuté lorsqu'un pointeur de la souris survole un élément, et `mouseout` -- lorsqu’il le quitte.\n\n![](mouseover-mouseout.svg)\n\nCes événements sont spéciaux, car ils ont la propriété `relatedTarget`. Cette propriété complète `target`. Quand une souris quitte un élément pour un autre, l’un d’eux devient `target`, et l'autre - `relatedTarget`.\n\nPour  `mouseover`:\n\n- `event.target` -- l'élément que survole la souris.\n- `event.relatedTarget` -- voici l'élément  d'origine la souris (`relatedTarget` -> `target`).\n\nA l'inverse du  `mouseout`:\n\n- `event.target` -- est l'élément que la souris a quitté.\n- `event.relatedTarget` -- est le nouvel élément situé sous le pointeur, celui pour lequel la souris a quitté (`target` -> `relatedTarget`).\n\n```online\nDans l'exemple ci-dessous  chaque aspect facial  est un élément. Lorsque vous déplacez  la souris, vous pouvez voir les évènements de souris dans la zone de texte.\n\nChaque événement contient les informations sur `target` et `relatedTarget` :\n\n[codetabs src=\"mouseoverout\" height=280]\n```\n\n```warn header=\"`relatedTarget` peut être  `null`\"\nLa propriété `relatedTarget` peut être  `null`.\n\nC'est normal et cela signifie simplement que la souris de provient pas d'un autre élément, mais hors de la fenêtre  Windows. Ou bien qu'elle a quitté la fenêtre Windows.\n\nNous devons garder cette éventualité à l'esprit lorsqu'on utilise `event.relatedTarget` dans notre code. Si nous accédons a la propriété `event.relatedTarget.tagName`, alors il y aura une erreur.\n```\n\n## Ignorer des éléments\n\nL'évènement  `mousemove`   se déclenche lorsque la souris se déplace. Mais cela ne signifie pas chaque pixel mène a  un évènement.\n\nLe navigateur vérifie la position de la souris de temps en temps. Et s'il remarque des changements, déclenche les événements.\n\nCela signifie que si le visiteur déplace la souris très rapidement, certains éléments DOM peuvent être ignorés :\n\n![](mouseover-mouseout-over-elems.svg)\n\nSi la souris se déplace très rapidement de `#FROM` aux éléments `#TO` telle que décrite en haut, alors le `<div>`  intermédiaire (ou certains d'entre eux) peuvent être sautés. L'évènement `mouseout` peut être déclenche sur `#FROM` et ensuite immédiatement le `mouseover` sur `#TO`.\n\nC'est bon pour la performance, car s'il peut y avoir beaucoup d'éléments intermédiaires. Nous ne voulons pas vraiment traiter dans et hors de chacun d'entre eux.\n\nD'autre part, nous devons garder à l'esprit que le pointeur de la souris ne \"visite\" pas tous les éléments le long du chemin. Il peut \"sauter\".\n\nEn particulier, il est possible que le pointeur saute directement au centre de la page depuis l'extérieur de la fenêtre. Dans ce cas `relatedTarget` est `null`, parce qu'il venait de \"nulle part\" :\n\n![](mouseover-mouseout-from-outside.svg)\n\n```online\nVous pouvez le vérifier \"en direct\" sur un banc d'essai ci-dessous.\n\nSon code HTML comporte deux éléments imbriqués: la `<div id=\"child\">` est à l'intérieur de `<div id=\"parent\">`. Si vous déplacez rapidement la souris dessus, alors peut-être que seule la div enfant déclenchera les événements, ou peut-être la div parent, ou peut-être qu'il n'y aura aucun événement.\n\nDéplacez également le pointeur dans la `div` enfant, puis le déplacer rapidement en dehors à travers le parent. Si le mouvement est assez rapide, l'élément parent est ignoré. La souris traversera l'élément parent sans le remarquer.\n\n[codetabs height=360 src=\"mouseoverout-fast\"]\n```\n\n```smart header=\"Si `mouseover` est déclenché, il doit y avoir `mouseout`\"\nEn cas de mouvements rapides de la souris, les éléments intermédiaires peuvent être ignorés, mais une chose est sûre : si le pointeur est \"officiellement\" entré dans un élément (événement `mouseover` généré), alors en le quittant, on aura toujours `mouseout`.\n```\n\n## Mouseout en quittant pour un enfant\n\nUne caractéristique importante de `mouseout` -- il se déclenche lorsque le pointeur se déplace d’un élément à son descendant, par ex. de `#parent` à `#enfant` dans ce code HTML :\n\n```html\n<div id=\"parent\">\n  <div id=\"child\">...</div>\n</div>\n```\n\nSi nous sommes sur `#parent`, puis déplaçons le pointeur plus profondément dans `#enfant`, nous obtenons `mouseout` sur `#parent` !\n\n![](mouseover-to-child.svg)\n\nCela semble étrange, mais peut être facilement expliqué.\n\n**Selon la logique du navigateur, le curseur de la souris ne peut survoler qu'*un seul* élément à tout moment - l'élément le plus imbriqué et le plus élevé par z-index.**\n\nDonc, s'il passe à un autre élément (même un descendant), alors il quitte le précédent.\n\nVeuillez noter un autre détail important du traitement de l'événement.\n\nL'événement `mouseover` sur un descendant \"bubble up\" (remonte). Donc, si `#parent` a un gestionnaire `mouseover`, il se déclenche :\n\n![](mouseover-bubble-nested.svg)\n\n```online\nVous pouvez le voir très bien dans l'exemple ci-dessous : `<div id=\"child \">` est à l'intérieur de `<div id=\"parent\">`. Il existe des gestionnaires `mouseover/out` sur l'élément `#parent` qui fournissent les détails de l'événement.\n\nSi vous déplacez la souris de `#parent` à `#enfant`, vous voyez deux événements sur `#parent` :\n1. `mouseout [target: parent]` (quitte le parent), alors\n2. `mouseover [target: child]` (est arrivé à l'enfant, bubbled).\n\n[codetabs height=360 src=\"mouseoverout-child\"]\n```\n\nComme indiqué, lorsque le pointeur passe de l'élément `#parent` à l'élément `#child`,  deux gestionnaires se déclenchent sur l'élément parent : `mouseout` et `mouseover`:\n\n```js\nparent.onmouseout = function(event) {\n  /* event.target: élément parent */\n};\nparent.onmouseover = function(event) {\n  /* event.target: élément enfant */\n};\n```\n\n**Si nous n'examinons pas `event.target` à l'intérieur des gestionnaires, il peut alors sembler que le pointeur de la souris a quitté l'élément `#parent`, puis est immédiatement revenu dessus.**\n\nMais ce n'est pas le cas! Le pointeur se trouve toujours sur le parent, il s’est déplacé plus profondément dans l’élément enfant.\n\nS'il y a des actions lorsque vous quittez l'élément parent, par exemple une animation qui s'exécute dans `parent.onmouseout`, nous ne le souhaitons généralement pas lorsque le pointeur va plus en profondeur dans `#parent`.\n\nPour l'éviter, nous pouvons vérifier `relatedTarget` dans le gestionnaire et, si la souris est toujours dans l'élément, alors ignorer cet événement.\n\nAlternativement, nous pouvons utiliser d'autres événements : `mouseenter` et `mouseleave`, que nous allons couvrir maintenant, car ils n'ont pas ce genre de problèmes.\n\n## Evènements mouseenter and mouseleave\n\nLes évènements `mouseenter/mouseleave` sont comme `mouseover/mouseout`. Ils se déclenchent lorsque le pointeur de la souris entre/sort de l'élément.\n\nMais il y a deux différences importantes :\n\n1. Les transitions à l'intérieur de l'élément, vers/depuis les descendants, ne sont pas comptées.\n2. Les évènements `mouseenter/mouseleave`  ne \"bubble\" pas.\n\nCes événements sont extrêmement simples.\n\nQuand le pointeur entre dans un élément -- `mouseenter` se déclenche. L'emplacement exact du pointeur à l'intérieur de l'élément ou de ses descendants n'a pas d'importance.\n\nQuand le pointeur quitte un élément -- `mouseleave` se déclenche.\n\n```online\nCet exemple est similaire à celui ci-dessus, mais maintenant l’élément supérieur a `mouseenter/mouseleave` au lieu de  `mouseover/mouseout`.\n\nComme vous pouvez le voir, les seuls événements générés sont ceux liés au déplacement du pointeur dans et hors de l'élément supérieur. Rien ne se passe lorsque le pointeur se dirige vers l'enfant et vice-versa. Les transitions entre les descendants sont ignorées.\n\n[codetabs height=340 src=\"mouseleave\"]\n```\n\n## Délégation des évènements\n\nLes évènements `mouseenter/leave`  sont très simple et facile à utiliser. Mais il ne remonte pas.  Donc nous ne pouvons pas utiliser la délégation d'évènements sur eux.\n\nImaginez qu'on veuille gérer les évènements de souris enter/leave pour les cellules d'un tableau et qu'il ait une centaine de cellules.\n\nLa solution naturelle serait de définir le gestionnaire sur `<table>` et d'y traiter les événements. Mais `mouseenter/leave` ne \"bubble\" pas. Donc, si un tel événement se produit sur `<td>`, alors seul un gestionnaire sur ce `<td>` est capable de l'attraper.\n\nLes gestionnaires pour `mouseenter/leave` sur `<table>` ne se déclenche que lorsque le pointeur entre/sort du tableau dans son ensemble. Il est impossible d'obtenir des informations sur les transitions à l'intérieur.\n\nAlors, utilisons `mouseover/mouseout`.\n\nCommençons par des gestionnaires simples qui mettent en évidence l'élément sous la souris :\n\n```js\n// mettons en évidence un élément sous le pointeur\ntable.onmouseover = function(event) {\n  let target = event.target;\n  target.style.background = 'pink';\n};\n\ntable.onmouseout = function(event) {\n  let target = event.target;\n  target.style.background = '';\n};\n```\n\n```online\nLes voici en action. Lorsque la souris se déplace sur les éléments de ce tableau, celui qui est survolé est mis en évidence:\n\n[codetabs height=480 src=\"mouseenter-mouseleave-delegation\"]\n```\nCes gestionnaires fonctionnent lorsqu'on se déplace sur n'importe quel élément dans le tableau.\n\nDans notre cas, nous aimerions gérer les transitions entre les cellules du tableau `<td>`: entrer dans une cellule et la quitter. Les autres transitions, comme à l'intérieur de la cellule ou à l'extérieur de celles-ci, ne nous intéressent pas. Filtrons-les.\n\nVoici ce que nous pouvons faire :\n\n- Mémoriser le <td> actuellement sélectionné dans une variable, appelons le `currentElem`.\n- Sur `mouseover` -- ignorer l'événement si nous sommes toujours dans le `<td>` actuel.\n- Sur `mouseout` -- ignorer si nous n'avons pas quitté le `<td>` actuel.\n\nVoici un exemple de code qui prend en compte toutes les situations possibles :\n\n[js src=\"mouseenter-mouseleave-delegation-2/script.js\"]\n\nLes caractéristiques importantes sont les suivantes:\n1. On utilise la délégation d'événements pour gérer l'entrée/sortie de tout `<td>` à l'intérieur de la table. On s'appuie donc sur le `mouseover/out` au lieu du `mouseenter/leave` qui ne fait pas de bulles et ne permet donc aucune délégation..\n2. Les événements supplémentaires, tels que le déplacement entre les descendants de `<td>` sont filtrés, de sorte que `onEnter/Leave` ne fonctionne que si le pointeur quitte ou entre dans `<td>` dans son ensemble.\n\n```online\nVoici l'exemple complet avec tous les détails:\n\n[codetabs height=460 src=\"mouseenter-mouseleave-delegation-2\"]\n\nEssayez de déplacer le curseur dans et hors des cellules du tableau et à l'intérieur de celles-ci. Rapide ou lent - peu importe. Seul `<td>` dans son ensemble est mis en surbrillance, contrairement à l'exemple précédent.\n```\n\n\n## Résumé\n\nNous avons abordé les évènements `mouseover`, `mouseout`, `mousemove`, `mouseenter` et `mouseleave`.\n\nCes choses sont bonnes à noter :\n\n- Un mouvement rapide de la souris peut ignorer les éléments intermédiaires.\n- Les évènements `mouseover/out` et `mouseenter/leave` ont une propriété supplémentaire : `relatedTarget`. C'est l'élément duquel nous venons de/à, complémentaire à `target`.\n\nLes évènements `mouseover/out` se déclenchent même lorsque nous passons de l'élément parent à un élément enfant. Le navigateur suppose que la souris ne peut survoler qu'un seul élément à la fois, le plus profond.\n\nLes évènements `mouseenter/leave` sont différents à cet égard : ils ne se déclenchent que lorsque la souris entre et sort de l’élément dans son ensemble. En outre, ils ne \"bubble\" pas.\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation-2.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n\n  <table id=\"table\">\n    <tr>\n      <th colspan=\"3\"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>\n    </tr>\n    <tr>\n      <td class=\"nw\"><strong>Northwest</strong>\n        <br>Metal\n        <br>Silver\n        <br>Elders\n      </td>\n      <td class=\"n\"><strong>North</strong>\n        <br>Water\n        <br>Blue\n        <br>Change\n      </td>\n      <td class=\"ne\"><strong>Northeast</strong>\n        <br>Earth\n        <br>Yellow\n        <br>Direction\n      </td>\n    </tr>\n    <tr>\n      <td class=\"w\"><strong>West</strong>\n        <br>Metal\n        <br>Gold\n        <br>Youth\n      </td>\n      <td class=\"c\"><strong>Center</strong>\n        <br>All\n        <br>Purple\n        <br>Harmony\n      </td>\n      <td class=\"e\"><strong>East</strong>\n        <br>Wood\n        <br>Blue\n        <br>Future\n      </td>\n    </tr>\n    <tr>\n      <td class=\"sw\"><strong>Southwest</strong>\n        <br>Earth\n        <br>Brown\n        <br>Tranquility\n      </td>\n      <td class=\"s\"><strong>South</strong>\n        <br>Fire\n        <br>Orange\n        <br>Fame\n      </td>\n      <td class=\"se\"><strong>Southeast</strong>\n        <br>Wood\n        <br>Green\n        <br>Romance\n      </td>\n    </tr>\n\n  </table>\n\n  <textarea id=\"text\"></textarea>\n\n  <input type=\"button\" onclick=\"text.value=''\" value=\"Clear\">\n\n  <script src=\"script.js\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation-2.view/script.js",
    "content": "// <td> sous la souris actuellement(le cas échéant)\nlet currentElem = null;\n\ntable.onmouseover = function(event) {\n  // avant d'entrer dans un nouvel élément, la souris quitte toujours le précédent\n  // Si currentElem est défini, nous n'avons pas quitté le précédent <td>,\n  // c'est un mouseover à l'intérieur, ignore l'événement\n  if (currentElem) return;\n\n  let target = event.target.closest(\"td\");\n\n  // nous ne sommes pas passés dans un <td> - ignorer\n  if (!target) return;\n\n  // déplacé dans <td>, mais en dehors de notre tableau (possible en cas de tableaux imbriquées)\n  // ignorer\n  if (!table.contains(target)) return;\n\n  // hourra! nous sommes entrés dans un nouveau <td>\n  currentElem = target;\n  onEnter(currentElem);\n};\n\ntable.onmouseout = function(event) {\n  // si nous sommes en dehors de tout <td> maintenant, alors ignorez l'événement\n  // c'est probablement un mouvement à l'intérieur du tableau, mais en dehors des <td>,\n  // par exemple. d'un <tr> à un autre <tr>\n  if (!currentElem) return;\n\n  // nous quittons l'élément - où aller ? Peut-être à un descendant ?\n  let relatedTarget = event.relatedTarget;\n\n  while (relatedTarget) {\n    // monte dans la chaîne parente et vérifie - si nous sommes toujours dans currentElem\n    // alors c'est une transition interne - l'ignorer\n    if (relatedTarget == currentElem) return;\n\n    relatedTarget = relatedTarget.parentNode;\n  }\n\n  // nous avons quitté le <td>. vraiment.\n  onLeave(currentElem);\n  currentElem = null;\n};\n\n// any functions to handle entering/leaving an element\nfunction onEnter(elem) {\n  elem.style.background = \"pink\";\n\n  // show that in textarea\n  text.value += `over -> ${currentElem.tagName}.${currentElem.className}\\n`;\n  text.scrollTop = 1e6;\n}\n\nfunction onLeave(elem) {\n  elem.style.background = \"\";\n\n  // show that in textarea\n  text.value += `out <- ${elem.tagName}.${elem.className}\\n`;\n  text.scrollTop = 1e6;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation-2.view/style.css",
    "content": "#text {\n  display: block;\n  height: 100px;\n  width: 456px;\n}\n\n#table th {\n  text-align: center;\n  font-weight: bold;\n}\n\n#table td {\n  width: 150px;\n  white-space: nowrap;\n  text-align: center;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 12px;\n  cursor: pointer;\n}\n\n#table .nw {\n  background: #999;\n}\n\n#table .n {\n  background: #03f;\n  color: #fff;\n}\n\n#table .ne {\n  background: #ff6;\n}\n\n#table .w {\n  background: #ff0;\n}\n\n#table .c {\n  background: #60c;\n  color: #fff;\n}\n\n#table .e {\n  background: #09f;\n  color: #fff;\n}\n\n#table .sw {\n  background: #963;\n  color: #fff;\n}\n\n#table .s {\n  background: #f60;\n  color: #fff;\n}\n\n#table .se {\n  background: #0c3;\n  color: #fff;\n}\n\n#table .highlight {\n  background: red;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n\n  <table id=\"table\">\n    <tr>\n      <th colspan=\"3\"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>\n    </tr>\n    <tr>\n      <td class=\"nw\"><strong>Northwest</strong>\n        <br>Metal\n        <br>Silver\n        <br>Elders\n      </td>\n      <td class=\"n\"><strong>North</strong>\n        <br>Water\n        <br>Blue\n        <br>Change\n      </td>\n      <td class=\"ne\"><strong>Northeast</strong>\n        <br>Earth\n        <br>Yellow\n        <br>Direction\n      </td>\n    </tr>\n    <tr>\n      <td class=\"w\"><strong>West</strong>\n        <br>Metal\n        <br>Gold\n        <br>Youth\n      </td>\n      <td class=\"c\"><strong>Center</strong>\n        <br>All\n        <br>Purple\n        <br>Harmony\n      </td>\n      <td class=\"e\"><strong>East</strong>\n        <br>Wood\n        <br>Blue\n        <br>Future\n      </td>\n    </tr>\n    <tr>\n      <td class=\"sw\"><strong>Southwest</strong>\n        <br>Earth\n        <br>Brown\n        <br>Tranquility\n      </td>\n      <td class=\"s\"><strong>South</strong>\n        <br>Fire\n        <br>Orange\n        <br>Fame\n      </td>\n      <td class=\"se\"><strong>Southeast</strong>\n        <br>Wood\n        <br>Green\n        <br>Romance\n      </td>\n    </tr>\n\n  </table>\n\n  <textarea id=\"text\"></textarea>\n\n  <input type=\"button\" onclick=\"text.value=''\" value=\"Clear\">\n\n  <script src=\"script.js\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation.view/script.js",
    "content": "table.onmouseover = function(event) {\n  let target = event.target;\n  target.style.background = 'pink';\n\n  text.value += `over -> ${target.tagName}\\n`;\n  text.scrollTop = text.scrollHeight;\n};\n\ntable.onmouseout = function(event) {\n  let target = event.target;\n  target.style.background = '';\n\n  text.value += `out <- ${target.tagName}\\n`;\n  text.scrollTop = text.scrollHeight;\n};\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation.view/style.css",
    "content": "#text {\n  display: block;\n  height: 100px;\n  width: 456px;\n}\n\n#table th {\n  text-align: center;\n  font-weight: bold;\n}\n\n#table td {\n  width: 150px;\n  white-space: nowrap;\n  text-align: center;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 12px;\n  cursor: pointer;\n}\n\n#table .nw {\n  background: #999;\n}\n\n#table .n {\n  background: #03f;\n  color: #fff;\n}\n\n#table .ne {\n  background: #ff6;\n}\n\n#table .w {\n  background: #ff0;\n}\n\n#table .c {\n  background: #60c;\n  color: #fff;\n}\n\n#table .e {\n  background: #09f;\n  color: #fff;\n}\n\n#table .sw {\n  background: #963;\n  color: #fff;\n}\n\n#table .s {\n  background: #f60;\n  color: #fff;\n}\n\n#table .se {\n  background: #0c3;\n  color: #fff;\n}\n\n#table .highlight {\n  background: red;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave-table.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n\n  <table id=\"table\">\n    <tr>\n      <th colspan=\"3\"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>\n    </tr>\n    <tr>\n      <td class=\"nw\"><strong>Northwest</strong>\n        <br>Metal\n        <br>Silver\n        <br>Elders\n      </td>\n      <td class=\"n\"><strong>North</strong>\n        <br>Water\n        <br>Blue\n        <br>Change\n      </td>\n      <td class=\"ne\"><strong>Northeast</strong>\n        <br>Earth\n        <br>Yellow\n        <br>Direction\n      </td>\n    </tr>\n    <tr>\n      <td class=\"w\"><strong>West</strong>\n        <br>Metal\n        <br>Gold\n        <br>Youth\n      </td>\n      <td class=\"c\"><strong>Center</strong>\n        <br>All\n        <br>Purple\n        <br>Harmony\n      </td>\n      <td class=\"e\"><strong>East</strong>\n        <br>Wood\n        <br>Blue\n        <br>Future\n      </td>\n    </tr>\n    <tr>\n      <td class=\"sw\"><strong>Southwest</strong>\n        <br>Earth\n        <br>Brown\n        <br>Tranquility\n      </td>\n      <td class=\"s\"><strong>South</strong>\n        <br>Fire\n        <br>Orange\n        <br>Fame\n      </td>\n      <td class=\"se\"><strong>Southeast</strong>\n        <br>Wood\n        <br>Green\n        <br>Romance\n      </td>\n    </tr>\n\n  </table>\n\n  <textarea id=\"text\"></textarea>\n\n  <input type=\"button\" onclick=\"text.value=''\" value=\"Clear\">\n\n\n  <script src=\"script.js\"></script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave-table.view/script.js",
    "content": "table.onmouseenter = table.onmouseleave = log;\n\nfunction log(event) {\n  text.value += event.type + ' [target: ' + event.target.tagName + ']\\n';\n  text.scrollTop = text.scrollHeight;\n}"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave-table.view/style.css",
    "content": "#text {\n  display: block;\n  height: 100px;\n  width: 456px;\n}\n\n#table th {\n  text-align: center;\n  font-weight: bold;\n}\n\n#table td {\n  width: 150px;\n  white-space: nowrap;\n  text-align: center;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 12px;\n  cursor: pointer;\n}\n\n#table .nw {\n  background: #999;\n}\n\n#table .n {\n  background: #03f;\n  color: #fff;\n}\n\n#table .ne {\n  background: #ff6;\n}\n\n#table .w {\n  background: #ff0;\n}\n\n#table .c {\n  background: #60c;\n  color: #fff;\n}\n\n#table .e {\n  background: #09f;\n  color: #fff;\n}\n\n#table .sw {\n  background: #963;\n  color: #fff;\n}\n\n#table .s {\n  background: #f60;\n  color: #fff;\n}\n\n#table .se {\n  background: #0c3;\n  color: #fff;\n}\n\n#table .highlight {\n  background: red;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"parent\" onmouseenter=\"mouselog(event)\" onmouseleave=\"mouselog(event)\">parent\n    <div id=\"child\">child</div>\n  </div>\n\n  <textarea id=\"text\"></textarea>\n  <input type=\"button\" onclick=\"text.value=''\" value=\"Clear\">\n\n  <script src=\"script.js\"></script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave.view/script.js",
    "content": "function mouselog(event) {\n  let d = new Date();\n  text.value += `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()} | ${event.type} [target: ${event.target.id}]\\n`.replace(/(:|^)(\\d\\D)/, '$10$2');\n  text.scrollTop = text.scrollHeight;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave.view/style.css",
    "content": "#parent {\n  background: #99C0C3;\n  width: 160px;\n  height: 120px;\n  position: relative;\n}\n\n#child {\n  background: #FFDE99;\n  width: 50%;\n  height: 50%;\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  transform: translate(-50%, -50%);\n}\n\ntextarea {\n  height: 140px;\n  width: 300px;\n  display: block;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-child.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"parent\" onmouseover=\"mouselog(event)\" onmouseout=\"mouselog(event)\">parent\n    <div id=\"child\">child</div>\n  </div>\n\n  <textarea id=\"text\"></textarea>\n  <input type=\"button\" onclick=\"text.value=''\" value=\"Clear\">\n\n  <script src=\"script.js\"></script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-child.view/script.js",
    "content": "function mouselog(event) {\n  let d = new Date();\n  text.value += `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()} | ${event.type} [target: ${event.target.id}]\\n`.replace(/(:|^)(\\d\\D)/, '$10$2');\n  text.scrollTop = text.scrollHeight;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-child.view/style.css",
    "content": "#parent {\n  background: #99C0C3;\n  width: 160px;\n  height: 120px;\n  position: relative;\n}\n\n#child {\n  background: #FFDE99;\n  width: 50%;\n  height: 50%;\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  transform: translate(-50%, -50%);\n}\n\ntextarea {\n  height: 140px;\n  width: 300px;\n  display: block;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"parent\">parent\n    <div id=\"child\">child</div>\n  </div>\n  <textarea id=\"text\"></textarea>\n  <input onclick=\"clearText()\" value=\"Clear\" type=\"button\">\n\n  <script src=\"script.js\"></script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/script.js",
    "content": "let parent = document.getElementById('parent');\nparent.onmouseover = parent.onmouseout = parent.onmousemove = handler;\n\nfunction handler(event) {\n  let type = event.type;\n  while (type.length < 11) type += ' ';\n\n  log(type + \" target=\" + event.target.id)\n  return false;\n}\n\n\nfunction clearText() {\n  text.value = \"\";\n  lastMessage = \"\";\n}\n\nlet lastMessageTime = 0;\nlet lastMessage = \"\";\nlet repeatCounter = 1;\n\nfunction log(message) {\n  if (lastMessageTime == 0) lastMessageTime = new Date();\n\n  let time = new Date();\n\n  if (time - lastMessageTime > 500) {\n    message = '------------------------------\\n' + message;\n  }\n\n  if (message === lastMessage) {\n    repeatCounter++;\n    if (repeatCounter == 2) {\n      text.value = text.value.trim() + ' x 2\\n';\n    } else {\n      text.value = text.value.slice(0, text.value.lastIndexOf('x') + 1) + repeatCounter + \"\\n\";\n    }\n\n  } else {\n    repeatCounter = 1;\n    text.value += message + \"\\n\";\n  }\n\n  text.scrollTop = text.scrollHeight;\n\n  lastMessageTime = time;\n  lastMessage = message;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/style.css",
    "content": "#parent {\n  background: #99C0C3;\n  width: 160px;\n  height: 120px;\n  position: relative;\n}\n\n#child {\n  background: #FFDE99;\n  width: 50%;\n  height: 50%;\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  transform: translate(-50%, -50%);\n}\n\ntextarea {\n  height: 140px;\n  width: 300px;\n  display: block;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"container\">\n    <div class=\"smiley-green\">\n      <div class=\"left-eye\"></div>\n      <div class=\"right-eye\"></div>\n      <div class=\"smile\"></div>\n    </div>\n\n    <div class=\"smiley-yellow\">\n      <div class=\"left-eye\"></div>\n      <div class=\"right-eye\"></div>\n      <div class=\"smile\"></div>\n    </div>\n\n    <div class=\"smiley-red\">\n      <div class=\"left-eye\"></div>\n      <div class=\"right-eye\"></div>\n      <div class=\"smile\"></div>\n    </div>\n  </div>\n\n  <textarea id=\"log\"> Les évènements apparaîtront ici!\n</textarea>\n\n  <script src=\"script.js\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout.view/script.js",
    "content": "container.onmouseover = container.onmouseout = handler;\n\nfunction handler(event) {\n\n  function str(el) {\n    if (!el) return \"null\"\n    return el.className || el.tagName;\n  }\n\n  log.value += event.type + ':  ' +\n    'target=' + str(event.target) +\n    ',  relatedTarget=' + str(event.relatedTarget) + \"\\n\";\n  log.scrollTop = log.scrollHeight;\n\n  if (event.type == 'mouseover') {\n    event.target.style.background = 'pink'\n  }\n  if (event.type == 'mouseout') {\n    event.target.style.background = ''\n  }\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout.view/style.css",
    "content": "body,\nhtml {\n  margin: 0;\n  padding: 0;\n}\n\n#container {\n  border: 1px solid brown;\n  padding: 10px;\n  width: 330px;\n  margin-bottom: 5px;\n  box-sizing: border-box;\n}\n\n#log {\n  height: 120px;\n  width: 350px;\n  display: block;\n  box-sizing: border-box;\n}\n\n[class^=\"smiley-\"] {\n  display: inline-block;\n  width: 70px;\n  height: 70px;\n  border-radius: 50%;\n  margin-right: 20px;\n}\n\n.smiley-green {\n  background: #a9db7a;\n  border: 5px solid #92c563;\n  position: relative;\n}\n\n.smiley-green .left-eye {\n  width: 18%;\n  height: 18%;\n  background: #84b458;\n  position: relative;\n  top: 29%;\n  left: 22%;\n  border-radius: 50%;\n  float: left;\n}\n\n.smiley-green .right-eye {\n  width: 18%;\n  height: 18%;\n  border-radius: 50%;\n  position: relative;\n  background: #84b458;\n  top: 29%;\n  right: 22%;\n  float: right;\n}\n\n.smiley-green .smile {\n  position: absolute;\n  top: 67%;\n  left: 16.5%;\n  width: 70%;\n  height: 20%;\n  overflow: hidden;\n}\n\n.smiley-green .smile:after,\n.smiley-green .smile:before {\n  content: \"\";\n  position: absolute;\n  top: -50%;\n  left: 0%;\n  border-radius: 50%;\n  background: #84b458;\n  height: 100%;\n  width: 97%;\n}\n\n.smiley-green .smile:after {\n  background: #84b458;\n  height: 80%;\n  top: -40%;\n  left: 0%;\n}\n\n.smiley-yellow {\n  background: #eed16a;\n  border: 5px solid #dbae51;\n  position: relative;\n}\n\n.smiley-yellow .left-eye {\n  width: 18%;\n  height: 18%;\n  background: #dba652;\n  position: relative;\n  top: 29%;\n  left: 22%;\n  border-radius: 50%;\n  float: left;\n}\n\n.smiley-yellow .right-eye {\n  width: 18%;\n  height: 18%;\n  border-radius: 50%;\n  position: relative;\n  background: #dba652;\n  top: 29%;\n  right: 22%;\n  float: right;\n}\n\n.smiley-yellow .smile {\n  position: absolute;\n  top: 67%;\n  left: 19%;\n  width: 65%;\n  height: 14%;\n  background: #dba652;\n  overflow: hidden;\n  border-radius: 8px;\n}\n\n.smiley-red {\n  background: #ee9295;\n  border: 5px solid #e27378;\n  position: relative;\n}\n\n.smiley-red .left-eye {\n  width: 18%;\n  height: 18%;\n  background: #d96065;\n  position: relative;\n  top: 29%;\n  left: 22%;\n  border-radius: 50%;\n  float: left;\n}\n\n.smiley-red .right-eye {\n  width: 18%;\n  height: 18%;\n  border-radius: 50%;\n  position: relative;\n  background: #d96065;\n  top: 29%;\n  right: 22%;\n  float: right;\n}\n\n.smiley-red .smile {\n  position: absolute;\n  top: 57%;\n  left: 16.5%;\n  width: 70%;\n  height: 20%;\n  overflow: hidden;\n}\n\n.smiley-red .smile:after,\n.smiley-red .smile:before {\n  content: \"\";\n  position: absolute;\n  top: 50%;\n  left: 0%;\n  border-radius: 50%;\n  background: #d96065;\n  height: 100%;\n  width: 97%;\n}\n\n.smiley-red .smile:after {\n  background: #d96065;\n  height: 80%;\n  top: 60%;\n  left: 0%;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.md",
    "content": "Comme nous pouvons le voir avec HTML / CSS, le curseur est un `<div>` avec un fond coloré, qui contient un executeur - une autre `<div>` avec `position: relative`.\n\nPour positionner l'executeur , nous utilisons `position: relative`, afin de fournir les coordonnées relatives à son parent, ici c'est plus pratique que` position: absolute`.\n\nEnsuite, nous implémentons un glisser-déposer horizontal uniquement avec une limitation par la largeur.\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"slider\" class=\"slider\">\n    <div class=\"thumb\"></div>\n  </div>\n\n  <script>\n    let thumb = slider.querySelector('.thumb');\n\n    thumb.onmousedown = function(event) {\n      event.preventDefault(); // empeche le declenchement de la selection (action du navigateur)\n\n      let shiftX = event.clientX - thumb.getBoundingClientRect().left;\n      // on n'a pas besoin de shiftY , le pouce se deplace seulement horizontalement\n\n      document.addEventListener('mousemove', onMouseMove);\n      document.addEventListener('mouseup', onMouseUp);\n\n      function onMouseMove(event) {\n        let newLeft = event.clientX - shiftX - slider.getBoundingClientRect().left;\n\n        // le pointeur est en dehors de la barre de defilement => bloque le pouce a l'interieur des limites\n        if (newLeft < 0) {\n          newLeft = 0;\n        }\n        let rightEdge = slider.offsetWidth - thumb.offsetWidth;\n        if (newLeft > rightEdge) {\n          newLeft = rightEdge;\n        }\n\n        thumb.style.left = newLeft + 'px';\n      }\n\n      function onMouseUp() {\n        document.removeEventListener('mouseup', onMouseUp);\n        document.removeEventListener('mousemove', onMouseMove);\n      }\n\n    };\n\n    thumb.ondragstart = function() {\n      return false;\n    };\n\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.view/style.css",
    "content": ".slider {\n  border-radius: 5px;\n  background: #E0E0E0;\n  background: linear-gradient(left top, #E0E0E0, #EEEEEE);\n  width: 310px;\n  height: 15px;\n  margin: 5px;\n}\n\n.thumb {\n  width: 10px;\n  height: 25px;\n  border-radius: 3px;\n  position: relative;\n  left: 10px;\n  top: -5px;\n  background: blue;\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"slider\" class=\"slider\">\n    <div class=\"thumb\"></div>\n  </div>\n\n  <script>\n    // ...your code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/source.view/style.css",
    "content": ".slider {\n  border-radius: 5px;\n  background: #E0E0E0;\n  background: linear-gradient(left top, #E0E0E0, #EEEEEE);\n  width: 310px;\n  height: 15px;\n  margin: 5px;\n}\n\n.thumb {\n  width: 10px;\n  height: 25px;\n  border-radius: 3px;\n  position: relative;\n  left: 10px;\n  top: -5px;\n  background: blue;\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/task.md",
    "content": "importance: 5\n\n---\n\n# Barre de défilement\n\nCréer une barre de défilement:\n\n[iframe src=\"solution\" height=60 border=1]\n\nFaire glisser le pouce bleu avec la souris et le déplacer -.\n\nDétails importants:\n\n- Lorsque la souris est appuyée, durant le glissement la souris peut se mettre au-dessus ou en bas de la barre de défilement. La barre de défilement va toujours fonctionner  (c'est convenable pour l'utilisateur).\n- Si la souris se déplace très rapidement vers la gauche or la droite, le pouce doit s'arrêter exactement sur le rebord."
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.md",
    "content": "Pour déplacer l’élément nous pouvons utiliser `position:fixed`, cela rend les coordonnées plus facile à manipuler. A la fin nous devons le  repositionner à `position:absolute` pour mettre l'élément dans le document.\n\nEnsuite, lorsque les coordonnées sont en haut ou au bas de la fenêtre, nous pouvons utiliser `window.scrollTo` pour le faire défiler.\n\nPlus de détails dans votre code, en commentaires.\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"soccer.css\">\n</head>\n\n<body>\n\n<h2>Placez les superhéros un peu partout dans le terrain de football.</h2>\n\n  <p>Les superhéros  et la balle sont des éléments ayant la classe \"draggable\".  Rendez les effectivement déplaçable.</p>\n\n  <p>Important: limitez le déplacement au niveau de la fenêtre. Si un élément déplaçable atteint le haut ou le bas de la fenêtre, alors la page doit défiler pour nous permettre de le déplacer d'avantage.</p>\n\n  <p> Si votre écran est assez large pour contenir tout le document -- rendez la fenêtre plus petite afin d'obtenir un défilement vertical, pour que vous puissiez le tester.</p>\n\n  <p>dans cette tâche, il est suffisant de gérer le défilement vertical. Souvent, il n’y a pas de défilement horizontal, et il est géré de manière similaire au besoin.</p>\n\n  <p>Et aussi: Les héros ne doivent jamais quitter la page. S'ils atteignent le bord document, il ne doit pas avoir de déplacement en dehors de ce dernier.</p>\n\n  <div id=\"field\">\n\n  </div>\n\n  <div class=\"hero draggable\" id=\"hero1\"></div>\n  <div class=\"hero draggable\" id=\"hero2\"></div>\n  <div class=\"hero draggable\" id=\"hero3\"></div>\n  <div class=\"hero draggable\" id=\"hero4\"></div>\n  <div class=\"hero draggable\" id=\"hero5\"></div>\n  <div class=\"hero draggable\" id=\"hero6\"></div>\n\n  <img src=\"https://en.js.cx/clipart/ball.svg\" class=\"draggable\">\n\n  <div style=\"clear:both\"></div>\n\n  <script src=\"soccer.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.css",
    "content": "html, body {\n  margin: 0;\n  padding: 0;\n}\n\n#field {\n  background: url(field.svg);\n  width: 800px;\n  height: 500px;\n  float: left;\n}\n\n/* heros et la  balle (deplacable) */\n\n.hero {\n  background: url(https://js.cx/drag-heroes/heroes.png);\n  width: 130px;\n  height: 128px;\n  float: left;\n}\n\n#hero1 {\n  background-position: 0 0;\n}\n\n#hero2 {\n  background-position: 0 -128px;\n}\n\n#hero3 {\n  background-position: -120px 0;\n}\n\n#hero4 {\n  background-position: -125px -128px;\n}\n\n#hero5 {\n  background-position: -248px -128px;\n}\n\n#hero6 {\n  background-position: -244px 0;\n}\n\n.draggable {\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js",
    "content": "let isDragging = false;\n\ndocument.addEventListener('mousedown', function(event) {\n\n  let dragElement = event.target.closest('.draggable');\n\n  if (!dragElement) return;\n\n  event.preventDefault();\n\n  dragElement.ondragstart = function() {\n      return false;\n  };\n\n  let coords, shiftX, shiftY;\n\n  startDrag(dragElement, event.clientX, event.clientY);\n\n  function onMouseUp(event) {\n    finishDrag();\n  };\n\n  function onMouseMove(event) {\n    moveAt(event.clientX, event.clientY);\n  }\n\n  // commencer a l'evenement  deplacer:\n  //  se rappeler du changement initial\n  //   depalcerla position:fixed de  l' element  et un element enfant directe de l'element body\n  function startDrag(element, clientX, clientY) {\n    if(isDragging) {\n      return;\n    }\n\n    isDragging = true;\n\n    document.addEventListener('mousemove', onMouseMove);\n    element.addEventListener('mouseup', onMouseUp);\n\n    shiftX = clientX - element.getBoundingClientRect().left;\n    shiftY = clientY - element.getBoundingClientRect().top;\n\n    element.style.position = 'fixed';\n\n    moveAt(clientX, clientY);\n  };\n\n  // changer a absolue les coordonnees en  bas, pour fixer l'element dans le document\n  function finishDrag() {\n    if(!isDragging) {\n      return;\n    }\n\n    isDragging = false;\n\n    dragElement.style.top = parseInt(dragElement.style.top) + window.pageYOffset + 'px';\n    dragElement.style.position = 'absolute';\n\n    document.removeEventListener('mousemove', onMouseMove);\n    dragElement.removeEventListener('mouseup', onMouseUp);\n  }\n\n  function moveAt(clientX, clientY) {\n    // nouvelles coordonnees relatives a la fenetre\n    let newX = clientX - shiftX;\n    let newY = clientY - shiftY;\n\n    // controler si les nouvelles coordonneses sont au dessous sous du bord inferieur de la fenetre\n    let newBottom = newY + dragElement.offsetHeight; // new bottom\n\n    // au dessous de la fenetre? on fait defiler la page\n    if (newBottom > document.documentElement.clientHeight) {\n      // fin de coordonne relativement a la fenetre du document\n      let docBottom = document.documentElement.getBoundingClientRect().bottom;\n\n      // fait defiler le document vers le bas par 10px contient un probleme\n      // cela peut defiler au dela de la fin du document\n      // Math.min(combien reste t il vers la fin du document, 10)\n      let scrollY = Math.min(docBottom - newBottom, 10);\n\n      // les calsculs sont imprecises, il peut subsiter des erreurs d'arrondis pouvant mener a un defilement vers le haut\n      // cela ne devrait pas exister, on resouds cela ici\n      if (scrollY < 0) scrollY = 0;\n\n      window.scrollBy(0, scrollY);\n\n      // un deplacement rapide de la souris  peut mettre le curseur au dela des limites du document\n      // Si cela survient-\n      // limite le nouveau Y (new Y) au maximum possible ( tout just au bas dudocument)\n      newY = Math.min(newY, document.documentElement.clientHeight - dragElement.offsetHeight);\n    }\n\n    // Controler si les nouvelles coordonnees sont au dessus des limites superieures de la fenetre ( logique similaire)\n    if (newY < 0) {\n      // defile vers le haut\n      let scrollY = Math.min(-newY, 10);\n      if (scrollY < 0) scrollY = 0; // controler les erreurs de precisions\n\n      window.scrollBy(0, -scrollY);\n      //un deplacement rapide de la souris  peut mettre le curseur au dela du debut du document\n      newY = Math.max(newY, 0); // newY ne doit pas etre moins de 0\n    }\n\n\n    // limite le nouveau X  (new X) dans les limites de la fenetre\n    // il n'y a pas de defilement donc c'est simple\n    if (newX < 0) newX = 0;\n    if (newX > document.documentElement.clientWidth - dragElement.offsetWidth) {\n      newX = document.documentElement.clientWidth - dragElement.offsetWidth;\n    }\n\n    dragElement.style.left = newX + 'px';\n    dragElement.style.top = newY + 'px';\n  }\n\n});\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"soccer.css\">\n</head>\n\n<body>\n\n  <h2>Placez les superhéros un peu partout dans le terrain de football.</h2>\n\n   <p>Les superhéros  et la balle sont des éléments ayant la classe \"draggable\".  Rendez les effectivement déplaçable.</p>\n\n    <p>Important: limitez le déplacement au niveau de la fenêtre. Si un élément déplaçable atteint le haut ou le bas de la fenêtre, alors la page doit défiler pour nous permettre de le déplacer d'avantage.</p>\n\n  <p> Si votre écran est assez large pour contenir tout le document -- rendez la fenêtre plus petite afin d'obtenir un défilement vertical, pour que vous puissiez le tester.</p>\n\n  <p>dans cette tâche, il est suffisant de gérer le défilement vertical. Souvent, il n’y a pas de défilement horizontal, et il est géré de manière similaire au besoin.</p>\n\n  <p>Et aussi: Les héros ne doivent jamais quitter la page. S'ils atteignent le bord document, il ne doit pas avoir de déplacement en dehors de ce dernier.</p>\n\n  <div id=\"field\">\n\n  </div>\n\n  <div class=\"hero draggable\" id=\"hero1\"></div>\n  <div class=\"hero draggable\" id=\"hero2\"></div>\n  <div class=\"hero draggable\" id=\"hero3\"></div>\n  <div class=\"hero draggable\" id=\"hero4\"></div>\n  <div class=\"hero draggable\" id=\"hero5\"></div>\n  <div class=\"hero draggable\" id=\"hero6\"></div>\n\n  <img src=\"https://en.js.cx/clipart/ball.svg\" class=\"draggable\">\n\n  <div style=\"clear:both\"></div>\n\n  <script src=\"soccer.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/source.view/soccer.css",
    "content": "html, body {\n  margin: 0;\n  padding: 0;\n}\n\n#field {\n  background: url(field.svg);\n  width: 800px;\n  height: 500px;\n  float: left;\n}\n\n/* heros et la balle (deplacable) */\n\n.hero {\n  background: url(https://js.cx/drag-heroes/heroes.png);\n  width: 130px;\n  height: 128px;\n  float: left;\n}\n\n#hero1 {\n  background-position: 0 0;\n}\n\n#hero2 {\n  background-position: 0 -128px;\n}\n\n#hero3 {\n  background-position: -120px 0;\n}\n\n#hero4 {\n  background-position: -125px -128px;\n}\n\n#hero5 {\n  background-position: -248px -128px;\n}\n\n#hero6 {\n  background-position: -244px 0;\n}\n\n.draggable {\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/source.view/soccer.js",
    "content": "// Your code\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/task.md",
    "content": "importance: 5\n\n---\n\n# Glisser les superhéros à l’intérieur du terrain\n\nCette tache peut vous aider à contrôler votre compréhension de plusieurs aspects du Glissez-déplacez et du DOM\n\nDonnez tous les éléments la classe `draggable` -- déplaçable. Comme une balle dans le chapitre. \nEtapes requises:\n\n- Utilisez la délégation d’évènement pour détecter start: un gestionnaire d’évènement unique  sur `document` pour l’évènement  `mousedown`.\n- Si les éléments sont glisses jusqu’a aux limites supérieures ou inferieure de la fenêtre – la page défile en haut/bas pour permettre plus de déplacement.\n- IL n’y a pas de défilement horizontal.\n- Les éléments déplaçables éléments ne doivent jamais quitter la fenêtre, même si après un déplacement  rapide de la souris.\n\n- Utiliser la délégation d’événements pour suivre le début du glissement: un seul gestionnaire d’événements sur `document` pour `mousedown`.\n- Si des éléments sont déplacés vers les bords supérieur/inférieur de la fenêtre -- la page défilera vers le haut/le bas pour permettre un déplacement ultérieur.\n- Il n'y a pas de défilement horizontal (cela simplifie un peu la tâche car l'ajouter est facile).\n- Les éléments déplaçables ou leurs parties ne doivent jamais quitter la fenêtre, même après un déplacement rapide de la souris.\n\n[demo src=\"solution\"]\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/article.md",
    "content": "# Les évènements Glisser-Déposer de la souris\n\nDrag'n'Drop est une excellente solution d'interface. Prendre quelque chose et le faire glisser est un moyen clair et simple de faire beaucoup de choses, de la copie et du déplacement de documents (comme dans les gestionnaires de fichiers) à la commande (déposer des articles dans un panier).\n\nDans la norme HTML moderne, il y a une [section à propos du Drag and Drop](https://html.spec.whatwg.org/multipage/interaction.html#dnd) avec des événements spéciaux tels que `dragstart`, `dragend`, etc.\n\nCes événements nous permettent de prendre en charge des types spéciaux de glisser-déposer, tels que la gestion du glisser-déposer d'un fichier depuis le gestionnaire de fichiers du système d'exploitation et le déposer dans la fenêtre du navigateur. Ensuite, JavaScript peut accéder au contenu de ces fichiers.\n\nMais les événements de drag natifs ont également des limites. Par exemple, nous ne pouvons pas empêcher de faire glisser depuis une certaine zone. Nous ne pouvons pas non plus faire de glisser \"horizontal\" ou \"vertical\" uniquement. Et il existe de nombreuses autres tâches de glisser-déposer qui ne peuvent pas être effectuées en les utilisant. En outre, la prise en charge des appareils mobiles pour de tels événements est très faible.\n\nIci, nous verrons comment implémenter le glisser-déposer à l'aide d'événements de souris.\n\n## l'algorithme Drag’and’Drop\n\n1. Sur `mousedown` - préparer l'élément pour le déplacement, si nécessaire (éventuellement en créer une copie, y ajouter une classe ou autre).\n2. Puis sur `mousemove` le déplacer en changeant `left/top` et `position:absolute`.\n3. Sur `mouseup` - effectue toutes les actions liées à un Drag'n'Drop terminé.\n\n\nCe sont les bases. Plus tard, nous verrons comment utiliser d'autres fonctionnalités, telles que la mise en évidence des éléments sous-jacents actuels pendant que nous les glissons.\n\nVoici la mise en œuvre de faire glisser une balle :\n\n```js\nball.onmousedown = function(event) { \n  // (1) la préparer au déplacement :  réglé en absolute et en haut par z-index\n  ball.style.position = 'absolute';\n  ball.style.zIndex = 1000;\n\n  // déplacez-le de tout parent actuel directement dans body\n  // pour le placer par rapport à body\n  document.body.append(ball);  \n\n  // Centrer la balle aux coordonnées (pageX, pageY)\n  function moveAt(pageX, pageY) {\n    ball.style.left = pageX - ball.offsetWidth / 2 + 'px';\n    ball.style.top = pageY - ball.offsetHeight / 2 + 'px';\n  }\n\n  // déplacer notre balle en positionnement absolu sous le pointeur\n  moveAt(event.pageX, event.pageY);\n\n  function onMouseMove(event) {\n    moveAt(event.pageX, event.pageY);\n  }\n\n  // (2) déplacer la balle sur le déplacement de la souris\n  document.addEventListener('mousemove', onMouseMove);\n\n  // (3) laisser tomber la balle, retirer les gestionnaires inutiles\n  ball.onmouseup = function() {\n    document.removeEventListener('mousemove', onMouseMove);\n    ball.onmouseup = null;\n  };\n\n};\n```\n\nSi nous exécutons le code, nous pouvons remarquer quelque chose d’étrange. Au début de l’action Glisser-Déposer, la  balle est prise en \"fourchette\": Nous commençons à faire glisser son \"clone\".\n\n```online\nVoici un exemple en action:\n\n[iframe src=\"ball\" height=230]\n\nEssayez de faire glisser-déposer avec la souris et vous verrez un tel comportement.\n```\n\nC’est parce que le navigateur a son propre Glisser-Déposer pour les images  et quelques autres  éléments qui s’exécute automatiquement et entre en conflit avec le nôtre.\n\nPour le désactiver:\n\n```js\nball.ondragstart = function() {\n  return false;\n};\n```\n\nMaintenant tout rentre dans l’ordre.\n\n```online\nEn action:\n\n[iframe src=\"ball2\" height=230]\n```\n\nUn autre aspect important—nous suivons l’évènement `mousemove` sur le `document`, pas sur la `balle`. A première vue, il semblerait que la souris soit toujours sur la balle, et nous pouvons lui appliquer l’évènement `mousemove`.\n\nMais si nous nous rappelons, l’évènement `mousemove` se déclenche souvent, mais pas sur chaque pixel. Donc après un mouvement rapide, le curseur peut sauter de la balle pour aller quelque part au milieu du document (ou bien même hors de la fenêtre).\n\nDonc nous devons écouter le `document` pour le capturer.\n\n## Positionnent correcte\n\nDans les exemples ci-dessus la balle est toujours déplacée ainsi, de sorte à ce que son centre soit au-dessous  du curseur:\n\n```js\nball.style.left = pageX - ball.offsetWidth / 2 + 'px';\nball.style.top = pageY - ball.offsetHeight / 2 + 'px';\n```\n\nPas mal,  mais il y a un  effet secondaire. Pour initier le Glissser-Deposer, nous pouvons appliquer un `mousedown` n’importe où sur la balle. Mais si nous le faisons sur le rebord, alors la balle \"saute\" soudainement  pour être centrée sous le curseur.\n\nCe serait mieux si nous gardions le  changement initial de l’élément relativement au curseur.\n\nPar exemple, si nous commençons le glissement par le rebord de la balle, alors le curseur doit rester sur le rebord pendant le déplacement.\n\n![](ball_shift.svg)\n\n1. Lorsqu’un visiteur appuye sur le bouton (`mousedown`) – nous pouvons garder en mémoire la distance du curseur au coin gauche en haut de la balle dans des variables `shiftX/shiftY`. Nous devons garder cette distance en faisant le glissement.\n\n   Pour obtenir ces changements nous pouvons soustraire les coordonnées:\n\n    ```js\n    // onmousedown\n    let shiftX = event.clientX - ball.getBoundingClientRect().left;\n    let shiftY = event.clientY - ball.getBoundingClientRect().top;\n    ```\n\n2. Ensuite pendant qu’on fait le glissement nous positionnons la balle sur le même changement relativement au curseur, ainsi:\n\n    ```js\n    // onmousemove\n    // la balle a une position:absolute\n    ball.style.left = event.pageX - *!*shiftX*/!* + 'px';\n    ball.style.top = event.pageY - *!*shiftY*/!* + 'px';\n    ```\n\nLe code final avec un meilleur positionnement:\n\n```js\nball.onmousedown = function(event) {\n\n*!*\n  let shiftX = event.clientX - ball.getBoundingClientRect().left;\n  let shiftY = event.clientY - ball.getBoundingClientRect().top;\n*/!*\n\n  ball.style.position = 'absolute';\n  ball.style.zIndex = 1000;\n  document.body.append(ball);\n\n  moveAt(event.pageX, event.pageY);\n\n  // Déplace la balle aux cordonnées (pageX, pageY) \n  // Prenant en compte les changements initiaux \n  function moveAt(pageX, pageY) {\n    ball.style.left = pageX - *!*shiftX*/!* + 'px';\n    ball.style.top = pageY - *!*shiftY*/!* + 'px';\n  }\n\n  function onMouseMove(event) {\n    moveAt(event.pageX, event.pageY);\n  }\n\n  // déplace la balle à l’évènement mousemove\n  document.addEventListener('mousemove', onMouseMove);\n\n  // dépose la balle, enlève les gestionnaires d’évènements dont on a pas besoin\n  ball.onmouseup = function() {\n    document.removeEventListener('mousemove', onMouseMove);\n    ball.onmouseup = null;\n  };\n\n};\n\nball.ondragstart = function() {\n  return false;\n};\n```\n\n```online\nEn action (inside `<iframe>`):\n\n[iframe src=\"ball3\" height=230]\n```\n\nLa différence est particulièrement notable si nous faisons glisser la balle depuis son coin gauche. Dans l’exemple précèdent la balle \"saute\" sous le curseur. Maintenant il suit facilement le curseur à partir de sa position en cours.\n\n## Potentiels Cibles pour un Déposer (déposables)\n\nDans les exemples précédents la balle pouvait être déposée juste \"n’importe où\" pour qu’elle y soit. En réalité, nous prenons souvent un élément et le déposons sur un  autre. Par exemple, un fichier dans un dossier, ou autre chose.\n\nEn d’autres termes, nous prenons un élément \"déplaçable\" et le déposons sur un élément  ou l’on peut déposer un élément \"déposable\".\n\nNous avons besoin de savoir :\n- où l'élément a été déposé à la fin du glisser-déposer -- pour effectuer l'action correspondante,\n- et, de préférence, connaitre l'élément droppable que nous déplaçons pour le mettre en surbrillance.\n\nLa solution est assez intéressante et juste un peu délicate, alors couvrons-la ici.\n\nQuelle peut être la première idée ? Probablement pour définir des gestionnaires de `mouseover/mouseup` sur des droppables potentiels ?\n\nMais cela ne marche pas.\n\nLe problème est que, pendant que nous exécutons le déplacement, l’élément déplaçable est toujours sur les autres éléments. Et les évènements de la souris ont lieu seulement sur l’élément, pas sur ceux se trouvant au-dessous  de ce dernier.\n\nPar exemple, ci-dessous deux éléments `<div>`, un en rouge au-dessus d’un autre en bleu (couvert complètement). Il n’y a aucun moyen de capturer un évènement sur celui en bleu, parce que le rouge se trouve au-dessus de celui-ci:\n\n```html run autorun height=60\n<style>\n  div {\n    width: 50px;\n    height: 50px;\n    position: absolute;\n    top: 0;\n  }\n</style>\n<div style=\"background:blue\" onmouseover=\"alert(Ne marche jamais')\"></div>\n<div style=\"background:red\" onmouseover=\"alert('sur le rouge!')\"></div>\n```\n\nDe la même manière avec l’élément déplaçable, la balle est toujours au-dessus des autres éléments, donc les évènements s’exécutent sur lui. Quel que soit les gestionnaires que l’on assigne aux éléments se trouvant en bas, ils ne vont pas fonctionner.\n\nC’est pourquoi l’idée initiale de mettre les gestionnaires sur des potentiels objets déposables ne fonctionne pas  en pratique. Ils ne vont pas démarrer.\n\nAlors, que faire?\n\nIl existe une méthode appelée `document.elementFromPoint(clientX, clientY)`. Elle retourne l’élément le plus imbriqué sur les coordonnées données  relatif à une fenêtre (ou bien `null` si les coordonnées données sont hors de la fenêtre). S'il y a plusieurs éléments qui se chevauchent sur les mêmes coordonnées, le plus haut est renvoyé.\n\nNous pouvons l’utiliser dans n’importe lequel de nos gestionnaires d’événements à la souris pour détecter le droppable potentiel sous le pointeur, comme ceci :\n\n```js\n// dans un gestionnaire d'événements de souris\nball.hidden = true; // (*) hide the element that we drag\n\nlet elemBelow = document.elementFromPoint(event.clientX, event.clientY);\n// elemBelow est l'élément situé sous la balle, peut être droppable\n\nball.hidden = false;\n```\n\nRemarque: nous devons cacher la balle avant l'appel `(*)`. Sinon, nous aurons généralement une balle sur ces coordonnées, car c'est l'élément supérieur sous le pointeur : `elemBelow=ball`. Donc, nous le cachons et montrons immédiatement à nouveau.\n\nNous pouvons utiliser ce code pour voir quel élément nous sommes entrain de \"survoler\" à n’importe quel moment. Et gérer le déposer lorsque cela survient.\n\nUne version élaborée du code de `onMouseMove` pour trouver les éléments qui peuvent recevoir un \"déposer\":\n\n```js\n// droppable potentiel que nous survolons en ce moment\nlet currentDroppable = null;\n\nfunction onMouseMove(event) {\n  moveAt(event.pageX, event.pageY);\n\n  ball.hidden = true;\n  let elemBelow = document.elementFromPoint(event.clientX, event.clientY);\n  ball.hidden = false;\n\n  // les événements de déplacement de souris peuvent se déclencher en dehors de la fenêtre (lorsque la balle est déplacée hors de l'écran)\n  // si clientX/clientY sont en dehors de la fenêtre, alors elementFromPoint renvoie null\n  if (!elemBelow) return;\n\n  // Les objets potentiels pouvant recevoir les déposer sont étiquetés avec la classe \"droppable\" (peut être une autre logique)\n  let droppableBelow = elemBelow.closest('.droppable');\n\n  if (currentDroppable != droppableBelow) {\n    // on vole dedans ou dehors …\n    // note: les deux valeurs peuvent être nulles\n    //   currentDroppable=null si nous n'étions pas sur un droppable avant cet événement (par exemple sur un espace vide)\n    //   droppableBelow=null si nous ne sommes pas sur un droppable maintenant, pendant cet événement\n\n    if (currentDroppable) {\n      // logique pour évaluer  le \"survol hors\" de l’objet déposable (enlever la mise en valeur)\n      leaveDroppable(currentDroppable);\n    }\n    currentDroppable = droppableBelow;\n    if (currentDroppable) {\n      // logique pour évaluer  le \"survol vers l’intérieur\" de l’objet déposable\n      enterDroppable(currentDroppable);\n    }\n  }\n}\n```\n\nDans l’exemple ci-dessous quand la balle est glissée sur le camp du gardien de but, ce dernier est mis en valeur.\n\n[codetabs height=250 src=\"ball4\"]\n\nMaintenant nous avons  \"l’objet cible\" en cours,  que nous survolons, dans une  variable `currentDroppable` Durant tout le processus et nous pouvons l’utiliser pour mettre en valeur une chose ou faire une autre chose.\n\n## Résumé\n\nNous avons étudié un algorithme de base du glisser-déposer.\nLes composantes clés sont:\n\n1. Flux d'événements : `ball.mousedown` -> `document.mousemove` -> `ball.mouseup` (annule l’évènement natif de `ondragstart`).\n2. Au démarrage du glissement – capturer le changement initial du curseur relativement à l’élément: `shiftX/shiftY` et le garder durant le glissement.\n3. Détecter les éléments pouvant recevoir l’action déposer sous le curseur en utilisant `document.elementFromPoint`.\n\n- A l’exécution de l’évènement `mouseup` nous pouvons finaliser le déposer: changer les données, déplacer les évènements.\n- Nous pouvons mettre en valeur les éléments que nous survolons.\n- Nous pouvons limiter le glissement sur une surface et selon une direction voulue. \n- Nous pouvons utiliser la délégation d’évènements avec  `mousedown/up`.  Un gestionnaire d’évènement sur une grande surface qui contrôle la propriété `event.target` peut gérer  le Glisser-déposer pour des centaines d’éléments.\n- Ainsi de suite.\n\nIl existe des frameworks qui fondent leur architecture sur cela : `DragZone`, `Droppable`, `Draggable` et d'autres classes. La plupart d’entre eux font des choses similaires à celles décrites au-dessus, donc cela devrait être facile pour vous de les comprendre maintenant. Ou bien même déployer le vôtre, parce que vous savez déjà comment gérer le processus, et comme vous le voyez cela pourrait être plus flexible que d’adapter une solution tierce.\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/ball.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body style=\"height: 200px\">\n\n  <p>Fait glisser la balle.</p>\n\n  <img src=\"https://js.cx/clipart/ball.svg\" style=\"cursor:pointer\" width=\"40\" height=\"40\" id=\"ball\">\n\n\n  <script>\n    ball.onmousedown = function(event) {\n      ball.style.position = 'absolute';\n      ball.style.zIndex = 1000;\n      document.body.appendChild(ball);\n\n      moveAt(event.pageX, event.pageY);\n\n      function moveAt(pageX, pageY) {\n        ball.style.left = pageX - ball.offsetWidth / 2 + 'px';\n        ball.style.top = pageY - ball.offsetHeight / 2 + 'px';\n      }\n\n      function onMouseMove(event) {\n        moveAt(event.pageX, event.pageY);\n      }\n\n      document.addEventListener('mousemove', onMouseMove);\n\n      ball.onmouseup = function() {\n        document.removeEventListener('mousemove', onMouseMove);\n        ball.onmouseup = null;\n      };\n\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/ball2.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body style=\"height: 200px\">\n\n  <p>Drag the ball.</p>\n\n  <img src=\"https://js.cx/clipart/ball.svg\" style=\"cursor:pointer\" width=\"40\" height=\"40\" id=\"ball\">\n\n\n  <script>\n    ball.onmousedown = function(event) {\n      ball.style.position = 'absolute';\n      ball.style.zIndex = 1000;\n      document.body.appendChild(ball);\n\n      moveAt(event.pageX, event.pageY);\n\n      function moveAt(pageX, pageY) {\n        ball.style.left = pageX - ball.offsetWidth / 2 + 'px';\n        ball.style.top = pageY - ball.offsetHeight / 2 + 'px';\n      }\n\n      function onMouseMove(event) {\n        moveAt(event.pageX, event.pageY);\n      }\n\n      document.addEventListener('mousemove', onMouseMove);\n\n      ball.onmouseup = function() {\n        document.removeEventListener('mousemove', onMouseMove);\n        ball.onmouseup = null;\n      };\n\n    };\n\n\n    ball.ondragstart = function() {\n      return false;\n    };\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/ball3.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body style=\"height: 200px\">\n\n  <p>Drag the ball.</p>\n\n  <img src=\"https://js.cx/clipart/ball.svg\" style=\"cursor:pointer\" width=\"40\" height=\"40\" id=\"ball\">\n\n\n  <script>\n    ball.onmousedown = function(event) {\n\n      let shiftX = event.clientX - ball.getBoundingClientRect().left;\n      let shiftY = event.clientY - ball.getBoundingClientRect().top;\n\n      ball.style.position = 'absolute';\n      ball.style.zIndex = 1000;\n      document.body.append(ball);\n\n      moveAt(event.pageX, event.pageY);\n\n      function moveAt(pageX, pageY) {\n        ball.style.left = pageX - shiftX + 'px';\n        ball.style.top = pageY - shiftY + 'px';\n      }\n\n      function onMouseMove(event) {\n        moveAt(event.pageX, event.pageY);\n      }\n\n      document.addEventListener('mousemove', onMouseMove);\n\n      ball.onmouseup = function() {\n        document.removeEventListener('mousemove', onMouseMove);\n        ball.onmouseup = null;\n      };\n\n    };\n\n    ball.ondragstart = function() {\n      return false;\n    };\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/ball4.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <p>Drag the ball.</p>\n\n  <img src=\"https://en.js.cx/clipart/soccer-gate.svg\" id=\"gate\" class=\"droppable\">\n\n  <img src=\"https://en.js.cx/clipart/ball.svg\" id=\"ball\">\n\n  <script>\n    let currentDroppable = null;\n\n    ball.onmousedown = function(event) {\n\n      let shiftX = event.clientX - ball.getBoundingClientRect().left;\n      let shiftY = event.clientY - ball.getBoundingClientRect().top;\n\n      ball.style.position = 'absolute';\n      ball.style.zIndex = 1000;\n      document.body.append(ball);\n\n      moveAt(event.pageX, event.pageY);\n\n      function moveAt(pageX, pageY) {\n        ball.style.left = pageX - shiftX + 'px';\n        ball.style.top = pageY - shiftY + 'px';\n      }\n\n      function onMouseMove(event) {\n        moveAt(event.pageX, event.pageY);\n\n        ball.hidden = true;\n        let elemBelow = document.elementFromPoint(event.clientX, event.clientY);\n        ball.hidden = false;\n\n        if (!elemBelow) return;\n\n        let droppableBelow = elemBelow.closest('.droppable');\n        if (currentDroppable != droppableBelow) {\n          if (currentDroppable) { // null lorsque nous étions sur un élément déposable avant cet évènement\n            leaveDroppable(currentDroppable);\n          }\n          currentDroppable = droppableBelow;\n          if (currentDroppable) { // null si nous ne n'atterrissions pas sur un élément déposable maintenant\n            // (peut être a juste quitte l'objet déposable)\n            enterDroppable(currentDroppable);\n          }\n        }\n      }\n\n      document.addEventListener('mousemove', onMouseMove);\n\n      ball.onmouseup = function() {\n        document.removeEventListener('mousemove', onMouseMove);\n        ball.onmouseup = null;\n      };\n\n    };\n\n    function enterDroppable(elem) {\n      elem.style.background = 'pink';\n    }\n\n    function leaveDroppable(elem) {\n      elem.style.background = '';\n    }\n\n    ball.ondragstart = function() {\n      return false;\n    };\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/ball4.view/style.css",
    "content": "\n#gate {\n  cursor: pointer;\n  margin-bottom: 100px;\n  width: 83px;\n  height: 46px;\n}\n\n#ball {\n  cursor: pointer;\n  width: 40px;\n  height: 40px;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/6-pointer-events/article.md",
    "content": "# Les événements de pointeur\n\nLes événements de pointeur sont un moyen moderne de gérer les entrées d'une grande variété de périphérique de pointage, tel que les souris, les stylets, les écrans tactiles, etc.\n\n## L'histoire en bref\n\nRéalisons un aperçu rapide, pour que vous compreniez l'idée générale et la place des événements de pointeur parmi les autres types d'événement.\n\n- Autrefois, il y avait uniquement des événements de souris.\n\n    Puis, les appareils à écran tactile se sont généralisés, plus particulièrement les téléphones portables et les tablettes. Pour que les scripts existants continuent de fonctionner, ces appareils ont généré (et génèrent toujours) des événements de souris. Par exemple, tapoter sur un écran tactile génère un événement `mousedown`. Ainsi, les appareils à écran tactile fonctionnaient bien avec les pages web.\n    \n    Mais les appareils à écran tactile ont plus de potentiel qu'une souris. Par exemple, il est possible de cibler plusieurs endroits à la fois (\"multi-touch\"). Néanmoins, les événements de souris n'ont pas les propriétés nécessaires pour gérer le multi-touch.\n\n- Ainsi, les événements tactiles ont été introduit, tels que `touchstart`, `touchend`, `touchmove`, qui ont des propriétés tactiles spécifiques (nous ne les couvrirons pas en détails ici, car les événements de pointeur sont bien meilleur).\n\n    Pourtant, cela n’a pas suffit, puisqu'il existe beaucoup d'autres périphériques, tels que les stylets, qui ont leurs propres particularités. Également, écrire du code qui gérait à la fois les événements tactiles et de souris était fastidieux.\n\n- Pour résoudre ces problèmes, le nouveau standard Pointer Events a été introduit. Il fournit un ensemble d'événements pour tout type de périphérique de pointage.\n\nÀ ce jour, les spécifications [Pointer Events Level 2](https://www.w3.org/TR/pointerevents2/) sont prises en charge dans tous les principaux navigateurs, tandis que les spécifications [Pointer Events Level 3](https://w3c.github.io/pointerevents/), plus récentes, sont en cours de rédaction et sont en grande partie compatible avec Pointer Events Level 2.\n\nÀ moins que vous développiez pour de vieux navigateurs, tels qu'Internet Explorer 10, Safari 12 ou antérieur, il est inutile d'utiliser les événements de souris ou tactiles -- nous pouvons passer aux événements de pointeur.\n\nAinsi votre code fonctionnera aussi bien avec un périphérique tactile qu'avec une souris.\n\nCela dit, il existe quelques particularités importantes à connaître pour se servir des événements de pointeur correctement et éviter les surprises. Nous mettrons l'accent sur ces derniers dans cet article.\n\n## Les types d'événement de pointeur\n\nLes événements de pointeur sont nommés de façon similaire aux événements de souris:\n\n| Événement de pointeur | Événement de souris équivalent |\n|---------------|-------------|\n| `pointerdown` | `mousedown` |\n| `pointerup` | `mouseup` |\n| `pointermove` | `mousemove` |\n| `pointerover` | `mouseover` |\n| `pointerout` | `mouseout` |\n| `pointerenter` | `mouseenter` |\n| `pointerleave` | `mouseleave` |\n| `pointercancel` | - |\n| `gotpointercapture` | - |\n| `lostpointercapture` | - |\n\nComme nous pouvons le voir, pour chaque `mouse<event>`, il existe un `pointer<event>` jouant un rôle similaire. Il existe également 3 événements de pointeur supplémentaires qui n'ont pas d'événement `mouse...` équivalent. Nous les étudierons en détails bientôt.\n\n```smart header=\"Remplacer `mouse<event>` par `pointer<event>` dans notre code\"\nNous pouvons remplacer les événements `mouse<event>` par `pointer<event>` dans notre code et s'attendre à ce qu'il continue de fonctionner correctement avec une souris.\n\nLa prise en charge des périphériques tactiles s'améliorera aussi \"comme par magie\", bien que nous ayons besoin de rajouter `touch-action: none` à certains endroits du CSS. Nous couvrirons ce sujet plus bas dans la partie sur l'événement `pointercancel`.\n```\n\n## Les propriétés de l'événement de pointeur\n\nLes événements de pointeur ont les mêmes propriétés que les événements de souris, telles que `clientX/Y`, `target`, etc, ainsi que d'autres:\n\n- `pointerId` - l'identifiant unique du pointeur provoquant l'événement.\n    \n    Généré par le navigateur. Nous permet de gérer plusieurs pointeurs, tels qu'un écran tactile multi-touch muni d'un stylet (des exemples suivront).\n- `pointerType` - le type de périphérique de pointage. Doit être une chaîne de caractère, parmi ceux-ci : \"mouse\", \"pen\" ou \"touch\".\n\n    Nous pouvons utiliser cette propriété pour réagir différemment en fonction du type de pointeur.\n- `isPrimary` - est `true` pour le pointeur principal (le premier doigt en multi-touch).\n\nCertains périphériques de pointage mesurent la surface de contact et la pression appliquée, par exemple pour un doigt sur l'écran tactile. Il existe des propriétés supplémentaires pour cela:\n\n- `width` - la largeur de la zone du pointeur (par exemple un doigt) en contact avec l'appareil. Si incompatible, pour une souris par exemple, prend la valeur `1`.\n- `height` - la hauteur de la zone du pointeur en contact avec l'appareil. Si incompatible, prend la valeur `1`.\n- `pressure` - la pression de l'extrémité du pointeur, prenant des valeurs comprises entre 0 et 1. Pour les appareils qui ne prennent pas en charge la pression, la valeur doit être soit `0.5` (pression appliquée) ou `0`.\n- `tangentialPressure` - la pression tangentielle normalisée.\n- `tiltX`, `tiltY`, `twist` - propriétés spécifiques au stylet qui décrivent la position relative du stylet par rapport à la surface.\n\nCes propriétés ne sont pas prises en charge par la plupart des appareils, et sont donc rarement utilisées. Vous trouverez plus de détails sur ces propriétés dans les [spécifications](https://w3c.github.io/pointerevents/#pointerevent-interface) si besoin.\n\n## Le multi-touch\n\nUne des choses que les événements de souris ne prennent pas du tout en charge est le multi-touch: un utilisateur peut cibler plusieurs endroits en même temps sur l'écran de son téléphone portable ou de sa tablette, ou réaliser des gestes particuliers.\n\nLes événements de pointeur permettent la gestion du multi-touch avec l'aide des propriétés `pointerId` et `isPrimary`.\n\nVoila ce qui arrive lorsqu'un utilisateur touche un écran tactile à un endroit, puis rajoute un second doigt à un autre endroit:\n\n1. Au contact du premier doigt:\n    - `pointerdown` avec `isPrimary=true` et un `pointerId`.\n2. Pour le deuxième doigt et les suivants (en considérant que le premier est toujours en contact avec l'écran):\n    - `pointerdown` avec `isPrimary=false` et un `pointerId` différent pour chaque doigt.\n\nRemarque: le `pointerId` n'est pas attribué à l'ensemble du périphérique, mais à chaque doigt en contact. Si nous utilisons 5 doigts simultanément pour toucher l'écran, nous avons 5 événements `pointerdown`, chacun avec ces coordonnées respectives et un `pointerId` différent.\n\nLes événements associés au premier doigt ont toujours `isPrimary=true`.\n\nNous pouvons suivre plusieurs doigts en contact en utilisant leur `pointerId`. Quand l'utilisateur déplace un doigt et le déplace à nouveau, nous recevons des événements `pointermove` et `pointerup` avec un `pointerId` identique à celui de `pointerdown`.\n\n```online\nVoici la démo qui consigne les événements `pointerdown` et `pointerup`:\n\n[iframe src=\"multitouch\" edit height=200]\n\nRemarque: vous devez utiliser un appareil à écran tactile, tel qu'un téléphone portable ou une tablette, pour voir la différence sur `pointerId/isPrimary`. Pour les périphériques single-touch, tels qu'une souris, il y aura toujours le même `pointerId` avec `isPrimary=true`, pour tous les événements de pointeur.\n```\n\n## L'événement pointercancel\n\nL'événement `pointercancel` se déclenche quand une interaction de pointeur est en cours, et qu'un événement provoquant son interruption se produit, de façon à ce que plus aucun événement de pointeur soit généré.\n\nDe tels événements sont:\n- Le périphérique de pointage a été physiquement désactivé.\n- L'orientation de l'appareil a été modifié (pivotement de la tablette).\n- Le navigateur a décidé de gérer l'interaction lui-même, la considérant comme un mouvement de souris, une action de zoom et panorama ou autres.\n\nNous allons montrer le fonctionnement de `pointercancel` à l'aide d'un exemple pratique pour voir comment il nous impacte.\n\nSupposons que nous mettions en place un glisser-déposer pour un ballon, comme au début de l'article <info:mouse-drag-and-drop>.\n\nVoici le flux d'actions de l'utilisateur et les événements correspondants:\n\n1) L'utilisateur appuie sur l'image pour commencer à la déplacer\n    - l'événement `pointerdown` se déclenche\n2) Ensuite, il commence à déplacer le pointeur (en faisant ainsi glisser l'image)\n    - l'événement `pointerdown` se déclenche, peut-être même plusieurs fois\n3) Et là, surprise! Le navigateur prend nativement en charge le glisser-déposer d'images, qui fait alors effet et prend le contrôle du processus de glisser-déposer, générant ainsi un événement `pointercancel`.\n    - Le navigateur gère maintenant seul le glisser-déposer de l'image. L'utilisateur peut même déplacer l'image du ballon hors du navigateur, dans sa messagerie électronique ou son gestionnaire de fichier.\n    - Plus d'événements `pointermove` pour nous.\n\nAinsi, le problème est le \"détournement\" de l'interaction par le navigateur: `pointercancel` se déclenche au début du processus de glisser-déposer, et plus aucun événement `pointermove` est généré.\n\n```online\nVoici la démo du glisser-déposer avec consignation des événements de pointeur (uniquement `up/down`, `move` et `cancel`) dans la `textarea` :\n\n[iframe src=\"ball\" height=240 edit]\n```\n\nNous aimerions implémenter nous même le glisser-déposer, alors indiquons au navigateur de ne pas en prendre le contrôle.\n\n**Empêcher l'action par défaut du navigateur afin d'éviter `pointercancel`.**\n\nNous avons besoin de deux choses:\n\n1. Empêcher le glisser-déposer d'origine de se produire:\n    - Nous pouvons faire cela en définissant `ball.ondragstart = () => false`, comme décrit dans l'article <info:mouse-drag-and-drop>.\n    - Ceci fonctionne bien pour les événements de souris.\n2. Pour les appareils tactiles, il existe d'autres actions de navigateur liées au toucher (en plus du glisser-déposer). Pour éviter les problèmes avec eux aussi :\n    - Les empêcher en définissant `#ball { touch-action: none }` dans le CSS.\n    - Ainsi notre code fonctionnera sur les périphériques tactiles.\n\nAprès avoir fait cela, les événements fonctionneront comme prévu. Le navigateur ne détournera pas le processus et n'émettra pas `pointercancel`. \n\n```online\nCette démo rajoute ces lignes:\n\n[iframe src=\"ball-2\" height=240 edit]\n\nComme vous pouvez le voir, `pointercancel` n'apparaît plus.\n```\n\nMaintenant, nous pouvons ajouter le code pour effectivement déplacer le ballon, et notre glisser-déposer fonctionnera pour les souris et les périphériques tactiles.\n\n## La capture de pointeur\n\nLa capture de pointeur est une fonctionnalité particulière aux événements de pointeur.\n\nL'idée est très simple, mais peut sembler un peu étrange à première vue, car rien de similaire existe pour tout autre type d'événement.\n\nLa méthode principale est:\n- `elem.setPointerCapture(pointerId)` - lie les événements du `pointerId` renseigné à `elem`. Après cet appel, tous les événements de pointeur partageant le même `pointerId` auront `elem` comme cible (comme s'ils avaient lieu sur `elem`), peu importe l'endroit où ils ont réellement été généré dans le document.\n\nEn d'autres termes, `elem.setPointerCapture(pointerId)` modifie la cible de tout les événements ultérieurs du `pointerId` renseigné vers `elem`.\n\nLe lien est supprimé:\n- automatiquement quand les événements `pointerup` ou `pointercancel` se produisent,\n- automatiquement quand `elem` est supprimé du document,\n- quand `elem.releasePointerCapture(pointerId)` est appelé.\n\nMaintenant à quoi ça sert ? Il est temps de voir un exemple concret.\n\n**La capture de pointeur peut être utilisé pour simplifier les interactions de type glisser-déposer.**\n\nRappelons nous comment intégrer une barre de défilement, comme détaillé dans l'article <info:mouse-drag-and-drop>.\n\nNous réalisons une barre de défilement constituée d'une règle et d'un curseur (`thumb`).\n\nNous pouvons créer un élément `slider` pour représenter la bande et le \"runner\" (`thumb`) à l'intérieur :\n\n```html\n<div class=\"slider\">\n  <div class=\"thumb\"></div>\n</div>\n```\n\nAvec les styles, ça ressemble à ça :\n\n[iframe src=\"slider-html\" height=40 edit]\n\n<p></p>\n\nEt voici la logique de travail, telle qu'elle a été décrite, après avoir remplacé les événements de souris par des événements de pointeur similaires :\n\n\n1. L'utilisateur appuie sur le curseur `thumb` - `pointerdown` se déclenche.\n2. Ensuite, il déplace le pointeur - `pointermove` se déclenche, et nous déplaçons le `thumb` le long de la règle.\n    - ...Lorsque le pointeur se déplace, il peut quitter le `thumb` de la barre de défilement: allez au-dessus ou en-dessous de lui. Le `thumb` doit se déplacer uniquement horizontalement, en restant aligné avec le pointeur.\n\nDans la solution basée sur les événements de la souris, pour suivre tous les mouvements du pointeur, y compris lorsqu'il passe au-dessus/au-dessous du `thumb`, nous avons dû affecter le gestionnaire d'événements `mousemove` sur l'ensemble du `document`.\n\nCette solution semble un peu \"sale\". Un des problèmes est que les mouvements de pointeur autour du document peuvent provoquer des effets secondaires, déclencher d'autres gestionnaires d'événements (comme `mouseover`), totalement indépendants de la barre de défilement.\n\nC'est là où `setPointerCapture` entre en jeu.\n\n- Nous pouvons appeler `thumb.setPointerCapture(event.pointerId)` dans le gestionnaire de `pointerdown`,\n- Ainsi, les événements de pointeur ultérieurs prendront `thumb` pour cible jusqu'à `pointerup/cancel`.\n- Quand `pointerup` se déclenche (déplacement achevé), le lien est automatiquement supprimé, nous n'avons pas besoin de nous en préoccupé.\n\nAinsi, même si l'utilisateur déplace le pointeur sur l'ensemble du document, les gestionnaires d'événement seront appelés sur `thumb`. De plus, les propriétés de coordonnées des objets événement, telles que `clientX/clientY`, restent toujours valide - la capture affecte uniquement `target/currentTarget`.\n\n\nVoici le code de base:\n\n```js\nthumb.onpointerdown = function(event) {\n  // modifie la cible de tout les événements de pointeur (jusqu'à pointerup) sur thumb\n  thumb.setPointerCapture(event.pointerId);\n\n  // commencer à suivre les mouvements du pointeur\nthumb.onpointermove = function(event) {\n  // déplacement du curseur: guette les événements sur thumb, comme tous les événements de pointeur le prennent pour cible\n  let newLeft = event.clientX - slider.getBoundingClientRect().left;\n  thumb.style.left = newLeft + 'px';\n  };\n  \n  // sur le pointeur vers le haut terminer le suivi des mouvements du pointeur\n  thumb.onpointerup = function(event) {\n    thumb.onpointermove = null;\n    thumb.onpointerup = null;\n    // ...traiter également le \"drag end\" si nécessaire\n  };\n};\n\n// remarque: pas besoin d'appeler thumb.releasePointerCapture,\n// qui se produit automatiquement sur pointerup\n```\n\n```online\nLa démo complète:\n\n[iframe src=\"slider\" height=100 edit]\n\n<p></p>\n\nIn the demo, there's also an additional element with `onmouseover` handler showing the current date.\n\nPlease note: while you're dragging the thumb, you may hover over this element, and its handler *does not* trigger.\n\nSo the dragging is now free of side effects, thanks to `setPointerCapture`.\n```\n\nFinalement, la capture de pointeur nous confère deux avantages:\n1. Le code devient plus propre comme nous n'avons plus besoin d'ajouter/enlever des gestionnaires sur l'ensemble du `document`. Le lien est libéré automatiquement.\n2. Si il existe des gestionnaires de `pointermove` dans le document, ils ne seront pas accidentellement activés par le pointeur lorsque l'utilisateur déplace le curseur.\n\n\n### Les événements de capture de pointeur\n\nIl y a encore une chose à mentionner ici, par souci d'exhaustivité.\n\nIl existe deux événements de pointeur associés :\n\n- `gotpointercapture` se déclenche quand un élément utilise `setPointerCapture` pour activer la capture.\n- `lostpointercapture` se déclenche quand la capture est libérée: soit de manière explicite avec un appel à `releasePointerCapture`, ou automatiquement sur `pointerup`/`pointercancel`.\n\n## Résumé\n\nLes événements de pointeur autorisent la gestion simultanée d'événements de souris, tactile et de stylet, avec un seul morceau de code.\n\nLes événements de pointeur héritent des événements de souris. Nous pouvons remplacer `mouse` par `pointer` dans les noms d'événement et s'attendre à ce que le code continue de fonctionner pour les souris, avec une meilleure prise en charge d'autres types d'appareil.\n\nPour les interactions de glisser-déposer et tactiles complexes que le navigateur pourrait choisir de détourner et de gérer lui-même - pensez à annuler l'action par défaut sur les événements et à définir `touch-action: none` dans le CSS pour les éléments impliqués.\n\nLes capacités additionnelles des événements de pointeur sont:\n\n- La prise en charge du multi-touch en utilisant `pointerId` et `isPrimary`.\n- Les propriétés spécifiques à un périphérique, tel que `pressure`, `width/height` et autres.\n- La capture de pointeur: nous pouvons modifier la cible de tout les événements de pointeur vers un élément spécifique jusqu'à `pointerup`/`pointercancel`.\n\nÀ ce jour, les événements de pointeur sont pris en charge dans tous les principaux navigateurs, ainsi nous pouvons y passer sans problème, plus particulièrement si IE10 et Safari 12 ne sont pas requis. Et même avec ces navigateurs, il existe des polyfills qui permettent la prise en charge des événements de pointeur.\n"
  },
  {
    "path": "2-ui/3-event-details/6-pointer-events/ball-2.view/index.html",
    "content": "<!doctype html>\n<body style=\"height: 200px\">\n  <style>\n    #ball {\n      touch-action: none;\n    }\n  </style>\n\n  <p>Drag the ball.</p>\n\n  <img src=\"https://js.cx/clipart/ball.svg\" style=\"cursor:pointer\" width=\"40\" height=\"40\" id=\"ball\">\n\n  <script>\n\n    ball.onpointerdown = log;\n    ball.onpointerup = log;\n    ball.onpointermove = log;\n    ball.onpointercancel = log;\n\n    ball.ondragstart = () => false;\n\n    let lastEventType;\n    let n = 1;\n    function log(event) {\n      if (lastEventType == event.type) {\n        n++;\n        text.value = text.value.replace(/.*\\n$/, `${event.type} * ${n}\\n`);\n        return;\n      }\n      lastEventType = event.type;\n      n = 1;\n      text.value += event.type + '\\n';\n      text.scrollTop = 1e9;\n    }\n  </script>\n\n  <textarea id=\"text\" style=\"display:block;width:300px;height:100px\"></textarea>\n</body>\n"
  },
  {
    "path": "2-ui/3-event-details/6-pointer-events/ball.view/index.html",
    "content": "<!doctype html>\n<body style=\"height: 200px\">\n  <p>Drag the ball.</p>\n\n  <img src=\"https://js.cx/clipart/ball.svg\" style=\"cursor:pointer\" width=\"40\" height=\"40\" id=\"ball\">\n\n  <script>\n\n    ball.onpointerdown = log;\n    ball.onpointerup = log;\n    ball.onpointermove = log;\n    ball.onpointercancel = log;\n\n    let lastEventType;\n    let n = 1;\n    function log(event) {\n      if (lastEventType == event.type) {\n        n++;\n        text.value = text.value.replace(/.*\\n$/, `${event.type} * ${n}\\n`);\n        return;\n      }\n      lastEventType = event.type;\n      n = 1;\n      text.value += event.type + '\\n';\n      text.scrollTop = 1e9;\n    }\n  </script>\n\n  <textarea id=\"text\" style=\"display:block;width:300px;height:100px\"></textarea>\n</body>\n"
  },
  {
    "path": "2-ui/3-event-details/6-pointer-events/multitouch.view/index.html",
    "content": "<!doctype html>\n<body>\n  <style>\n    #area {\n      border: 1px solid black; \n      width: 90%; \n      height: 180px; \n      cursor: pointer;\n      overflow: scroll;\n      user-select: none;\n    }\n  </style>\n  \n  <div id=\"area\">\n  Multi-touch here\n  </div>\n  <script>\n    document.onpointerdown = document.onpointerup = log;\n\n    function log(event) {\n      area.insertAdjacentHTML(\"beforeend\", `\n        <div>${event.type} isPrimary=${event.isPrimary} pointerId=${event.pointerId}</div>\n      `)\n     area.scrollTop = 1e9;\n    }\n  </script>\n\n</body>\n"
  },
  {
    "path": "2-ui/3-event-details/6-pointer-events/slider-html.view/index.html",
    "content": "<!DOCTYPE html>\n<link rel=\"stylesheet\" href=\"style.css\">\n\n<div id=\"slider\" class=\"slider\">\n  <div class=\"thumb\"></div>\n</div>\n"
  },
  {
    "path": "2-ui/3-event-details/6-pointer-events/slider-html.view/style.css",
    "content": ".slider {\n  border-radius: 5px;\n  background: #E0E0E0;\n  background: linear-gradient(left top, #E0E0E0, #EEEEEE);\n  width: 310px;\n  height: 15px;\n  margin: 5px;\n}\n\n.thumb {\n  width: 10px;\n  height: 25px;\n  border-radius: 3px;\n  position: relative;\n  left: 10px;\n  top: -5px;\n  background: blue;\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/6-pointer-events/slider.view/index.html",
    "content": "<!DOCTYPE html>\n<link rel=\"stylesheet\" href=\"style.css\">\n\n<div id=\"slider\" class=\"slider\">\n  <div class=\"thumb\"></div>\n</div>\n\n<p style=\"border:1px solid gray\" onmousemove=\"this.textContent = new Date()\">Mouse over here to see the date</p>\n\n<script>\n  let thumb = slider.querySelector('.thumb');\n  let shiftX;\n\n  function onThumbDown(event) {\n    event.preventDefault(); // prevent selection start (browser action)\n\n    shiftX = event.clientX - thumb.getBoundingClientRect().left;\n\n    thumb.setPointerCapture(event.pointerId);\n\n    thumb.onpointermove = onThumbMove;\n\n    thumb.onpointerup = event => {\n      // dragging finished, no need to track pointer any more\n      // ...any other \"drag end\" logic here...\n      thumb.onpointermove = null;\n      thumb.onpointerup = null;\n    }\n  };\n\n  function onThumbMove(event) {\n    let newLeft = event.clientX - shiftX - slider.getBoundingClientRect().left;\n\n    // if the pointer is out of slider => adjust left to be within the boundaries\n    if (newLeft < 0) {\n      newLeft = 0;\n    }\n    let rightEdge = slider.offsetWidth - thumb.offsetWidth;\n    if (newLeft > rightEdge) {\n      newLeft = rightEdge;\n    }\n\n    thumb.style.left = newLeft + 'px';\n  };\n\n  thumb.onpointerdown = onThumbDown;\n\n  thumb.ondragstart = () => false;\n\n</script>\n"
  },
  {
    "path": "2-ui/3-event-details/6-pointer-events/slider.view/style.css",
    "content": ".slider {\n  border-radius: 5px;\n  background: #E0E0E0;\n  background: linear-gradient(left top, #E0E0E0, #EEEEEE);\n  width: 310px;\n  height: 15px;\n  margin: 5px;\n}\n\n.thumb {\n  touch-action: none;\n  width: 10px;\n  height: 25px;\n  border-radius: 3px;\n  position: relative;\n  left: 10px;\n  top: -5px;\n  background: blue;\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.md",
    "content": "Nous devons utiliser deux gestionnaires: `document.onkeydown` et `document.onkeyup`.\n\nL'ensemble `pressed` doit garder les touches en cours appuyées.\n\nCréons un set `pressed = new Set()` pour garder les touches actuellement enfoncées.\n\nLe premier gestionnaire en ajoute, tandis que le second en supprime. Chaque fois sur `keydown` nous vérifions si nous avons suffisamment de touches enfoncées et exécutons la fonction si c'est le cas.\n\n"
  },
  {
    "path": "2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <p>Appuyez sur \"Q\" et \"W\" ensemble (cela peut être dans n'importe quelle langue).</p>\n\n  <script>\n    function runOnKeys(func, ...codes) {\n      let pressed = new Set();\n\n      document.addEventListener('keydown', function(event) {\n        pressed.add(event.code);\n\n        for (let code of codes) { // est ce que toutes les touches sont un ensemble ?\n          if (!pressed.has(code)) {\n            return;\n          }\n        }\n\n        // oui, elles le sont\n\n        // durant le message d'alerte, si le visiteur relâche les touches,\n        // JavaScript n'obtient pas l'évènement \"keyup\" \n        // et les ensembles appuyées continuent d'assumer que la touche est appuyée \n        // alors, pour échapper aux touches \"sticky\" , nous réinitialisons le status\n        // Si un usager veut exécuter le raccourcis encore - laisser le appuyer toutes les touches encore\n        pressed.clear();\n\n        func();\n      });\n\n      document.addEventListener('keyup', function(event) {\n        pressed.delete(event.code);\n      });\n\n    }\n\n    runOnKeys(\n      () => alert(\"Hello!\"),\n      \"KeyQ\",\n      \"KeyW\"\n    );\n  </script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/task.md",
    "content": "importance: 5\n\n---\n\n# Raccourcis clavier étendus\n\nCréer une fonction `runOnKeys(func, code1, code2, ... code_n)` exécutant la fonction `func` lorsqu’on appuie simultanément sur les touches avec les codes suivant `code1`, `code2`, ..., `code_n`.\n\nPar exemple, le code ci-dessous montre `alert` lorsque `\"Q\"` et `\"W\"` sont appuyées ensemble (dans n'importe quelle langue, avec ou sans l'activation de La touche Majuscule,  CapsLock)\n\n```js no-beautify\nrunOnKeys(\n  () => alert(\"Hello!\"),\n  \"KeyQ\",\n  \"KeyW\"\n);\n```\n\n[demo src=\"solution\"]\n"
  },
  {
    "path": "2-ui/3-event-details/7-keyboard-events/article.md",
    "content": "# Le Clavier: les évènements keydown et keyup\n\nAvant que nous en arrivions au clavier, veuillez noter que sur des appareils modernes il y a d'autres manières de “récupérer quelque chose\". Par exemple, les gens utilisent la reconnaissance vocale (en particulier sur les appareils mobiles) oubien le copier/coller avec la souris.\n\nDonc si nous voulons contrôler une entrée dans un champ `<input>`, alors les évènements du clavier ne sont pas assez suffisants. Il y a un autre évènement nommé `input` pour gérer les changements d'un champ `<input>`, par n'importe quelle moyen. Et il peut être un meilleur choix pour une telle tâche. Nous allons traiter cela plus tard dans le chapitre <info:events-change-input>.\n\nLes évènements du clavier doivent être utilisés  lorsqu'on veut gérer les actions sur le clavier (Le clavier virtuel compte aussi). Par exemple, pour réagir sur les touches de directions `key:Up` et `key:Down` oubien les touches de raccourcis (y compris les combinaisons de touches).\n\n## Teststand [#keyboard-test-stand]\n\n```offline\nPour mieux comprendre les évènements du clavier, vous pouvez utiliser le [teststand](sandbox:keyboard-dump).\n```\n\n```online\nPour mieux comprendre les évènements du clavier, vous pouvez utiliser le teststand en bas.\n\nEssayez les différentes combinaisons de touches dans la zone de texte.\n\n[codetabs src=\"keyboard-dump\" height=480]\n```\n\n## Keydown et keyup\n\nLes évènements  `keydown` surviennent lorsqu'une touche est appuyée, et ensuite intervient `keyup` -- lorsqu'elle est relâchée.\n\n### event.code et event.key\n\nLa propriété `key` de l’objet évènement permet d'obtenir un caractère, tandis que la propriété `code` de l'objet évènement objet permet d'obtenir  le \"code de la touche physique\".\n\nPar exemple, la même touche `key:Z`  peut être appuyée avec ou sans `key:Shift`. Cela nous donne deux caractères différents : minuscule `z` et majuscule `Z`.\n\nLa propriété `event.key` est exactement le caractère, et il sera diffèrent. Cependant `event.code` est la même:\n\n\n| Touche       | `event.key`    | `event.code` |\n|--------------|----------------|--------------|\n| `key:Z`      |`z` (minuscule) | `KeyZ`       |\n| `key:Shift+Z`|`Z` (majuscule) | `KeyZ`       |\n\n\nSi un utilisateur travaille avec des langues différentes, alors le fait de changer vers une autre langue aura pour effet de créer un caractère totalement diffèrent de `\"Z\"`.  Cela va devenir la valeur de `event.key`, tandis que `event.code` est toujours la même que: `\"KeyZ\"`.\n\n```smart header=\"\\\"KeyZ\\\" et autres codes de touches\"\nChaque touche a un code qui dépend de sa position sur le clavier. Les codes des touches sont décrits dans le document [Spécification des codes des évènements Interfaces Utilisateurs](https://www.w3.org/TR/uievents-code/).\n\nPar exemple:\n- Les touches des lettres ont des codes de type: `\"Key<letter>\"`: `\"KeyA\"`, `\"KeyB\"` etc.\n- Les touches numériques ont des codes de type: `\"Digit<number>\"`: `\"Digit0\"`, `\"Digit1\"` etc.\n- Les touches spéciales sont codées par leurs noms: `\"Enter\"`, `\"Backspace\"`, `\"Tab\"` etc.\n\nIl existe plusieurs formats de claviers usuels, différents de par la présentation, et la spécification donne des codes pour les touches pour chacun d'entre eux.\n\nvoir [la section alphanumérique de la specification](https://www.w3.org/TR/uievents-code/#key-alphanumeric-section) pour plus de codes, ou essayez juste le [teststand](#keyboard-test-stand) au-dessus.\n\n```\n\n```warn header=\"Le cas importe: `\\\"KeyZ\\\"`,  pas `\\\"keyZ\\\"`\"\nSemble être évident, mais beaucoup de gens font toujours des fautes.\n\nS'il vous plait éviter les fautes de frappes: c'est `KeyZ`, pas `keyZ`. Le control tel que `event.code==\"keyZ\"` ne vas pas fonctionner: la première lettre de `\"Key\"` doit être une majuscule.\n```\n\nEt si une touche ne donne aucun caractère ? Par exemple, `key:Shift` ou `key:F1` ou autres.  Pour ces touches, `event.key` est approximativement la même chose que `event.code` :\n\n| Key          | `event.key` | `event.code` |\n|--------------|-------------|--------------|\n| `key:F1`      |`F1`          |`F1`        |\n| `key:Backspace`      |`Backspace`          |`Backspace`        |\n| `key:Shift`|`Shift`          |`ShiftRight` or `ShiftLeft`        |\n\nVeuillez noter que `event.code` spécifie exactement quelle touche est appuyée. Par exemple, la plupart des claviers ont deux touches de `key:Shift`: à gauche et à droite. La propriété `event.code` nous dit exactement laquelle fut appuyée, et `event.key` est responsable de la \" signification \" de la touche: comment il s'agit d'un (\"Shift\").\n\n\nDisons, nous voulons gérer un raccourci : `key:Ctrl+Z` (ou `key:Cmd+Z` pour Mac). La plupart des éditeurs accrochent l'action du \"Defaire\" sur cette touche. Nous pouvons mettre un écouteur lorsqu’on déclenche l'évènement `keydown` et chercher à savoir quelle touche est appuyée.\n\n\nIl existe un dilemme ici: Dans cet écouteur d’évènement, devons-nous contrôler la valeur de `event.key` oubien `event.code`?\n\n\nD'une part, la valeur de `event.key` est un caractère, elle change en fonction de la langue. Si le visiteur a plusieurs langues dans le système d'exploitation et bascule entre elles, la même clé donne des caractères différents. Il est donc logique de vérifier `event.code`, c'est toujours pareil.\n\nAinsi:\n\n```js run\ndocument.addEventListener('keydown', function(event) {\n  if (event.code == 'KeyZ' && (event.ctrlKey || event.metaKey)) {\n    alert('Undo!')\n  }\n});\n```\n\nD'autre part, il existe un problème avec `event.code`. Pour des dispositions de clavier différentes, la même touche peut avoir des caractères différents.\n\nPar exemple, voici la disposition du clavier Américain (\"QWERTY\") et Allemand (\"QWERTZ\") dessous (de Wikipedia) :\n\n![](us-layout.svg)\n\n![](german-layout.svg)\n\nPour la même touche, le clavier Américain a un \"Z\", tandis que celui Allemand a un \"Y\" (les lettres sont échanges).\n\nDonc, `event.code` sera égal à `KeyZ` pour les gens utilisant le clavier Allemand lorsqu'ils appuient sur `key:Y`.\n\nSi on vérifie `event.code == 'KeyZ'` dans notre code, alors pour les personnes avec la disposition allemande ce genre de test passera quand ils appuient sur `key:Y`.\n\nCela semble être bizarre, mais c'est ainsi. La [specification](https://www.w3.org/TR/uievents-code/#table-key-code-alphanumeric-writing-system) mentionne explicitement ce comportement.\n\nDonc, `event.code` peut correspondre à un caractère incorrect pour une disposition inattendue. Les mêmes lettres dans différentes disposition peuvent mapper sur différentes clés physiques, conduisant à des codes différents. Heureusement, cela ne se produit qu'avec plusieurs codes, par exemple `keyA`, `keyQ`, `keyZ` (comme nous l'avons vu), et ne se produit pas avec des touches spéciales telles que `Shift`. Vous pouvez trouver la liste dans la [specification](https://www.w3.org/TR/uievents-code/#table-key-code-alphanumeric-writing-system).\n\nPour suivre de manière fiable les caractères dépendant de la disposition, `event.key` peut être un meilleur moyen.\n\nD'un autre côté, `event.code` a l'avantage de rester toujours le même, lié à l'emplacement de la clé physique. Ainsi, les raccourcis clavier qui en dépendent fonctionnent bien même en cas de changement de langue.\n\nVoulons-nous gérer des clés dépendantes de la disposition ? Alors `event.key` est la voie à suivre.\n\nOu voulons-nous un raccourci clavier même après un changement de langue ? Alors, `event.code` peut être une meilleure option.\n\n## Auto-repeat\n\nSi une touche est appuyée assez longtemps, elle commence la répétition avec la propriété \"auto-repeat\": l'évènement `keydown` se déclenche de manière répétitive, et ensuite lorsqu'elle est relâchée nous obtenons finalement l'évènement `keyup`. Donc c'est normal d'avoir plusieurs `keydown`  et un unique évènement `keyup`.\n\nPour les évènements déclenchés par auto-repeat, l'évènement objet a une propriété `event.repeat` dont la valeur est assignée à `true`.\n\n## Actions par défaut\n\nLes actions par défaut varient, comme il y a beaucoup de possibilités pouvant être initiées par le clavier.\n\nPa exemple:\n\n- Un caractère apparait sur l'écran (le résultat le plus évident).\n- Un caractère est supprimé (`key:Delete` key).\n- Une page est défilée (`key:PageDown` key).\n- Le navigateur ouvre la boite de dialogue \"Sauvegarder la Page\" dialog (`key:Ctrl+S`)\n- ...ainsi de suite.\n\nL’empêchement de l'action par défaut à l'évènement `keydown` peut annuler la plupart d'entre elles, à l'exception des touches spéciales spécifiques aux systèmes d'exploitations. Par exemple, sur Windows `key:Alt+F4` ferme la fenêtre en cours du navigateur. Et il n'existe aucun moyen de l'arrêter en empêchant l'action par défaut JavaScript.\n\nPar exemple, la balise `<input>` en bas s'attend à recevoir un numéro de téléphone, alors elle n'accepte que les touches numériques, `+`, `()` oubien `-`:\n\n```html autorun height=60 run\n<script>\nfunction checkPhoneKey(key) {\n  return (key >= '0' && key <= '9') || ['+','(',')','-'].includes(key);\n}\n</script>\n<input *!*onkeydown=\"return checkPhoneKey(event.key)\"*/!* placeholder=\"Phone, please\" type=\"tel\">\n```\n\nLe gestionnaire `onkeydown` utilise ici `checkPhoneKey` pour vérifier la touche enfoncée. S'il est valide (de `0..9` ou l'un des `+-()`), alors il renvoie `true`, sinon `false`.\n\nComme nous le savons, la valeur `false` renvoyée par le gestionnaire d'événements, attribuée à l'aide d'une propriété DOM ou d'un attribut, comme ci-dessus, empêche l'action par défaut, donc rien n'apparaît dans le `<input>` pour les clés qui ne passent pas le test. (La valeur 'true' renvoyée n'affecte rien, seul le renvoi de 'false' compte)\n\nVeuillez noter que les touches spéciales, telles que `key:Backspace`, `key:Left`, `key:Right`, ne fonctionnent pas dans l'input. C'est un effet secondaire du filtre strict `checkPhoneKey`. Ces clés lui font retourner `false`.\n\nDétendons un peu le filtre en autorisant les touches fléchées `key:Left`, `key:Right` et `key:Delete`, `key:Backspace` :\n\n```html autorun height=60 run\n<script>\nfunction checkPhoneKey(key) {\n  return (key >= '0' && key <= '9') ||\n    ['+','(',')','-',*!*'ArrowLeft','ArrowRight','Delete','Backspace'*/!*].includes(key);\n}\n</script>\n<input onkeydown=\"return checkPhoneKey(event.key)\" placeholder=\"Phone, please\" type=\"tel\">\n```\n\nmaintenant les flèches et la suppression marchent bien.\n\nMême si nous avons le filtre de clavier, on peut toujours entrer n'importe quoi à l'aide d'une souris et faire un clic droit + Coller. Les appareils mobiles offrent d'autres moyens de saisir des valeurs. Le filtre n'est donc pas fiable à 100%.\n\nL'approche alternative serait de suivre l'événement `oninput` -- il se déclenche *après* toute modification. Là, nous pouvons vérifier le nouveau `input.value` et le modifier/mettre en surbrillance le `<input>` lorsqu'il est invalide. Ou nous pouvons utiliser les deux gestionnaires d'événements ensemble.\n\n## Héritage\n\nDans le passé, il y'avait un évènement `keypress`, et aussi les propriétés `keyCode`, `charCode`, `which` de l'objet évènement.\n\nIl y avait tellement d'incompatibilités au niveau des navigateurs en travaillant avec eux que les développeurs de la spécification n'avaient autre moyen que de les déprécier tous et d’en créer de  nouveaux et plus modernes (tels que ceux décrits en haut dans ce chapitre). L'ancien code marche encore, étant donné que les navigateurs continuent de les supporter, mais nous n'avons nullement besoin de les utiliser maintenant.\n\n## Claviers mobiles\n\nLors de l'utilisation de claviers virtuels / mobiles, officiellement appelés IME (Input-Method Editor), la norme W3C indique qu'un KeyboardEvent [`e.keyCode` devrait être `229`](https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode) et [`e.key` devrait être `\"Unidentified\"`](https://www.w3.org/TR/uievents-key/#key-attr-values).\n\nBien que certains de ces claviers puissent toujours utiliser les bonnes valeurs pour `e.key`, `e.code`, `e.keyCode`, ... lorsque vous appuyez sur certaines touches telles que les flèches ou le retour arrière, il n'y a aucune garantie, donc la logique de votre clavier peut ne pas toujours fonctionner sur les appareils mobiles.\n\n## Résumé\n\nLe fait d'appuyer sur une touche génère toujours un évènement du clavier, que cela soit une touche de symbole ou des touches spéciales telles que `key:Shift` oubien `key:Ctrl` et ainsi de suite. La seule exception est la touche `key:Fn` qui est dès fois présente  sur un clavier d’ordinateur portable. Il n’y a pas d'évènement de clavier pour cela, parce que cela est implémenté à un niveau plus bas que celui du system d'exploitation.\n\nLes Evènements du Clavier:\n\n- `keydown` -- en appuyant sur la touche (auto-repeats répète automatiquement si un appui long de la  touche est effectué),\n- `keyup` -- en relâchant la touche.\n\nLes propriétés principales des évènements du clavier:\n\n- `code` -- la propriété \"key code\" (`\"KeyA\"`, `\"ArrowLeft\"` ainsi de suite), spécifique à la position physique de la touche sur le  clavier.\n- `key` -- le caractère (`\"A\"`, `\"a\"` et ainsi de suite), pour les touches ne prenant pas en charge les caractères, telles que `key:Esc`, a souvent la même valeur que `code`.\n\nDans le passé, les évènements du clavier étaient des fois utilisés pour contrôler les entrées dans les champs de formulaires. Cela n'est pas fiable, parce que les données recueillies peuvent provenir de plusieurs sources. Nous avons les évènements `input` et `change` pour gérer toute entrée de donnée (traitée plus tard dans le chapitre <info:events-change-input>). Elles se déclenchent après toute sorte d'entrée, y compris le copier-coller oubien la reconnaissance vocale.\n\nNous devons utiliser les évènements du clavier quand nous voulons vraiment utiliser le clavier. Par exemple, pour réagir aux raccourcis ou touches spéciales.\n"
  },
  {
    "path": "2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <form id=\"form\" onsubmit=\"return false\">\n\n    Prevent default for:\n    <label>\n      <input type=\"checkbox\" name=\"keydownStop\" value=\"1\"> keydown</label>&nbsp;&nbsp;&nbsp;\n    <label>\n      <input type=\"checkbox\" name=\"keyupStop\" value=\"1\"> keyup</label>\n\n    <p>\n      Ignore:\n      <label>\n        <input type=\"checkbox\" name=\"keydownIgnore\" value=\"1\"> keydown</label>&nbsp;&nbsp;&nbsp;\n      <label>\n        <input type=\"checkbox\" name=\"keyupIgnore\" value=\"1\"> keyup</label>\n    </p>\n\n    <p>Mettre le focus sur le champ d'entrée et appuyer une touche.</p>\n\n    <input type=\"text\" placeholder=\"Press keys here\" id=\"kinput\">\n\n    <textarea id=\"area\" readonly></textarea>\n    <input type=\"button\" value=\"Clear\" onclick=\"area.value = ''\" />\n  </form>\n  <script src=\"script.js\"></script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js",
    "content": "kinput.onkeydown = kinput.onkeyup = kinput.onkeypress = handle;\n\nlet lastTime = Date.now();\n\nfunction handle(e) {\n  if (form.elements[e.type + 'Ignore'].checked) return;\n\n  area.scrollTop = 1e6;\n\n  let text = e.type +\n    ' key=' + e.key +\n    ' code=' + e.code +\n    (e.shiftKey ? ' shiftKey' : '') +\n    (e.ctrlKey ? ' ctrlKey' : '') +\n    (e.altKey ? ' altKey' : '') +\n    (e.metaKey ? ' metaKey' : '') +\n    (e.repeat ? ' (repeat)' : '') +\n    \"\\n\";\n\n  if (area.value && Date.now() - lastTime > 250) {\n    area.value += new Array(81).join('-') + '\\n';\n  }\n  lastTime = Date.now();\n\n  area.value += text;\n\n  if (form.elements[e.type + 'Stop'].checked) {\n    e.preventDefault();\n  }\n}\n"
  },
  {
    "path": "2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/style.css",
    "content": "#kinput {\n  font-size: 150%;\n  box-sizing: border-box;\n  width: 95%;\n}\n\n#area {\n  width: 95%;\n  box-sizing: border-box;\n  height: 250px;\n  border: 1px solid black;\n  display: block;\n}\n\nform label {\n  display: inline;\n  white-space: nowrap;\n}"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/1-endless-page/solution.md",
    "content": "\nL'essence de la solution est une fonction qui ajoute plus de dates à la page  (ou charge plus de chose en réalité) alors que nous sommes en fin de page.\n\n\nNous pouvons l'appeler immédiatement et y ajouter un contrôleur d’évènement avec  `window.onscroll`.\n\nLa question cruciale est: \" Comment détectons nous que la page est défilée vers le bas?\"\n\nUtilisons les coordonnées relatives de window: window-relative.\n\nLe document est représenté (et contenu) dans la balise  `<html>`, qui est `document.documentElement`.\n\n\nNous pouvons obtenir les coordonnées relatives à la fenêtre du document en entier avec  `document.documentElement.getBoundingClientRect()`, la propriété `bottom` sera la coordonnée relative à la fenêtre de la fin du document.\n\nPar exemple, si la hauteur totale du document HTML est de 2000px, alors :\n\n```js\n// Lorsqu'on est en haut de la page \n\n// window-relative top = 0\ndocument.documentElement.getBoundingClientRect().top = 0\n\n// window-relative en bas de page = 2000\n// le document est long, alors c'est probablement bien au-delà des limites inferieures de la fenêtre\ndocument.documentElement.getBoundingClientRect().bottom = 2000\n```\n\nSi nous défilonsla page de `500px` vers le bas, alors:\n\n```js\n// document top is above the window 500px\ndocument.documentElement.getBoundingClientRect().top = -500\n// le bas du document est  500px plus proche\ndocument.documentElement.getBoundingClientRect().bottom = 1500\n```\n\nLorsque nous défilons jusque vers la fin, en assumant que la hauteur de la fenêtre  est de `600px`:\n\n\n```js\n// La limite supérieure du document est au-dessus de la fenêtre à 1400px\ndocument.documentElement.getBoundingClientRect().top = -1400\n// Le bas du document est au bas de la fenêtre à 600px\ndocument.documentElement.getBoundingClientRect().bottom = 600\n```\n\n\nVeuillez noter que le `bottom` ne peut être `0`, parce qu’elle n'atteint jamais le haut de la fenêtre. La limite la plus basse de coordonées `bottom` est la hauteur de la fenêtre (nous avons supposé que ce soit `600`),, nous ne pouvons plus la défiler vers le haut.\n\nNous pouvons obtenir la hauteur de la fenêtre comme `document.documentElement.clientHeight`.\n\nPour notre tâche, nous devons savoir quand la fin du document n’est pas plus éloigné de `100px` (c’est-à-dire `600-700px` si la hauteur est `600`).\n\n\nDonc voici la fonction:\n\n```js\nfunction populate() {\n  while(true) {\n    // la fin du document\n    let windowRelativeBottom = document.documentElement.getBoundingClientRect().bottom;\n\n    // si l'utilisateur n'a pas fait défiler suffisamment loin (> 100px jusqu'à la fin)\n    if (windowRelativeBottom > document.documentElement.clientHeight + 100) break;\n    \n    // ajoutons plus de données\n    document.body.insertAdjacentHTML(\"beforeend\", `<p>Date: ${new Date()}</p>`);\n  }\n}\n```\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/1-endless-page/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n<h1>Défile moi</h1>\n\n<script>\n  function populate() {\n    while(true) {\n      let windowRelativeBottom = document.documentElement.getBoundingClientRect().bottom;\n      if (windowRelativeBottom > document.documentElement.clientHeight + 100) break;\n      document.body.insertAdjacentHTML(\"beforeend\", `<p>Date: ${new Date()}</p>`);\n    }\n  }\n\n  window.addEventListener('scroll', populate);\n\n  populate(); // initialise le document.\n</script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/1-endless-page/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n<h1>Défile moi</h1>\n\n<script>\n  // ... votre code ...\n</script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/1-endless-page/task.md",
    "content": "importance: 5\n\n---\n\n# Page sans Fin\n\nCréez une page sans fin. Lorsqu'un visiteur la défile vers la fin, elle y appose automatiquement au texte l'heure et la date en temps réelle   (de sorte à ce que le visiteur puisse défiler d'avantage la page).\n\nComme ceci:\n\n[iframe src=\"solution\" height=200]\n\nS'il vous plait veuillez noter deux importants aspects du défilement:\n\n1. **Le défilement est \"élastique\".** Nous pouvons défiler un peu au-delà du  début ou la fin du document avec certains navigateurs /appareils (l'espace vide en bas est montrée, et ensuite le document va automatiquement \"retourner\" à la normale).\n2. **Le défilement est imprécis.** Quand nous défilons vers la fin de la page, alors nous pouvons être en réalité entre 0-50px de la fin réelle document.\n\ndonc, \"le défilement vers la fin\" doit signifier que le visiteur n'est  pas à  plus de 100px de la fin du document.\n\nP.S. En réalité nous pourrions vouloir montrer \" plus de messages\" ou \" plus de bonnes choses\".\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/2-updown-button/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/2-updown-button/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <style>\n    body,\n    html {\n      height: 100%;\n      width: 100%;\n      padding: 0;\n      margin: 0;\n    }\n\n    #matrix {\n      width: 400px;\n      margin: auto;\n      overflow: auto;\n      text-align: justify;\n    }\n\n    #arrowTop {\n      height: 9px;\n      width: 14px;\n      color: green;\n      position: fixed;\n      top: 10px;\n      left: 10px;\n      cursor: pointer;\n    }\n\n    #arrowTop::before {\n      content: '▲';\n    }\n\n  </style>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <div id=\"matrix\">\n    <script>\n      for (let i = 0; i < 2000; i++) document.writeln(i)\n    </script>\n  </div>\n\n  <div id=\"arrowTop\" hidden></div>\n\n  <script>\n\n    arrowTop.onclick = function() {\n      window.scrollTo(pageXOffset, 0);\n      // Après  scrollTo, il y aura un évènement \"scroll\", de sorte à ce que la flèche puisse être cachée automatiquement\n    };\n\n    window.addEventListener('scroll', function() {\n      arrowTop.hidden = (pageYOffset < document.documentElement.clientHeight);\n    });\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/2-updown-button/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <style>\n    body,\n    html {\n      height: 100%;\n      width: 100%;\n      padding: 0;\n      margin: 0;\n    }\n\n    #matrix {\n      width: 400px;\n      margin: auto;\n      overflow: auto;\n      text-align: justify;\n    }\n\n    #arrowTop {\n      height: 9px;\n      width: 14px;\n      color: green;\n      position: fixed;\n      top: 10px;\n      left: 10px;\n      cursor: pointer;\n    }\n\n    #arrowTop::before {\n      content: '▲';\n    }\n\n  </style>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <div id=\"matrix\">\n    <script>\n      for (let i = 0; i < 2000; i++) document.writeln(i)\n    </script>\n  </div>\n\n  <div id=\"arrowTop\"></div>\n\n  <script>\n    // ... votre code ...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/2-updown-button/task.md",
    "content": "importance: 5\n\n---\n\n# Bouton Up/down \n\nCréer un bouton nomme \"to the top\" pour aider à effectuer un défilement de page.\n\nCela doit être ainsi:\n- Tandis que la page n'est pas défilée vers le bas pour au moins une hauteur égale a la fenêtre --  être invisible.\n- Quand la page est défilée vers le bas au-delà de la hauteur de la fenêtre -- il doit apparaitre une flèche pointant \"vers le haut\" au coin gauche supérieur. Si la page est défilée dans l'autre sens, elle disparait.\n- Lorsqu'on clique sur la flèche, la page défile vers le haut.\n\n\nComme ceci (coin supérieur gauche, faites défiler pour voir) :\n\n\n[iframe border=\"1\" height=\"200\" link src=\"solution\"]\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/3-load-visible-img/solution.md",
    "content": "\n\nNous souhaitons également l’exécuter lors du chargement de la page, afin de détecter les images immédiatement visibles et de les charger.\n\nLe code doit être exécuté lors du chargement du document afin qu'il ait accès à son contenu.\n\nOu le mettre en dessous du `<body>` :\n\n\n```js\n// ...Le contenu de la page est en haut...\n\nfunction isVisible(elem) {\n\n  let coords = elem.getBoundingClientRect();\n\n  let windowHeight = document.documentElement.clientHeight;\n\n\n  // Le bord supérieur de l'elem est visible ?\n\n  let topVisible = coords.top > 0 && coords.top < windowHeight;\n\n  // bottom elem edge is visible?\n  let bottomVisible = coords.bottom < windowHeight && coords.bottom > 0;\n\n  return topVisible || bottomVisible;\n}\n```\n\nThe `showVisible()` function uses the visibility check, implemented by `isVisible()`, to load visible images:\n\n```js\nfunction showVisible() {\n  for (let img of document.querySelectorAll('img')) {\n    let realSrc = img.dataset.src;\n    if (!realSrc) continue;\n\n    if (isVisible(img)) {\n      img.src = realSrc;\n      img.dataset.src = '';\n    }\n  }\n}\n\n*!*\nshowVisible();\nwindow.onscroll = showVisible;\n*/!*\n```\n\n\nPour les images visibles nous pouvons prendre `img.dataset.src` et l'assigner à `img.src` (si cela n’a pas été fait déjà).\n\nP.S. La solution propose également une variante de `isVisible` qui \"précharge\" les images situées dans une page au-dessus / au-dessous du document en cours de défilement.\n\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/3-load-visible-img/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <p>Les Textes et images proviennent de https://wikipedia.org.</p>\n\n  <h3>Toutes les images ayant <code>data-src</code> se chargent lorsqu'elles deviennent visible.</h3>\n\n  <h1>Le système Solaire</h1>\n\n  <p>Le système solaire est un système centre gravitationnel comprenant  le soleil et les objets orbitant autour de celui-ci, soit directement ou indirectement.  Parmi ces objets qui tournent en orbite directement autour du soleil, les huit plus grands sont des planètes, le reste étant des objets significativement plus petits, tels que les planètes naines et les petits corps du système solaire. Des objets qui gravitent indirectement autour du soleil, les lunes, deux d’entre eux sont plus grands que la plus petite planète, Mercure.</p>\n\n  <p>Le système solaire est forme il y a 4.6 milliards années à partir de la chute gravitationnelle d'un géant nuage moléculaire interstellaire. La grande majorité de la masse du système est dans le soleil, avec la plus grande partie du reste de la masse contenue dans Jupiter. Les quatre planètes intérieures les plus petites, Mercure, Venus,\n    la Terre et Mars, sont des planètes terrestres, étant principalement composées de roche et de métal.\n    Les quatre planètes se situant vers l'extérieure sont des planètes géantes, étant de manière substantielle plus massive que celles terrestres.\n    Les deux plus grandes, Jupiter et Saturne, sont des géantes gazeuses, étant principalement compose d'hydrogène et de hélium;\n    Les deux planètes les plus à l'extérieure, Uranus et Neptune, sont des géantes glaciales,\n    étant composées de substances ayant des points de fusion relativement élevés compare à l'hydrogène\n    et l'hélium, appelés les volatiles, telle que l'eau, ammonium et le méthane.\n    Toutes les planètes ont presque des orbites circulaires pouvant être couches sur un disque presque plat appelé l'elliptique.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/planets.jpg\" width=\"640\" height=\"360\">\n  </figure>\n\n  <h1>Le Soleil</h1>\n\n  <p>Le soleil est une étoile du système solaire et est de loin sa composante la plus massive.\n    Sa masse la plus large (332,900 masses terrestre) produit d'assez fortes températures et de densités dans son noyau\n    pour soutenir une fusion de  l'hydrogène en hélium, faisant d'elle une étoile de séquence principale.\n     Cela dégage une énorme quantité d'énergie, en majeur partie radiée dans l'espace en radiation électromagnétique devenant en son summum une lumière visible.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/sun.jpg\" width=\"658\" height=\"658\">\n  </figure>\n\n  <h1>Mercure</h1>\n\n  <p>Mercure (0,4 UA du Soleil) est la planète la plus proche du soleil et est la plus petite planète du système solaire (0,055 masse terrestre).\n    Mercure n'a pas de satellites naturels, en outre des cratères d'impacts, ses seuls aspects géologiques connues sont\n    les escarpements lobés ou rupes qui étaient probablement produits par une période de contraction tôt dans son histoire.[67] L'atmosphère ténu de Mercure est composé d' atomes projetés à sa surface par le vent solaire.[68] Son noyau de fer relativement large et son mince manteau n'ont pas été expliqué de manière adéquate jusqu'à présent. Les hypothèses incluent le fait que ses couches externes soient écorchées par un géant impact; ou, que cela fut empêché de totalement accréter par l’énergie solaire juvénile.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/mercury.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Venus</h1>\n\n  <p>Venus (0,7 UA du Soleil)  est proche de par sa taille de la terre (0,815 masse terrestre) et, comme la terre,\n   a un épais manteau en silicate autour de son noyau de fer, une atmosphère substantielle, et l’évidence d'une activité géologique interne.\n   Elle est plus sèche que la Terre, et son atmosphère est quatre-vingt-dix fois plus dense. Venus n'as pas de satellites naturels.\n     C'est la planète la plus chaude, avec des températures à la surface  allant au-delà de  400 °C (752°F), très probablement due à la quantité de gazes à effet de serre dans l'atmosphère.\n   Aucune évidence définitive d'une activité géologique actuelle n’a été détectée sur Venus,\n   mais elle n'as pas de champ magnétique qui empêcherait la raréfaction de son atmosphère substantielle, ce qui suggère que son atmosphère est entrain remplie par les éruptions volcaniques.</p>\n\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/venus.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>La Terre</h1>\n\n  <p>La Terre (1 UA du Soleil) est la plus grande et la plus dense des planètes internes,\n    la seule connue pour avoir une activité géologique actuellement, et la seule place ou l'existence de vie est connue. Son liquide hydrosphère est unique parmi les planètes terrestres,\n    et c'est la seule planète ou la tectonique des plaques a été observée.\n    L'atmosphère de la terre est radicalement différente de celle des autres planètes,\n    ayant été altérée par la présence de vie pour contenir 21% oxygène libre.\n    Elle a un satellite naturel, la Lune, la seule grande satellite d'une planète terrestre du système solaire.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/earth.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Mars</h1>\n\n  <p>Mars (1,5 UA du Soleil) est plus petite que la Terre et Venus (0.107 masse terrestre).\n    Elle a une atmosphère en majorité composée de dioxyde de carbone avec une pression à la surface de 6,1 millibars\n    (Approximativement 0.6%  de celle de la Terre). Sa surface, poivrée de vastes volcans,\n    tels que Olympus Mons, tel que  la Vallée du Marineris, montrent une activité géologique qui a pu persister jusqu'à une période récente remontant à 2 million années.\n     Sa couleur rouge provient du dioxyde de fer (la rouille) dans la terre.\n      Mars a deux satellites naturels (Deimos and Phobos) dont on pense être des astéroïdes capturés.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/mars.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Jupiter</h1>\n\n  <p>Jupiter (5,2 UA), a  318 masse terrestre, est 2,5 fois la masse de toutes les autres planètes mises ensemble.\n     Elle est composée en grande partie d’hydrogène et d'hélium.\n    la forte chaleur de Jupiter crée des aspects semi-permanent dans son atmosphère,\n     tel que des bandes de nuages et la Grande Tache Rouge. Jupiter a  67  satellites connus.\n    Les quatre grands les plus connus, Ganymede, Callisto, Io, and Europe, montrent des similarités aux planètes terrestres,\n      tel que le volcanisme et le réchauffement interne.\n    Ganymede, le plus grand satellite dans le système solaire,  est plus large que Mercure.</p>\n\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/jupiter.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Saturne</h1>\n  <p>Saturne (9,5 UA), qui se distingue par son extensif système d'anneau, à plusieurs similarités avec Jupiter,\n   tel que sa composition atmosphérique et son magnétosphère.\n    Bien que Saturne ait 60% du volume de Jupiter, elle est moins massive au tiers,\n    à 95 masse terrestre. Saturne est la seule planète du système Solaire qui est moins dense que l'eau.\n    Les anneaux de Saturne sont faits de petites quantités de glaces et de particules de roches.\n    Saturne a 62 satellites confirmés  composés  largement de glace.\n    Deux d'entre eux, Titan and Encelade, montrent des signes d'une activité géologique.\n    Titan, la deuxième plus grande lune du système solaire, est plus grande que Mercure\n    et est le seul satellite  du système solaire  avec une atmosphère substantielle.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/saturn.jpg\" width=\"805\" height=\"390\">\n  </figure>\n\n  <h1>Uranus</h1>\n  <p>Uranus (19,2 UA), a 14 masse terrestre, est la plus légère des planètes externes.\n    Particulière parmi les autres planètes, elle tourne en orbite autour du soleil sur sa face;\n   Son inclinaison axiale est a plus de quatre-vingt-dix dégrée elliptique.\n    Elle a un noyau beaucoup plus froid que les autres planètes géantes et elle radie peu de chaleur dans l'espace.\n    Uranus a 27 satellites connus, les plus grandes étant Titania,\n    Oberon, Umbriel, Ariel, et  Miranda.</p>\n\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/uranus.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Neptune</h1>\n  <p>Neptune (30,1 UA), bien que un peu plus petite que Uranus, est plus massive (équivalent à 17 fois la Terre)\n     et dès lors plus dense. Elle radie plus de chaleur interne,\n     mais pas autant que Jupiter ou Saturne.\n     Neptune  a 14 satellites  connus. Le plus grand, Triton, est géologiquement actif,\n     avec des geysers de nitrogène liquide.\n     Triton est le seul grand satellite avec un orbite rétrograde.\n     Neptune est accompagnée dans son orbite par plusieurs petites planètes, dénommées les troyens de Neptune,\n     qui sont en résonance 1:1 avec elle.</p>\n\n   <figure>\n     <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/neptune.jpg\" width=\"390\" height=\"390\">\n   </figure>\n\n   <script>\n    /**\n     * Teste si l’élément est visible (dans la partie visible de la page)\n     * C'est assez que le haut ou le rebord inferieur de l'élément sont visible\n     */\n    function isVisible(elem) {\n\n      let coords = elem.getBoundingClientRect();\n\n      let windowHeight = document.documentElement.clientHeight;\n\n      // le rebord supérieur de l'elem est visible ou le rebord inferieur de l' elem est visible\n      let topVisible = coords.top > 0 && coords.top < windowHeight;\n      let bottomVisible = coords.bottom < windowHeight && coords.bottom > 0;\n\n      return topVisible || bottomVisible;\n    }\n\n    /**\n    une variante du teste qui considère l'élément  visible s elle ne fait pas plus d'une page après/derrière l'écran en cours.\n\n    function isVisible(elem) {\n\n      let coords = elem.getBoundingClientRect();\n\n      let windowHeight = document.documentElement.clientHeight;\n\n      let extendedTop = -windowHeight;\n      let extendedBottom = 2 * windowHeight;\n\n      // top visible || bottom visible\n      let topVisible = coords.top > extendedTop && coords.top < extendedBottom;\n      let bottomVisible = coords.bottom < extendedBottom && coords.bottom > extendedTop;\n\n      return topVisible || bottomVisible;\n    }\n    */\n\n    function showVisible() {\n      for (let img of document.querySelectorAll('img')) {\n        let realSrc = img.dataset.src;\n        if (!realSrc) continue;\n\n        if (isVisible(img)) {\n          // desactiver le cache\n          // Cette ligne  doit être enlevée dans la production du code\n          realSrc += '?nocache=' + Math.random();\n\n          img.src = realSrc;\n\n          img.dataset.src = '';\n        }\n      }\n\n    }\n\n    window.addEventListener('scroll', showVisible);\n    showVisible();\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/3-load-visible-img/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <p>Les Textes et images proviennent de https://wikipedia.org.</p>\n\n  <h3>Toutes les images ayant <code>data-src</code> se chargent lorsqu'elles deviennent visible.</h3>\n\n  <h1>Le système Solaire</h1>\n\n  <p>Le système solaire est un système centre gravitationnel comprenant  le soleil et les objets orbitant autour de celui-ci, soit directement ou indirectement.  Parmi ces objets qui tournent en orbite directement autour du soleil, les huit plus grands sont des planètes, le reste étant des objets significativement plus petits, tels que les planètes naines et les petits corps du système solaire. Des objets qui gravitent indirectement autour du soleil, les lunes, deux d’entre eux sont plus grands que la plus petite planète, Mercure.</p>\n\n  <p>Le système solaire est forme il y a 4.6 milliards années à partir de la chute gravitationnelle d'un géant nuage moléculaire interstellaire. La grande majorité de la masse du système est dans le soleil, avec la plus grande partie du reste de la masse contenue dans Jupiter. Les quatre planètes intérieures les plus petites, Mercure, Venus,\n    la Terre et Mars, sont des planètes terrestres, étant principalement composées de roche et de métal.\n    Les quatre planètes se situant vers l'extérieure sont des planètes géantes, étant de manière substantielle plus massive que celles terrestres.\n    Les deux plus grandes, Jupiter et Saturne, sont des géantes gazeuses, étant principalement compose d'hydrogène et de hélium;\n    Les deux planètes les plus à l'extérieure, Uranus et Neptune, sont des géantes glaciales,\n    étant composées de substances ayant des points de fusion relativement élevés compare à l'hydrogène\n    et l'hélium, appelés les volatiles, telle que l'eau, ammonium et le méthane.\n    Toutes les planètes ont presque des orbites circulaires pouvant être couches sur un disque presque plat appelé l'elliptique.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/planets.jpg\" width=\"640\" height=\"360\">\n  </figure>\n\n  <h1>Le Soleil</h1>\n\n  <p>Le soleil est une étoile du système solaire et est de loin sa composante la plus massive.\n    Sa masse la plus large (332,900 masses terrestre) produit d'assez fortes températures et de densités dans son noyau\n    pour soutenir une fusion de  l'hydrogène en hélium, faisant d'elle une étoile de séquence principale.\n     Cela dégage une énorme quantité d'énergie, en majeur partie radiée dans l'espace en radiation électromagnétique devenant en son summum une lumière visible.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/sun.jpg\" width=\"658\" height=\"658\">\n  </figure>\n\n  <h1>Mercure</h1>\n\n  <p>Mercure (0,4 UA du Soleil) est la planète la plus proche du soleil et est la plus petite planète du système solaire (0,055 masse terrestre).\n    Mercure n'a pas de satellites naturels, en outre des cratères d'impacts, ses seuls aspects géologiques connues sont\n    les escarpements lobés ou rupes qui étaient probablement produits par une période de contraction tôt dans son histoire.[67] L'atmosphère ténu de Mercure est composé d' atomes projetés à sa surface par le vent solaire.[68] Son noyau de fer relativement large et son mince manteau n'ont pas été expliqué de manière adéquate jusqu'à présent. Les hypothèses incluent le fait que ses couches externes soient écorchées par un géant impact; ou, que cela fut empêché de totalement accréter par l’énergie solaire juvénile.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/mercury.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Venus</h1>\n\n  <p>Venus (0,7 UA du Soleil)  est proche de par sa taille de la terre (0,815 masse terrestre) et, comme la terre,\n   a un épais manteau en silicate autour de son noyau de fer, une atmosphère substantielle, et l’évidence d'une activité géologique interne.\n   Elle est plus sèche que la Terre, et son atmosphère est quatre-vingt-dix fois plus dense. Venus n'as pas de satellites naturels.\n     C'est la planète la plus chaude, avec des températures à la surface  allant au-delà de  400 °C (752°F), très probablement due à la quantité de gazes à effet de serre dans l'atmosphère.\n   Aucune évidence définitive d'une activité géologique actuelle n’a été détectée sur Venus,\n   mais elle n'as pas de champ magnétique qui empêcherait la raréfaction de son atmosphère substantielle, ce qui suggère que son atmosphère est entrain remplie par les éruptions volcaniques.</p>\n\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/venus.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n\n    <h1>La Terre</h1>\n\n  <p>La Terre (1 UA du Soleil) est la plus grande et la plus dense des planètes internes,\n    la seule connue pour avoir une activité géologique actuellement, et la seule place ou l'existence de vie est connue. Son liquide hydrosphère est unique parmi les planètes terrestres,\n    et c'est la seule planète ou la tectonique des plaques a été observée.\n    L'atmosphère de la terre est radicalement différente de celle des autres planètes,\n    ayant été altérée par la présence de vie pour contenir 21% oxygène libre.\n    Elle a un satellite naturel, la Lune, la seule grande satellite d'une planète terrestre du système solaire.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/earth.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Mars</h1>\n\n  <p>Mars (1,5 UA du Soleil) est plus petite que la Terre et Venus (0.107 masse terrestre).\n    Elle a une atmosphère en majorité composée de dioxyde de carbone avec une pression à la surface de 6,1 millibars\n    (Approximativement 0.6%  de celle de la Terre). Sa surface, poivrée de vastes volcans,\n    tels que Olympus Mons, tel que  la Vallée du Marineris, montrent une activité géologique qui a pu persister jusqu'à une période récente remontant à 2 million années.\n     Sa couleur rouge provient du dioxyde de fer (la rouille) dans la terre.\n      Mars a deux satellites naturels (Deimos and Phobos) dont on pense être des astéroïdes capturés.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/mars.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Jupiter</h1>\n\n  <p>Jupiter (5,2 UA), a  318 masse terrestre, est 2,5 fois la masse de toutes les autres planètes mises ensemble.\n     Elle est composée en grande partie d’hydrogène et d'hélium.\n    la forte chaleur de Jupiter crée des aspects semi-permanent dans son atmosphère,\n     tel que des bandes de nuages et la Grande Tache Rouge. Jupiter a  67  satellites connus.\n    Les quatre grands les plus connus, Ganymede, Callisto, Io, and Europe, montrent des similarités aux planètes terrestres,\n      tel que le volcanisme et le réchauffement interne.\n    Ganymede, le plus grand satellite dans le système solaire,  est plus large que Mercure.</p>\n\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/jupiter.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Saturne</h1>\n  <p>Saturne (9,5 UA), qui se distingue par son extensif système d'anneau, à plusieurs similarités avec Jupiter,\n   tel que sa composition atmosphérique et son magnétosphère.\n    Bien que Saturne ait 60% du volume de Jupiter, elle est moins massive au tiers,\n    à 95 masse terrestre. Saturne est la seule planète du système Solaire qui est moins dense que l'eau.\n    Les anneaux de Saturne sont faits de petites quantités de glaces et de particules de roches.\n    Saturne a 62 satellites confirmés  composés  largement de glace.\n    Deux d'entre eux, Titan and Encelade, montrent des signes d'une activité géologique.\n    Titan, la deuxième plus grande lune du système solaire, est plus grande que Mercure\n    et est le seul satellite  du système solaire  avec une atmosphère substantielle.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/saturn.jpg\" width=\"805\" height=\"390\">\n  </figure>\n\n  <h1>Uranus</h1>\n  <p>Uranus (19,2 UA), a 14 masse terrestre, est la plus légère des planètes externes.\n    Particulière parmi les autres planètes, elle tourne en orbite autour du soleil sur sa face;\n   Son inclinaison axiale est a plus de quatre-vingt-dix dégrée elliptique.\n    Elle a un noyau beaucoup plus froid que les autres planètes géantes et elle radie peu de chaleur dans l'espace.\n    Uranus a 27 satellites connus, les plus grandes étant Titania,\n    Oberon, Umbriel, Ariel, et  Miranda.</p>\n\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/uranus.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n\n    <h1>Neptune</h1>\n  <p>Neptune (30,1 UA), bien que un peu plus petite que Uranus, est plus massive (équivalent à 17 fois la Terre)\n     et dès lors plus dense. Elle radie plus de chaleur interne,\n     mais pas autant que Jupiter ou Saturne.\n     Neptune  a 14 satellites  connus. Le plus grand, Triton, est géologiquement actif,\n     avec des geysers de nitrogène liquide.\n     Triton est le seul grand satellite avec un orbite rétrograde.\n     Neptune est accompagnée dans son orbite par plusieurs petites planètes, dénommées les troyens de Neptune,\n     qui sont en résonance 1:1 avec elle.</p>\n\n   <figure>\n     <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/neptune.jpg\" width=\"390\" height=\"390\">\n   </figure>\n\n   <script>\n    /**\n     * Teste si l’élément est visible (dans la partie visible de la page)\n     * C'est assez que le haut ou le rebord inferieur de l'élément sont visible\n     */\n\n    function isVisible(elem) {\n      // todo: your code\n    }\n\n    function showVisible() {\n      for (let img of document.querySelectorAll('img')) {\n        let realSrc = img.dataset.src;\n        if (!realSrc) continue;\n\n        if (isVisible(img)) {\n          // desactiver le cache\n          // Cette ligne  doit être enlevée dans la production du code\n          realSrc += '?nocache=' + Math.random();\n\n          img.src = realSrc;\n\n          img.dataset.src = '';\n        }\n      }\n\n    }\n\n    window.addEventListener('scroll', showVisible);\n    showVisible();\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/3-load-visible-img/task.md",
    "content": "importance: 4\n\n---\n\n# Charger les images visibles\n\nDisons que nous avons un client lent et nous voulons sauvegarder leur trafique de donnée mobile.\n\nA cet effet, nous décidons de ne pas montrer les images immédiatement, mais plutôt les remplacer avec des conteneurs d'images, comme ainsi:\n\n```html\n<img *!*src=\"placeholder.svg\"*/!* width=\"128\" height=\"128\" *!*data-src=\"real.jpg\"*/!*>\n```\n\nDonc, initialement toutes les images sont des `placeholder.svg`. Lorsque la page défile a la  position ou l'utilisateur peut voir l'image -- nous changeons `src` à celui qui est dans `data-src`, ainsi l'image va être chargée.\n\nVoici un exemple dans un `iframe`:\n\n[iframe src=\"solution\"]\n\nDéfilez-le pour voir les images s'afficher à la \"demande\".\n\nLes requis:\n- Quand la page est chargée, ces images qui sont à l'écran doivent s'afficher immédiatement, avant tout défilement.\n- Certaines images peuvent être régulières, sans la propriété `data-src`. Le code ne doit pas les toucher.\n- Une fois une image est chargée, elle ne doit plus être rechargée lorsqu'elle est défilée en vue/hors de vue.\n\nP.S. Si vous pouvez, trouvez une solution plus avancée  qui pourrait \"pré-charger\" les images qui sont sur une page en bas/après la position actuelle.\n\nP.P.S. Le défilement vertical sera seulement géré, et non pas le défilement horizontal."
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/article.md",
    "content": "# Le Défilement\n\nL'événement `scroll` permet de réagir sur le défilement d'une page ou d'un élément. Il y a pas mal de bonnes choses que nous pouvons faire ici.\n\n\nPar exemple:\n- Montrer/cacher des contrôles additionnelles ou information selon la ou se trouve l'utilisateur sur le document.\n- Charger plus de données lorsque l'utilisateur défile vers le bas jusqu'à la fin de la page.\n\nVoici une petite fonction pour montrer la position actuelle du défilement:\n\n```js autorun\nwindow.addEventListener('scroll', function() {\n  document.getElementById('showScroll').innerHTML = window.pageYOffset + 'px';\n});\n```\n\n```online\nen action:\n\nCurrent scroll = <b id=\"showScroll\">Faites défiler la fenêtre</b>\n```\n\nL'évènement `scroll` fonctionne aussi bien avec `window` qu'avec les éléments defilables.\n\n## Empêcher le défilement\n\n\nComment fait-on quelque chose de non-scrollable ?\n\nNous ne pouvons pas empêcher le défilement en utilisant `event.preventDefault()` dans l'écouteur `onscroll`, car il se déclenche *après* le défilement qui est déjà passé.\n\nMais nous pouvons empêcher le défilement avec `event.preventDefault()` sur un événement qui provoque le défilement, par exemple, l'événement `keydown` pour `key:pageUp` et `key:pageDown`.\n\n\nSi nous ajoutons un gestionnaire d'évènement a ces évènements et à `event.preventDefault()` , alors le défilement ne pas se déclencher.\n\n\nIl existe de nombreuses façons d’initialiser un défilement. Il est donc plus fiable d’utiliser la propriété CSS `overflow`.\n\n\nVoici quelques taches que vous pouvez résoudre oubien regarder afin de voir une application de l'évènement `onscroll`.\n"
  },
  {
    "path": "2-ui/3-event-details/index.md",
    "content": "# UI Events\n\nHere we cover most important user interface events and how to work with them.\n"
  },
  {
    "path": "2-ui/4-forms-controls/1-form-elements/1-add-select-option/solution.md",
    "content": "La solution, étape par étape:\n\n```html run\n<select id=\"genres\">\n  <option value=\"rock\">Rock</option>\n  <option value=\"blues\" selected>Blues</option>\n</select>\n\n<script>\n  // 1)\n  let selectedOption = genres.options[genres.selectedIndex];\n  alert( selectedOption.value );\n\n  // 2)\n  let newOption = new Option(\"Classic\", \"classic\");\n  genres.append(newOption);\n\n  // 3)\n  newOption.selected = true;\n</script>\n```\n"
  },
  {
    "path": "2-ui/4-forms-controls/1-form-elements/1-add-select-option/task.md",
    "content": "importance: 5\n\n---\n\n# Ajouter une option à un select\n\nVoici un `<select>`:\n\n```html\n<select id=\"genres\">\n  <option value=\"rock\">Rock</option>\n  <option value=\"blues\" selected>Blues</option>\n</select>\n```\n\nUtiliser JavaScript pour:\n\n1. Afficher la valeur et le texte de l'option sélectionnée.\n2. Ajouter une option: `<option value=\"classic\">Classic</option>`.\n3. La définir comme \"selectionné\".\n\nNote: Si tout a été fait correctement, l'alerte devrait afficher `blues`.\n"
  },
  {
    "path": "2-ui/4-forms-controls/1-form-elements/article.md",
    "content": "# Propriétés de formulaire\n\nLes formulaires et les éléments, tel que `<input>` ont beaucoup de propriétés et évènements spécifiques.\n\nTravailler avec les formulaires sera beaucoup plus facile une fois que nous aurons exploré leurs principales propriétés.\n\n## Navigation: formulaires et éléments\n\nLes formulaires du document font partie d'une collection spéciale, `document.forms`.\n\nC'est une *\"collection nommée\"*: ses élements sont à la fois nommés et triés. On peut utiliser soit le nom, soit l'index pour récupérer un formulaire.\n\n```js no-beautify\ndocument.forms.my; // le formulaire avec la propriété name=\"my\"\ndocument.forms[0]; // le premier formulaire dans le document\n```\n\nTous les éléments d'un formulaire sont accessibles via la collection nommée `form.elements`.\n\nPar exemple:\n\n```html run height=40\n<form name=\"my\">\n  <input name=\"un\" value=\"1\">\n  <input name=\"deux\" value=\"2\">\n</form>\n\n<script>\n  // Récupére le formulaire\n  let form = document.forms.my; // L'élément <form name=\"my\">\n\n  // Récupére l'élément \"un\"\n  let elem = form.elements.un; // L'élément <input name=\"un\">\n\n  alert(elem.value); // 1\n</script>\n```\n\nIl peut exister plusieurs éléments avec le même nom. C'est le cas par exemple pour les boutons radios et les cases à cocher.\n\nDans ce cas, `form.elements[name]` est une *collection*. Par exemple:\n\n```html run height=40\n<form>\n  <input type=\"radio\" *!*name=\"age\"*/!* value=\"10\">\n  <input type=\"radio\" *!*name=\"age\"*/!* value=\"20\">\n</form>\n\n<script>\nlet form = document.forms[0];\n\nlet ageElems = form.elements.age;\n\n*!*\nalert(ageElems[0]); // [object HTMLInputElement]\n*/!*\n</script>\n```\n\nCes propriétés de navigation ne dépendent pas de la structure des balises. Tous les éléments de contrôle, quel que soit leur profondeur dans l'imbrication HTML, sont accessibles via `form.elements`.\n\n\n````smart header=\"Utiliser des fieldsets comme des \\\"sous-formulaires\\\"\"\nUn formulaire peut contenir un ou plusieurs éléments de type `<fieldset>`. Un fieldset a aussi une propriété `elements` contenant la liste des contrôles qu'il contient.\n\nPar exemple:\n\n```html run height=80\n<body>\n  <form id=\"form\">\n    <fieldset name=\"userFields\">\n      <legend>info</legend>\n      <input name=\"login\" type=\"text\">\n    </fieldset>\n  </form>\n\n  <script>\n    alert(form.elements.login); // <input name=\"login\">\n\n*!*\n    let fieldset = form.elements.userFields;\n    alert(fieldset); // HTMLFieldSetElement\n\n    // On peut récupérer l'élément à la fois depuis le form et depuis le fieldset\n    alert(fieldset.elements.login == form.elements.login); // true\n*/!*\n  </script>\n</body>\n```\n````\n\n````warn header=\"Notation raccourcie: `form.name`\"\nIl existe une notation plus courte: on peut accéder à l'élément via `form[index/name]`.\n\nAutrement dit, au lieu de `form.elements.login`, on peut simplement écrire `form.login`.\n\nCela fonctionne, mais avec un bémol: si on accède à un élément, et qu'on change ensuite son `name`, il reste accessible via son ancien nom (en plus du nouveau).\n\nUn exemple pour démontrer cette particularité:\n\n```html run height=40\n<form id=\"form\">\n  <input name=\"login\">\n</form>\n\n<script>\n  alert(form.elements.login == form.login); // true, c'est le même <input>\n\n  form.login.name = \"username\"; // On change le nom de l'input\n\n  // form.elements a bien mis à jour le nom:\n  alert(form.elements.login); // undefined\n  alert(form.elements.username); // input\n\n*!*\n  // La propriété form contient bien les 2 noms: le nouveau et l'ancien\n  alert(form.username == form.login); // true\n*/!*\n</script>\n```\n\nCe n'est cependant généralement pas un problème, car c'est très rare de modifier le nom d'un élément dans un formulaire.\n\n````\n\n## Backreference: element.form\n\nDans chaque élement, le formulaire associé est disponible via la propriété `element.form`. Un formulaire référence donc tous ses éléments, et tous les élements référencent leur formulaire.\n\nVoici un schéma de leurs relations:\n\n![](form-navigation.svg)\n\nPar exemple:\n\n```html run height=40\n<form id=\"form\">\n  <input type=\"text\" name=\"login\">\n</form>\n\n<script>\n*!*\n  // form -> element\n  let login = form.login;\n\n  // element -> form\n  alert(login.form); // HTMLFormElement\n*/!*\n</script>\n```\n\n## Eléments de formulaire\n\nParlons un peu des contrôles de formulaires.\n\n### input et textarea\n\nOn peut accéder à leur valeur via `input.value` (string) ou `input.checked` (boolean) pour les cases à cocher et les boutons radios.\n\nExemple:\n\n```js\ninput.value = \"Nouvelle valeur\";\ntextarea.value = \"Nouveau texte\";\n\ninput.checked = true; // pour une case à cocher ou un bouton radio\n```\n\n```warn header=\"Utilisez `textarea.value`, et pas `textarea.innerHTML`\"\nNotez que même si `<textarea>...</textarea>` stocke sa valeur sous forme de HTML imbriqué, il ne faut jamais utiliser `textarea.innerHTML` pour y accéder.\n\nCette propriété stocke le HTML qui était sur la page d'origine, et non sa valeur actuelle.\n```\n\n### select et option\n\nUn élément `<select>` a 3 propriétés importantes:\n\n1. `select.options` -- une collection de ses sous-élements `<option>`,\n2. `select.value` -- la *valeur* de l'`<option>` actuellement sélectionnée,\n3. `select.selectedIndex` -- l'*index* de l'`<option>` actuellement sélectionnée.\n\nElles nous donnent 3 moyens différents de définir la valeur d'un `<select>`:\n\n1. Trouver l'élement `<option>` correspondant (parmi `select.options`) et mettre sa valeur `option.selected` à `true`.\n2. Si on connaît la nouvelle valeur: mettre `select.value` à la nouvelle valeur.\n3. Si on connaît l'index de la nouvelle option: mettre `select.selectedIndex` à ce nombre.\n\nVoici un exemple pour ces 3 méthodes:\n\n```html run\n<select id=\"select\">\n  <option value=\"apple\">Pomme</option>\n  <option value=\"pear\">Pêche</option>\n  <option value=\"banana\">Banane</option>\n</select>\n\n<script>\n  // Les 3 lignes suivantes font la même chose\n  select.options[2].selected = true; \n  select.selectedIndex = 2;\n  select.value = 'banana';\n  // Note: la liste 'options' démarre à 0, donc l'index 2 pointe sur la 3ème option.\n</script>\n```\n\nContrairement à la plupart des contrôles, `<select>` permet de sélectionner plusieurs options en même temps si il a l'attribut `multiple`. Cet attribut est cependant rarement utilisé.\n\nEn cas de valeurs sélectionnées multiples, utilisez la première méthode: ajouter/supprimer la propriété `selected` de chaque sous-élement `<option>`.\n\nVoici un exemple montrant comment récupérer toutes les valeurs sélectionnées d'un multi-select:\n\n```html run\n<select id=\"select\" *!*multiple*/!*>\n  <option value=\"blues\" selected>Blues</option>\n  <option value=\"rock\" selected>Rock</option>\n  <option value=\"classic\">Classic</option>\n</select>\n\n<script>\n  // Récupère toutes les valeurs sélectionnées du multi-select\n  let selected = Array.from(select.options)\n    .filter(option => option.selected)\n    .map(option => option.value);\n\n  alert(selected); // blues,rock  \n</script>\n```\n\nLa spécification complète de l'élement `<select>` est disponible dans la spécification HTML <https://html.spec.whatwg.org/multipage/forms.html#the-select-element>.\n\n### Nouvelle option\n\nDans la [spécification](https://html.spec.whatwg.org/multipage/forms.html#the-option-element), on peut trouver une syntaxe raccourcie pour créer un élément `<option>` :\n\n```js\noption = new Option(text, value, defaultSelected, selected);\n```\n\nCette syntaxe est optionnelle. On peut très bien utiliser `document.createElement('option')` et définir ses attributs manuellement. Mais cette syntaxe est plus courte, voici donc ses paramètres:\n\n- `text` -- Le texte à l'intérieur de l'option,\n- `value` -- La valeur de l'option,\n- `defaultSelected` -- Si `true`, alors l'attribut HTML `selected` est créé,\n- `selected` -- Si `true`, l'option est sélectionnée.\n\nLa différence entre `defaultSelected` and `selected` est que `defaultSelected` définit l'attribut HTML (qu'on peut récupérer via `option.getAttribute('selected')`), alors que `selected` définit si l'option est sélectionnée ou non.\n\nEn pratique, il vaut mieux mettre les _deux_ valeurs à `true` ou `false`. (Ou simplement ne pas les utiliser, leur valeur sera `false` par défaut.)\n\nPar exemple, voici une nouvelle option \"non sélectionnée\" :\n\n```js\nlet option = new Option(\"Texte\", \"valeur\");\n// Créé un élément <option value=\"value\">Text</option>\n```\n\nLa même option, mais cette fois-ci sélectionnée:\n\n```js\nlet option = new Option(\"Texte\", \"valeur\", true, true);\n```\n\nUn élément d'option a les propriétés suivantes:\n\n`option.selected`\n: Est l'option sélectionnée.\n\n`option.index`\n: L'index de l'option parmi les autres dans son `<select>`.\n\n`option.text`\n: Le texte de l'option (visible par le visiteur de la page).\n\n## Références\n\n- Spécification: <https://html.spec.whatwg.org/multipage/forms.html>.\n\n## Résumé\n\nNavigation d'un formulaire:\n\n`document.forms`\n: Un formulaire est disponible via `document.forms[name/index]`.\n\n`form.elements`  \n: Les élements d'un formulaire sont disponibles via `form.elements[name/index]`, ou `form[name/index]`. La propriété `elements` fonctionne aussi pour un `<fieldset>`.\n\n`element.form`\n: Chaque élement référence son formulaire via la propriété `form`.\n\nLa valeur est disponible via `input.value`, `textarea.value`, `select.value`, etc. (Pour les cases à cocher et les boutons radios, utilisez `input.checked` pour déterminer si une valeur est sélectionnée ou non.)\n\nPour `<select>`, on peut aussi récuperer la valeur par son index via `select.selectedIndex`, ou dans la liste des options via `select.options`.\n\nCe sont les bases pour commencer à travailler avec les formulaires. Nous verrons d'autres exemples plus loin dans le tutoriel.\n\nDans le prochain chapitre, nous couvrirons les évènements `focus` et `blur` qui peuvenet se déclencher sur n'importe quel élément, mais sont principalement utilisés sur les formulaires.\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <link type=\"text/css\" rel=\"stylesheet\" href=\"my.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <ul>\n    <li>Click the div to edit.</li>\n    <li>Enter or blur saves the result.</li>\n  </ul>\n\n  HTML is allowed.\n\n  <div id=\"view\" class=\"view\">Text</div>\n\n  <script>\n    let area = null;\n    let view = document.getElementById('view');\n\n    view.onclick = function() {\n      editStart();\n    };\n\n    function editStart() {\n      area = document.createElement('textarea');\n      area.className = 'edit';\n      area.value = view.innerHTML;\n\n      area.onkeydown = function(event) {\n        if (event.key == 'Enter') {\n          this.blur();\n        }\n      };\n\n      area.onblur = function() {\n        editEnd();\n      };\n\n      view.replaceWith(area);\n      area.focus();\n    }\n\n    function editEnd() {\n      view.innerHTML = area.value;\n      area.replaceWith(view);\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.view/my.css",
    "content": ".view,\n.edit {\n  height: 150px;\n  width: 400px;\n  font-family: sans-serif;\n  font-size: 14px;\n  display: block;\n}\n\n.view {\n  /* padding + border = 3px */\n  padding: 2px;\n  border: 1px solid black;\n}\n\n.edit {\n  /* remplace le padding par des bordures (toujours 3px pour ne pas déplacer le contenu) */\n  border: 3px solid blue;\n  padding: 0px;\n}\n\n.edit:focus {\n  /* retire les bordure de focus sur Safari */\n  outline: none;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/3-editable-div/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <link type=\"text/css\" rel=\"stylesheet\" href=\"my.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <ul>\n    <li>Click the div to edit.</li>\n    <li>Enter or blur saves the result.</li>\n  </ul>\n\n  HTML is allowed.\n\n  <div id=\"view\" class=\"view\">Text</div>\n\n  <script>\n    // ...votre code...\n    // Note: <textarea> devrait avoir class=\"edit\"\n    // my.css possède les styles pour la rendre de la même taille que div\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/3-editable-div/source.view/my.css",
    "content": ".view,\n.edit {\n  height: 150px;\n  width: 400px;\n  font-family: sans-serif;\n  font-size: 14px;\n  display: block;\n}\n\n.view {\n  /* padding + border = 3px */\n  padding: 2px;\n  border: 1px solid black;\n}\n\n.edit {\n  /* remplace le padding par des bordures (toujours 3px pour ne pas déplacer le contenu) */\n  border: 3px solid blue;\n  padding: 0px;\n}\n\n.edit:focus {\n  /* retire les bordure de focus sur Safari */\n  outline: none;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/3-editable-div/task.md",
    "content": "importance: 5\n\n---\n\n# Div éditable\n\nCréez une `<div>` qui se transforme en `<textarea>` lorsque l'on clique dessus.\n\nLa zone de texte permet de modifier le HTML présent dans la `<div>`.\n\nLorsque l'utilisateur appuie sur `key:Enter` ou perds le focus, la `<textarea>` se transforme à nouveau en `<div>`, et son contenu devient le HTML dans `<div>`.\n\n[demo src=\"solution\"]\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.md",
    "content": "\n1. Au clique -- remplacer `innerHTML` de la cellule par `<textarea>` avec les mêmes tailles et aucune bordure. Possibilité d'utiliser JavaScript ou CSS pour mettre les bonnes tailles.\n2. Mettre `textarea.value` a `td.innerHTML`.\n3. Mettre le focus sur la zone de texte.\n4. Afficher les boutons OK/CANCEL en dessous de la cellule, gèrent les cliques sur eux-mêmes.\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.view/bagua.css",
    "content": "/* styles communs pour la table, pas besoin de les modifier */\n\nth {\n  text-align: center;\n  font-weight: bold;\n}\n\ntd {\n  width: 150px;\n  white-space: nowrap;\n  text-align: center;\n  vertical-align: middle;\n  padding: 10px;\n}\n\n.nw {\n  background-color: #999;\n}\n\n.n {\n  background-color: #03f;\n  color: #fff;\n}\n\n.ne {\n  background-color: #ff6;\n}\n\n.w {\n  background-color: #ff0;\n}\n\n.c {\n  background-color: #60c;\n  color: #fff;\n}\n\n.e {\n  background-color: #09f;\n  color: #fff;\n}\n\n.sw {\n  background-color: #963;\n  color: #fff;\n}\n\n.s {\n  background-color: #f60;\n  color: #fff;\n}\n\n.se {\n  background-color: #0c3;\n  color: #fff;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n  <link rel=\"stylesheet\" href=\"bagua.css\">\n  <link rel=\"stylesheet\" href=\"my.css\">\n\n\n  <p>Click on a table cell to edit it. Press OK or CANCEL when you finish.</p>\n\n  <table id=\"bagua-table\">\n    <tr>\n      <th colspan=\"3\"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>\n    </tr>\n    <tr>\n      <td class=\"nw\"><strong>Northwest</strong>\n        <br>Metal\n        <br>Silver\n        <br>Elders\n      </td>\n      <td class=\"n\"><strong>North</strong>\n        <br>Water\n        <br>Blue\n        <br>Change\n      </td>\n      <td class=\"ne\"><strong>Northeast</strong>\n        <br>Earth\n        <br>Yellow\n        <br>Direction\n      </td>\n    </tr>\n    <tr>\n      <td class=\"w\"><strong>West</strong>\n        <br>Metal\n        <br>Gold\n        <br>Youth\n      </td>\n      <td class=\"c\"><strong>Center</strong>\n        <br>All\n        <br>Purple\n        <br>Harmony\n      </td>\n      <td class=\"e\"><strong>East</strong>\n        <br>Wood\n        <br>Blue\n        <br>Future\n      </td>\n    </tr>\n    <tr>\n      <td class=\"sw\"><strong>Southwest</strong>\n        <br>Earth\n        <br>Brown\n        <br>Tranquility\n      </td>\n      <td class=\"s\"><strong>South</strong>\n        <br>Fire\n        <br>Orange\n        <br>Fame\n      </td>\n      <td class=\"se\"><strong>Southeast</strong>\n        <br>Wood\n        <br>Green\n        <br>Romance\n      </td>\n    </tr>\n\n  </table>\n\n\n  <script src=\"script.js\"></script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.view/my.css",
    "content": ".edit-td .edit-area {\n  border: none;\n  margin: 0;\n  padding: 0;\n  display: block;\n\n  /* retire la poignée de redimensionnement dans Firefox */\n  resize: none;\n\n  /* retire le contour lors du focus sur Chrome */\n  outline: none;\n\n  /* retire la barre de défillement sur IE */\n  overflow: auto;\n}\n\n.edit-controls {\n  position: absolute;\n}\n\n.edit-td {\n  position: relative;\n  padding: 0;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.view/script.js",
    "content": "let table = document.getElementById('bagua-table');\n\nlet editingTd;\n\ntable.onclick = function(event) {\n\n  // 3 cibles possible\n  let target = event.target.closest('.edit-cancel,.edit-ok,td');\n\n  if (!table.contains(target)) return;\n\n  if (target.className == 'edit-cancel') {\n    finishTdEdit(editingTd.elem, false);\n  } else if (target.className == 'edit-ok') {\n    finishTdEdit(editingTd.elem, true);\n  } else if (target.nodeName == 'TD') {\n    if (editingTd) return; // déjà en cours d'édition\n\n    makeTdEditable(target);\n  }\n\n};\n\nfunction makeTdEditable(td) {\n  editingTd = {\n    elem: td,\n    data: td.innerHTML\n  };\n\n  td.classList.add('edit-td'); // td est en train d'être édité, CSS stylise aussi la zone à l'intérieur\n\n  let textArea = document.createElement('textarea');\n  textArea.style.width = td.clientWidth + 'px';\n  textArea.style.height = td.clientHeight + 'px';\n  textArea.className = 'edit-area';\n\n  textArea.value = td.innerHTML;\n  td.innerHTML = '';\n  td.appendChild(textArea);\n  textArea.focus();\n\n  td.insertAdjacentHTML(\"beforeEnd\",\n    '<div class=\"edit-controls\"><button class=\"edit-ok\">OK</button><button class=\"edit-cancel\">CANCEL</button></div>'\n  );\n}\n\nfunction finishTdEdit(td, isOk) {\n  if (isOk) {\n    td.innerHTML = td.firstChild.value;\n  } else {\n    td.innerHTML = editingTd.data;\n  }\n  td.classList.remove('edit-td'); \n  editingTd = null;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/bagua.css",
    "content": "/* styles communs pour la table, pas besoin de les modifier */\n\nth {\n  text-align: center;\n  font-weight: bold;\n}\n\ntd {\n  width: 150px;\n  white-space: nowrap;\n  text-align: center;\n  vertical-align: middle;\n  padding: 10px;\n}\n\n.nw {\n  background-color: #999;\n}\n\n.n {\n  background-color: #03f;\n  color: #fff;\n}\n\n.ne {\n  background-color: #ff6;\n}\n\n.w {\n  background-color: #ff0;\n}\n\n.c {\n  background-color: #60c;\n  color: #fff;\n}\n\n.e {\n  background-color: #09f;\n  color: #fff;\n}\n\n.sw {\n  background-color: #963;\n  color: #fff;\n}\n\n.s {\n  background-color: #f60;\n  color: #fff;\n}\n\n.se {\n  background-color: #0c3;\n  color: #fff;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n  <link rel=\"stylesheet\" href=\"bagua.css\">\n  <link rel=\"stylesheet\" href=\"my.css\">\n\n\n  <p>Click on a table cell to edit it. Press OK or CANCEL when you finish.</p>\n\n  <table id=\"bagua-table\">\n    <tr>\n      <th colspan=\"3\"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>\n    </tr>\n    <tr>\n      <td class=\"nw\"><strong>Northwest</strong>\n        <br>Metal\n        <br>Silver\n        <br>Elders\n      </td>\n      <td class=\"n\"><strong>North</strong>\n        <br>Water\n        <br>Blue\n        <br>Change\n      </td>\n      <td class=\"ne\"><strong>Northeast</strong>\n        <br>Earth\n        <br>Yellow\n        <br>Direction\n      </td>\n    </tr>\n    <tr>\n      <td class=\"w\"><strong>West</strong>\n        <br>Metal\n        <br>Gold\n        <br>Youth\n      </td>\n      <td class=\"c\"><strong>Center</strong>\n        <br>All\n        <br>Purple\n        <br>Harmony\n      </td>\n      <td class=\"e\"><strong>East</strong>\n        <br>Wood\n        <br>Blue\n        <br>Future\n      </td>\n    </tr>\n    <tr>\n      <td class=\"sw\"><strong>Southwest</strong>\n        <br>Earth\n        <br>Brown\n        <br>Tranquility\n      </td>\n      <td class=\"s\"><strong>South</strong>\n        <br>Fire\n        <br>Orange\n        <br>Fame\n      </td>\n      <td class=\"se\"><strong>Southeast</strong>\n        <br>Wood\n        <br>Green\n        <br>Romance\n      </td>\n    </tr>\n\n  </table>\n\n\n  <script src=\"script.js\"></script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/my.css",
    "content": "/* vos styles */\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/script.js",
    "content": "let table = document.getElementById('bagua-table');\n\n/* votre code */\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/task.md",
    "content": "importance: 5\n\n---\n\n# Modifier TD au clique\n\nRendre des cellules de table éditables au clique.\n\n- Au clique -- la cellule doit devenir \"éditable\" (une zone de texte apparait à l'intérieur), nous pouvons changer le HTML. Il ne doit pas y avoir de redimensionnement, toute la géométrie doit rester la même.\n- Les boutons OK et CANCEL apparaissent sous la cellule pour terminer/annuler l'édition.\n- Seulement une cellule devrait être éditable à la fois. Pendant qu'un `<td>` est en \"mode d'édition\", les cliques sur les autres cellules sont ignorés.\n- La tableau devrait avoir beaucoup de cellules. Utilisez la délégation d'évènements.\n\nLa démo:\n\n[iframe src=\"solution\" height=400]\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.md",
    "content": "\nNous pouvons utiliser `mouse.onclick` pour gérer le clique et rendre la souris \"déplaçable\" avec `position:fixed`, puis `mouse.onkeydown` pour gérer les touches flèche.\n\nLe seul problème est que `keydown` ne se déclenche que sur les éléments avec le focus. Donc nous avons besoin d'ajouter `tabindex` à l'élément. Comme nous n'avons pas le droit de modifier le HTML, nous pouvons utiliser la propriété `mouse.tabIndex` pour ceci.\n\nP.S. Nous pouvons aussi remplacer `mouse.onclick` par `mouse.onfocus`.\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    #mouse {\n      display: inline-block;\n      cursor: pointer;\n      margin: 0;\n    }\n\n    #mouse:focus {\n      outline: 1px dashed black;\n    }\n  </style>\n</head>\n\n<body>\n\n  <p>Click on the mouse and move it with arrow keys.</p>\n\n  <pre id=\"mouse\">\n _   _\n(q\\_/p)\n /. .\\\n=\\_t_/=   __\n /   \\   (\n((   ))   )\n/\\) (/\\  /\n\\  Y  /-'\n nn^nn\n</pre>\n\n\n  <script>\n    mouse.tabIndex = 0;\n    \n    mouse.onclick = function() {\n      this.style.left = this.getBoundingClientRect().left + 'px';\n      this.style.top = this.getBoundingClientRect().top + 'px';\n\n      this.style.position = 'fixed';\n    };\n\n\n    mouse.onkeydown = function(e) {\n      switch (e.key) {\n        case 'ArrowLeft':\n          this.style.left = parseInt(this.style.left) - this.offsetWidth + 'px';\n          return false;\n        case 'ArrowUp':\n          this.style.top = parseInt(this.style.top) - this.offsetHeight + 'px';\n          return false;\n        case 'ArrowRight':\n          this.style.left = parseInt(this.style.left) + this.offsetWidth + 'px';\n          return false;\n        case 'ArrowDown':\n          this.style.top = parseInt(this.style.top) + this.offsetHeight + 'px';\n          return false;\n      }\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    #mouse {\n      display: inline-block;\n      cursor: pointer;\n      margin: 0;\n    }\n\n    #mouse:focus {\n      outline: 1px dashed black;\n    }\n  </style>\n</head>\n\n<body>\n\n  <p>Click on the mouse and move it with arrow keys.</p>\n\n  <pre id=\"mouse\">\n _   _\n(q\\_/p)\n /. .\\\n=\\_t_/=   __\n /   \\   (\n((   ))   )\n/\\) (/\\  /\n\\  Y  /-'\n nn^nn\n</pre>\n\n\n  <script>\n    // ...votre code...\n  </script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md",
    "content": "importance: 4\n\n---\n\n# Souris dirigée par le clavier\n\nMettre le focus sur la souris. Puis utiliser les touches flèches pour la déplacer:\n\n[demo src=\"solution\"]\n\nP.S. Ne mettez de gestionnaire d'évènement que sur l'élément `#mouse`.\n\nP.P.S. Ne modifiez pas le HTML/CSS, l'approche devrait être générique et fonctionner avec n'importe quel élément.\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/article.md",
    "content": "# Focus: focus/blur\n\nUn élément reçois le focus quand l'utilisateur clique dessus ou utilise la touche `key:Tab` du clavier. Il y a aussi un attribut HTML `autofocus` qui met le focus sur un élément par défaut lorsque la page charge et d'autres moyens d'obtenir un focus sont utilisées.\n\nAvoir le focus sur un élément signifie généralement: \"préparez-vous à accepter des données ici\", c'est donc le moment où nous pouvons exécuter le code pour initialiser la fonctionnalité requise.\n\nLe moment où le focus est perdu (\"blur\") peut être encore plus important. C'est quand l'utilisateur clique ailleurs ou appuie sur `key:Tab`, ou d'autre moyen, pour aller au champs de formulaire suivant.\n\nPerdre le focus signifie généralement: \"la donnée a été entrée\", nous pouvons donc exécuter le code pour la vérifier ou même pour la sauvegarder sur le serveur etc.\n\nIl y a d'importantes particularités lorsque l'on travaille avec les événements de focsu. Nous ferons de notre mieux pour les couvrir plus loin.\n\n## Évènements focus/blur\n\nL'évènement `focus` est appelé lors du focus, et `blur` -- lorsque l'élément perds le focus\n\nUtilisons les pour la validation d'un champ de saisie.\n\nDans l'example ci-dessous:\n\n- Le gestionnaire `blur` vérifie si l'adresse mail est entrée, et sinon -- affiche une erreur.\n- Le gestionnaire `focus` masque le message d'erreur (au moment de `blur` le champ sera vérifié à nouveau):\n\n```html run autorun height=60\n<style>\n  .invalid { border-color: red; }\n  #error { color: red }\n</style>\n\nEntrez votre email: <input type=\"email\" id=\"input\">\n\n<div id=\"error\"></div>\n\n<script>\n*!*input.onblur*/!* = function() {\n  if (!input.value.includes('@')) { // pas une adresse email\n    input.classList.add('invalid');\n    error.innerHTML = 'Veuillez entrer une adresse email valide.'\n  }\n};\n\n*!*input.onfocus*/!* = function() {\n  if (this.classList.contains('invalid')) {\n    // retire l'erreur pour que l'utilisateur puisse entrer une nouvelle valeur\n    this.classList.remove('invalid');\n    error.innerHTML = \"\";\n  }\n};\n</script>\n```\n\nLe HTML moderne nous permet de faire plusieurs validations en utilisant les attributs de champ de saisie: `required`, `pattern`, etc. Et parfois nous n'avons pas besoin de plus. JavaScript peut être utilisé lorsque nous avons besoin de plus de flexibilité. Nous pourrions aussi automatiquement envoyer les données modifiées sur le serveur si elles sont correctes.\n\n\n## Méthodes focus/blur\n\nLes méthodes `elem.focus()` et `elem.blur()` mettent/retirent le focus d'un élément.\n\nPar exemple, rendons impossible pour le visiteur de quitter le champ de saisie si la valeur est invalide:\n\n```html run autorun height=80\n<style>\n  .error {\n    background: red;\n  }\n</style>\n\nEntrez votre email: <input type=\"email\" id=\"input\">\n<input type=\"text\" style=\"width:220px\" placeholder=\"entrez une adresse email invalide et essayez de mettre le focus sur ce champ\">\n\n<script>\n  input.onblur = function() {\n    if (!this.value.includes('@')) { // pas une adresse email\n      // affiche l'erreur\n      this.classList.add(\"error\");\n*!*\n      // ...et remet le focus\n      input.focus();\n*/!*\n    } else {\n      this.classList.remove(\"error\");\n    }\n  };\n</script>\n```\n\nCela fonctionne sur tous les navigateurs à l'exception de Firefox ([bug](https://bugzilla.mozilla.org/show_bug.cgi?id=53579)).\n\nSi nous entrons quelque chose dans le champ de saisie puis essayons de `key:Tab` ou de cliquer en dehors du `<input>`, alors `onblur` remet le focus.\n\nVeuillez noter que nous ne pouvons pas \"empêcher la perte de focus\" en appelant `event.preventDefault()` dans `onblur`, parce que `onblur` fonctionne *après* que l'élément ait perdu le focus.\n\n```warn header=\"Perte de focus initiée par le JavaScript\"\nUne perte de focus peut avoir lieu pour plusieurs raisons.\n\nL'une d'elle est lorsque le visiteur clique ailleurs. Mais JavaScript peut aussi la causer, par exemple:\n\n- Une `alert` déplace le focus sur elle-même, cela cause donc la perte de focus sur l'élément (évènement `blur`), et lorsque l'`alert` est fermée le focus reviens (évènement `focus`).\n- Si un élément est retiré du DOM, alors il entraine aussi la perte de focus. S'il est ré-ajouté plus tard le focus ne reviens pas.\n\nCes fonctionnalités amènent parfois les gestionnaires `focus/blur` à mal se conduire -- se déclencher lorsque l'on n'en a pas besoin.\n\nLa meilleure recette est de faire attention lors de l'utilisation de ces événements. Si nous voulons suivre des pertes de focus initiées par l'utilisateur, alors nous devrions éviter de les causer nous-même.\n```\n## Permettre de se focus sur n'importe quel élément: tabindex\n\nPar défaut beaucoup d'éléments ne supportent pas le focus.\n\nLa liste change un peu selon les navigateurs, mais une chose est toujours vrai: le support de `focus/blur` est garanti pour les éléments avec lesquels le visiteur peut interagir: `<button>`, `<input>`, `<select>`, `<a>`, etc.\n\nD'un autre côté, les éléments qui existent pour mettre quelque chose en forme, comme `<div>`, `<span>`, `<table>` -- ne peuvent pas recevoir de focus par défaut. La méthode `elem.focus()` ne fonctionne pas sur eux, et les évènements `focus/blur` ne sont jamais déclenchés.\n\nCela peut être changé en utilisant l'attribut HTML `tabindex`.\n\nN'importe quel élément peut recevoir le focus s'il a `tabindex`. La valeur de l'attribut est l'ordre de l'élément lorsque `key:Tab` (ou quelque chose dans le genre) est utilisé pour changer d'élément.\n\nC'est-à-dire: si nous avons deux éléments, le premier ayant `tabindex=\"1\"`, et le deuxième ayant `tabindex=\"2\"`, alors appuyer sur `key:Tab` en étant sur le premier élément -- déplace le focus sur le deuxième.\n\nL'ordre de changement est: les éléments avec `tabindex` à `1` et plus sont en premier (dans l'ordre des `tabindex`), puis les éléments sans `tabindex` (ex. un `<input>` régulier).\n\nLes éléments sans `tabindex` correspondant sont basculés dans l'ordre source du document (l'ordre par défaut).\n\nIl y a deux valeurs spéciales:\n\n- `tabindex=\"0\"` met l'élément parmi ceux qui ont `tabindex`. C'est-à-dire, lorsque l'on change d'élément, les éléments avec `tabindex=0` vont après ceux avec `tabindex ≥ 1`.\n\n    Généralement c'est utilisé pour permettre à un élément d'obtenir le focus tout en gardant l'ordre de changement par défaut. Pour intégrer un élément dans le formulaire de la même  manière qu'un `<input>`.\n\n- `tabindex=\"-1\"` permet seulement le focus par programmation. La touche `key:Tab` ignore ces éléments, mais la méthode `elem.focus()` fonctionne.\n\nPar exemple, voici une liste. Cliquez sur le premier élément et appuyez sur `key:Tab`:\n\n```html autorun no-beautify\nCliquez sur le premier élément et appuyez sur Tabulation. Suivez l'ordre. Veuillez noter que plusieurs Tabulations à la suite peuvent déplacer le focus en dehors de l'iframe avec l'exemple.\n<ul>\n  <li tabindex=\"1\">Un</li>\n  <li tabindex=\"0\">Zéro</li>\n  <li tabindex=\"2\">Deux</li>\n  <li tabindex=\"-1\">Moins un</li>\n</ul>\n\n<style>\n  li { cursor: pointer; }\n  :focus { outline: 1px dashed green; }\n</style>\n```\n\nL'ordre est comme ceci: `1 - 2 - 0`. Normalement, `<li>` ne supporte pas le focus, mais `tabindex` l'active, avec les évènements et le pseudo-sélecteur CSS `:focus`.\n\n```smart header=\"La propriété `elem.tabIndex` fonctionne aussi\"\nNous pouvons ajouter `tabindex` depuis le JavaScript en utilisant la propriété `elem.tabIndex`. Cela a le même effet.\n```\n\n## Délégation: focusin/focusout\n\nLes évènements `focus` et `blur` ne bubble pas.\n\nPar exemple, nous ne pouvont pas ajouter `onfocus` sur le `<form>` pour le mettre en avant, comme ceci:\n\n```html autorun height=80\n<!-- lors du focus sur le formulaire -- ajouter la classe -->\n<form *!*onfocus=\"this.className='focused'\"*/!*>\n  <input type=\"text\" name=\"name\" value=\"Name\">\n  <input type=\"text\" name=\"surname\" value=\"Surname\">\n</form>\n\n<style> .focused { outline: 1px solid red; } </style>\n```\n\nL'exemple ci-dessus ne fonctionne pas, parce que lorsqu'un un user se focus sur un `<input>`, l'évènement `focus` ne se déclenche que sur ce champ de saisie. Il ne bubble pas. Donc `form.onfocus` ne se déclenche jamais.\n\nIl y a deux solutions.\n\nPremièrement, il y a une fonctionnalité historique: `focus/blur` ne bubble pas, mais il se propage durant la phase de capture.\n\nCela fonctionnera:\n\n```html autorun height=80\n<form id=\"form\">\n  <input type=\"text\" name=\"name\" value=\"Name\">\n  <input type=\"text\" name=\"surname\" value=\"Surname\">\n</form>\n\n<style> .focused { outline: 1px solid red; } </style>\n\n<script>\n*!*\n  // ajouter le gestionnaire à la phase de capture (dernier argument true)\n  form.addEventListener(\"focus\", () => form.classList.add('focused'), true);\n  form.addEventListener(\"blur\", () => form.classList.remove('focused'), true);\n*/!*\n</script>\n```\n\nSecondement, il y a les évènements `focusin` et `focusout` -- exactement les mêmes que `focus/blur`, mais ils bubble.\n\nNotez qu'ils doivent être assignés en utilisant `elem.addEventListener`, pas `on<event>`.\n\nVoici donc une autre variante qui fonctionne:\n\n```html autorun height=80\n<form id=\"form\">\n  <input type=\"text\" name=\"name\" value=\"Name\">\n  <input type=\"text\" name=\"surname\" value=\"Surname\">\n</form>\n\n<style> .focused { outline: 1px solid red; } </style>\n\n<script>\n*!*\n  form.addEventListener(\"focusin\", () => form.classList.add('focused'));\n  form.addEventListener(\"focusout\", () => form.classList.remove('focused'));\n*/!*\n</script>\n```\n\n## Résumé\n\nLes évènements `focus` et `blur` se déclenchent lors du focus/perte de focus sur un élément.\n\nLeur particularités sont:\n\n- Ils ne bubble pas. Possibilité d'utiliser la capture au lieu de `focusin/focusout`.\n- La plupart des éléments ne supportent pas le focus par défaut. Utilisez `tabindex` pour permettre à n'importe quoi d'avoir le focus.\n\nL'élément ayant actuellement le focus est disponible en tant que `document.activeElement`.\n"
  },
  {
    "path": "2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    td select,\n    td input {\n      width: 150px;\n    }\n\n    #diagram td {\n      vertical-align: bottom;\n      text-align: center;\n      padding: 10px;\n    }\n\n    #diagram div {\n      margin: auto;\n    }\n  </style>\n</head>\n\n<body>\n\n  Deposit calculator.\n\n  <form name=\"calculator\">\n    <table>\n      <tr>\n        <td>Initial deposit</td>\n        <td>\n          <input name=\"money\" type=\"number\" value=\"10000\" required>\n        </td>\n      </tr>\n      <tr>\n        <td>How many months?</td>\n        <td>\n          <select name=\"months\">\n            <option value=\"3\">3 (minimum)</option>\n            <option value=\"6\">6 (half-year)</option>\n            <option value=\"12\" selected>12 (one year)</option>\n            <option value=\"18\">18 (1.5 years)</option>\n            <option value=\"24\">24 (2 years)</option>\n            <option value=\"30\">30 (2.5 years)</option>\n            <option value=\"36\">36 (3 years)</option>\n            <option value=\"60\">60 (5   years)</option>\n          </select>\n        </td>\n      </tr>\n      <tr>\n        <td>Interest per year?</td>\n        <td>\n          <input name=\"interest\" type=\"number\" value=\"5\" required>\n        </td>\n      </tr>\n    </table>\n\n\n  </form>\n\n\n  <table id=\"diagram\">\n    <tr>\n      <th>Was:</th>\n      <th>Becomes:</th>\n    </tr>\n    <tr>\n      <th id=\"money-before\"></th>\n      <th id=\"money-after\"></th>\n    </tr>\n    <td>\n      <div style=\"background: red;width:40px;height:100px\"></div>\n    </td>\n    <td>\n      <div style=\"background: green;width:40px;height:0\" id=\"height-after\"></div>\n    </td>\n  </table>\n\n  <script>\n\n    let form = document.forms.calculator;\n\n    form.money.oninput = calculate;\n    form.months.onchange = calculate;\n    form.interest.oninput = calculate;\n\n    function calculate() {\n      let initial = +form.money.value;\n      if (!initial) return;\n\n      let interest = form.interest.value * 0.01;\n\n      if (!interest) return;\n\n      let years = form.months.value / 12;\n      if (!years) return;\n\n      let result = Math.round(initial * (1 + interest) ** years);\n\n      let height = result / form.money.value * 100 + 'px';\n      document.getElementById('height-after').style.height = height;\n      document.getElementById('money-before').innerHTML = form.money.value;\n      document.getElementById('money-after').innerHTML = result;\n    }\n\n    calculate();\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    td select,\n    td input {\n      width: 150px;\n    }\n\n    #diagram td {\n      vertical-align: bottom;\n      text-align: center;\n      padding: 10px;\n    }\n\n    #diagram div {\n      margin: auto;\n    }\n  </style>\n</head>\n\n<body>\n\n  Deposit calculator.\n\n  <form name=\"calculator\">\n    <table>\n      <tr>\n        <td>Initial deposit</td>\n        <td>\n          <input name=\"money\" type=\"number\" value=\"10000\" required>\n        </td>\n      </tr>\n      <tr>\n        <td>How many months?</td>\n        <td>\n          <select name=\"months\">\n            <option value=\"3\">3 (minimum)</option>\n            <option value=\"6\">6 (half-year)</option>\n            <option value=\"12\" selected>12 (one year)</option>\n            <option value=\"18\">18 (1.5 years)</option>\n            <option value=\"24\">24 (2 years)</option>\n            <option value=\"30\">30 (2.5 years)</option>\n            <option value=\"36\">36 (3 years)</option>\n            <option value=\"60\">60 (5   years)</option>\n          </select>\n        </td>\n      </tr>\n      <tr>\n        <td>Interest per year?</td>\n        <td>\n          <input name=\"interest\" type=\"number\" value=\"5\" required>\n        </td>\n      </tr>\n    </table>\n\n\n  </form>\n\n\n  <table id=\"diagram\">\n    <tr>\n      <th>Was:</th>\n      <th>Becomes:</th>\n    </tr>\n    <tr>\n      <th id=\"money-before\"></th>\n      <th id=\"money-after\"></th>\n    </tr>\n    <td>\n      <div style=\"background: red;width:40px;height:100px\"></div>\n    </td>\n    <td>\n      <div style=\"background: green;width:40px;height:0\" id=\"height-after\"></div>\n    </td>\n  </table>\n\n  <script>\n\n    let form = document.forms.calculator;\n\n    // your code\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/task.md",
    "content": "importance: 5\n\n---\n\n# Deposit calculator\n\nCreate an interface that allows to enter a sum of bank deposit and percentage, then calculates how much it will be after given periods of time.\n\nHere's the demo:\n\n[iframe src=\"solution\" height=\"350\" border=\"1\"]\n\nAny input change should be processed immediately.\n\nThe formula is:\n```js\n// initial: the initial money sum\n// interest: e.g. 0.05 means 5% per year\n// years: how many years to wait\nlet result = Math.round(initial * (1 + interest) ** years);\n```\n"
  },
  {
    "path": "2-ui/4-forms-controls/3-events-change-input/article.md",
    "content": "# Les événements: change, input, cut, copy, paste\n\nDécouvrons différents événements qui accompagnent les mises à jour des données.\n\n## Événement: change\n\nL'événement `change` se déclenche lorsque le changement de la valeur de l'élément a fini de se réaliser.\n\nPour les éléments `input` de type `text` cela signifie que l'événement se produit lorsqu'ils perdent le focus.\n\nPar exemple, pendant que nous saisissons dans le champ de texte ci-dessous -- il n'y a aucun événement. Mais lorsque nous déplaçons le focus ailleurs, par exemple, en cliquant sur un bouton -- il y aura un événement `change`:\n\n```html autorun height=40 run\n<input type=\"text\" onchange=\"alert(this.value)\">\n<input type=\"button\" value=\"Button\">\n```\n\nPour les autres éléments: `select`, `input type=checkbox/radio` il se déclenche juste après les changements de sélection:\n\n```html autorun height=40 run\n<select onchange=\"alert(this.value)\">\n  <option value=\"\">Select something</option>\n  <option value=\"1\">Option 1</option>\n  <option value=\"2\">Option 2</option>\n  <option value=\"3\">Option 3</option>\n</select>\n```\n\n\n## Événement: input\n\nL'événement `input` se déclenche à chaque fois qu'une valeur est modifiée par l'utilisateur.\n\nContrairement aux événements liés au clavier, il se déclenche sur toute modification de valeur, même celles qui n'impliquent pas d'actions du clavier: coller avec une souris ou utiliser la reconnaissance vocale pour dicter le texte.\n\nPar exemple:\n\n```html autorun height=40 run\n<input type=\"text\" id=\"input\"> oninput: <span id=\"result\"></span>\n<script>\n  input.oninput = function() {\n    result.innerHTML = input.value;\n  };\n</script>\n```\n\nSi nous voulons gérer chaque modification d'un `<input>` alors cet événement est le meilleur choix.\n\nD'un autre coté, l'événement `input` ne se déclenche pas lors de la saisie au clavier et d'autres actions qui n'impliquent de changement de valeur, par ex. en appuyant sur les touches fléchées `touche:⇦` `touche:⇨` pendant la saisie.\n\n```\nsmart header=\"Impossible d'empêcher quoi que ce soit dans `oninput`\"\nL'événement `input` se produit après la modification de la valeur.\n\nNous ne pouvons donc pas utiliser `event.preventDefault()` - c'est trop tard, il n'y aurait aucun effet.\n```\n\n## Événements: cut, copy, paste\n\nCes événements se produisent lors de la coupe/copie/collage d'une valeur.\n\nIls appartiennent à la classe [ClipboardEvent](https://www.w3.org/TR/clipboard-apis/#clipboard-event-interfaces) et permettent d'accéder aux données coupées/copiées/collées.\n\nNous pouvons également utiliser `event.preventDefault()` pour interrompre l'action, puis rien n'est copié/collé.\n\nPar exemple, le code ci-dessous empêche tous ces événements `cut/copy/paste` et montre ce que nous essayons de couper/copier/coller:\n\n```html autorun height=40 run\n<input type=\"text\" id=\"input\">\n<script>\n  input.onpaste = function(event) {\n    alert(\"paste: \" + event.clipboardData.getData('text/plain'));\n    event.preventDefault();\n  };\n\n  input.oncut = input.oncopy = function(event) {\n    alert(event.type + '-' + document.getSelection());\n    event.preventDefault();\n  };\n</script>\n```\n\nRemarque : à l'intérieur des gestionnaires d'événements `cut` et `copy`, un appel à `event.clipboardData.getData(...)` renvoie une chaîne vide. C'est parce que techniquement, les données ne sont pas encore dans le presse-papiers. Si nous utilisons `event.preventDefault()`, il ne sera pas du tout copié.\n\nAinsi, l'exemple ci-dessus utilise `document.getSelection()` pour obtenir le texte sélectionné. Vous pouvez trouver plus de détails sur la sélection de documents dans l'article <info:selection-range>.\n\nIl est possible de copier/coller pas seulement du texte, mais tout. Par exemple, nous pouvons copier un fichier dans le gestionnaire de fichiers du système d'exploitation et le coller.\n\nC'est parce que `clipboardData` implémente l'interface `DataTransfer`, couramment utilisée pour le glisser-déposer et le copier/coller. C'est un peu hors de notre portée maintenant, mais vous pouvez trouver ses méthodes dans la [spécification DataTransfer](https://html.spec.whatwg.org/multipage/dnd.html#the-datatransfer-interface).\n\nEn outre, il existe une API asynchrone supplémentaire pour accéder au presse-papiers : `navigator.clipboard`. Plus d'informations à ce sujet dans la spécification [Clipboard API and events](https://www.w3.org/TR/clipboard-apis/), [non pris en charge par Firefox](https://caniuse.com/async-clipboard).\n\n### Restrictions de sécurité\n\nLe presse-papiers est une chose \"globale\" au niveau du système d'exploitation. Un utilisateur peut basculer entre différentes applications, copier/coller différentes choses, et une page de navigateur ne devrait pas voir tout cela.\n\nAinsi, la plupart des navigateurs permettent un accès en lecture/écriture transparent au presse-papiers uniquement dans le cadre de certaines actions de l'utilisateur, telles que le copier/coller, etc.\n\nIl est interdit de générer des événements de presse-papiers \"personnalisés\" avec `dispatchEvent` dans tous les navigateurs sauf Firefox. Et même si nous parvenons à envoyer un tel événement, la spécification indique clairement que de tels événements \"synthétiques\" ne doivent pas donner accès au presse-papiers.\n\nMême si quelqu'un décide d'enregistrer `event.clipboardData` dans un gestionnaire d'événements, puis d'y accéder plus tard, cela ne fonctionnera pas.\n\nPour réitérer, [event.clipboardData](https://www.w3.org/TR/clipboard-apis/#clipboardevent-clipboarddata) fonctionne uniquement dans le contexte des gestionnaires d'événements initiés par l'utilisateur.\n\nD'autre part, [navigator.clipboard](https://www.w3.org/TR/clipboard-apis/#h-navigator-clipboard) est l'API la plus récente, destinée à être utilisée dans n'importe quel contexte. Elle demande l'autorisation de l'utilisateur, si nécessaire. \n\n## Récapitulatif\n\nÉvénements de changement de données:\n\n| Événement | Description | Specials |\n|---------|----------|-------------|\n| `change`| Une valeur a été modifiée | Pour les entrées de texte, les déclencheurs sont sur la perte de mise au point. |\n| `input` | Pour les champs textes à chaque modification. | Se déclenche immédiatement contrairement à `change`. |\n| `cut/copy/paste` | Les actions couper/copier/coller. | L'action peut être empêchée. La propiété `event.clipboardData` donne un accès en lecture/écriture au presse-papiers. Tous les navigateurs, à l'exception de Firefox, prennent également en charge `navigator.clipboard`. |\n"
  },
  {
    "path": "2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/solution.md",
    "content": "Une fenêtre modale peut être implémentée en utilisant un `<div id=\"cover-div\">` semi-transparent qui couvre toute la fenêtre, comme ceci:\n\n```css\n#cover-div {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 9000;\n  width: 100%;\n  height: 100%;\n  background-color: gray;\n  opacity: 0.3;\n}\n```\n\nParce que la `<div>` couvre tout, il obtient tous les clics, pas la page en dessous.\n\nNous pouvons également empêcher le défilement de la page en définissant `body.style.overflowY ='hidden'`.\n\nLe formulaire ne doit pas être dans`<div>`, mais à côté, car nous ne voulons pas qu'il ait `opacity`.\n"
  },
  {
    "path": "2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body style=\"height:3000px\">\n\n  <h2>Click the button below</h2>\n\n  <input type=\"button\" value=\"Click to show the form\" id=\"show-button\">\n\n\n  <div id=\"prompt-form-container\">\n    <form id=\"prompt-form\">\n      <div id=\"prompt-message\"></div>\n      <input name=\"text\" type=\"text\">\n      <input type=\"submit\" value=\"Ok\">\n      <input type=\"button\" name=\"cancel\" value=\"Cancel\">\n    </form>\n  </div>\n\n  <script>\n    // Show a half-transparent DIV to \"shadow\" the page\n    // (the form is not inside, but near it, because it shouldn't be half-transparent)\n    function showCover() {\n      let coverDiv = document.createElement('div');\n      coverDiv.id = 'cover-div';\n\n      // make the page unscrollable while the modal form is open\n      document.body.style.overflowY = 'hidden';\n\n      document.body.append(coverDiv);\n    }\n\n    function hideCover() {\n      document.getElementById('cover-div').remove();\n      document.body.style.overflowY = '';\n    }\n\n    function showPrompt(text, callback) {\n      showCover();\n      let form = document.getElementById('prompt-form');\n      let container = document.getElementById('prompt-form-container');\n      document.getElementById('prompt-message').innerHTML = text;\n      form.text.value = '';\n\n      function complete(value) {\n        hideCover();\n        container.style.display = 'none';\n        document.onkeydown = null;\n        callback(value);\n      }\n\n      form.onsubmit = function() {\n        let value = form.text.value;\n        if (value == '') return false; // ignore empty submit\n\n        complete(value);\n        return false;\n      };\n\n      form.cancel.onclick = function() {\n        complete(null);\n      };\n\n      document.onkeydown = function(e) {\n        if (e.key == 'Escape') {\n          complete(null);\n        }\n      };\n\n      let lastElem = form.elements[form.elements.length - 1];\n      let firstElem = form.elements[0];\n\n      lastElem.onkeydown = function(e) {\n        if (e.key == 'Tab' && !e.shiftKey) {\n          firstElem.focus();\n          return false;\n        }\n      };\n\n      firstElem.onkeydown = function(e) {\n        if (e.key == 'Tab' && e.shiftKey) {\n          lastElem.focus();\n          return false;\n        }\n      };\n\n      container.style.display = 'block';\n      form.elements.text.focus();\n    }\n\n    document.getElementById('show-button').onclick = function() {\n      showPrompt(\"Enter something<br>...smart :)\", function(value) {\n        alert(\"You entered: \" + value);\n      });\n    };\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/solution.view/style.css",
    "content": "html,\nbody {\n  margin: 0;\n  padding: 0;\n  width: 100%;\n  height: 100%;\n}\n\n#prompt-form {\n  display: inline-block;\n  padding: 5px 5px 5px 70px;\n  width: 200px;\n  border: 1px solid black;\n  background: white url(https://en.js.cx/clipart/prompt.png) no-repeat left 5px;\n  vertical-align: middle;\n}\n\n#prompt-form-container {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 9999;\n  display: none;\n  width: 100%;\n  height: 100%;\n  text-align: center;\n}\n\n#prompt-form-container:before {\n  display: inline-block;\n  height: 100%;\n  content: '';\n  vertical-align: middle;\n}\n\n#cover-div {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 9000;\n  width: 100%;\n  height: 100%;\n  background-color: gray;\n  opacity: 0.3;\n}\n\n#prompt-form input[name=\"text\"] {\n  display: block;\n  margin: 5px;\n  width: 180px;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n\n  <div id=\"prompt-form-container\">\n    <form id=\"prompt-form\">\n      <div id=\"prompt-message\">Enter something...\n        <br>Please..</div>\n      <input name=\"text\" type=\"text\">\n      <input type=\"submit\" value=\"Ok\">\n      <input type=\"button\" name=\"cancel\" value=\"Cancel\">\n    </form>\n  </div>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/source.view/style.css",
    "content": "html,\nbody {\n  width: 100%;\n  height: 100%;\n  padding: 0;\n  margin: 0;\n}\n\n#prompt-form {\n  display: inline-block;\n  padding: 5px 5px 5px 70px;\n  width: 200px;\n  border: 1px solid black;\n  background: white url(https://en.js.cx/clipart/prompt.png) no-repeat left 5px;\n  vertical-align: middle;\n}\n\n#prompt-form-container {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 9999;\n  width: 100%;\n  height: 100%;\n  text-align: center;\n}\n\n#prompt-form-container:before {\n  display: inline-block;\n  height: 100%;\n  content: '';\n  vertical-align: middle;\n}\n\n#prompt-form input[name=\"text\"] {\n  display: block;\n  margin: 5px;\n  width: 180px;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/task.md",
    "content": "importance: 5\n\n---\n\n# Formulaire modal\n\nCréez une fonction `showPrompt(html, callback)` qui montre un formulaire avec le message `html`, un champ de saisie et des boutons `OK/CANCEL`.\n\n- Un utilisateur doit taper quelque chose dans un champ de texte et appuyer sur `key:Enter` ou sur le bouton OK, puis `callback(value)` est appelé avec la valeur saisie.\n- Sinon, si l'utilisateur appuie sur `key:Esc` ou CANCEL, alors `callback(null)` est appelé.\n\nDans les deux cas, cela met fin au processus de saisie et supprime le formulaire.\n\nConditions:\n\n- Le formulaire doit être au centre de la fenêtre.\n- Le formulaire est *modal*. En d'autres termes, aucune interaction avec le reste de la page n'est possible tant que l'utilisateur ne la ferme pas.\n- Lorsque le formulaire est affiché, le focus doit être à l'intérieur de `<input>` pour l'utilisateur.\n- Les touches `key:Tab`/`key:Shift+Tab` devraient déplacer le focus entre les champs du formulaire, ne pas lui permettre de partir pour d'autres éléments de la page.\n\nExemple d'utilisation:\n\n```js\nshowPrompt(\"Enter something<br>...smart :)\", function(value) {\n  alert(value);\n});\n```\n\nUne démo dans l'iframe:\n\n[iframe src=\"solution\" height=160 border=1]\n\nP.S. Le document source contient HTML/CSS pour le formulaire avec un positionnement fixe, mais c'est à vous de le rendre modal.\n"
  },
  {
    "path": "2-ui/4-forms-controls/4-forms-submit/article.md",
    "content": "# Formulaires: l'événement et la méthode \"submit\"\n\nL'événement `submit` se déclenche lorsque le formulaire est soumis, il est généralement utilisé pour valider le formulaire avant de l'envoyer au serveur ou pour abandonner la soumission et la traiter en JavaScript.\n\nLa méthode `form.submit()` permet de lancer l'envoi de formulaire depuis JavaScript. Nous pouvons l'utiliser pour créer et envoyer dynamiquement nos propres formulaires au serveur.\n\nVoyons-les plus en détail.\n\n## Évènement: submit\n\n1. Le premier - cliquer sur `<input type=\"submit\">` ou `<input type=\"image\">`.\n2. La seconde - appuyez sur `key:Enter` dans un champ de saisie.\n\nLes deux actions mènent à l'événement `submit` sur le formulaire. Le gestionnaire peut vérifier les données, et s'il y a des erreurs, les afficher et appeler `event.preventDefault()`, alors le formulaire ne sera pas envoyé au serveur.\n\nDans le formulaire ci-dessous:\n1. Allez dans le champ de texte et appuyez sur `key:Enter`.\n2. Cliquez sur `<input type =\"submit\">`.\n\nLes deux actions affichent `alert` et le formulaire n'est envoyé nulle part en raison de `return false`:\n\n```html autorun height=60 no-beautify\n<form onsubmit=\"alert('submit!');return false\">\n  First: Enter in the input field <input type=\"text\" value=\"text\"><br>\n  Second: Click \"submit\": <input type=\"submit\" value=\"Submit\">\n</form>\n```\n\n````smart header=\"Relation entre `submit` et `click`\"\nLorsqu'un formulaire est envoyé en utilisant `key:Enter` sur un champ de saisie, un événement `click` se déclenche sur `<input type=\"submit\">`.\n\nC'est plutôt drôle, car il n'y a pas eu de clic du tout.\n\nVoici la démo:\n```html autorun height=60\n<form onsubmit=\"return false\">\n <input type=\"text\" size=\"30\" value=\"Focus here and press enter\">\n <input type=\"submit\" value=\"Submit\" *!*onclick=\"alert('click')\"*/!*>\n</form>\n```\n\n````\n\n## Méthode: submit\n\nPour soumettre manuellement un formulaire au serveur, nous pouvons appeler `form.submit()`.\n\nEnsuite, l'événement `submit` n'est pas généré. On suppose que si le programmeur appelle `form.submit()`, alors le script a déjà effectué tous les traitements associés.\n\nParfois, cela est utilisé pour créer et envoyer manuellement un formulaire, comme ceci:\n\n```js run\nlet form = document.createElement('form');\nform.action = 'https://google.com/search';\nform.method = 'GET';\n\nform.innerHTML = '<input name=\"q\" value=\"test\">';\n\n// le formulaire doit être dans le document pour le soumettre\ndocument.body.append(form);\n\nform.submit();\n```\n"
  },
  {
    "path": "2-ui/4-forms-controls/index.md",
    "content": "# Forms, controls\n\nSpecial properties and events for forms `<form>` and controls: `<input>`, `<select>` and other.\n"
  },
  {
    "path": "2-ui/5-loading/01-onload-ondomcontentloaded/article.md",
    "content": "# Page: DOMContentLoaded, load, beforeunload, unload\n\nLe cycle de vie d'une page HTML comporte trois événements importants:\n\n- `DOMContentLoaded` -- le navigateur a complètement chargé le HTML et l'arborescence DOM est construite, mais les ressources externes telles que les images `<img>` et les feuilles de style peuvent ne pas être encore chargées.\n- `load` -- non seulement le HTML est chargé, mais également toutes les ressources externes : images, styles, etc.\n- `beforeunload/unload` -- l'utilisateur quitte la page.\n\n\nChaque événement peut être utile:\n\n- `DOMContentLoaded` événement -- DOM est prêt, le gestionnaire peut donc rechercher des nœuds DOM, initialiser l'interface.\n- `load` événement -- les ressources externes sont chargées, donc les styles sont appliqués, les tailles d'image sont connues, etc.\n- `beforeunload` événement -- l'utilisateur quitte: nous pouvons vérifier si l'utilisateur a enregistré les modifications et leur demander s'ils veulent vraiment partir.\n- `unload` -- l'utilisateur est presque parti, mais nous pouvons toujours lancer certaines opérations, comme l'envoi de statistiques.\n\nExplorons les détails de ces événements.\n\n## DOMContentLoaded\n\nL'événement `DOMContentLoaded` se produit sur l'objet `document`.\n\nNous devons utiliser `addEventListener` pour l'attraper:\n\n```js\ndocument.addEventListener(\"DOMContentLoaded\", ready);\n// pas \"document.onDOMContentLoaded = ...\"\n```\n\nPar exemple:\n\n```html run height=200 refresh\n<script>\n  function ready() {\n    alert('DOM is ready');\n\n\n    // l'image n'est pas encore chargée (sauf si elle a été mise en cache), donc la taille est 0x0\n\n    alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);\n  }\n\n*!*\n  document.addEventListener(\"DOMContentLoaded\", ready);\n*/!*\n</script>\n\n<img id=\"img\" src=\"https://en.js.cx/clipart/train.gif?speed=1&cache=0\">\n```\n\nDans l'exemple, le gestionnaire `DOMContentLoaded` s'exécute lorsque le document est chargé, afin qu'il puisse voir tous les éléments, y compris `<img>` dessous.\n\nMais il n'attend pas que l'image se charge. Ainsi, `alert` n'affiche aucune taille.\n\nÀ première vue, l'événement `DOMContentLoaded` est très simple. L'arbre DOM est prêt -- voici l'événement. Il y a cependant quelques particularités.\n\n### DOMContentLoaded et les scripts\n\nLorsque le navigateur traite un document HTML et rencontre une balise `<script>`, il doit s'exécuter avant de continuer à construire le DOM. C'est une précaution, car les scripts peuvent vouloir modifier DOM, avec `document.write` par exemple, donc `DOMContentLoaded` doit attendre.\n\nDonc DOMContentLoaded se produit définitivement après de tels scripts:\n\n```html run\n<script>\n  document.addEventListener(\"DOMContentLoaded\", () => {\n    alert(\"DOM ready!\");\n  });\n</script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js\"></script>\n\n<script>\n  alert(\"Library loaded, inline script executed\");\n</script>\n```\n\nDans l'exemple ci-dessus, nous voyons d'abord \"Library loaded...\", puis \"DOM ready!\" (tous les scripts sont exécutés).\n\n```warn header=\"Les scripts qui ne bloquent pas DOMContentLoaded\"\nIl existe deux exceptions à cette règle:\n1. Les scripts avec l'attribut `async`, que nous aborderons [un peu plus tard](info:script-async-defer), ne bloquent pas `DOMContentLoaded`.\n2. Les scripts qui sont générés dynamiquement avec `document.createElement('script')` puis ajoutés à la page Web ne bloquent pas non plus cet événement.\n```\n\n### DOMContentLoaded et les styles\n\nLes feuilles de style externes n'affectent pas le DOM, donc `DOMContentLoaded` ne les attend pas.\n\nMais il y a une embûche. Si nous avons un script après le style, ce script doit attendre que la feuille de style se charge:\n\n```html run\n<link type=\"text/css\" rel=\"stylesheet\" href=\"style.css\">\n<script>\n  // le script ne s'exécute pas tant que la feuille de style n'est pas chargée\n  alert(getComputedStyle(document.body).marginTop);\n</script>\n```\n\nLa raison en est que le script peut souhaiter obtenir des coordonnées et d'autres propriétés d'éléments dépendant du style, comme dans l'exemple ci-dessus. Naturellement, il doit attendre le chargement des styles.\n\nComme `DOMContentLoaded` attend les scripts, il attend maintenant les styles avant eux également.\n\n### Saisie automatique intégrée du navigateur\n\nFirefox, Chrome et Opera remplissent automatiquement les formulairs sur `DOMContentLoaded`.\n\nPar exemple, si la page a un formulaire avec login et mot de passe, et que le navigateur se souvient des valeurs, alors sur `DOMContentLoaded`, il peut essayer de les remplir automatiquement (si approuvé par l'utilisateur).\n\nDonc, si `DOMContentLoaded` est reporté par des scripts à chargement long, le remplissage automatique attend également. Vous l'avez probablement vu sur certains sites (si vous utilisez le remplissage automatique du navigateur) - les champs de login/mot de passe ne sont pas remplis automatiquement immédiatement, mais il y a un délai jusqu'à ce que la page se charge complètement. C'est en fait le délai jusqu'à l'événement `DOMContentLoaded`.\n\n## window.onload [#window-onload]\n\nL'événement `load` sur l'objet `window` se déclenche lorsque la page entière est chargée, y compris les styles, images et autres ressources. Cet événement est disponible via la propriété `onload`.\n\nL'exemple ci-dessous montre correctement les tailles d'image, car `window.onload` attend toutes les images:\n\n```html run height=200 refresh\n<script>\n  window.onload = function() { // peut aussi utiliser window.addEventListener('load', (event) => {\n    alert('Page loaded');\n\n    // l'image est chargée à ce moment\n    alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);\n  };\n</script>\n\n<img id=\"img\" src=\"https://en.js.cx/clipart/train.gif?speed=1&cache=0\">\n```\n\n## window.onunload\n\nLorsqu'un visiteur quitte la page, l'événement `unload` se déclenche sur `window`. Nous pouvons y faire quelque chose qui n'implique pas de retard, comme la fermeture des fenêtres contextuelles associées.\n\nL'exception notable est l'envoi d'analyses.\n\nDisons que nous recueillons des données sur la façon dont la page est utilisée: clics de souris, défilements, zones de page visualisées, etc.\n\nNaturellement, l'événement `unload` est lorsque l'utilisateur nous quitte, et nous aimerions sauvegarder les données sur notre serveur.\n\nIl existe une méthode spéciale `navigator.sendBeacon(url, data)` pour de tels besoins, décrite dans la spécification <https://w3c.github.io/beacon/>.\n\nIl envoie les données en arrière-plan. La transition vers une autre page n'est pas retardée: le navigateur quitte la page, mais exécute toujours `sendBeacon`.\n\nVoici comment l'utiliser:\n\n```js\nlet analyticsData = { /* objet avec des données collectées */ };\n\nwindow.addEventListener(\"unload\", function() {\n  navigator.sendBeacon(\"/analytics\", JSON.stringify(analyticsData));\n});\n```\n\nLorsque la demande `sendBeacon` est terminée, le navigateur a probablement déjà quitté le document, donc il n'y a aucun moyen d'obtenir une réponse du serveur (qui est vide habituellement pour l'analyse).\n\nIl y a aussi un marqueur `keepalive` pour les demandes \"apres sortie du document\" dans [fetch](info:fetch) méthode pour les demandes de réseau génériques. Vous pouvez trouver plus d'informations dans le chapitre <info:fetch-api>.\n\n\nSi nous voulons annuler la transition vers une autre page, nous ne pouvons pas le faire ici. Mais nous pouvons utiliser un autre événement -- `onbeforeunload`.\n\n## window.onbeforeunload [#window.onbeforeunload]\n\nSi un visiteur a lancé la navigation hors de la page ou tente de fermer la fenêtre, le gestionnaire `beforeunload` demande une confirmation supplémentaire.\n\nSi nous annulons l'événement, le navigateur peut demander au visiteur s'il en est sûr.\n\nVous pouvez l'essayer en exécutant ce code, puis en rechargeant la page:\n\n```js run\nwindow.onbeforeunload = function() {\n  return false;\n};\n```\n\nPour des raisons historiques, renvoyer une chaîne non vide compte également comme une annulation de l'événement. Il y a quelque temps, les navigateurs le montraient sous forme de message, mais comme la [spécification moderne](https://html.spec.whatwg.org/#unloading-documents) le dit, ils ne devraient pas.\n\nVoici un exemple:\n\n```js run\nwindow.onbeforeunload = function() {\n  return \"There are unsaved changes. Leave now?\";\n};\n```\n\nLe comportement a été modifié, car certains webmasters ont abusé de ce gestionnaire d'événements en affichant des messages trompeurs et ennuyeux. Donc, à l'heure actuelle, les anciens navigateurs peuvent toujours l'afficher sous forme de message, mais à part cela -- il n'y a aucun moyen de personnaliser le message affiché à l'utilisateur.\n\n````warn header=\"The `event.preventDefault()` doesn't work from a `beforeunload` handler\"\nThat may sound weird, but most browsers ignore `event.preventDefault()`.\n\nWhich means, following code may not work:\n```js run\nwindow.addEventListener(\"beforeunload\", (event) => {\n  // doesn't work, so this event handler doesn't do anything\n\tevent.preventDefault();\n});\n```\n\nInstead, in such handlers one should set `event.returnValue` to a string to get the result similar to the code above:\n```js run\nwindow.addEventListener(\"beforeunload\", (event) => {\n  // works, same as returning from window.onbeforeunload\n\tevent.returnValue = \"There are unsaved changes. Leave now?\";\n});\n```\n````\n\n## readyState\n\nQue se passe-t-il si nous définissons le gestionnaire `DOMContentLoaded` après le chargement du document?\n\nNaturellement, il ne fonctionne jamais.\n\nIl y a des cas où nous ne savons pas si le document est prêt ou non. Nous aimerions que notre fonction s'exécute lorsque le DOM est chargé, que ce soit maintenant ou plus tard.\n\nLa propriété `document.readyState` nous renseigne sur l'état de chargement actuel.\n\nIl y a 3 valeurs possibles:\n\n- `\"loading\"` -- le document est en cours de chargement.\n- `\"interactif\"` -- le document a été entièrement lu.\n- `\"complete\"` -- le document a été entièrement lu et toutes les ressources (comme les images) sont également chargées.\n\nNous pouvons donc vérifier `document.readyState` et configurer un gestionnaire ou exécuter le code immédiatement s'il est prêt.\n\nComme ceci:\n\n```js\nfunction work() { /*...*/ }\n\nif (document.readyState == 'loading') {\n\n  // en cours de chargement, attendez l'événement\n\n  document.addEventListener('DOMContentLoaded', work);\n} else {\n  // DOM est prêt!\n  work();\n}\n```\n\nIl y a aussi l'événement `readystatechange` qui se déclenche lorsque l'état change, nous pouvons donc afficher tous ces états comme ceci:\n\n```js run\n// état actuel\nconsole.log(document.readyState);\n\n// afficher les changements d'état\ndocument.addEventListener('readystatechange', () => console.log(document.readyState));\n```\n\nL'événement `readystatechange` est un mécanisme alternatif de suivi de l'état de chargement du document, il est apparu il y a longtemps. De nos jours, il est rarement utilisé.\n\nVoyons le flux complet des événements pour l'exhaustivité.\n\nVoici un document avec `<iframe>`, `<img>` et des gestionnaires qui consignent les événements:\n\n```html\n<script>\n  log('initial readyState:' + document.readyState);\n\n  document.addEventListener('readystatechange', () => log('readyState:' + document.readyState));\n  document.addEventListener('DOMContentLoaded', () => log('DOMContentLoaded'));\n\n  window.onload = () => log('window onload');\n</script>\n\n<iframe src=\"iframe.html\" onload=\"log('iframe onload')\"></iframe>\n\n<img src=\"https://en.js.cx/clipart/train.gif\" id=\"img\">\n<script>\n  img.onload = () => log('img onload');\n</script>\n```\n\nUn example [dans le sandbox](sandbox:readystate).\n\nLa sortie typique:\n1. [1] readyState:loading initiale\n2. [2] readyState:interactive\n3. [2] DOMContentLoaded\n4. [3] iframe onload\n5. [4] img onload\n6. [4] readyState:complete\n7. [4] window onload\n\nLes nombres entre crochets indiquent l'heure approximative à laquelle cela se produit. Les événements étiquetés avec le même chiffre se produisent à peu près au même moment (+- quelques ms).\n\n- `document.readyState` devient `interactive` juste avant `DOMContentLoaded`. Ces deux choses signifient en fait la même chose.\n- `document.readyState` devient `complete` lorsque toutes les ressources (`iframe` et `img`) sont chargées. Ici, nous pouvons voir que cela se produit à peu près en même temps que `img.onload` (`img` est la dernière ressource) et `window.onload`. Passer à l'état `complete` signifie la même chose que `window.onload`. La différence est que `window.onload` fonctionne toujours après tous les autres gestionnaires de `load`.\n\n\n## Résumé\n\nÉvénements de chargement de page:\n\n- L'événement `DOMContentLoaded` se déclenche sur `document` lorsque le DOM est prêt. Nous pouvons appliquer JavaScript aux éléments à ce stade.\n  - Les scripts tel que `<script>...</script>` ou `<script src =\"...\"></script>` bloquent DOMContentLoaded, le navigateur attend leur exécution.\n  - Les images et autres ressources peuvent également continuer à se charger.\n- L'événement `load` sur `window` se déclenche lorsque la page et toutes les ressources sont chargées. Nous l'utilisons rarement, car il n'est généralement pas nécessaire d'attendre si longtemps.\n- L'événement `beforeunload` sur `window` se déclenche lorsque l'utilisateur veut quitter la page. Si nous annulons l'événement, le navigateur demande si l'utilisateur souhaite vraiment partir (par exemple, nous avons des modifications non enregistrées).\n- L'événement `unload` sur `window` se déclenche lorsque l'utilisateur quitte enfin, dans le gestionnaire nous ne pouvons faire que des choses simples qui n'impliquent pas de retards ou de demandes à un utilisateur. En raison de cette limitation, il est rarement utilisé. Nous pouvons envoyer une requête réseau avec `navigator.sendBeacon`.\n- `document.readyState` est l'état actuel du document, les modifications peuvent être suivies dans l'événement `readystatechange`:\n  - `loading` -- le document est en cours de chargement.\n  - `interactive` -- le document est analysé, se produit à peu près au même moment que `DOMContentLoaded`, mais avant lui.\n  - `complete` -- le document et les ressources sont chargés, se produit à peu près en même temps que `window.onload`, mais avant lui.\n"
  },
  {
    "path": "2-ui/5-loading/01-onload-ondomcontentloaded/readystate.view/iframe.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n  test iframe\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/5-loading/01-onload-ondomcontentloaded/readystate.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n  <!-- the sequence should be like this:\n  [10] initial readyState:loading\n  [20] readyState:interactive\n  [21] DOMContentLoaded\n  [30] iframe onload\n  [40] img onload\n  [40] readyState:complete\n  [40] window onload\n  -->\n\n  <div id=\"events\"></div>\n\n  <script>\n    function log(txt) {\n      events.insertAdjacentHTML('beforeend', `<div>[${Math.floor(performance.now())}] ${txt}</div>`);\n    }\n\n    log('initial readyState:' + document.readyState);\n\n    document.addEventListener('readystatechange', () => log('readyState:' + document.readyState));\n    document.addEventListener('DOMContentLoaded', () => log('DOMContentLoaded'));\n\n    window.onload = () => log('window onload');\n  </script>\n\n  <iframe src=\"iframe.html?speed=1&cache=0\" style=\"visibility:hidden\" onload=\"log('iframe onload')\"></iframe>\n\n  <img src=\"http://en.js.cx/clipart/train.gif?speed=1&cache=0\" id=\"img\" style=\"position:absolute;right:0;top:0\">\n  <script>\n    img.onload = () => log('img onload');\n  </script>\n\n  <div style=\"visibility:hidden\">\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/5-loading/01-onload-ondomcontentloaded/window-onbeforeunload.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n  <script>\n    function setHandler() {\n      window.onbeforeunload = function() {\n        return \"There are unsaved changes. Leave now?\";\n      };\n    }\n  </script>\n\n  <button onclick=\"setHandler()\">Set window.onbeforeunload</button>\n\n  <a href=\"http://example.com\">Leave for EXAMPLE.COM</a>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/5-loading/02-script-async-defer/article.md",
    "content": "\n# Les scripts: async, defer\n\nDans les sites Web modernes, les scripts sont souvent \"plus lourds\" que le HTML: leur taille de téléchargement est plus grande et le temps de traitement est également plus long.\n\nLorsque le navigateur charge le HTML et rencontre une balise `<script>...</script>`, il ne peut pas continuer à construire le DOM. Il doit exécuter le script de suite. Il en va de même pour les scripts externes `<script src =\"...\"></script>`: le navigateur doit attendre le téléchargement du script, l'exécuter, puis traiter le reste de la page.\n\nCela conduit à deux problèmes importants:\n\n1. Les scripts ne peuvent pas voir les éléments DOM en dessous d'eux, ils ne peuvent donc pas ajouter de gestionnaires, etc.\n2. S'il y a un script volumineux en haut de la page, il \"bloque la page\". Les utilisateurs ne peuvent pas voir le contenu de la page tant qu'il n'est pas téléchargé et exécuté:\n\n```html run height=100\n<p>...content before script...</p>\n\n<script src=\"https://javascript.info/article/script-async-defer/long.js?speed=1\"></script>\n\n<!-- Ceci n'est pas visible tant que le script n'est pas chargé -->\n<p>...content after script...</p>\n```\n\nIl existe quelques solutions pour contourner cela. Par exemple, nous pouvons mettre un script en bas de page. Comme ça, il peut voir les éléments au-dessus, et cela ne bloque pas l'affichage du contenu de la page:\n\n```html\n<body>\n  ...all content is above the script...\n\n  <script src=\"https://javascript.info/article/script-async-defer/long.js?speed=1\"></script>\n</body>\n```\n\nMais cette solution est loin d'être parfaite. Par exemple, le navigateur remarque le script (et peut commencer à le télécharger) uniquement après avoir téléchargé le document HTML complet. Pour les longs documents HTML, cela peut être un retard notable.\n\nDe telles choses sont invisibles pour les personnes utilisant des connexions très rapides, mais de nombreuses personnes dans le monde ont encore des vitesses Internet lentes et utilisent une connexion Internet mobile loin d'être parfaite.\n\nHeureusement, il y a deux attributs de `<script>` qui résolvent le problème pour nous: `defer` et `async`.\n\n## defer\n\nL'attribut `defer` indique au navigateur de ne pas attendre le script. Au lieu de cela, le navigateur continuera à traiter le HTML, à construire le DOM. Le script se charge \"en arrière-plan\", puis s'exécute lorsque le DOM est entièrement construit.\n\nVoici le même exemple que ci-dessus, mais avec `defer` :\n\n```html run height=100\n<p>...content before script...</p>\n\n<script defer src=\"https://javascript.info/article/script-async-defer/long.js?speed=1\"></script>\n\n<!-- visible immédiatement -->\n<p>...content after script...</p>\n```\n\n- Les scripts avec `defer` ne bloquent jamais la page.\n- Les scripts avec `defer` s'exécutent toujours lorsque le DOM est prêt, mais avant l'événement `DOMContentLoaded`.\n\nL'exemple suivant montre que:\n\n```html run height=100\n<p>...content before scripts...</p>\n\n<script>\n  document.addEventListener('DOMContentLoaded', () => alert(\"DOM ready after defer!\")); // (2)\n</script>\n\n<script defer src=\"https://javascript.info/article/script-async-defer/long.js?speed=1\"></script>\n\n<p>...content after scripts...</p>\n```\n\n1. Le contenu de la page s'affiche immédiatement.\n2. `DOMContentLoaded` attend le script différé. Il ne se déclenche que lorsque le script `(2)` est téléchargé et exécuté.\n\nLes scripts différés conservent leur ordre relatif, tout comme les scripts classiques.\n\nDonc, si nous avons d'abord un long script, puis un plus petit, alors ce dernier attend.\n\n```html\n<script defer src=\"https://javascript.info/article/script-async-defer/long.js\"></script>\n<script defer src=\"https://javascript.info/article/script-async-defer/small.js\"></script>\n```\n\nLes navigateurs analysent la page à la recherche de scripts et les téléchargent en parallèle pour améliorer les performances. Ainsi, dans l'exemple ci-dessus, les deux scripts se téléchargent en parallèle. Le `small.js` se termine probablement en premier.\n\n... Mais l'attribut `defer`, en plus de dire au navigateur \"de ne pas bloquer\", garantit que l'ordre relatif est conservé. Ainsi, même si `small.js` se charge en premier, il attend et s'exécute toujours après l'exécution de `long.js`.\nMais la spécification exige que les scripts s'exécutent dans l'ordre des documents, donc elle attend que `long.js` s'exécute.\n```\n\n```smart header=\"L'attribut `defer` est uniquement pour les scripts externes\"\nL'attribut `defer` est ignoré si la balise `<script>` n'a pas de `src`.\n```\n\n## async\n\n- Le navigateur ne bloque pas les scripts `async` (comme `defer`).\n- D'autres scripts n'attendent pas les scripts `async`, et les scripts `async` ne les attendent pas.\n- `DOMContentLoaded` et les scripts asynchrones ne s’attendent pas :\n    - `DOMContentLoaded` peut se produire à la fois avant un script asynchrone (si un script async termine le chargement une fois la page terminée)\n    - ... ou après un script async (si un script async est court ou était dans le cache HTTP)\n\nEn d'autres termes, les scripts `async` se chargent en arrière-plan et s'exécutent lorsqu'ils sont prêts. Le DOM et les autres scripts ne les attendent pas, et ils n'attendent rien. Un script entièrement indépendant qui s'exécute lorsqu'il est chargé. Aussi simple que cela puisse être, non ?\n\nDonc, si nous avons plusieurs scripts `async`, ils peuvent s'exécuter dans n'importe quel ordre. Premier chargé -- premier exécuté:\n\n```html run height=100\n<p>...content before scripts...</p>\n\n<script>\n  document.addEventListener('DOMContentLoaded', () => alert(\"DOM ready!\"));\n</script>\n\n<script async src=\"https://javascript.info/article/script-async-defer/long.js\"></script>\n<script async src=\"https://javascript.info/article/script-async-defer/small.js\"></script>\n\n<p>...content after scripts...</p>\n```\n\n- Le contenu de la page apparaît immédiatement: `async` ne la bloque pas.\n- `DOMContentLoaded` peut arriver soit avant ou après `async`, aucune garantie ici.\n- Les scripts asynchrones n'attendent pas les uns les autres. Un script plus petit `small.js` passe en second, mais se charge probablement avant `long.js`, donc s'exécute en premier. C'est ce qu'on appelle une commande \"load-first\".\n\nLes scripts asynchrones sont parfaits lorsque nous intégrons un script tiers indépendant dans la page: compteurs, publicités, etc., car ils ne dépendent pas de nos scripts et nos scripts ne doivent pas les attendre:\n\n```html\n<!-- Google Analytics est généralement ajouté comme ceci -->\n<script async src=\"https://google-analytics.com/analytics.js\"></script>\n```\n\n```smart header=\"L'attribut `async` est uniquement pour les scripts externes\"\nTout comme `defer`, l'attribut `async` est ignoré si la balise `<script>` n'a pas de `src`.\n```\n\n## Les scripts dynamiques\n\nIl existe un autre moyen important d'ajouter un script à la page.\n\nNous pouvons également ajouter un script dynamiquement en utilisant JavaScript:\n\n```js run\nlet script = document.createElement('script');\nscript.src = \"/article/script-async-defer/long.js\";\ndocument.body.append(script); // (*)\n```\n\nLe script commence à se charger dès qu'il est ajouté au document `(*)`.\n\n**Les scripts dynamiques se comportent comme \"asynchrones\" par défaut.**\n\nC'est-à-dire :\n\n- Ils n'attendent rien, rien ne les attend.\n- Le script qui se charge en premier -- s'exécute en premier (\"load-first\").\n\n```js run\nlet script = document.createElement('script');\nscript.src = \"/article/script-async-defer/long.js\";\n\n*!*\nscript.async = false;\n*/!*\n\ndocument.body.append(script);\n```\n\nPar exemple, nous ajoutons ici deux scripts. Sans `script.async=false`, ils s'exécuteraient dans l'ordre de chargement (le `small.js` probablement en premier). Mais avec, l'ordre est \"comme dans le document\":\n\n```js run\nfunction loadScript(src) {\n  let script = document.createElement('script');\n  script.src = src;\n  script.async = false;\n  document.body.append(script);\n}\n\n// long.js s'exécute en premier à cause de async=false\nloadScript(\"/article/script-async-defer/long.js\");\nloadScript(\"/article/script-async-defer/small.js\");\n```\n\n## Résumé\n\n`Async` et `defer` ont un point commun: le téléchargement de tels scripts ne bloque pas le rendu des pages. Ainsi, l'utilisateur peut lire le contenu de la page et se familiariser immédiatement avec la page.\n\nMais il existe également des différences essentielles entre eux:\n\n|         | L'ordre | `DOMContentLoaded` |\n|---------|---------|---------|\n| `async` | *Load-first*. Leur ordre dans le document n'a pas d'importance -- premier chargé, premier exécuté |  Sans importance. Peut se charger et s'exécuter alors que le document n'a pas encore été entièrement téléchargé. Cela se produit si les scripts sont petits ou mis en cache et que le document est suffisamment long. |\n| `defer` | *Ordre de document* (tel qu'il apparaît dans le document).|  Exécute après le chargement et l'analyse du document (ils attendent si nécessaire), juste avant `DOMContentLoaded`. |\n\nEn pratique, `defer` est utilisé pour les scripts qui ont besoin de tout le DOM et/ou leur ordre d'exécution relatif est important.\n\nEt `async` est utilisé pour des scripts indépendants, comme des compteurs ou des publicités. Et leur ordre d'exécution relatif n'a pas d'importance.\n\n```warn header=\"La page sans scripts devrait être utilisable\"\nVeuillez noter que si vous utilisez `defer` ou `async`, l'utilisateur verra alors la page *avant* le chargement du script.\n\nL'utilisateur peut donc lire la page, mais certains composants graphiques ne sont probablement pas encore prêts.\n\nIl devrait y avoir des indications de \"chargement\" aux bons endroits et les boutons désactivés devraient s'afficher comme tels, afin que l'utilisateur puisse voir clairement ce qui est prêt et ce qui ne l'est pas.\n```\n"
  },
  {
    "path": "2-ui/5-loading/02-script-async-defer/long.js",
    "content": "// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n\nalert(\"Long script loaded\");\n"
  },
  {
    "path": "2-ui/5-loading/02-script-async-defer/small.js",
    "content": "// ...small js...\n\nalert(\"Small script loaded\");\n"
  },
  {
    "path": "2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.md",
    "content": "\nL'algorithme:\n1. Créez `img` pour chaque source.\n2. Ajoutez `onload/onerror` pour chaque image.\n3. Augmentez le compteur lorsque `onload` ou `onerror` se déclenchent.\n4. Lorsque la valeur du compteur est égale au nombre de sources -- nous avons terminé: `callback()`.\n"
  },
  {
    "path": "2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n\n  <script>\n    function preloadImages(sources, callback) {\n      let counter = 0;\n\n      function onLoad() {\n        counter++;\n        if (counter == sources.length) callback();\n      }\n\n      for(let source of sources) {\n        let img = document.createElement('img');\n        img.onload = img.onerror = onLoad;\n        img.src = source;\n      }\n    }\n\n    // ---------- The test ----------\n\n    let sources = [\n      \"https://en.js.cx/images-load/1.jpg\",\n      \"https://en.js.cx/images-load/2.jpg\",\n      \"https://en.js.cx/images-load/3.jpg\"\n    ];\n\n    // add random characters to prevent browser caching\n    for (let i = 0; i < sources.length; i++) {\n      sources[i] += '?' + Math.random();\n    }\n\n    // for each image,\n    // let's create another img with the same src and check that we have its width \n    function testLoaded() {\n      let widthSum = 0;\n      for (let i = 0; i < sources.length; i++) {\n        let img = document.createElement('img');\n        img.src = sources[i];\n        widthSum += img.width;\n      }\n      alert(widthSum);\n    }\n\n    // should output 300\n    preloadImages(sources, testLoaded);\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/5-loading/03-onload-onerror/1-load-img-callback/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n\n  <script>\n    function preloadImages(sources, callback) {\n      /* your code */\n    }\n\n    // ---------- The test ----------\n\n    let sources = [\n      \"https://en.js.cx/images-load/1.jpg\",\n      \"https://en.js.cx/images-load/2.jpg\",\n      \"https://en.js.cx/images-load/3.jpg\"\n    ];\n\n    // add random characters to prevent browser caching\n    for (let i = 0; i < sources.length; i++) {\n      sources[i] += '?' + Math.random();\n    }\n\n    // for each image,\n    // let's create another img with the same src and check that we have its width immediately\n    function testLoaded() {\n      let widthSum = 0;\n      for (let i = 0; i < sources.length; i++) {\n        let img = document.createElement('img');\n        img.src = sources[i];\n        widthSum += img.width;\n      }\n      alert(widthSum);\n    }\n\n    // every image is 100x100, the total width should be 300\n    preloadImages(sources, testLoaded);\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/5-loading/03-onload-onerror/1-load-img-callback/task.md",
    "content": "importance: 4\n\n---\n\n# Charger des images avec une fonction de rappel\n\nNormalement, les images sont chargées lors de leur création. Ainsi, lorsque nous ajoutons `<img>` à la page, l'utilisateur ne voit pas l'image immédiatement. Le navigateur doit d'abord le charger.\n\nPour afficher une image immédiatement, nous pouvons la créer \"à l'avance\", comme ceci:\n\n```js\nlet img = document.createElement('img');\nimg.src = 'my.jpg';\n```\n\nLe navigateur commence à charger l'image et s'en souvient dans le cache. Plus tard, lorsque la même image apparaît dans le document (peu importe comment), elle apparaît immédiatement.\n\n**Créez une fonction `preloadImages(sources, callback)` qui charge toutes les images du tableau `sources` et, une fois prête, exécute `callback`.**\n\nPar exemple, cela affichera `alert` après le chargement des images:\n\n```js\nfunction loaded() {\n  alert(\"Images loaded\")\n}\n\npreloadImages([\"1.jpg\", \"2.jpg\", \"3.jpg\"], loaded);\n```\n\nEn cas d'erreur, la fonction doit toujours supposer que l'image est \"chargée\".\n\nEn d'autres termes, le `callback` est exécuté lorsque toutes les images sont chargées ou en erreur.\n\nLa fonction est utile, par exemple, lorsque nous prévoyons d'afficher une galerie avec de nombreuses images déroulantes et que nous voulons être sûrs que toutes les images sont chargées.\n\nDans le document source, vous pouvez trouver des liens vers des images de test, ainsi que le code pour vérifier si elles sont chargées ou non. Il devrait afficher `300`.\n"
  },
  {
    "path": "2-ui/5-loading/03-onload-onerror/article.md",
    "content": "# Chargement des ressources: onload et onerror\n\nLe navigateur nous permet de suivre le chargement des ressources externes - scripts, iframes, images, etc.\n\nIl y a deux événements pour cela:\n\n- `onload` - chargement réussi,\n- `onerror` - une erreur s'est produite.\n\n## Chargement d'un script\n\nDisons que nous devons charger un script tiers et appeler une fonction qui y réside.\n\nNous pouvons le charger dynamiquement, comme ceci:\n\n```js\nlet script = document.createElement('script');\nscript.src = \"my.js\";\n\ndocument.head.append(script);\n```\n\n...Mais comment exécuter la fonction déclarée dans ce script? Nous devons attendre que le script se charge pour l'appeler.\n\n```smart\nPour nos propres scripts, nous pourrions utiliser des [modules JavaScript] (info:modules) ici, mais ils ne sont pas largement adoptés par les bibliothèques tierces.\n```\n\n### script.onload\n\nL'assistant principal est l'événement `load`. Il se déclenche après le chargement et l'exécution du script.\n\nPar exemple:\n\n```js run untrusted\nlet script = document.createElement('script');\n\n// peut charger n'importe quel script, depuis n'importe quel domaine\nscript.src = \"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js\"\ndocument.head.append(script);\n\n*!*\nscript.onload = function() {\n  // le script crée une variable \"_\"\n  alert( _.VERSION ); // affiche la version de la bibliothèque\n\n};\n*/!*\n```\n\nDonc, dans `onload`, nous pouvons utiliser des variables de script, exécuter des fonctions, etc.\n\n...Et si le chargement échouait? Par exemple, il n'y a pas de tel script (erreur 404) ou le serveur est en panne (indisponible).\n\n### script.onerror\n\nLes erreurs qui se produisent pendant le chargement du script peuvent être suivies dans un événement `error`.\n\nPar exemple, demandons un script qui n'existe pas:\n\n```js run\nlet script = document.createElement('script');\nscript.src = \"https://example.com/404.js\"; // pas de tel script\ndocument.head.append(script);\n\n*!*\nscript.onerror = function() {\n  alert(\"Error loading \" + this.src); // Erreur de chargement de https://example.com/404.js\n};\n*/!*\n```\n\nVeuillez noter que nous ne pouvons pas obtenir les détails des erreurs HTTP ici. Nous ne savons pas si c'était une erreur 404 ou 500 ou autre chose. Juste que le chargement a échoué.\n\n```warn\nLes événements `onload`/`onerror` ne suivent que le chargement lui-même.\n\nLes erreurs qui peuvent survenir lors du traitement et de l'exécution du script sont hors de portée de ces événements. C'est-à-dire: si un script s'est chargé avec succès, alors `onload` se déclenche, même s'il contient des erreurs de programmation. Pour suivre les erreurs de script, on peut utiliser le gestionnaire global `window.onerror`.\n```\n\n## Autres ressources\n\nLes événements `load` et `error` fonctionnent aussi pour d'autres ressources, essentiellement pour toute ressource qui a un `src` externe.\n\nPar exemple:\n\n```js run\nlet img = document.createElement('img');\nimg.src = \"https://js.cx/clipart/train.gif\"; // (*)\n\nimg.onload = function() {\n  alert(`Image loaded, size ${img.width}x${img.height}`);\n};\n\nimg.onerror = function() {\n  alert(\"Error occurred while loading image\");\n};\n```\n\nIl y a quelques notes cependant:\n\n- La plupart des ressources commencent à se charger lorsqu'elles sont ajoutées au document. Mais `<img>` est une exception. Elle commence à se charger lorsqu'elle obtient un src `(*)`.\n- Pour `<iframe>`, l'événement `iframe.onload` se déclenche lorsque le chargement de l'iframe est terminé, à la fois pour un chargement réussi et en cas d'erreur.\n\nC'est pour des raisons historiques.\n\n## Politique de crossorigin\n\nIl y a une règle: les scripts d'un site ne peuvent pas accéder au contenu de l'autre site. Donc, par exemple un script sur `https://facebook.com` ne peut pas lire la boîte aux lettres de l'utilisateur sur `https://gmail.com`.\n\nOu, pour être plus précis, une origine (triplet domaine/port/protocole) ne peut pas accéder au contenu à partir d'une autre. Donc, même si nous avons un sous-domaine, ou juste un autre port, ce sont des origines différentes sans accès les uns aux autres.\n\nCette règle affecte également les ressources d'autres domaines.\n\nSi nous utilisons un script d'un autre domaine et qu'il contient une erreur, nous ne pouvons pas obtenir les détails de l'erreur.\n\nPar exemple, prenons un script `error.js` qui consiste en un seul (mauvais) appel de fonction:\n\n```js\n// 📁 error.js\nnoSuchFunction();\n```\n\nMaintenant, chargez-le depuis le même site où il se trouve:\n\n```html run height=0\n<script>\nwindow.onerror = function(message, url, line, col, errorObj) {\n  alert(`${message}\\n${url}, ${line}:${col}`);\n};\n</script>\n<script src=\"/article/onload-onerror/crossorigin/error.js\"></script>\n```\n\nNous pouvons voir un bon rapport d'erreur, comme ceci:\n\n```\nUncaught ReferenceError: noSuchFunction is not defined\nhttps://javascript.info/article/onload-onerror/crossorigin/error.js, 1:1\n```\n\nMaintenant, chargeons le même script à partir d'un autre domaine:\n\n```html run height=0\n<script>\nwindow.onerror = function(message, url, line, col, errorObj) {\n  alert(`${message}\\n${url}, ${line}:${col}`);\n};\n</script>\n<script src=\"https://cors.javascript.info/article/onload-onerror/crossorigin/error.js\"></script>\n```\n\nLe rapport est différent, comme ceci:\n\n```\nScript error.\n, 0:0\n```\n\nLes détails peuvent varier en fonction du navigateur, mais l'idée est la même: toute information sur les éléments internes d'un script, y compris les traces de pile d'erreurs, est masquée. Exactement parce que c'est d'un autre domaine.\n\nPourquoi avons-nous besoin de détails d'erreur?\n\nIl existe de nombreux services (et nous pouvons créer le nôtre) qui écoutent les erreurs globales en utilisant `window.onerror`, enregistrent les erreurs et fournissent une interface pour y accéder et les analyser. C'est génial, car nous pouvons voir de vraies erreurs, déclenchées par nos utilisateurs. Mais si un script vient d'une autre origine, alors il n'y a pas beaucoup d'informations sur les erreurs, comme nous venons de le voir.\n\nUne politique d’origine croisée similaire (CORS) est également appliquée pour d’autres types de ressources.\n\n**Pour permettre l'accès cross-origin, la balise `<script>` doit avoir l'attribut `crossorigin`, et le serveur distant doit fournir des en-têtes spéciaux.**\n\nIl existe trois niveaux d'accès cross-origin:\n\n1. **Aucun attribut `crossorigin`** -- accès interdit.\n2. **`crossorigin=\"anonymous\"`** -- accès autorisé si le serveur répond avec l'en-tête `Access-Control-Allow-Origin` avec `*` ou notre origine. Le navigateur n'envoie pas d'autorisationinformation and cookies to remote server.\n3. **`crossorigin=\"use-credentials\"`** -- accès autorisé si le serveur renvoie l'en-tête `Access-Control-Allow-Origin` avec notre origine et `Access-Control-Allow-Credentials:true`. Le navigateur envoie des informations d'autorisation et des cookies au serveur distant.\n\n```smart\nVous pouvez en savoir plus sur l'accès cross-origin dans le chapitre <info:fetch-crossorigin>. Il décrit la méthode `fetch` pour les requêtes réseau, mais la politique est exactement la même.\n\nLes \"cookies\" sont hors de notre portée actuelle, mais vous pouvez les lire dans le chapitre <info:cookie>.\n```\n\nDans notre cas, nous n'avions aucun attribut crossorigin. L'accès cross-origin était donc interdit. Ajoutons-le.\n\nNous pouvons choisir entre `\"anonymous\"` (aucun cookie envoyé, un en-tête côté serveur nécessaire) et `\"use-credentials\"` (envoie également des cookies, deux en-têtes côté serveur nécessaires).\n\nSi nous ne nous soucions pas des cookies, alors `\"anonymous\"` est la voie à suivre:\n\n```html run height=0\n<script>\nwindow.onerror = function(message, url, line, col, errorObj) {\n  alert(`${message}\\n${url}, ${line}:${col}`);\n};\n</script>\n<script *!*crossorigin=\"anonymous\"*/!* src=\"https://cors.javascript.info/article/onload-onerror/crossorigin/error.js\"></script>\n```\n\nMaintenant, en supposant que le serveur fournit un en-tête `Access-Control-Allow-Origin`, tout va bien. Nous avons le rapport d'erreur complet.\n\n## Résumé\n\nLes images `<img>`, les styles externes, les scripts et autres ressources fournissent des événements `load` et `error` pour suivre leur chargement:\n\n- `load` se déclenche en cas de chargement réussi.\n- `error` se déclenche en cas d'échec du chargement.\n\nLa seule exception est `<iframe>`: pour des raisons historiques, il déclenche toujours `load`, pour tout achèvement de chargement, même si la page n'est pas trouvée.\n\nL'événement `readystatechange` fonctionne également pour les ressources, mais est rarement utilisé, car les événements `load/error` sont plus simples.\n"
  },
  {
    "path": "2-ui/5-loading/03-onload-onerror/crossorigin.view/error.js",
    "content": "noSuchFunction();\n"
  },
  {
    "path": "2-ui/5-loading/index.md",
    "content": "\n# Chargement du document et des ressources\n"
  },
  {
    "path": "2-ui/99-ui-misc/01-mutation-observer/article.md",
    "content": "\n# Mutation observer\n\n`MutationObserver` est un objet intégré qui observe un élément DOM et déclenche une callback (fonction de rappel) lorsqu'il détecte un changement.\n\nNous examinerons d'abord la syntaxe, puis nous étudierons un cas d'utilisation réel, pour voir où ce genre de chose peut être utile.\n\n## Syntaxe\n\n`MutationObserver` est facile à utiliser.\n\nTout d'abord, nous créons un observateur avec un callback:\n\n```js\nlet observer = new MutationObserver(callback);\n```\n\nEt ensuite on l'attache à un nœud DOM:\n\n```js\nobserver.observe(node, config);\n```\n\n`config` est un objet avec des options booléennes \"sur quel type de changements réagir\":\n- `childList` -- les changements dans les enfants directs de `node`,\n- `subtree` -- dans tous les descendants de `node`,\n- `attributes` -- dans les attributs de `node`,\n- `attributeFilter` -- dans un tableau de noms d'attributs, pour n'observer que ceux qui sont sélectionnés, \n- `characterData` -- s'il faut observer `node.data` (contenu du texte),\n\nQuelques autres options:\n- `attributeOldValue` -- si `true`, passer l'ancienne et la nouvelle valeur de l'attribut au callback (voir ci-dessous), sinon, seule la nouvelle valeur (a besoin de l'option `attributes`).\n- `characterDataOldValue` -- si `true`, passer l'ancienne et la nouvelle valeur de `node.data` au callback (voir ci-dessous), sinon, seule la nouvelle valeur (a besoin de l'option `characterData`)\n\nEnsuite, après tout changement, le `callback` est exécuté : les changements sont passés dans le premier argument comme une liste d'objets [MutationRecord](https://dom.spec.whatwg.org/#mutationrecord), et l'observer lui-même comme deuxième argument.\n\nLes objects [MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) ont les propriétés suivantes:\n\n- `type` -- type de mutation, valeurs possibles:\n    - `\"attributes\"`: attribut modifié, \n    - `\"characterData\"`: données modifiées, utilisées pour les nœuds de texte, \n    - `\"childList\"`: éléments enfants ajoutés/supprimés, \n- `target` -- où le changement a eu lieu: un élément pour les `attributes`, ou un nœud de texte pour les `characterData`, ou un élément pour une mutation `childList`,\n- `addedNodes/removedNodes`  -- les nœuds qui ont été ajoutés/supprimés,\n- `previousSibling/nextSibling` -- le frère ou la sœur précédent(e) et suivant(e) aux nœuds ajoutés/supprimés,\n- `attributeName/attributeNamespace` -- le nom/espace de nommage (pour XML) de l'attribut modifié,\n- `oldValue` -- la valeur précédente, uniquement pour les modifications d'attributs ou de texte, si l'option correspondante est définie `attributeOldValue/characterDataOldValue`.\n\nPar exemple, voici un `<div>` avec un attribut `contentEditable`. Cet attribut nous permet de \"focus\" contenu et de l'éditer.\n\n```html run\n<div contentEditable id=\"elem\">Click and <b>edit</b>, please</div>\n\n<script>\nlet observer = new MutationObserver(mutationRecords => {\n  console.log(mutationRecords); // console.log(les changements)\n});\n\n// observer tout sauf les attributs\nobserver.observe(elem, {\n  childList: true, // observer les enfants directs\n  subtree: true, // et les descendants aussi\n  characterDataOldValue: true // transmettre les anciennes données au callback\n});\n</script>\n```\n\nSi nous exécutons ce code dans le navigateur, puis qu'on focus la `<div>` donné et changeons le texte à l'intérieur de `<b>edit</b>`, `console.log` affichera une mutation:\n\n```js\nmutationRecords = [{\n  type: \"characterData\",\n  oldValue: \"edit\",\n  target: <text node>,\n  // autres propriétés vides\n}];\n```\n\nSi nous effectuons des opérations d'édition plus complexes, par exemple en supprimant le `<b>edit</b>`, l'événement de mutation peut contenir plusieurs enregistrements de mutation:\n\n```js\nmutationRecords = [{\n  type: \"childList\",\n  target: <div#elem>,\n  removedNodes: [<b>],\n  nextSibling: <text node>,\n  previousSibling: <text node>\n  // autres propriétés vides\n}, {\n  type: \"characterData\"\n  target: <text node>\n  // ...les détails de la mutation dépendent de la façon dont le navigateur gère cette suppression\n  // il peut regrouper deux nœuds de texte adjacents \"edit\" et \", please\" en un seul nœud\n  // ou il peut leur laisser des nœuds de texte séparés\n}];\n```\n\n`MutationObserver` permet donc de réagir à tout changement dans le sous-arbre DOM\n\n## Utilisation pour l'intégration\n\nQuand une telle chose peut-elle être utile ?\n\nImaginez la situation où vous devez ajouter un script tiers qui contient des fonctionnalités utiles, mais qui fait aussi quelque chose d'indésirable, par exemple afficher des annonces `<div class=\"ads\">Unwanted ads</div>`.\n\nNaturellement, le script tiers ne prévoit aucun mécanisme permettant de le supprimer.\n\nGrâce à `MutationObserver`, nous pouvons détecter quand l'élément indésirable apparaît dans notre DOM et le supprimer.\n\nIl y a d'autres situations où un script tiers ajoute quelque chose dans notre document, et nous aimerions détecter, quand cela se produit, d'adapter notre page, de redimensionner dynamiquement quelque chose, etc.\n\n`MutationObserver` permet de faire tout ça.\n\n## Utilisation pour l'architecture\n\nIl y a aussi des situations où `MutationObserver` est bon du point de vue architectural.\n\nDisons que nous faisons un site web sur la programmation. Naturellement, les articles et autres matériels peuvent contenir des extraits de code source.\n\nVoici à quoi ressemble un tel extrait dans un balisage HTML:\n\n```html\n...\n<pre class=\"language-javascript\"><code>\n  // voici le code\n  let hello = \"world\";\n</code></pre>\n...\n```\n\nPour une meilleure lisibilité et en même temps, pour l'embellir, nous utiliserons une bibliothèque de coloration syntaxique JavaScript sur notre site, comme [Prism.js](https://prismjs.com/). Pour obtenir la coloration syntaxique de l'extrait de code ci-dessus dans Prism, `Prism.highlightElem(pre)` est appelé, qui examine le contenu de ces éléments `pre` et ajoute des balises et des styles spéciaux pour la coloration syntaxique colorée dans ces éléments, similaire à ce que vous voyez en exemples ici, sur cette page.\n\nQuand exactement faut-il appliquer cette méthode de mise en évidence ? Nous pouvons le faire sur l'événement `DOMContentLoaded`, ou en bas de page. À ce moment, nous avons notre DOM prêt, nous pouvons rechercher des éléments `pre[class*=\"language\"]` et appeler `Prism.highlightElem` dessus :\n\n```js\n// mettre en évidence tous les extraits de code sur la page\ndocument.querySelectorAll('pre[class*=\"language\"]').forEach(Prism.highlightElem);\n```\n\nTout est simple jusqu'à présent, n'est-ce pas ? Nous trouvons des extraits de code en HTML et les mettons en évidence.\n\nMaintenant, continuons. Disons que nous allons chercher dynamiquement des éléments sur un serveur. Nous étudierons les méthodes pour cela [plus tard dans le tutoriel](info:fetch). Pour l'instant, il suffit d'aller chercher un article HTML sur un serveur web et de l'afficher à la demande :\n\n```js\nlet article = /* récupérer du nouveau contenu sur le serveur */\narticleElem.innerHTML = article;\n```\n\nLe nouvel `article` HTML peut contenir des extraits de code. Nous devons appeler `Prism.highlightElem` sur eux, sinon ils ne seront pas mis en évidence.\n\n**Où et quand appeler `Prism.highlightElem` pour un article chargé dynamiquement ?**\n\nNous pourrions ajouter cet appel au code qui charge un article, comme ceci:\n\n```js\nlet article = /* récupérer du nouveau contenu sur le serveur */\narticleElem.innerHTML = article;\n\n*!*\nlet snippets = articleElem.querySelectorAll('pre[class*=\"language-\"]');\nsnippets.forEach(Prism.highlightElem);\n*/!*\n```\n\n... Mais imaginez si nous avons de nombreux endroits dans le code où nous chargeons notre contenu -- articles, quiz, messages de forum, etc. Devons-nous mettre l'appel de mise en évidence partout, pour mettre en évidence le code dans le contenu après le chargement? Ce n'est pas très pratique.\n\nEt si le contenu est chargé par un module tiers ? Par exemple, nous avons un forum écrit par quelqu'un d'autre, qui charge le contenu dynamiquement, et nous aimerions y ajouter une mise en évidence syntaxique. Personne n'aime patcher des scripts tiers.\n\nHeureusement, il y a une autre option.\n\nNous pouvons utiliser `MutationObserver` pour détecter automatiquement quand des extraits de code sont insérés dans la page et les mettre en évidence.\n\nNous allons donc gérer la fonctionnalité de mise en évidence en un seul endroit.\n\n### Démonstration dynamique de mise en évidence\n\nSi vous exécutez ce code, il commence à observer l'élément ci-dessous et à mettre en évidence tout extrait de code qui y apparaît:\n\n```js run\nlet observer = new MutationObserver(mutations => {\n\n  for(let mutation of mutations) {\n    // examiner les nouveaux nœuds, y a-t-il quelque chose à mettre en évidence ?\n\n    for(let node of mutation.addedNodes) {\n      // nous ne suivons que les éléments, nous sautons les autres nœuds (par exemple les nœuds de texte)\n      if (!(node instanceof HTMLElement)) continue;\n\n      // vérifier que l'élément inséré est un extrait de code\n      if (node.matches('pre[class*=\"language-\"]')) {\n        Prism.highlightElement(node);\n      }\n\n      // ou peut-être qu'il y a un extrait de code quelque part dans son sous-arbre ?\n      for(let elem of node.querySelectorAll('pre[class*=\"language-\"]')) {\n        Prism.highlightElement(elem);\n      }\n    }\n  }\n\n});\n\nlet demoElem = document.getElementById('highlight-demo');\n\nobserver.observe(demoElem, {childList: true, subtree: true});\n```\n\nCi-dessous, il y a un élément HTML et JavaScript qui le remplit dynamiquement en utilisant `innerHTML`.\n\nVeuillez exécuter le code précédent (ci-dessus, qui observe cet élément), puis le code ci-dessous. Vous verrez comment `MutationObserver` détecte et met en évidence l'extrait.\n\n<p id=\"highlight-demo\" style=\"border: 1px solid #ddd\">Voici un élément de démonstration avec <code>id=\"highlight-demo\"</code>, exécutez le code ci-dessus pour l'observer.</p>\n\nLe code suivant remplit son `innerHTML`, qui fait réagir le `MutationObserver` et met en évidence son contenu:\n\n```js run\nlet demoElem = document.getElementById('highlight-demo');\n\n// insérer dynamiquement du contenu avec des extraits de code\ndemoElem.innerHTML = `Vous trouverez ci-dessous un extrait de code:\n  <pre class=\"language-javascript\"><code> let hello = \"world!\"; </code></pre>\n  <div>Un autre:</div>\n  <div>\n    <pre class=\"language-css\"><code>.class { margin: 5px; } </code></pre>\n  </div>\n`;\n```\n\nNous avons maintenant `MutationObserver` qui peut suivre tous les surlignages dans les éléments observés ou dans le `document` entier. Nous pouvons ajouter/supprimer des bribes de code en HTML sans y penser.\n\n## Méthodes supplémentaires\n\nIl y a une méthode pour arrêter d'observer le nœud:\n\n- `observer.disconnect()` -- arrête l'observation.\n\nLorsque nous arrêtons l'observation, il est possible que certaines modifications n'aient pas encore été traitées par l'observateur.\n\n- `observer.takeRecords()` -- obtient une liste des dossiers de mutation non traités, ceux qui se sont produits, mais le rappel n'a pas permis de les traiter.\n\nCes méthodes peuvent être utilisées ensemble, comme ceci:\n\n```js\n// obtenir une liste des mutations non traitées\n// doit être appelé avant de se déconnecter,\n// si vous vous souciez de mutations récentes éventuellement non gérées\nlet mutationRecords = observer.takeRecords();\n\n// stop tracking changes\nobserver.disconnect();\n...\n```\n\n\n```smart header=\"Les enregistrements retournés par ʻobserver.takeRecords() `sont supprimés de la file d'attente de traitement\"\nLe rappel ne sera pas appelé pour les enregistrements, renvoyé par `observer.takeRecords()`.\n```\n\n```smart header=\"Interaction avec le garbage collection\"\nLes observateurs utilisent des références faibles aux nœuds en interne. Autrement dit, si un nœud est retiré du DOM et devient inaccessible, il devient alors un déchet collecté.\n\nLe simple fait qu'un nœud DOM soit observé n'empêche pas le ramassage des ordures.\n```\n\n## Résumé  \n\n`MutationObserver` peut réagir aux changements dans le DOM -- attributs, contenu de texte et ajout / suppression d'éléments.\n\nNous pouvons l'utiliser pour suivre les changements introduits par d'autres parties de notre code, ainsi que pour intégrer des scripts tiers.\n\n`MutationObserver` peut suivre tout changement. Les options de configuration \"ce qu'il faut observer\" sont utilisées pour des optimisations, afin de ne pas dépenser des ressources pour des callback inutiles.\n"
  },
  {
    "path": "2-ui/99-ui-misc/02-selection-range/article.md",
    "content": "libs:\n  - d3\n  - domtree\n\n---\n\n# Selection et Range\n\nDans ce chapitre, nous aborderons la sélection dans le document, ainsi que la sélection dans les champs de formulaire, tels que `<input>`.\n\nJavaScript peut accéder à une sélection existante, sélectionner/désélectionner des nœuds du DOM dans leur ensemble ou partiellement, supprimer le contenu sélectionné du document, l'envelopper dans une balise, etc.\n\nVous pouvez trouver quelques recettes de tâches courantes à la fin du chapitre, dans la section \"Résumé\". Peut-être que cela couvre vos besoins actuels, mais vous obtiendrez beaucoup plus si vous lisez le texte en entier.\n\nLes objets `Range` et `Selection` sont faciles à comprendre, et vous n'aurez besoin d'aucune recette pour les faire faire ce que vous voulez.\n\n## Range\n\nLe concept de base de la sélection est la [plage](https://dom.spec.whatwg.org/#ranges) (Range), qui est essentiellement une paire de \"points limites\": le début et la fin de la plage.\n\nUn objet `Range` est créé sans paramètres :\n\n```js\nlet range = new Range();\n```\n\nEnsuite, nous pouvons définir les limites de la sélection en utilisant `range.setStart(node, offset)` et `range.setEnd(node, offset)`.\n\nComme vous pouvez le deviner, nous allons utiliser les objets `Range` pour la sélection, mais créons d'abord quelques-uns de ces objets.\n\n### Sélection partielle du texte\n\nLa chose intéressante est que le premier argument `node` dans les deux méthodes peut être soit un noeud de texte ou un noeud d'élément, et la signification du deuxième argument dépend de cela.\n\n**Si `node` est un noeud de texte, alors `offset` doit être la position dans son texte.**\n\nPar exemple, étant donné l'élément `<p>Hello</p>`, nous pouvons créer la plage contenant les lettres \"ll\" comme suit :\n\n```html run\n<p id=\"p\">Hello</p>\n<script>\n  let range = new Range();\n  range.setStart(p.firstChild, 2);\n  range.setEnd(p.firstChild, 4);\n\n  // toString d'une plage renvoie son contenu sous forme de texte\n  console.log(range); // ll\n</script>\n```\n\nIci, nous prenons le premier enfant de `<p>` (c'est le noeud texte) et nous spécifions les positions du texte à l'intérieur de celui-ci :\n\n![](range-hello-1.svg)\n\n### Sélection des noeuds d'éléments\n\n**Alternativement, si `node` est un noeud d'élément, alors `offset` doit être le numéro de l'enfant.**\n\nC'est pratique pour faire des plages qui contiennent les noeuds dans leur ensemble, et non pas s'arrêter quelque part dans leur texte.\n\nPar exemple, nous avons un fragment de document plus complexe :\n\n```html autorun\n<p id=\"p\">Example: <i>italic</i> and <b>bold</b></p>\n```\n\nVoici sa structure DOM avec les nœuds d'élément et de texte :\n\n<div class=\"select-p-domtree\"></div>\n\n<script>\nlet selectPDomtree = {\n  \"name\": \"P\",\n  \"nodeType\": 1,\n  \"children\": [{\n    \"name\": \"#text\",\n    \"nodeType\": 3,\n    \"content\": \"Example: \"\n  }, {\n    \"name\": \"I\",\n    \"nodeType\": 1,\n    \"children\": [{\n      \"name\": \"#text\",\n      \"nodeType\": 3,\n      \"content\": \"italic\"\n    }]\n  }, {\n    \"name\": \"#text\",\n    \"nodeType\": 3,\n    \"content\": \" and \"\n  }, {\n    \"name\": \"B\",\n    \"nodeType\": 1,\n    \"children\": [{\n      \"name\": \"#text\",\n      \"nodeType\": 3,\n      \"content\": \"bold\"\n    }]\n  }]\n}\n\ndrawHtmlTree(selectPDomtree, 'div.select-p-domtree', 690, 320);\n</script>\n\nCréons une plage pour `\"Example: <i>italic</i>\"`.\n\nComme nous pouvons le voir, cette phrase est composée d'exactement deux enfants de `<p>`, avec les indices `0` et `1` :\n\n![](range-example-p-0-1.svg)\n\n- Le point de départ a `<p>` comme `node` parent, et `0` comme décalage (offset).\n\n    On peut donc le définir comme `range.setStart(p, 0)`.\n- Le point de fin a aussi `<p>` comme `node` parent, mais `2` comme décalage (il spécifie la plage jusqu'à, mais sans inclure `offset`).\n\n    On peut donc le définir comme `range.setEnd(p, 2)`.\n\nVoici la démo. Si vous l'exécutez, vous pouvez voir que le texte est sélectionné :\n\n```html run\n<p id=\"p\">Example: <i>italic</i> and <b>bold</b></p>\n\n<script>\n*!*\n  let range = new Range();\n\n  range.setStart(p, 0);\n  range.setEnd(p, 2);\n*/!*\n\n  // toString d'une plage renvoie son contenu sous forme de texte, sans balises\n  console.log(range); // Example: italic\n\n  // appliquer cette plage pour la sélection du document (expliqué plus loin)\n  document.getSelection().addRange(range);\n</script>\n```\n\nVoici un banc d'essai plus flexible dans lequel vous pouvez définir des valeurs de début et de fin de plage et explorer d'autres variantes :\n\n```html run autorun\n<p id=\"p\">Example: <i>italic</i> and <b>bold</b></p>\n\nFrom <input id=\"start\" type=\"number\" value=1> – To <input id=\"end\" type=\"number\" value=4>\n<button id=\"button\">Click to select</button>\n<script>\n  button.onclick = () => {\n  *!*\n    let range = new Range();\n\n    range.setStart(p, start.value);\n    range.setEnd(p, end.value);\n  */!*\n\n    // appliquer la sélection, expliquée plus loin\n    document.getSelection().removeAllRanges();\n    document.getSelection().addRange(range);\n  };\n</script>\n```\n\nE.g. en sélectionnant dans le même `<p>` de l'offset `1` à `4` on obtient `<i>italic</i> and <b>bold</b>`:\n\n![](range-example-p-1-3.svg)\n\n```smart header=\"Les nœuds de début et de fin peuvent être différents\"\nNous ne sommes pas obligés d'utiliser le même noeud dans `setStart` et `setEnd`. Une plage peut s'étendre sur de nombreux noeuds non liés. Il est seulement important que la fin soit après le début dans le document.\n```\n\n### Sélection d'un plus grand fragment\n\nFaisons une sélection plus grande dans notre exemple, comme ceci :\n\n![](range-example-p-2-b-3.svg)\n\nNous savons déjà comment faire. Nous devons juste définir le début et la fin comme un offset relatif dans les nœuds de texte.\n\nNous devons créer une plage, qui :\n- commence à la position 2 dans `<p>` firstChild (en prenant toutes les lettres sauf les deux premières de \"Ex<b>ample:</b> \").\n- se termine à la position 3 dans `<b>` firstChild (en prenant les trois premières lettres de \"<b>bol</b>d\", mais pas plus) :\n\n```html run\n<p id=\"p\">Example: <i>italic</i> and <b>bold</b></p>\n\n<script>\n  let range = new Range();\n\n  range.setStart(p.firstChild, 2);\n  range.setEnd(p.querySelector('b').firstChild, 3);\n\n  console.log(range); // ample: italic and bol\n\n  // utiliser cette plage pour la sélection (expliqué plus loin)\n  window.getSelection().addRange(range);\n</script>\n```\n\nComme vous pouvez le voir, il est assez facile de créer une plage de ce que l'on veut.\n\nSi nous voulons prendre les noeuds dans leur ensemble, nous pouvons passer des éléments dans `setStart/setEnd`. Sinon, nous pouvons travailler au niveau du texte.\n\n## Propriétés de la plage\n\nL'objet range que nous avons créé dans l'exemple ci-dessus a les propriétés suivantes :\n\n![](range-example-p-2-b-3-range.svg)\n\n- `startContainer`, `startOffset` -- nœud et offset du début,\n  - dans l'exemple ci-dessus : premier noeud de texte à l'intérieur de `<p>` et `2`.\n- `endContainer`, `endOffset` -- noeud et offset de la fin,\n  - dans l'exemple ci-dessus : premier noeud de texte dans `<b>` et `3`.\n- `collapsed` -- booléen, `true` si la plage commence et se termine sur le même point (donc il n'y a pas de contenu à l'intérieur de la plage),\n  - dans l'exemple ci-dessus : `false`.\n- `commonAncestorContainer` -- l'ancêtre commun le plus proche de tous les noeuds de la plage,\n  - dans l'exemple ci-dessus : `<p>`.\n\n\n## Méthodes de sélection de plages\n\nIl existe de nombreuses méthodes pratiques pour manipuler les plages.\n\nNous avons déjà vu `setStart` et `setEnd`, voici d'autres méthodes similaires.\n\nDéfinir le début de la plage :\n\n- `setStart(node, offset)` définit le début à : position `offset` dans `node`.\n- `setStartBefore(node)` définit le début à : juste avant `node`.\n- `setStartAfter(node)` définit le début à : juste après `node`.\n\nDéfinir la fin de la plage (méthodes similaires) :\n\n- `setEnd(node, offset)` définit la fin à : position `offset` dans `node`.\n- `setEndBefore(node)` définit la fin à : juste avant `node`.\n- `setEndAfter(node)` définit la fin à : juste après `node`.\n\nTechniquement, `setStart/setEnd` peuvent faire n'importe quoi, mais plus de méthodes fournissent plus de commodité.\n\nDans toutes ces méthodes, `node` peut être un noeud de texte ou d'élément : pour les noeuds de texte, `offset` saute autant de caractères, tandis que pour les noeuds d'éléments, autant de noeuds enfants.\n\nEncore plus de méthodes pour créer des plages :\n- `selectNode(node)` définit la plage pour sélectionner le `node` entier.\n- `selectNodeContents(node)` sélectionne le contenu du `node` dans son intégralité.\n- `collapse(toStart)` si `toStart=true`, définissez end=start, sinon définissez start=end, ce qui réduit la plage de sélection.\n- `cloneRange()` crée une nouvelle plage avec le même début et la même fin.\n\n## Méthodes d'édition des plages\n\nUne fois que la plage est créée, nous pouvons manipuler son contenu en utilisant ces méthodes :\n\n- `deleteContents()` -- supprime le contenu de la plage du document.\n- `extractContents()` -- supprime le contenu de la plage du document et le retourne en tant que [DocumentFragment](info:modifying-document#document-fragment).\n- `cloneContents()` -- clone le contenu d'une plage et le renvoie en tant que [DocumentFragment](info:modifying-document#document-fragment).\n- `insertNode(node)` -- Insère `node` dans le document au début de la plage.\n- `surroundContents(node)` -- entoure `node` du contenu de la plage. Pour que cela fonctionne, la plage doit contenir à la fois des balises d'ouverture et de fermeture pour tous les éléments qu'elle contient : pas de plages partielles comme `<i>abc`.\n\nAvec ces méthodes, nous pouvons faire pratiquement n'importe quoi avec les noeuds sélectionnés.\n\nVoici le banc d'essai pour les voir en action :\n\n```html run refresh autorun height=260\nCliquez sur les boutons pour exécuter des méthodes sur la sélection, \"resetExample\" pour la réinitialiser.\n\n<p id=\"p\">Example: <i>italic</i> and <b>bold</b></p>\n\n<p id=\"result\"></p>\n<script>\n  let range = new Range();\n\n  // Chaque méthode démontrée est représentée ici :\n  let methods = {\n    deleteContents() {\n      range.deleteContents()\n    },\n    extractContents() {\n      let content = range.extractContents();\n      result.innerHTML = \"\";\n      result.append(\"extracted: \", content);\n    },\n    cloneContents() {\n      let content = range.cloneContents();\n      result.innerHTML = \"\";\n      result.append(\"cloned: \", content);\n    },\n    insertNode() {\n      let newNode = document.createElement('u');\n      newNode.innerHTML = \"NEW NODE\";\n      range.insertNode(newNode);\n    },\n    surroundContents() {\n      let newNode = document.createElement('u');\n      try {\n        range.surroundContents(newNode);\n      } catch(e) { console.log(e) }\n    },\n    resetExample() {\n      p.innerHTML = `Example: <i>italic</i> and <b>bold</b>`;\n      result.innerHTML = \"\";\n\n      range.setStart(p.firstChild, 2);\n      range.setEnd(p.querySelector('b').firstChild, 3);\n\n      window.getSelection().removeAllRanges();\n      window.getSelection().addRange(range);\n    }\n  };\n\n  for(let method in methods) {\n    document.write(`<div><button onclick=\"methods.${method}()\">${method}</button></div>`);\n  }\n\n  methods.resetExample();\n</script>\n```\n\nIl existe également des méthodes permettant de comparer des plages, mais elles sont rarement utilisées. Si vous en avez besoin, veuillez vous reporter à la [spec](https://dom.spec.whatwg.org/#interface-range) ou au [manuel MDN](mdn:/api/Range).\n\n## Sélection\n\n`Range` est un objet générique pour gérer les plages de sélection. Bien que la création d'une `Range` ne signifie pas que nous voyons une sélection à l'écran.\n\nNous pouvons créer des objets `Range`, les faire circuler - ils ne sélectionnent rien visuellement par eux-mêmes.\n\nLa sélection du document est représentée par un objet `Selection`, qui peut être obtenu par `window.getSelection()` ou `document.getSelection()`. Une sélection peut inclure zéro ou plusieurs plages. C'est du moins ce que dit la [spécification de Selection API](https://www.w3.org/TR/selection-api/). En pratique cependant, seul Firefox permet de sélectionner plusieurs plages dans le document en utilisant `key:Ctrl+click` (`key:Cmd+click` pour Mac).\n\nVoici une capture d'écran d'une sélection avec 3 plages, réalisée dans Firefox :\n\n![](sélection-firefox.svg)\n\nLes autres navigateurs prennent en charge au maximum 1 plage. Comme nous allons le voir, certaines méthodes de `Selection` impliquent qu'il peut y avoir plusieurs plages, mais là encore, dans tous les navigateurs sauf Firefox, il y a au maximum 1 plage.\n\nVoici une petite démo qui montre la sélection actuelle (sélectionner quelque chose et cliquer) sous forme de texte :\n\n<button onclick=\"alert(document.getSelection())\">alert(document.getSelection())</button>\n\n## Propriétés de Selection\n\nComme nous l'avons dit, une sélection peut en théorie contenir plusieurs plages. Nous pouvons obtenir ces objets range en utilisant la méthode :\n\n- `getRangeAt(i)` -- obtient la i-ième plage, en commençant par `0`. Dans tous les navigateurs sauf Firefox, seul `0` est utilisé.\n\nIl existe également des propriétés qui sont souvent plus pratiques.\n\nComme pour une plage, un objet selection a un début, appelé \"anchor\", et une fin, appelée \"focus\".\n\nLes principales propriétés de selection sont :\n\n- `anchorNode` -- le noeud où la sélection commence.\n- `anchorOffset` -- le décalage (offset) dans `anchorNode` où la sélection commence.\n- `focusNode` -- le noeud où la sélection se termine.\n- `focusOffset` -- le décalage dans `focusNode` où la sélection se termine.\n- `isCollapsed` -- `true` si la sélection ne sélectionne rien (plage vide), ou si elle n'existe pas.\n- `rangeCount` -- nombre de plages dans la sélection, maximum `1` dans tous les navigateurs sauf Firefox.\n\n\n```smart header=\"end/start de selection vs Range\"\n\nIl y a une différence importante entre anchor/focus d'une sélection et start/end d'un objet `Range`.\n\nComme nous le savons, les objets `Range` ont toujours leur début avant leur fin.\n\nPour les sélections, ce n'est pas toujours le cas.\n\nLa sélection d'un objet avec une souris peut se faire dans les deux sens : de gauche à droite ou de droite à gauche.\n\nEn d'autres termes, lorsque le bouton de la souris est enfoncé, puis qu'il avance dans le document, sa fin (focus) se trouvera après son début (anchor).\n\nE.g. si l'utilisateur commence à sélectionner avec la souris et passe de \"Example\" à \"italic\" :\n\n![](selection-direction-forward.svg)\n\n...Mais la même sélection pourrait être faite en sens inverse : en partant de \"italic\" vers \"Example\" (sens inverse), alors sa fin (focus) sera avant le début (anchor) :\n\n![](selection-direction-backward.svg)\n```\n\n## Événements de sélection\n\nIl y a des événements pour suivre la trace de la sélection :\n\n- `elem.onselectstart` -- quand une sélection *débute* spécifiquement sur l'élément `elem` (ou à l'intérieur de celui-ci). Par exemple, lorsque l'utilisateur appuie sur le bouton de la souris sur cet élément et commence à déplacer le pointeur.\n    - En empêchant l'action par défaut, on annule le démarrage de la sélection. Le démarrage d'une sélection à partir de cet élément devient donc impossible, mais l'élément reste tout de même sélectionnable. Le visiteur doit simplement démarrer la sélection à partir d'un autre endroit.\n- `document.onselectionchange` -- quand une sélection change ou commence.\n    - Attention : ce gestionnaire ne peut être défini que sur `document`, il suit toutes les sélections qu'il contient.\n\n### Démonstration du suivi des sélections\n\nVoici une petite démo. Elle suit la sélection courante sur le `document` et montre ses limites :\n\n```html run height=80\n<p id=\"p\">Select me: <i>italic</i> and <b>bold</b></p>\n\nFrom <input id=\"from\" disabled> – To <input id=\"to\" disabled>\n<script>\n  document.onselectionchange = function() {\n    let selection = document.getSelection();\n\n    let {anchorNode, anchorOffset, focusNode, focusOffset} = selection;\n\n    // anchorNode et focusNode sont des nœuds de texte habituellement\n    from.value = `${anchorNode?.data}, offset ${anchorOffset}`;\n    to.value = `${focusNode?.data}, offset ${focusOffset}`;\n  };\n</script>\n```\n\n### Démonstration de la copie d'une sélection\n\nIl existe deux approches pour copier le contenu sélectionné :\n\n1. Nous pouvons utiliser `document.getSelection().toString()` pour le récupérer sous forme de texte.\n2. Sinon, pour copier le DOM complet, par exemple si nous devons garder le formatage, nous pouvons obtenir les plages sous-jacentes avec `getRangesAt(...)`. Un objet `Range`, à son tour, a la méthode `cloneContents()` qui clone son contenu et retourne un objet `DocumentFragment`, que nous pouvons insérer ailleurs.\n\nVoici la démonstration de la copie du contenu sélectionné à la fois comme texte et comme noeuds DOM :\n\n```html run height=100\n<p id=\"p\">Select me: <i>italic</i> and <b>bold</b></p>\n\nCloned: <span id=\"cloned\"></span>\n<br>\nAs text: <span id=\"astext\"></span>\n\n<script>\n  document.onselectionchange = function() {\n    let selection = document.getSelection();\n\n    cloned.innerHTML = astext.innerHTML = \"\";\n\n    // Cloner les noeuds du DOM à partir de plages (nous supportons le multiselect ici)\n    for (let i = 0; i < selection.rangeCount; i++) {\n      cloned.append(selection.getRangeAt(i).cloneContents());\n    }\n\n    // Obtenir sous forme de texte\n    astext.innerHTML += selection;\n  };\n</script>\n```\n\n## Méthodes de sélection\n\nNous pouvons travailler avec la sélection en ajoutant/supprimant (add/remove) des plages :\n\n- `getRangeAt(i)` -- obtient la i-ième plage, en commençant par `0`. Dans tous les navigateurs sauf Firefox, seul `0` est utilisé.\n- `addRange(range)` -- ajoute `range` à la sélection. Tous les navigateurs, à l'exception de Firefox, ignorent l'appel, si la sélection a déjà une plage associée.\n- `removeRange(range)` -- supprime `range` de la sélection.\n- `removeAllRanges()` -- supprime toutes les plages.\n- `empty()` -- alias de `removeAllRanges`.\n\nIl y a aussi des méthodes pratiques pour manipuler directement la plage de sélection, sans appels intermédiaires à `Range` :\n\n- `collapse(node, offset)` -- remplace la plage sélectionnée par une nouvelle qui commence et se termine au `node` donné, à la position `offset`.\n- `setPosition(node, offset)` -- alias de `collapse`.\n- `collapseToStart()` - réduit (remplace par une plage vide) au début de la sélection.\n- `collapseToEnd()` - réduit à la fin de la sélection.\n- `extend(node, offset)` - déplace le focus de la sélection vers le `node` et la position `offset` donnés.\n- `setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset)` - remplace la plage de sélection avec les valeurs de début `anchorNode/anchorOffset` et de fin `focusNode/focusOffset`. Tout le contenu situé entre les deux est sélectionné.\n- `selectAllChildren(node)` -- sélectionne tous les enfants du `node`.\n- `deleteFromDocument()` -- supprime le contenu sélectionné du document.\n- `containsNode(node, allowPartialContainment = false)` -- vérifie si la sélection contient `node` (partiellement si le second argument est `true`).\n\nPour la plupart des tâches, ces méthodes sont suffisantes, il n'y a pas besoin d'accéder à l'objet `Range` sous-jacent.\n\nPar exemple, sélectionner le contenu entier du paragraphe `<p>` :\n\n```html run\n<p id=\"p\">Select me: <i>italic</i> and <b>bold</b></p>\n\n<script>\n  // sélectionnez du 0-ème enfant de <p> au dernier element fils\n  document.getSelection().setBaseAndExtent(p, 0, p, p.childNodes.length);\n</script>\n```\n\nLa même chose en utilisant Range :\n\n```html run\n<p id=\"p\">Select me: <i>italic</i> and <b>bold</b></p>\n\n<script>\n  let range = new Range();\n  range.selectNodeContents(p); // ou selectNode(p) pour sélectionner également la balise <p>\n\n  document.getSelection().removeAllRanges(); // effacer la sélection existante s'il y en a une\n  document.getSelection().addRange(range);\n</script>\n```\n\n```smart header=\"Pour sélectionner quelque chose, il faut d'abord supprimer la sélection existante\"\nSi une sélection du document existe déjà, videz-la d'abord avec `removeAllRanges()`. Puis ajoutez des plages. Sinon, tous les navigateurs, sauf Firefox, ignorent les nouvelles plages.\n\nL'exception est certaines méthodes de sélection, qui remplacent la sélection existante, comme `setBaseAndExtent`.\n```\n\n## Sélection dans les contrôles de formulaires\n\nLes éléments de formulaire, tels que `input` et `textarea` fournissent [une API spéciale pour la sélection](https://html.spec.whatwg.org/#textFieldSelection), sans objets `Selection` ou `Range`. Comme une valeur d'entrée est un texte pur, pas du HTML, il n'y a pas besoin de tels objets, tout est beaucoup plus simple.\n\nPropriétés :\n- `input.selectionStart` -- position du début de la sélection (accessible en écriture).\n- `input.selectionEnd` -- position de la fin de la sélection (accessible en écriture).\n- `input.selectionDirection` -- direction de la sélection, une parmi : \"forward\", \"backward\" ou \"none\" (si, par exemple, la sélection est effectuée par un double clic de souris).\n\nles événements :\n- `input.onselect` -- se déclenche lorsque quelque chose est sélectionné.\n\nMéthodes :\n\n- `input.select()` -- sélectionne tout dans le contrôle de texte (peut être `textarea` au lieu de `input`).\n- `input.setSelectionRange(start, end, [direction])` -- modifie la sélection pour qu'elle s'étende de la position `start` à `end`, dans la direction donnée (facultatif).\n- `input.setRangeText(replacement, [start], [end], [selectionMode])` -- remplace une plage de texte par le nouveau texte.\n\n    Les arguments facultatifs `start` et `end`, s'ils sont fournis, définissent le début et la fin de la plage, sinon la sélection de l'utilisateur est utilisée.\n\n    Le dernier argument, `selectionMode`, détermine comment la sélection sera définie après que le texte ait été remplacé. Les valeurs possibles sont :\n\n    - `\"select\"` -- le texte nouvellement inséré sera sélectionné.\n    - `\"start\"` -- la plage de sélection se réduit juste avant le texte inséré (le curseur sera immédiatement devant).\n    - `\"end\"` -- la plage de sélection se réduit juste après le texte inséré (le curseur sera juste après).\n    - `\"preserve\"` -- tente de préserver la sélection. C'est la valeur par défaut.\n\nVoyons maintenant ces méthodes en action.\n\n### Exemple : suivi de sélection\n\nPar exemple, ce code utilise l'événement `onselect` pour suivre la sélection :\n\n```html run autorun\n<textarea id=\"area\" style=\"width:80%;height:60px\">\nSelecting in this text updates values below.\n</textarea>\n<br>\nFrom <input id=\"from\" disabled> – To <input id=\"to\" disabled>\n\n<script>\n  area.onselect = function() {\n    from.value = area.selectionStart;\n    to.value = area.selectionEnd;\n  };\n</script>\n```\n\nVeuillez noter :\n- `onselect` se déclenche lorsque quelque chose est sélectionné, mais pas lorsque la sélection est supprimée.\n- L'événement `document.onselectionchange` ne devrait pas se déclencher pour les sélections à l'intérieur d'un contrôle de formulaire, selon la [spec](https://w3c.github.io/selection-api/#dfn-selectionchange), car il n'est pas lié à la sélection et aux plages du `document`. Certains navigateurs le génèrent, mais il ne faut pas s'y fier.\n\n\n### Exemple : déplacement du curseur\n\nNous pouvons changer `selectionStart` et `selectionEnd`, cela définit la sélection.\n\nUn cas particulier important est celui où `selectionStart` et `selectionEnd` sont égaux. Dans ce cas, il s'agit exactement de la position du curseur. Ou, pour le dire autrement, lorsque rien n'est sélectionné, la sélection est réduite à la position du curseur.\n\nDonc, en mettant `selectionStart` et `selectionEnd` à la même valeur, on déplace le curseur.\n\nPar exemple :\n\n```html run autorun\n<textarea id=\"area\" style=\"width:80%;height:60px\">\nFocus on me, the cursor will be at position 10.\n</textarea>\n\n<script>\n  area.onfocus = () => {\n    // setTimeout de délai zéro à exécuter après la fin de l'action \"focus\" du navigateur\n    setTimeout(() => {\n      // nous pouvons définir n'importe quelle sélection\n      // si start=end, le curseur se trouve exactement à cet endroit\n      area.selectionStart = area.selectionEnd = 10;\n    });\n  };\n</script>\n```\n\n### Exemple : modifier la sélection\n\nPour modifier le contenu de la sélection, on peut utiliser la méthode `input.setRangeText()`. Bien sûr, nous pouvons lire `selectionStart/End` et, avec la connaissance de la sélection, modifier la chaîne partielle (substring) correspondante de `value`, mais `setRangeText` est plus puissant et souvent plus pratique.\n\nC'est une méthode plus ou moins complexe. Dans sa forme la plus simple à un argument, elle remplace la plage sélectionnée par l'utilisateur et supprime la sélection.\n\nPar exemple, ici la sélection de l'utilisateur sera entourée de `*...*` :\n\n```html run autorun\n<input id=\"input\" style=\"width:200px\" value=\"Select here and click the button\">\n<button id=\"button\">Wrap selection in stars *...*</button>\n\n<script>\nbutton.onclick = () => {\n  if (input.selectionStart == input.selectionEnd) {\n    return; // rien n'est sélectionné\n  }\n\n  let selected = input.value.slice(input.selectionStart, input.selectionEnd);\n  input.setRangeText(`*${selected}*`);\n};\n</script>\n```\n\nAvec plus d'arguments, nous pouvons définir `start` et `end` de range.\n\nDans cet exemple, nous trouvons `\"THIS\"` dans le texte de saisie, le remplaçons et gardons le remplacement sélectionné :\n\n```html run autorun\n<input id=\"input\" style=\"width:200px\" value=\"Replace THIS in text\">\n<button id=\"button\">Replace THIS</button>\n\n<script>\nbutton.onclick = () => {\n  let pos = input.value.indexOf(\"THIS\");\n  if (pos >= 0) {\n    input.setRangeText(\"*THIS*\", pos, pos + 4, \"select\");\n    input.focus(); // focus pour rendre la sélection visible\n  }\n};\n</script>\n```\n\n### Exemple : insérer au curseur\n\nSi rien n'est sélectionné, ou si nous utilisons des `start` et `end` égaux dans `setRangeText`, alors le nouveau texte est juste inséré, rien n'est supprimé.\n\nOn peut aussi insérer quelque chose \"au curseur\" en utilisant `setRangeText`.\n\nVoici un bouton qui insère `\"HELLO\"` à la position du curseur et place le curseur immédiatement après. Si la sélection n'est pas vide, elle est remplacée (nous pouvons le détecter en comparant `selectionStart!=selectionEnd` et faire autre chose à la place) :\n\n```html run autorun\n<input id=\"input\" style=\"width:200px\" value=\"Text Text Text Text Text\">\n<button id=\"button\">Insert \"HELLO\" at cursor</button>\n\n<script>\n  button.onclick = () => {\n    input.setRangeText(\"HELLO\", input.selectionStart, input.selectionEnd, \"end\");\n    input.focus();\n  };\n</script>\n```\n\n\n## Rendre un élément non sélectionnable\n\nPour rendre quelque chose non sélectionnable, il y a trois approches :\n\n1. Utiliser la propriété CSS `user-select: none`.\n\n    ```html run\n    <style>\n    #elem {\n      user-select: none;\n    }\n    </style>\n    <div>Selectable <div id=\"elem\">Unselectable</div> Selectable</div>\n    ```\n\n    Cela ne permet pas à la sélection de commencer à `elem`. Mais l'utilisateur peut commencer la sélection ailleurs et inclure `elem` dans celle-ci.\n\n    Dans ce cas, `elem` deviendra une partie de `document.getSelection()`, et la sélection aura bien lieu, mais son contenu sera généralement ignoré dans le copier-coller.\n\n\n2. Empêcher l'action par défaut dans les événements `onselectstart` ou `mousedown`.\n\n    ```html run\n    <div>Selectable <div id=\"elem\">Unselectable</div> Selectable</div>\n\n    <script>\n      elem.onselectstart = () => false;\n    </script>\n    ```\n\n    Cela empêche de commencer la sélection sur `elem`, mais le visiteur peut la commencer sur un autre élément, puis l'étendre à `elem`.\n\n    C'est pratique quand il y a un autre gestionnaire d'événement sur la même action qui déclenche la sélection (par exemple `mousedown`). Nous désactivons donc la sélection pour éviter tout conflit, tout en permettant au contenu de `elem` d'être copié.\n\n3. On peut aussi effacer la sélection après le fait avec `document.getSelection().empty()`. C'est rarement utilisé, car cela provoque un clignotement indésirable lorsque la sélection apparaît-disparaît.\n\n## Quelques références\n\n- [DOM spec: Range](https://dom.spec.whatwg.org/#ranges)\n- [Selection API](https://www.w3.org/TR/selection-api/#dom-globaleventhandlers-onselectstart)\n- [HTML spec: APIs for the text control selections](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection)\n\n\n## Résumé\n\nNous avons couvert deux API différentes pour les sélections :\n\n1. Pour le document : objets `Selection` et `Range`.\n2. Pour `input`, `textarea` : méthodes et propriétés supplémentaires.\n\nLa deuxième API est très simple, puisqu'elle fonctionne avec du texte.\n\nLes recettes les plus utilisées sont probablement :\n\n1. Obtenir la sélection :\n    ```js\n    let selection = document.getSelection();\n\n    let cloned = /* élément pour cloner les nœuds sélectionnés vers */;\n\n    // puis appliquer les méthodes Range à selection.getRangeAt(0)\n    // ou, comme ici, à toutes les plages (range) pour supporter la sélection multiple.\n    for (let i = 0; i < selection.rangeCount; i++) {\n      cloned.append(selection.getRangeAt(i).cloneContents());\n    }\n    ```\n2. Mise en place de la sélection :\n    ```js\n    let selection = document.getSelection();\n\n    // directement:\n    selection.setBaseAndExtent(...from...to...);\n\n    // ou nous pouvons créer une plage et :\n    selection.removeAllRanges();\n    selection.addRange(range);\n    ```\n\nEt enfin, à propos du curseur. La position du curseur dans les éléments modifiables, comme `<textarea>` est toujours au début ou à la fin de la sélection. Nous pouvons l'utiliser pour obtenir la position du curseur ou pour le déplacer en définissant `elem.selectionStart` et `elem.selectionEnd`.\n"
  },
  {
    "path": "2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/solution.md",
    "content": "La sortie console est : 1 7 3 5 2 6 4.\n\nLa tâche est assez simple, nous avons juste besoin de savoir comment fonctionnent les files d'attente pour microtâches et macrotâches.\n\nVoyons ce qui se passe, étape par étape.\n\n```js\nconsole.log(1);\n// La première ligne s'exécute immédiatement, elle sort `1`.\n// Les files d'attente Macrotask et Microtask sont vides, pour l'instant.\n\nsetTimeout(() => console.log(2));\n// `setTimeout` ajoute la callback à la file d'attente macrotask.\n// - la file d'attente macrotask contient:\n//   `console.log(2)`\n\nPromise.resolve().then(() => console.log(3));\n// La callback est ajoutée à la file d'attente des microtâches.\n// - contenu de la file d'attente des microtâches:\n//   `console.log(3)`\n\nPromise.resolve().then(() => setTimeout(() => console.log(4)));\n// La callback avec `setTimeout (...4) `est ajoutée aux microtâche\n// - contenu de la file d'attente des microtâches:\n//   `console.log(3); setTimeout(...4)`\n\nPromise.resolve().then(() => console.log(5));\n// La callback est ajoutée à la file d'attente des microtâches\n// - contenu de la file d'attente des microtâches:\n//   `console.log(3); setTimeout(...4); console.log(5)`\n\nsetTimeout(() => console.log(6));\n// `setTimeout` ajoute la callback aux macrotasks\n// - contenu de la file d'attente macrotask:\n//   `console.log(2); console.log(6)`\n\nconsole.log(7);\n// Affiche 7 immédiatement.\n```\n\nPour résumer,\n\n1. Les numéros `1` & `7` apparaissent immédiatement, car les appels simples `console.log` n'utilisent aucune file d'attente.\n2. Ensuite, une fois le flux de code principal terminé, la file d'attente des microtâches s'exécute.\n    - Il possède les commandes: `console.log(3); setTimeout(...4); console.log(5) `.\n    - Les nombres `3` & `5` apparaissent, tandis que `setTimeout(() => console.log(4))` ajoute l'appel `console.log(4)` à la fin de la file d'attente macrotask.\n    - La file d'attente macrotask est maintenant: `console.log(2); console.log(6); console.log(4) `.\n3. Une fois la file d'attente des microtâches vide, la file d'attente des macrotasques s'exécute. Il sort `2`, `6`, `4`.\n\nAu final, nous avons la sortie: `1 7 3 5 2 6 4`.\n"
  },
  {
    "path": "2-ui/99-ui-misc/03-event-loop/2-micro-macro-queue/task.md",
    "content": "importance: 5\n\n---\n\n# Quelle sera la sortie de ce code?\n\n```js\nconsole.log(1);\n\nsetTimeout(() => console.log(2));\n\nPromise.resolve().then(() => console.log(3));\n\nPromise.resolve().then(() => setTimeout(() => console.log(4)));\n\nPromise.resolve().then(() => console.log(5));\n\nsetTimeout(() => console.log(6));\n\nconsole.log(7);\n```\n"
  },
  {
    "path": "2-ui/99-ui-misc/03-event-loop/article.md",
    "content": "\n# La boucle d'événement: les microtâches et les macrotâches\n\nLe flux d'exécution JavaScript dans un navigateur, de même que dans Node.js, est basé sur la *boucle d'événement*.\n\nComprendre comment fonctionne la boucle d'événement est important pour les optimisations, et parfois pour le bon choix d'une architecture.\n\nDans ce chapitre, nous couvrons d'abord les détails théoriques sur le fonctionnement des choses, puis nous verrons les applications pratiques de ces connaissances.\n\n## La boucle d'événement\n\nLe concept de *boucle d'événement* est très simple. Il y a une boucle sans fin, où le moteur JavaScript attend les tâches, les exécute puis dort, en attendant plus de tâches.\n\nL'algorithme général du moteur:\n\n1. Tant qu'il y a des tâches:\n    - il les exécute, en commençant par la tâche la plus ancienne.\n2. Dort jusqu'à ce qu'une tâche apparaisse, puis repasse à 1.\n\nC'est une formalisation de ce que nous voyons lors de la navigation sur une page. Le moteur JavaScript ne fait rien la plupart du temps, il ne fonctionne que si un script / gestionnaire / événement s'active.\n\nExemples de tâches:\n\n- Lorsqu'un script externe `< script src = \"...\">` charge, la tâche consiste à l'exécuter.\n- Lorsqu'un utilisateur déplace sa souris, la tâche consiste à envoyer un événement `mousemove` et à exécuter des gestionnaires.\n- Lorsque le temps est écoulé pour un `setTimeout` planifié, la tâche consiste à exécuter la fonction de callback.\n- ...et ainsi de suite.\n\nLes tâches sont définies -- le moteur les gère -- puis attend plus de tâches (tout en dormant et en consommant près de zéro CPU).\n\nIl peut arriver qu'une tâche arrive pendant que le moteur est occupé, il est alors mis en file d'attente.\n\nLes tâches forment une file d'attente, dite \"file d'attente des macrotâches\" (terme v8) :\n\n![](eventLoop.svg)\n\nPar exemple, alors que le moteur est occupé à exécuter un `script`, un utilisateur peut déplacer sa souris provoquant un `mousemove`, et un `setTimeout` peut être écoulé, et ainsi de suite, ces tâches forment une file d'attente, comme illustré sur l'image ci-dessus.\n\nLes tâches de la file d'attente sont traitées sur la base du «premier arrivé - premier servi». Lorsque le navigateur du moteur en a fini avec le `script`, il gère l'événement `mousemove`, puis le gestionnaire `setTimeout`, etc.\n\nJusqu'à présent, c'est assez simple, n'est-ce pas ?\n\nDeux détails supplémentaires:\n1. Le rendu ne se produit jamais pendant que le moteur exécute une tâche. Peu importe que la tâche prenne beaucoup de temps. Les modifications apportées au DOM ne sont peintes qu'après la fin de la tâche.\n2. Si une tâche prend trop de temps, le navigateur ne peut pas effectuer d'autres tâches, telles que le traitement des événements utilisateur. Donc, après un certain temps, cela soulève une alerte du type \"La page ne répond plus\", suggérant de tuer la tâche avec toute la page. Cela se produit lorsqu'il y a beaucoup de calculs complexes ou une erreur de programmation conduisant à une boucle infinie.\n\nC'était la théorie. Voyons maintenant comment nous pouvons appliquer ces connaissances.\n\n## Cas d'utilisation 1: fractionnement des tâches consommatrices de CPU\n\nSupposons que nous avons une tâche gourmande en CPU.\n\nPar exemple, la mise en évidence de la syntaxe (utilisée pour coloriser des exemples de code sur cette page) est assez lourde en termes de CPU. Pour mettre en évidence le code, il effectue l'analyse, crée de nombreux éléments colorés, les ajoute au document - pour une grande quantité de texte qui prend beaucoup de temps.\n\nBien que le moteur soit occupé à mettre en évidence la syntaxe, il ne peut pas faire d'autres trucs liés au DOM, traiter les événements utilisateur, etc. Cela peut même amener le navigateur à \"saccader\" ou même à \"figer\" un peu, ce qui est inacceptable.\n\nNous pouvons éviter les problèmes en divisant la grande tâche en morceaux. Mettez en surbrillance les 100 premières lignes, puis planifiez un `setTimeout` (avec un délai à 0) pour les 100 lignes suivantes, etc.\n\nPour démontrer cette approche, par souci de simplicité, au lieu de la colorisation du texte, prenons une fonction qui compte de `1` à `1000000000`.\n\nSi vous exécutez le code ci-dessous, le moteur va se \"figer\" pendant un certain temps. Pour du JS côté serveur, c'est clairement perceptible, et si vous l'exécutez dans le navigateur, essayez de cliquer sur d'autres boutons de la page - vous verrez qu'aucun autre événement n'est géré jusqu'à la fin du comptage.\n\n```js run\nlet i = 0;\n\nlet start = Date.now();\n\nfunction count() {\n\n  // réalise un gros job\n  for (let j = 0; j < 1e9; j++) {\n    i++;\n  }\n\n  alert(\"Effectué en \" + (Date.now() - start) + 'ms');\n}\n\ncount();\n```\n\nLe navigateur peut même afficher un avertissement \"le script prend trop de temps\".\n\nDivisons le travail en utilisant des appels de `setTimeout` imbriqués :\n\n```js run\nlet i = 0;\n\nlet start = Date.now();\n\nfunction count() {\n\n  // réalise un morceau du gros job (*)\n  do {\n    i++;\n  } while (i % 1e6 != 0);\n\n  if (i == 1e9) {\n    alert(\"Effectué en \" + (Date.now() - start) + 'ms');\n  } else {\n    setTimeout(count); // planifie un nouvel appel (**)\n  }\n\n}\n\ncount();\n```\n\nDésormais, l'interface du navigateur est pleinement fonctionnelle pendant le processus de \"comptage\".\n\nUne seule exécution de `count` fait une partie du travail `(*)`, puis se re-calcule `(**)` si nécessaire:\n\n1. La première manche compte : `i=1...1000000`.\n2. La deuxième manche compte : `i=1000001..2000000`.\n3. ...et ainsi de suite.\n\nMaintenant, si une nouvelle tâche secondaire (par ex. un événement `onclick`) apparaît pendant que le moteur est occupé à exécuter la partie 1, elle est mise en file d'attente, puis s'exécute lorsque la partie 1 est terminée, avant la partie suivante. Les retours périodiques à la boucle d'événement entre les exécutions «count» fournissent juste assez d '«air» pour que le moteur JavaScript fasse autre chose, pour réagir à d'autres actions de l'utilisateur.\n\nLa chose notable est que les deux variantes - avec et sans diviser le travail par `setTimeout` - sont comparables en vitesse. Il n'y a pas beaucoup de différence dans le temps de comptage global.\n\nPour les minimiser, faisons une amélioration.\n\nNous déplacerons la planification au début du `count()`:\n\n```js run\nlet i = 0;\n\nlet start = Date.now();\n\nfunction count() {\n\n  // déplace la planification au début\n  if (i < 1e9 - 1e6) {\n    setTimeout(count); // planifie le nouvel appel\n  }\n\n  do {\n    i++;\n  } while (i % 1e6 != 0);\n\n  if (i == 1e9) {\n    alert(\"Effectué en \" + (Date.now() - start) + 'ms');\n  }\n\n}\n\ncount();\n```\n\nMaintenant, quand nous commençons à `count()` et voyons que nous aurons besoin de `count()` supplémentaires, nous planifions cela immédiatement, avant de faire le travail.\n\nSi vous l'exécutez, il est facile de remarquer que cela prend beaucoup moins de temps.\n\nPourquoi ?  \n\nC'est simple: comme vous vous en souvenez, il y a le retard minimal dans le navigateur de 4 ms pour de nombreux appels de `setTimeout` imbriqués. Même si nous définissons un délai à `0`, c'est 4ms (ou un peu plus). Donc, plus nous les planifions tôt - plus ils s'exécuteront rapidement.\n\nVoilà, nous avons divisé une tâche gourmande en CPU en morceaux - maintenant elle ne bloque pas l'interface utilisateur. Et son temps d'exécution global n'est pas beaucoup plus long.\n\n## Cas d'utilisation 2: indicateur de progression\n\nUn autre avantage de la division de tâches lourdes pour les scripts de navigateur est que nous pouvons afficher un indicateur de progression.\n\nComme mentionné précédemment, les modifications apportées au DOM ne sont peintes qu'après la fin de la tâche en cours d'exécution, quel que soit le temps nécessaire.\n\nD'une part, c'est génial, car notre fonction peut créer de nombreux éléments, les ajouter un par un au document et changer leurs styles - le visiteur ne verra aucun état \"intermédiaire\" et inachevé. Une chose importante, non?\n\nVoici la démo, les modifications apportées à `i` n'apparaîtront pas avant la fin de la fonction, nous ne verrons donc que la dernière valeur :\n\n\n```html run\n<div id=\"progress\"></div>\n\n<script>\n\n  function count() {\n    for (let i = 0; i < 1e6; i++) {\n      i++;\n      progress.innerHTML = i;\n    }\n  }\n\n  count();\n</script>\n```\n\n...Mais nous pouvons également vouloir afficher quelque chose pendant la tâche, par exemple une barre de progression.\n\nSi nous divisons la tâche lourde en morceaux à l'aide d'un `setTimeout`, les modifications sont peintes entre elles.\n\nCela semble plus joli :\n\n```html run\n<div id=\"progress\"></div>\n\n<script>\n  let i = 0;\n\n  function count() {\n\n    // réalise un morceau du travail lourd (*)\n    do {\n      i++;\n      progress.innerHTML = i;\n    } while (i % 1e3 != 0);\n\n    if (i < 1e7) {\n      setTimeout(count);\n    }\n\n  }\n\n  count();\n</script>\n```\n\nMaintenant, la `<div>` montre des valeurs croissantes de `i`, une sorte de barre de progression.\n\n\n## Cas d'utilisation 3: faire quelque chose après l'événement\n\nDans un gestionnaire d'événements, nous pouvons décider de reporter certaines actions jusqu'à ce que l'événement \"bouillonne\" (bubble up) et soit géré à tous les niveaux. Nous pouvons le faire en enveloppant le code dans un `setTimeout` avec un délai nul.\n\nDans le chapitre <info:dispatch-events>, nous avons vu un exemple: l'événement personnalisé `menu-open` est envoyé dans un `setTimeout`, de sorte qu'il se produit après que l'événement \"clic\" soit entièrement géré.\n\n```js\nmenu.onclick = function() {\n  // ...\n\n  // crée un événement personnalisé avec les données de l'élément de menu cliqué\n  let customEvent = new CustomEvent(\"menu-open\", {\n    bubbles: true\n  });\n\n  // dispatche l'événement personnalisé de manière asynchrone\n  setTimeout(() => menu.dispatchEvent(customEvent));\n};\n```\n\n## Les Macrotâches et les Microtâches\n\nAvec les *macrotâches*, décrits dans ce chapitre, il y a les *microtâches*, mentionnés dans le chapitre <info:microtask-queue>.\n\nLes microtâches proviennent uniquement de notre code. Ils sont généralement créés par des Promesses: une exécution du gestionnaire `.then / catch / finally` devient une microtâche. Les microtâches sont également utilisées \"sous la couverture\" d'un `await`, car c'est une autre forme de gestion des Promesses.\n\nIl existe également une fonction spéciale `queueMicrotask(func)` qui met en file d'attente `func` pour l'exécution dans la file d'attente des microtâches.\n\n**Immédiatement après chaque *macrotâche*, le moteur exécute toutes les tâches à partir de la file d'attente des *microtâches*, avant d'exécuter d'autres macrotâches, ou rendu, ou quoi que ce soit d'autre.**\n\nPar exemple, jetez un œil là-dessus:\n\n```js run\nsetTimeout(() => alert(\"timeout\"));\n\nPromise.resolve()\n  .then(() => alert(\"promise\"));\n\nalert(\"code\");\n```\n\nQuel sera l'ordre ici?\n\n1. `code` s'affiche en premier, car il s'agit d'un appel synchrone régulier.\n2. `promise` s'affiche en second, car `.then` passe par la file d'attente des microtâches et s'exécute après le code actuel.\n3. `timeout` s'affiche en dernier, car c'est une macrotâche.\n\nUne image, plus parlante, de la boucle d'événements ressemble à ceci (l'ordre est de haut en bas, c'est-à-dire: le script d'abord, puis les microtâches, le rendu, etc.):\n\n![](eventLoop-full.svg)\n\nToutes les microtâches sont terminées avant toute autre gestion ou rendu d'événement ou toute autre macrotâche.\n\nC'est important, car cela garantit que l'environnement de l'application est fondamentalement le même (pas de changement de coordonnées de souris, pas de nouvelles données réseau, etc.) entre les microtâches.\n\nSi nous souhaitons exécuter une fonction de manière asynchrone (après le code actuel), mais avant que les modifications ne soient rendues ou que de nouveaux événements ne soient traités, nous pouvons la planifier avec un `queueMicrotask`.\n\nVoici un exemple avec le \"calcul de la barre de progression\", similaire à celui illustré précédemment, mais \"queueMicrotask\" est utilisé à la place de \"setTimeout\". Vous pouvez voir que cela se produit à la toute fin. Tout comme le code synchrone :\n\n```html run\n<div id=\"progress\"></div>\n\n<script>\n  let i = 0;\n\n  function count() {\n\n    // réalise un morceau du travail lourd (*)\n    do {\n      i++;\n      progress.innerHTML = i;\n    } while (i % 1e3 != 0);\n\n    if (i < 1e6) {\n  *!*\n      queueMicrotask(count);\n  */!*\n    }\n\n  }\n\n  count();\n</script>\n```\n\n## Résumé\n\nUn algorithme de boucle d'événement plus détaillé (bien que toujours simplifié par rapport à la [spécification](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model)):\n\n1. Dépile et exécute la tâche la plus ancienne de la file d'attente *macrotâches* (par ex. \"script\").\n2. Exécute toutes les *microtâches* :\n    - Tant que la file d'attente des microtâches n'est pas vide :\n        - Dépile et exécute la plus ancienne microtâche.\n3. Le rendu change le cas échéant.\n4. Si la file d'attente des macrotâches est vide, attend qu'une macrotâche apparaisse.\n5. Passez à l'étape 1.\n\nPour planifier une nouvelle *macrotâche*:\n- Utilisez un `setTimeout(f)` avec un délai de 0.\n\nCela peut être utilisé pour fractionner en morceaux une grande tâche de calcul lourd, pour que le navigateur puisse réagir aux événements utilisateur et afficher une progression entre eux.\n\nÉgalement, c'est utilisé dans les gestionnaires d'événements pour planifier une action après que l'événement ait été entièrement géré (\"bubbling\" terminé).\n\nPour planifier une nouvelle *microtâche*\n- Utilisez un `queueMicrotask(f)`.\n- Les gestionnaires de Promesses passent également par la file d'attente des microtâches.\n\nIl n'y a pas de gestion d'interface utilisateur ou d'événement réseau entre les microtâches: elles fonctionnent immédiatement l'une après l'autre.\n\nOn peut donc vouloir utiliser un `queueMicrotask` pour exécuter une fonction de manière asynchrone, mais dans l'état de l'environnement.\n\n```smart header=\"Web Workers\"\nPour les calculs longs et lourds qui ne devraient pas bloquer la boucle d'événement, nous pouvons utiliser les [Web Workers](https://html.spec.whatwg.org/multipage/workers.html).\n\nC'est une façon d'exécuter du code dans un autre thread parallèle.\n\nLes Web Workers peuvent échanger des messages avec le processus principal, mais ils ont leurs propres variables et leur propre boucle d'événements.\n\nLes Web Workers n'ont pas accès au DOM, ils sont donc utiles, principalement, pour les calculs, pour utiliser simultanément plusieurs cœurs CPU.\n```\n"
  },
  {
    "path": "2-ui/99-ui-misc/index.md",
    "content": "\n# Miscellaneous\n"
  },
  {
    "path": "2-ui/index.md",
    "content": "# Navigateur : Document, Évènements, Interfaces \n\nApprendre à gérer les pages du navigateur: ajouter des éléments, manipuler leur taille et leur position, créer dynamiquement des interfaces et interagir avec le visiteur.\n"
  },
  {
    "path": "3-frames-and-windows/01-popup-windows/article.md",
    "content": "# Les méthodes de pop-ups et fenêtres\n\nUne fenêtre pop-up est l'une des plus anciennes méthodes pour montrer un document supplémentaire à l'utilisateur.\n\nLe code suivant :\n\n```js\nwindow.open('https://javascript.info/')\n```\n\n... Et cela Ouvrira simplement une nouvelle fenêtre avec l'url renseignée. La plupart des navigateurs modernes sont configurés pour ouvrir un nouvel onglet plutôt qu'une nouvelle feneêtre.\n\nLes pop-up existent depuis longtemps. L'idée initiale était de montrer du contenu supplémentaire sans fermer la fenêtre principale. Désormais, il y a d'autres manières de faire ça : on peut charger du contenu dynamiquement avec [fetch](info:fetch) et l'afficher dans une `<div>` générée dynamiquement. Les pop-up ne sont donc plus des choses utilisées de nos jours.\n\nLes pop-up sont également délicate sur les appareils mobiles puis que ces derniers ne peuvent pas afficher plusieurs fenêtres simultanément.\n\nPourtant, il y a quelques tâches où les pop-up sont toujours utilisées, par exemple pour les authentifications OAuth (se connecter avec Google/Facebook..) pour les raisons suivantes :\n\n1. Une pop-up est une fenêtre séparée avec son environnement JavaScript indépendant. Donc ouvrir une pop-up d'une tierce partie venant d'un site peu fiable est sécurisé.\n2. Il est très facile d'ouvrir une pop-up.\n3. Une pop-up peut naviguer, changer d'url et envoyer des messages à la fenêtre qui l'a ouverte.\n\n## Bloquage de pop-up\n\nDans le passé, des sites malveillant usaient des pop-up à outrance. Une mauvaise page pouvait ouvrir beaucoup de fenêtre pop-up avec des pubs. Désormais, la plupart des navigateurs essaient de bloquer les pop-up et de protéger les utilisateurs.\n\n**Beaucoup de navigateurs bloquent les pop-up si elles sont appelés à l'extérieur de gestionnaire d'évènements déclenchés par l'utilisateurs comme `onclick`.**\n\nPar exemple :\n\n```js\n// popup bloquée\nwindow.open('https://javascript.info');\n\n// popup autorisée\nbutton.onclick = () => {\n  window.open('https://javascript.info');\n};\n```\n\nDe cette manière, les utilisateurs sont en quelques sortes protégés des pop-up indésirées, mais la fonctionnalité n'est pas complètement désactivée.\n\n## window.open\n\nLa syntaxe pour ouvrir une pop-up est : `window.open(url, name, params)`:\n\nurl\n: C'est l'URL à charger dans la nouvelle fenêtre.\n\nname\n: Le nom de la nouvelle fenêtre. Chaque fenêtre possède un `window.name`, et ici nous pouvons spécifier quelle fenêtre va être utilisée pour le pop-up. S'il existe déjà une fenêtre avec le même nom -- l'URL donnée s'ouvrira dedans, sinon une nouvelle fenêtre est ouverte.\n\nparams\n\n: La chaîne de caractères de la configuration pour la nouvelle fenêtre. Elle peut contenir des paramètres séparés par une virgule. Il ne peut pas y avoir d'espace dans les paramètres, par exemple : `width:200,height=100`.\n\nParamètres de `params` :\n\n- Position :\n  - `left/top` (numeric) -- paramètre la distance de la bordure gauche/supérieure par rapport à la zone de travail. Il y a une limitation : une nouvelle fenêtre ne peux pas dépasser de l'écran ou être positionnée à l'extérieur de celui ci.\n  - `width/height` (numeric) -- paramètre la largeur et la hauteur de la nouvelle fenêtre. La valeur minimale requise est de 100, il est donc impossible de créer une fenêtre invisible.\n- Caractéristiques de la fenêtre:\n  - `menubar` (yes/no) -- affiche ou cache la barre de menu du navigateur de la nouvelle fenêtre.\n  - `toolbar` (yes/no) -- affiche ou cache la barre de navigation (bouton précédente, suivante, actualiser etc) de la nouvelle fenêtre.\n  - `location` (yes/no) -- affiche ou cache la barre d'adresse de la nouvelle fenêtre. Firefox et Internet Explorer n'autorisent pas sa dissimulation par défaut.\n  - `status` (yes/no) -- affiche ou cache la barre d'état de la nouvelle fenêtre. La plupart des navigateurs forcent sa présence.\n  - `resizable` (yes/no) -- autorise ou désactive le redimensionnement de la nouvelle fenêtre. Non recommandé.\n  - `scrollbars` (yes/no) -- autorise ou désactive la barre de défilement de la nouvelle fenêtre. Non recommandé.\n\nIl existe également certains paramètres spécifiques aux navigateurs qui sont moins bien supportés et généralement pas utilisés. Consultez <a href=\"https://developer.mozilla.org/fr/docs/Web/API/Window/open\"> window.open sur MDN</a> pour plus d'exemples.\n\n## Exemple: une fenêtre minimaliste\n\nOuvrons une fenêtre avec le minimum de paramètres fonctionnels juste pour voir quels navigateurs nous autorise à les désactiver :\n\n```js run\nlet params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,\nwidth=0,height=0,left=-1000,top=-1000`;\n\nopen('/', 'test', params);\n```\n\nIci, la plupart des paramètres de fenêtre sont désactivés et la fenêtre est positionnée en dehors de l'écran. Essayons ce code et voyons ce qui arrive. La plupart des navigateurs vont règler les paramètres étranges comme le 0 `width/height` et le `left/top` hors écran. Par exemple, Chrome ouvrira cette fenêtre avec la largeur et la hauteur au maximum, la laissant occuper tout l'écran.\n\nAjoutons maintenant des paramètres `width`, `height`, `left`, `top` raisonnables :\n\n```js run\nlet params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,\nwidth=600,height=300,left=100,top=100`;\n\nopen('/', 'test', params);\n```\n\nLa plupart des navigateurs afficherons l'exemple ci dessus comme exigé.\n\nRègles des paramètres omis:\n\n- Si il n'y a pas de troisième argument passé dans la fonction `open` ou si elle est vide, alors les paramètres par défaut seront utilisés pour la nouvelle fenêtre.\n- Si il existe une chaîne de caractères pour les paramètres, mais que certaines options `yes/no` sont omises, alors celles ci prendrons par défaut la valeur `no`. Donc si vous spécifiez des paramètres, assurez vous de définir explicitement toutes les fonctionnalités requises sur `yes`.\n- Si il n'y a pas de paramètres `left/top`, alors le navigateur essaiera d'ouvrir la nouvelle feneêtre au plus proche de la dernière fenêtre ouverte.\n- Si il n'y a pas de paramètres `width/height`, alors la nouvelle fenêtre seras de la même taille que la dernière fenêtre ouverte.\n\n## Accèder a une pop-up depuis une fenêtre\n\nLa fonction `open` retourne une référence à la nouvelle fenêtre. Celle ci peut être utilisée pour manipuler ses propriétés, changer sa localisation et bien plus.\n\nDans cet exemple, nous générons du contenu en pop-up avec JavaScript :\n\n```js\nlet newWin = window.open(\"about:blank\", \"hello\", \"width=200,height=200\");\n\nnewWin.document.write(\"Hello, world!\");\n```\n\nEt ici nous modifions le contenu après le chargement:\n\n```js run\nlet newWindow = open('/', 'example', 'width=300,height=300')\nnewWindow.focus();\n\nalert(newWindow.location.href); // (*) about:blank, le chargement n'a pas encore commencé\n\nnewWindow.onload = function() {\n  let html = `<div style=\"font-size:30px\">Welcome!</div>`;\n*!*\n  newWindow.document.body.insertAdjacentHTML('afterbegin', html);\n*/!*\n};\n```\n\nIl faut prendre en compte qu'immédiatement après avoir appelé `window.open`, la nouvelle fenêtre ne s'est pas encore chargée. On peut le constater avec `alert` à la ligne `(*)`. Nous devons donc attendre la fonction `onload` pour modifier cette fenêtre. Nous pourrions également utiliser l'évènement `DOMContentLoaded` pour `newWin.document`.\n\n```warn header=\"Same origin policy\"\nLes fenêtres peuvent avoir accès au contenu de chacunes d'entres elles si elles ont la même origine (le même protocol://domain:port).\n\nAutrement, par exemple si la fenêtre principale vient de `site.com` et la pop-up de `gmail.com`, cela devient impossible pour des raisons de sécurité utilisateur. Pour plus de détails, vous pouvez consulter le chapitre <info:cross-window-communication>.\n```\n\n## Accèder à une fenêtre depuis une pop-up\n\nUne pop-up peut accèder à la fenêtre ouvrant en utilisant la référence `window.opener`. Elle vaut `null` pour toutes les fenêtres à part les pop-up.\n\nSi vous lancez le code ci dessous, cela remplacera le contenu de la fenêtre actuelle (ouvrant la pop-up) par \"Test\":\n\n```js run\nlet newWin = window.open(\"about:blank\", \"hello\", \"width=200,height=200\");\n\nnewWin.document.write(\n  \"<script>window.opener.document.body.innerHTML = 'Test'<\\/script>\"\n);\n```\n\nLa connexion entres les fenêtres est donc bidirectionnelle : la fenêtre principale et la pop-up ont une référence l'une envers l'autre.\n\n## Fermez une pop-up\n\nPour fermer une pop-up: `win.close()`.\n\nPour vérifier si la fenêtre est fermée: `win.closed`.\n\nEn théorie, la méthode `close()` est disponible pour n'importe quelle `window` mais `window.close()` est ignorée par la plupart des navigateurs si `window` n'a pas été créer avec `window.open()`. Cela devrait donc fonctionner seulement sur les pop-up.\n\nLa propriété `closed` est `true` si la fenêtre est fermée. C'est utilise pour vérifier si la pop-up (ou la fenêtre principale) est toujours ouverte ou non. Un utilisateur peut la fermer à tout moment , et le code devrait en tenir compte.\n\nCe code charge et ferme une fenêtre:\n\n```js run\nlet newWindow = open('/', 'example', 'width=300,height=300');\n\nnewWindow.onload = function() {\n  newWindow.close();\n  alert(newWindow.closed); // true\n};\n```\n\n## Se déplacer et redimensionner\n\nIl existe des méthodes pour bouger/redimensionner une fenêtre:\n\n`win.moveBy(x,y)`\n: Déplace la fenêtre de sa potion actuelle `x` pixels vers la droite et `y` pixels vers le bas. Les valeurs négatives sont autorisées (pour bouger la fenêtre vers la gauche/vers le haut).\n\n`win.moveTo(x,y)`\n: Déplace la fenêtre aux coordonnées `(x,y)` sur l'écran.\n\n`win.resizeBy(width,height)`\n: Redimensionne la fenêtre avec les paramètres `width/height` relatif à la taille actuelle. Les valeurs négatives sont autorisées.\n\n\n`win.resizeTo(width,height)`\n: Redimensionne la fenêtre à la taille donnée.\n\nIl existe un évènement `window.onresize`.\n\n```warn header=\"Only popups\"\nPour éviter les abus, le navigateur bloque généralement ces méthodes. Elles ne fonctionnent de manière fiable que sur les popups que nous avons ouverts, qui n'ont pas d'onglet supplémentaire.\n```\n\n```warn header=\"No minification/maximization\"\nJavaScript n'a aucun moyen de réduire ou d'agrandir une fenêtre. Ces fonctions au niveau du système d'exploitation sont cachées aux développeurs de Frontend.\n\nLes méthodes de déplacement/redimensionnement ne fonctionnent pas pour les fenêtres maximisées/minimisées.\n```\n\n## Défilement d'une fenêtre\n\nNous avons déjà parlé du défilement d'une fenêtre dans le chapitre <info:size-and-scroll-window>.\n\n`win.scrollBy(x,y)`\n: Fait défiler la fenêtre `x` pixels vers la droite et `y` vers le bas par rapport au défilement actuel. Les valeurs négatives sont autorisées.\n\n`win.scrollTo(x,y)`\n: Faites défiler la fenêtre jusqu'aux coordonnées données `(x,y)`.\n\n`elem.scrollIntoView(top = true)`\n: Fait défiler la fenêtre pour que les `elem` apparaissent en haut (par défaut) ou en bas pour `elem.scrollIntoView(false)`.\n\nIl existe un évènement `window.onscroll`.\n\n## Mettre ou enlever le focus sur une fenêtre\n\nThéoriquement, il existe des méthodes `window.focus()` et `window.blur()` pour faire/défaire le focus sur une fenêtre.  Il existe également des événements `focus/blur` qui permettent de saisir le moment où le visiteur focus une fenêtre et passe ailleurs.\n\nBien que, dans la pratique, ils soient sévèrement limités, car dans le passé, des pages malveillantes en abusaient.\n\nPar exemple, regardez ce code :\n\n```js run\nwindow.onblur = () => window.focus();\n```\n\nLorsqu'un utilisateur tente de sortir de la fenêtre (`window.onblur`), le focus va revenir dessus. L'intention est de \"verrouiller\" l'utilisateur dans la `window`.\n\nLes navigateurs ont donc dû introduire de nombreuses limitations pour interdire ce code et protéger l'utilisateur des publicités et des pages maléfiques. Elles dépendent du navigateur.\n\nPar exemple, un navigateur mobile ignore généralement complètement `window.focus()`. De même, le focus ne fonctionne pas lorsqu'une fenêtre contextuelle s'ouvre dans un onglet séparé plutôt que dans une nouvelle fenêtre.\n\nPourtant, il existe des cas d'utilisation où de tels appels fonctionnent et peuvent être utiles.\n\nPar exemple :\n\n- Lorsque nous ouvrons une pop-up, l peut être judicieux d'y lancer un `newWindow.focus()`. Juste au cas où, pour certaines combinaisons de systèmes d'exploitation/navigateurs, cela garantit que l'utilisateur se trouve maintenant dans la nouvelle fenêtre.\n- Si nous voulons savoir quand un visiteur utilise réellement notre application Web, nous pouvons suivre `window.onfocus/onblur`. Cela nous permet de suspendre/reprendre les activités sur la page, les animations, etc. Mais veuillez noter que l'événement `blur` signifie que le visiteur est sorti de la fenêtre, mais qu'il peut toujours l'observer. La fenêtre est en arrière-plan, mais peut toujours être visible.\n\n## Résumé\n\nLes fenêtres pop-up sont rarement utilisées, car il existe des alternatives : chargement et affichage des informations dans la page, ou dans l'iframe.\n\nSi nous devons ouvrir une pop-up, une bonne pratique consiste à en informer l'utilisateur. Une icône \"fenêtre d'ouverture\" à proximité d'un lien ou d'un bouton permettrait au visiteur de suivre le changement de focus et de garder les deux fenêtres à l'esprit.\n\n- Une pop-up peut être ouvert par l'appel `open(url, nom, params)`. Il renvoie la référence à la fenêtre nouvellement ouverte.\n- Les navigateurs bloquent les appels `open` du code en dehors des actions de l'utilisateur. Habituellement, une notification apparaît, afin que l'utilisateur puisse les autoriser.\n- Les navigateurs ouvrent un nouvel onglet par défaut, mais si les tailles sont précisées, il s'agira d'une fenêtre contextuelle.\n- La pop-up peut accéder à la fenêtre d'ouverture en utilisant la propriété `window.opener`.\n- La fenêtre principale et le popup peuvent se lire et se modifier librement s'ils ont la même origine. Dans le cas contraire, ils peuvent changer de localisation et [échanger des messages](info:cross-window-communication).\n\nPour ferme une pop-up: utilisez l'appel `close()`. L'utilisateur peut également les fermer (comme toutes les autres fenêtres). Le `window.closed` est `true` après ça.\n\n- Les méthodes `focus()` et `blur()` permettent de faire/défaire le focus sur une fenêtre. Mais elles ne fonctionnent pas tout le temps.\n- Les évènements `focus` et `blur` permettent de suivre les manœuvres d'entrée et de sortie de la fenêtre. Mais veuillez noter qu'une fenêtre peut être encore visible même en arrière-plan, après le `blur`.\n"
  },
  {
    "path": "3-frames-and-windows/03-cross-window-communication/article.md",
    "content": "# Communication entre les fenêtres\n\nLa politique \"Same Origin\" (même site) limite l'accès des fenêtres et des iframe les uns aux autres.\n\nL'idée est que si un utilisateur a deux pages ouvertes : une de `john-smith.com`, et une autre de `gmail.com`, alors il ne voudra pas d'un script de `john-smith.com` pour lire son courrier de `gmail.com`. L'objectif de la politique \"Same origin\" est donc de protéger les utilisateurs contre le vol d'informations.\n\n## Same Origin [#same-origin]\n\nDeux URLs sont dites de \"Same origin\" si elles ont le mêmes protocole, domaine et port.\n\nCes URLs partagent toutes la même origine (\"Same origin\"):\n\n- `http://site.com`\n- `http://site.com/`\n- `http://site.com/my/page.html`\n\nCe n'est pas le cas de celles ci:\n\n- <code>http://<b>www.</b>site.com</code> (différent domaine: `www.` importe)\n- <code>http://<b>site.org</b></code> (différent domaine: `.org` importe)\n- <code><b>https://</b>site.com</code> (différent protocole: `https`)\n- <code>http://site.com:<b>8080</b></code> (différent port: `8080`)\n\nLa politique \"Same Origin\" indique que:\n\n- si nous avons une référence à une autre fenêtre, par exemple un popup créé par `window.open` ou une fenêtre à l'intérieur de `<iframe>`, et que cette fenêtre provient de la même origine, alors nous avons un accès complet à cette fenêtre.\n- sinon, s'il provient d'une autre origine, alors nous ne pouvons pas accéder au contenu de cette fenêtre : variables, document, quoi que ce soit. La seule exception est la `location`:nous pouvons la modifier (et donc rediriger l'utilisateur). Mais nous ne pouvons pas *lire* la localisation (donc nous ne pouvons pas voir où se trouve l'utilisateur, pas de fuite d'information).\n\n### En action: iframe\n\nUne balise `<iframe>` héberge une fenêtre intégrée séparée, avec ses propres objets `document` et `window` séparés.\n\nNous pouvons y accèder en utilisant ces propriétés:\n\n- `iframe.contentWindow` pour accèder à la fenêtre à l'intérieur de `<iframe>`.\n- `iframe.contentDocument` pour accèder au document à l'intérieur de `<iframe>`, abréviation de `iframe.contentWindow.document`.\n\nLorsque nous accédons à quelque chose à l'intérieur de la fenêtre intégrée, le navigateur vérifie si l'iframe a la même origine. Si ce n'est pas le cas, l'accès est refusé  (`location` est une exception, c'est toujours autorisé).\n\nPar exemple, essayons de lire et d'écrire dans un `<iframe>` d'une autre origine :\n\n```html run\n<iframe src=\"https://example.com\" id=\"iframe\"></iframe>\n\n<script>\n  iframe.onload = function() {\n    // nous pouvons obtenir la référence de la fenêtre intérieure\n*!*\n    let iframeWindow = iframe.contentWindow; // OK\n*/!*\n    try {\n      // ...mais pas du document à l'intérieur\n*!*\n      let doc = iframe.contentDocument; // ERREUR\n*/!*\n    } catch(e) {\n      alert(e); // Erreur de sécurité (origine différente)\n    }\n\n    // Nous ne pouvons pas non plus LIRE l'URL de la page dans l'iframe\n    try {\n      // impossible de lire l'URL de l'objet Location\n*!*\n      let href = iframe.contentWindow.location.href; // ERREUR\n*/!*\n    } catch(e) {\n      alert(e); // Erreur de sécurité\n    }\n\n    // ...nous pouvons ECRIRE dans l'objet Location (et ainsi charger quelque chose d'autre dans l'iframe)!\n*!*\n    iframe.contentWindow.location = '/'; // OK\n*/!*\n\n    iframe.onload = null; // libère le gestionnaire, ne pas l'exécuter après le changement de Location\n  };\n</script>\n```\n\nLe code ci-dessus indique les erreurs pour toutes les opérations sauf:\n\n- Obtenir la référence de la fenêtre intérieure `iframe.contentWindow` - c'est autorisé.\n- Ecrire dans `location`.\n\nAu contraire, si `<iframe>` possède la même origine, nous pouvons tout faire avec:\n\n```html run\n<!-- iframe venant du même site -->\n<iframe src=\"/\" id=\"iframe\"></iframe>\n\n<script>\n  iframe.onload = function() {\n    // faites tout ce que vous voulez\n    iframe.contentDocument.body.prepend(\"Hello, world!\");\n  };\n</script>\n```\n\n```smart header=\"iframe.onload vs iframe.contentWindow.onload\"\nL'évènement `iframe.onload` (dans la balise `<iframe>`) est essentiellement la même chose que `iframe.contentWindow.onload` (sur l'objet fenêtre intégré). Il se déclenche lorsque la fenêtre intégrée se charge complètement avec toutes les ressources.\n\n...Mais nous ne pouvons pas accéder à `iframe.contentWindow.onload` pour une iframe provenant d'une autre origine, donc il faut utiliser `iframe.onload`.\n```\n\n## Fenêtre en sous-domaine: document.domain\n\nPar définition, deux URLs avec des domaines différents ont des origines différentes.\n\nMais si des fenêtres partagent le même domaine de second niveau, par exemple `john.site.com`, `peter.site.com` et `site.com` (de sorte que leur domaine de second niveau commun est `site.com`), nous pouvons faire en sorte que le navigateur ignore cette différence, afin qu'elles puissent être considérées comme provenant de la \"même origine\" pour utiliser la communication entre fenêtres.\n\nPour que cela fonctionne, chacune de ces fenêtres doit exécuter le code:\n\n```js\ndocument.domain = 'site.com';\n```\n\nC'est tout. Ils peuvent maintenant interagir sans limites. Encore une fois, cela n'est possible que pour les pages ayant le même domaine de second niveau.\n\n```warn header=\"Obsolète, mais fonctionne toujours\"\nLa propriété `document.domain` est en cours de suppression de la [spécification](https://html.spec.whatwg.org/multipage/origin.html#relaxing-the-same-origin-restriction). La messagerie inter-fenêtres (expliquée ci-dessous) est le remplacement suggéré.\n\nCela dit, actuellement tous les navigateurs le supportent. Et le support sera conservé pour l'avenir, pour ne pas casser l'ancien code qui repose sur `document.domain`.\n```\n\n\n## Iframe: le piège du mauvais document\n\nLorsqu'une iframe provient de la même origine, et que nous pouvons accéder à son  `document`, il y a un piège. Ce n'est pas lié à des questions d'origine croisée, mais il est important de le savoir.\n\nDès sa création, une iframe dispose immédiatement d'un document. Mais ce document est différent de celui qui s'y charge !\n\nDonc, si nous faisons quelque chose avec le document immédiatement, il sera probablement perdu.\n\nVoici par exemple:\n\n\n```html run\n<iframe src=\"/\" id=\"iframe\"></iframe>\n\n<script>\n  let oldDoc = iframe.contentDocument;\n  iframe.onload = function() {\n    let newDoc = iframe.contentDocument;\n*!*\n    // le document chargé est différent du document initial !\n    alert(oldDoc == newDoc); // false\n*/!*\n  };\n</script>\n```\n\nNous ne devrions pas travailler avec le document d'une iframe qui n'a pas finis de charger, car c'est le *mauvais document*. Si nous y plaçons des gestionnaires d'événements, ils seront ignorés.\n\nComment détecter le moment où le bon document est là ?\n\nLe bon document est définitivement présent quand  `iframe.onload` se déclenche. Mais il ne se déclenche que lorsque toute l'iframe avec toutes les ressources est chargée.\n\nNous pouvons essayer de saisir le moment plus tôt en utilisant `setInterval`:\n\n```html run\n<iframe src=\"/\" id=\"iframe\"></iframe>\n\n<script>\n  let oldDoc = iframe.contentDocument;\n\n  // toutes les 100ms on vérifie que le document est le nouveau\n  let timer = setInterval(() => {\n    let newDoc = iframe.contentDocument;\n    if (newDoc == oldDoc) return;\n\n    alert(\"New document is here!\");\n\n    clearInterval(timer); // annule setInterval, nous n'en n'avons plus besoin\n  }, 100);\n</script>\n```\n\n## Collection: window.frames\n\nUne autre façon d'obtenir l'objet fenêtre (window) de `<iframe>` -- est de l'obtenir à partir de la collection nommée `window.frames` :\n\n- Par nombre: `window.frames[0]` -- l'objet fenêtre pour la première frame du document.\n- Par nom: `window.frames.iframeName` -- l'objet fenêtre pour la frame avec  `name=\"iframeName\"`.\n\nPar exemple:\n\n```html run\n<iframe src=\"/\" style=\"height:80px\" name=\"win\" id=\"iframe\"></iframe>\n\n<script>\n  alert(iframe.contentWindow == frames[0]); // true\n  alert(iframe.contentWindow == frames.win); // true\n</script>\n```\n\nUne iframe peut contenir d'autres iframes. Les objets `window` correspondants forment une hiérarchie.\n\nLes liens de navigation sont:\n\n- `window.frames` -- la collection de fenêtres \"enfants\" (pour les cadres emboîtés).\n- `window.parent` -- la référence à la fenêtre \"parent\" (extérieure).\n- `window.top` -- la référence à la fenêtre parentale la plus élevée.\n\nPar exemple:\n\n```js run\nwindow.frames[0].parent === window; // true\n```\n\nNous pouvons utiliser la propriété `top` pour vérifier si le document actuel est ouvert à l'intérieur d'un cadre ou non:\n\n```js run\nif (window == top) { // fenêtre actuelle == window.top?\n  alert('The script is in the topmost window, not in a frame');\n} else {\n  alert('The script runs in a frame!');\n}\n```\n\n## L'attribut iframe \"sandbox\"\n\nL'attribut `sandbox` permet d'exclure certaines actions à l'intérieur d'une `<iframe>` afin d'empêcher l'exécution de code non fiable. Il \"sandboxe\" l'iframe en la traitant comme provenant d'une autre origine et/ou en lui appliquant d'autres limitations.\n\nIl y a un \"ensemble par défaut\" de restrictions appliquées pour `<iframe sandbox src=\"...\">`. Mais il peut être assoupli si nous fournissons une liste de restrictions séparées par des espaces qui ne doivent pas être appliquées comme valeur de l'attribut, comme ceci : `<iframe sandbox=\"allow-forms allow-popups\">`.\n\nEn d'autres termes, un attribut `\"sandbox\"` vide pose les limites les plus strictes possibles, mais nous pouvons mettre une liste délimitée par des espaces de celles que nous voulons lever.\n\nVoici la liste des limitations:\n\n`allow-same-origin`\n: Par défaut `\"sandbox\"` impose la politique des \"origines différentes\" pour l'iframe. En d'autres termes, elle oblige le navigateur à traiter l' `iframe` comme provenant d'une autre origine, même si sa `src`  pointe vers le même site. Avec toutes les restrictions implicites pour les scripts. Cette option supprime cette fonctionnalité.\n\n`allow-top-navigation`\n: Permet à l'`iframe` de changer `parent.location`.\n\n`allow-forms`\n: Permet de soumettre des formulaires à partir de l'`iframe`.\n\n`allow-scripts`\n: Permet d'exécuter des scripts à partir de l'`iframe`.\n\n`allow-popups`\n: Permet de `window.open` des pop-up depuis l'`iframe`\n\nVoir [le manuel](mdn:/HTML/Element/iframe) pour plus de détails.\n\nL'exemple ci-dessous montre une iframe en \"sandbox\" avec l'ensemble des restrictions par défaut : `<iframe sandbox src=\"...\">`. Il contient du JavaScript et un formulaire.\n\nNous pouvons noter que rien ne fonctionne. Le réglage par défaut est donc très sévère :\n\n[codetabs src=\"sandbox\" height=140]\n\n\n```smart\nLe but de l'attribut `\"sandbox\"` est uniquement *d'ajouter* des restrictions. Il ne peut pas les supprimer. En particulier, il ne peut pas assouplir les restrictions de même origine si l'iframe provient d'une autre origine.\n```\n\n## Messagerie entre fenêtres\n\nL'interface `postMessage` permet aux fenêtres de se parler, quelle que soit leur origine.\n\nC'est donc un moyen de contourner la politique de \"Same Origin\"  Elle permet à une fenêtre de `john-smith.com` de parler à `gmail.com` et d'échanger des informations, mais seulement si les deux parties sont d'accord et appellent les fonctions JavaScript correspondantes. Cela rend le système sûr pour les utilisateurs.\n\nL'interface comporte deux parties.\n\n### postMessage\n\nLa fenêtre qui veut envoyer un message appelle la méthode [postMessage](mdn:api/Window.postMessage) de la fenêtre de réception. En d'autres termes, si nous voulons envoyer le message à `win`, nous devons appeler `win.postMessage(data, targetOrigin)`.\n\nArguments:\n\n`data`\n: Les données à envoyer. Peut être n'importe quel objet, les données sont clonées à l'aide de l'\"algorithme de clonage structuré\". IE ne supporte que les chaînes de caractères, nous devrions donc `JSON.stringify` des objets complexes pour ce navigateur.\n\n\n`targetOrigin`\n: Spécifie l'origine de la fenêtre cible, de sorte que seule une fenêtre de l'origine donnée recevra le message.\n\nLe `targetOrigin` est une mesure de sécurité. Rappelez-vous que si la fenêtre cible provient d'une autre origine, nous ne pouvons pas lire sa `location` dans la fenêtre de l'expéditeur. Nous ne pouvons donc pas savoir quel site est ouvert dans la fenêtre cible à l'heure actuelle : l'utilisateur pourrait naviguer ailleurs, et la fenêtre émettrice n'en aurais aucune idée.\n\nEn spécifiant `targetOrigin` , on s'assure que la fenêtre ne reçoit les données que si elle se trouve toujours au bon endroit. C'est important lorsque les données sont sensibles.\n\nPar exemple, ici `win` ne recevra le message que s'il possède un document de l'origine `http://example.com` :\n\n```html no-beautify\n<iframe src=\"http://example.com\" name=\"example\">\n\n<script>\n  let win = window.frames.example;\n\n  win.postMessage(\"message\", \"http://example.com\");\n</script>\n```\n\nSi nous ne voulons pas de ce contrôle, nous pouvons régler `targetOrigin` sur `*`.\n\n```html no-beautify\n<iframe src=\"http://example.com\" name=\"example\">\n\n<script>\n  let win = window.frames.example;\n\n*!*\n  win.postMessage(\"message\", \"*\");\n*/!*\n</script>\n```\n\n\n### onmessage\n\nPour recevoir un message, la fenêtre cible doit avoir un gestionnaire sur l'événement `message`.  Il se déclenche lorsque `postMessage` est appelé (et que la vérification `targetOrigin` est réussie).\n\nL'objet de l'événement a des propriétés particulières :\n\n`data`\n: Les données de `postMessage`.\n\n`origin`\n: L'origine de l'expéditeur, par exemple `http://javascript.info`.\n\n`source`\n: La référence à la fenêtre de l'expéditeur. Nous pouvons immédiatement faire revenir `source.postMessage(...)` si nous le voulons.\n\nPour assigner ce gestionnaire, nous devrions utiliser `addEventListener`, la courte syntaxe `window.onmessage` ne fonctionne pas.\n\nVoici un exemple:\n\n```js\nwindow.addEventListener(\"message\", function(event) {\n  if (event.origin != 'http://javascript.info') {\n    // quelque chose d'un domaine inconnu, ignorons-le\n    return;\n  }\n\n  alert( \"received: \" + event.data );\n\n  // peut envoyer un message en retour en utilisant event.source.postMessage(...)\n});\n```\n\nL'exemple complet:\n\n[codetabs src=\"postmessage\" height=120]\n\n## Résumé\n\nPour appeler des méthodes et accéder au contenu d'une autre fenêtre, nous devons d'abord en avoir une référence.\n\nPour les pop-ups, nous avons ces références:\n- De la fenêtre ouvrante : `window.open` -- ouvre une nouvelle fenêtre et renvoie une référence à celle-ci,\n- From the popup: `window.opener` -- is a reference to the opener window from a popup.\n\nPour les iframes, nous pouvons accéder aux fenêtres parents/enfants en utilisant :\n- `window.frames` -- une collection d'objets de fenêtres emboîtées,\n- `window.parent`, `window.top` sont les références aux fenêtres parents et supérieures,\n- `iframe.contentWindow` est la fenêtre à l'intérieur d'une balise `<iframe>` .\n\nSi les fenêtres partagent la même origine (hôte, port, protocole), alors les fenêtres peuvent faire ce qu'elles veulent les unes avec les autres.\n\nSinon, les seules actions possibles sont:\n- Modifier la `location` d'une autre fenêtre (accès en écriture uniquement)..\n- Poster un message.\n\nLes exceptions sont:\n-  Les fenêtres qui partagent le même domaine de second niveau : `a.site.com` et `b.site.com`. Le fait de mettre `document.domain='site.com'` dans les deux les met dans l'état \"same origin\".\n- Si une iframe possède un attribut `sandbox` elle est mise de force dans l'état \"different origin\" à moins que la valeur de l'attribut ne spécifie `allow-same-origin`. Cela peut être utilisé pour exécuter du code non fiable dans les iframes du même site.\n\nL'interface `postMessage` permet à deux fenêtres, quelle que soit leur origine, de dialoguer:\n\n1. L'expéditeur appelle `targetWin.postMessage(data, targetOrigin)`.\n2. Si `targetOrigin` n'est pas `'*'`, alors le navigateur vérifie si la fenêtre `targetWin` possède l'origine `targetOrigin`.\n3. Si c'est le cas, alors `targetWin` déclenche l'événement `message` avec des propriétés spéciales:\n    - `origin` -- l'origine de la fenêtre de l'expéditeur (comme `http://my.site.com`)\n    - `source` -- la référence à la fenêtre de l'expéditeur.\n    - `data` -- les données, tout objet partout sauf dans IE qui ne supporte que des chaînes de caractères.\n\n    Nous devrions utiliser `addEventListener` pour définir le gestionnaire de cet événement à l'intérieur de la fenêtre cible.\n"
  },
  {
    "path": "3-frames-and-windows/03-cross-window-communication/postmessage.view/iframe.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body>\n\n  Receiving iframe.\n  <script>\n    window.addEventListener('message', function(event) {\n      alert(`Received ${event.data} from ${event.origin}`);\n    });\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/03-cross-window-communication/postmessage.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body>\n\n  <form id=\"form\">\n    <input type=\"text\" placeholder=\"Enter message\" name=\"message\">\n    <input type=\"submit\" value=\"Click to send\">\n  </form>\n\n  <iframe src=\"iframe.html\" id=\"iframe\" style=\"display:block;height:60px\"></iframe>\n\n  <script>\n    form.onsubmit = function() {\n      iframe.contentWindow.postMessage(this.message.value, '*');\n      return false;\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/03-cross-window-communication/sandbox.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body>\n\n  <div>The iframe below has the <code>sandbox</code> attribute.</div>\n\n  <iframe sandbox src=\"sandboxed.html\" style=\"height:60px;width:90%\"></iframe>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/03-cross-window-communication/sandbox.view/sandboxed.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body>\n\n  <button onclick=\"alert(123)\">Click to run a script (doesn't work)</button>\n\n  <form action=\"http://google.com\">\n    <input type=\"text\">\n    <input type=\"submit\" value=\"Submit (doesn't work)\">\n  </form>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/article.md",
    "content": "# L'attaque par clickjacking\n\nL'attaque par \"clickjacking\" permet à une page malveillante de cliquer sur un \"site victime\" *au nom du visiteur*.\n\nDe nombreux sites ont été piratés de cette manière, notamment Twitter, Facebook, Paypal et d'autres sites. Ils ont tous été réparés, bien sûr.\n\n## L'idée\n\nL'idée est très simple.\n\nVoici comment le clickjacking a été fait avec Facebook:\n\n1. Un visiteur est attiré vers la page malveillante. Peu importe comment.\n2. La page contient un lien inoffensif (du type \"enrichissez-vous maintenant\" ou \"cliquez ici, très drôle\").\n3. Au-dessus de ce lien, la page maléfique place un `<iframe>` transparent avec `src` de facebook.com, de telle sorte que le bouton \"J'aime\" se trouve juste au-dessus de ce lien. Habituellement, cela se fait avec `z-index`.\n4. En essayant de cliquer sur le lien, le visiteur clique en fait sur le bouton.\n\n## La démo\n\nVoici à quoi ressemble la page malveillante. Pour que les choses soient claires, le `<iframe>` est semi-transparent (dans les vraies pages maléfiques, il est totalement transparent):\n\n```html run height=120 no-beautify\n<style>\niframe { /* iframe du site de la victime */\n  width: 400px;\n  height: 100px;\n  position: absolute;\n  top:0; left:-20px;\n*!*\n  opacity: 0.5; /* en réalité opacity:0 */\n*/!*\n  z-index: 1;\n}\n</style>\n\n<div>Click to get rich now:</div>\n\n<!-- L'url du site de la victime -->\n*!*\n<iframe src=\"/clickjacking/facebook.html\"></iframe>\n\n<button>Click here!</button>\n*/!*\n\n<div>...And you're cool (I'm a cool hacker actually)!</div>\n```\n\nLa démo complète de l'attaque:\n\n[codetabs src=\"clickjacking-visible\" height=160]\n\nIci, nous avons un `<iframe src=\"facebook.html\">` semi-transparent, et dans l'exemple, nous pouvons le voir planer au-dessus du bouton. En cliquant sur le bouton, on clique en fait sur l'iframe, mais l'utilisateur ne le voit pas, car l'iframe est transparent.\n\nPar conséquent, si le visiteur est connecté sur Facebook (l'option \"Se souvenir de moi\" est généralement activée), un bouton \"J'aime\" est ajouté. Sur Twitter, ce serait un bouton \"Suivre\".\n\nVoici le même exemple, mais plus proche de la réalité, avec `opacity:0` pour `<iframe>`:\n\n[codetabs src=\"clickjacking\" height=160]\n\nTout ce dont nous avons besoin pour attaquer -- c'est de positionner le `<iframe>` sur la page malveillante de manière à ce que le bouton soit juste au-dessus du lien. Ainsi, lorsqu'un utilisateur clique sur le lien, il clique en fait sur le bouton. C'est généralement faisable avec CSS.\n\n```smart header=\"Le clickjacking est pour les clics, pas pour le clavier\"\nL'attaque n'affecte que les actions de la souris (ou des actions similaires, comme les tapotements sur les mobiles).\n\nLa saisie au clavier est très difficile à rediriger. Techniquement, si nous avons un champ de texte à pirater, nous pouvons alors positionner un iframe de telle sorte que les champs de texte se chevauchent. Ainsi, lorsqu'un visiteur essaie de se concentrer sur la saisie qu'il voit sur la page, il se concentre en fait sur la saisie à l'intérieur de l'iframe.\n\nMais il y a un problème. Tout ce que le visiteur tape sera caché, car l'iframe n'est pas visible.\n\nLes gens s'arrêtent généralement de taper lorsqu'ils ne voient pas leurs nouveaux caractères s'imprimer à l'écran.\n```\n\n## Défenses de la vieille école (faibles)\n\nLa défense la plus ancienne est un bout de JavaScript qui interdit l'ouverture de la page dans un iframe (ce qu'on appelle le \"framebusting\").\n\nCela ressemble à ceci:\n\n```js\nif (top != window) {\n  top.location = window.location;\n}\n```\n\nC'est-à-dire que si la fenêtre découvre qu'elle n'est pas en haut, elle se met automatiquement en haut.\n\nCe n'est pas une défense fiable, car il existe de nombreuses façons de la contourner. En voici quelques-unes.\n\n### Blocage de la navigation supérieure\n\nNous pouvons bloquer la transition provoquée par le changement de `top.location` dans l'événement [beforeunload](info:onload-ondomcontentloaded#window.onbeforeunload).\n\nLa page du haut (appartenant au pirate) lui attribue un événement de prévention, comme ceci:\n\n```js\nwindow.onbeforeunload = function() {\n  return false;\n};\n```\n\nLorsque le `iframe` essaie de changer `top.location`, le visiteur reçoit un message lui demandant s'il veut quitter le site.\n\nDans la plupart des cas, le visiteur répondra négativement parce qu'il ne connaît pas l'iframe - tout ce qu'il peut voir est la page d'accueil, il n'a aucune raison de la quitter. Donc `top.location` ne changera pas!\n\nEn action:\n\n[codetabs src=\"top-location\"]\n\n### L'attribut Sandbox\n\nL'une des choses limitées par l'attribut `sandbox` est la navigation. Une iframe protégée par un sandbox ne peut pas modifier l'attribut `top.location`.\n\nNous pouvons donc ajouter l'iframe avec `sandbox=\"allow-scripts allow-forms\"`. Cela assouplirait les restrictions, autorisant les scripts et les formulaires. Mais nous omettons `allow-top-navigation` pour que la modification de `top.location` soit interdite.\n\nVoici le code:\n\n```html\n<iframe *!*sandbox=\"allow-scripts allow-forms\"*/!* src=\"facebook.html\"></iframe>\n```\n\nIl existe également d'autres moyens de contourner cette simple protection.\n\n## X-Frame-Options\n\nL'en-tête `X-Frame-Options` côté serveur peut autoriser ou interdire l'affichage de la page dans un iframe.\n\nIl doit être envoyé exactement comme un en-tête HTTP : le navigateur l'ignorera s'il se trouve dans la balise HTML `<meta>`. Ainsi, `<meta http-equiv=\"X-Frame-Options\"...>` ne fera rien.\n\nL'en-tête peut avoir 3 valeurs:\n\n\n`DENY`\n: Ne jamais afficher la page à l'intérieur d'un iframe.\n\n`SAMEORIGIN`\n: Autoriser à l'intérieur d'un iframe si le document parent a la même origine..\n\n`ALLOW-FROM domain`\n: Autoriser à l'intérieur d'un iframe si le document parent appartient au domaine donné..\n\nPar exemple, Twitter utilise `X-Frame-Options: SAMEORIGIN`.\n\n````online\nVoici le résultat:\n\n```html\n<iframe src=\"https://twitter.com\"></iframe>\n```\n\n<!-- ebook: prerender/ chrome headless dies and timeouts on this iframe -->\n<iframe src=\"https://twitter.com\"></iframe>\n\nSelon votre navigateur, le `iframe` ci-dessus est soit vide, soit vous avertit que le navigateur ne permet pas de naviguer sur cette page de cette manière.\n````\n\n## Affichage de la fonctionnalité désactivée\n\nL'en-tête `X-Frame-Options` a un effet secondaire. Les autres sites ne pourront pas afficher notre page dans un iframe, même s'ils ont de bonnes raisons de le faire.\n\nIl existe donc d'autres solutions... Par exemple, on peut \"couvrir\" la page avec une `<div>` avec les styles `height : 100% ; width : 100%;`, pour qu'elle intercepte tous les clics. Cette `<div>` doit être retirée si `window == top` ou si l'on s'aperçoit que l'on n'a pas besoin de cette protection.\n\nQuelque chose comme ça:\n\n```html\n<style>\n  #protector {\n    height: 100%;\n    width: 100%;\n    position: absolute;\n    left: 0;\n    top: 0;\n    z-index: 99999999;\n  }\n</style>\n\n<div id=\"protector\">\n  <a href=\"/\" target=\"_blank\">Go to the site</a>\n</div>\n\n<script>\n  // il y aura une erreur si la fenêtre supérieure est d'une origine différente\n  // mais c'est bon ici\n  if (top.document.domain == document.domain) {\n    protector.remove();\n  }\n</script>\n```\n\nLa démo:\n\n[codetabs src=\"protector\"]\n\n## Attribut de cookie \"samesite\"\n\nL'attribut de cookie `samesite` peut également prévenir les attaques de clickjacking.\n\nUn cookie avec un tel attribut n'est envoyé à un site web que s'il est ouvert directement, et non par l'intermédiaire d'un iframe ou autre. Plus d'informations dans le chapitre <info:cookie#samesite>.\n\nSi le site, tel que Facebook, avait l'attribut `samesite` dans son cookie d'authentification, comme ceci:\n\n```\nSet-Cookie: authorization=secret; samesite\n```\n\n...Ce cookie ne serait donc pas envoyé lorsque Facebook est ouvert dans une iframe depuis un autre site. Donc l'attaque échoue.\n\nL'attribut de cookie `samesite` n'aura pas d'effet lorsque les cookies ne sont pas utilisés. Cela peut permettre à d'autres sites Web d'afficher facilement nos pages publiques, non authentifiées, dans des iframes.\n\nToutefois, cela peut également permettre aux attaques par détournement de clics de fonctionner dans quelques cas limités. Un site Web de vote anonyme qui empêche les doubles votes en vérifiant les adresses IP, par exemple, serait toujours vulnérable au détournement de clics parce qu'il n'authentifie pas les utilisateurs à l'aide de cookies.\n\n## Résumé\n\nLe détournement de clics est un moyen de \"tromper\" les utilisateurs en les incitant à cliquer sur un site victime sans même savoir ce qui se passe. C'est dangereux s'il y a des actions importantes activées par le clic.\n\nUn hacker peut poster un lien vers sa page malveillante dans un message, ou attirer les visiteurs sur sa page par d'autres moyens. Il existe de nombreuses variantes.\n\nD'un certain point de vue, l'attaque n'est \"pas profonde\" : le pirate ne fait qu'intercepter un seul clic. Mais d'un autre point de vue, si le pirate sait qu'après le clic, un autre contrôle apparaîtra, il peut utiliser des messages astucieux pour contraindre l'utilisateur à cliquer également sur ces contrôles.\n\nCette attaque est assez dangereuse, car lorsque nous concevons l'interface utilisateur, nous ne prévoyons généralement pas qu'un pirate puisse cliquer au nom du visiteur. Les vulnérabilités peuvent donc se trouver dans des endroits totalement inattendus.\n\n- Il est recommandé d'utiliser `X-Frame-Options : SAMEORIGIN` sur les pages (ou les sites entiers) qui ne sont pas destinées à être affichées dans des iframes.\n- Utilisez une couverture `<div>` si nous voulons permettre à nos pages d'être affichées dans des iframes, tout en restant sécurisées.\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/clickjacking-visible.view/facebook.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<body style=\"margin:10px;padding:10px\">\n\n  <input type=\"button\" onclick=\"alert('Like pressed on facebook.html!')\" value=\"I LIKE IT !\">\n\n</body>\n\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/clickjacking-visible.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body>\n\n  <style>\n    iframe {\n      width: 400px;\n      height: 100px;\n      position: absolute;\n      top: 5px;\n      left: -14px;\n      opacity: 0.5;\n      z-index: 1;\n    }\n  </style>\n\n  <div>Click to get rich now:</div>\n\n  <!-- The url from the victim site -->\n  <iframe src=\"facebook.html\"></iframe>\n\n  <button>Click here!</button>\n\n  <div>...And you're cool (I'm a cool hacker actually)!</div>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/clickjacking.view/facebook.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<body style=\"margin:10px;padding:10px\">\n\n  <input type=\"button\" onclick=\"alert('Like pressed on facebook.html!')\" value=\"I LIKE IT !\">\n\n</body>\n\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/clickjacking.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body>\n\n  <style>\n    iframe {\n      width: 400px;\n      height: 100px;\n      position: absolute;\n      top: 5px;\n      left: -14px;\n      opacity: 0;\n      z-index: 1;\n    }\n  </style>\n\n  <div>Click to get rich now:</div>\n\n  <!-- The url from the victim site -->\n  <iframe src=\"facebook.html\"></iframe>\n\n  <button>Click here!</button>\n\n  <div>...And you're cool (I'm a cool hacker actually)!</div>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/protector.view/iframe.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n\n  <style>\n    #protector {\n      height: 100%;\n      width: 100%;\n      position: absolute;\n      left: 0;\n      top: 0;\n      z-index: 99999999;\n    }\n  </style>\n\n</head>\n\n<body>\n\n<div id=\"protector\">\n  <a href=\"/\" target=\"_blank\">Go to the site</a>\n</div>\n\n<script>\n\n  if (top.document.domain == document.domain) {\n    protector.remove();\n  }\n\n</script>\n\n  This text is always visible.\n\n  But if the page was open inside a document from another domain, the div over it would prevent any actions.\n\n  <button onclick=\"alert(1)\">Click wouldn't work in that case</button>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/protector.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n<body>\n\n  <iframe src=\"iframe.html\"></iframe>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/top-location.view/iframe.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body>\n\n  <div>Changes top.location to javascript.info</div>\n\n  <script>\n    top.location = 'https://javascript.info';\n  </script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/top-location.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n\n  <style>\n    iframe {\n      width: 400px;\n      height: 100px;\n      position: absolute;\n      top: 0;\n      left: -20px;\n      opacity: 0;\n      z-index: 1;\n    }\n  </style>\n\n  <script>\n    function attack() {\n\n      window.onbeforeunload = function() {\n        window.onbeforeunload = null;\n        return \"Want to leave without learning all the secrets (he-he)?\";\n      };\n\n      document.body.insertAdjacentHTML('beforeend', '<iframe src=\"iframe.html\">');\n    }\n  </script>\n</head>\n\n<body>\n\n  <p>After a click on the button the visitor gets a \"strange\" question about whether they want to leave.</p>\n\n  <p>Probably they would respond \"No\", and the iframe protection is hacked.</p>\n\n  <button onclick=\"attack()\">Add a \"protected\" iframe</button>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/index.md",
    "content": "# Cadres et fenêtres\n\n"
  },
  {
    "path": "4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/solution.js",
    "content": "function concat(arrays) {\n  // sum of individual array lengths\n  let totalLength = arrays.reduce((acc, value) => acc + value.length, 0);\n\n  let result = new Uint8Array(totalLength);\n  \n  if (!arrays.length) return result;\n\n  // for each array - copy it over result\n  // next array is copied right after the previous one\n  let length = 0;\n  for(let array of arrays) {\n    result.set(array, length);\n    length += array.length;\n  }\n\n  return result;\n}\n"
  },
  {
    "path": "4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/source.js",
    "content": "function concat(arrays) {\n  // ...your code...\n}\n\nlet chunks = [\n  new Uint8Array([0, 1, 2]),\n  new Uint8Array([3, 4, 5]),\n  new Uint8Array([6, 7, 8])\n];\n\nconsole.log(Array.from(concat(chunks))); // 0, 1, 2, 3, 4, 5, 6, 7, 8\n\nconsole.log(concat(chunks).constructor.name); // Uint8Array\n"
  },
  {
    "path": "4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/test.js",
    "content": "describe(\"concat\", function() {\n  let chunks = [\n    new Uint8Array([0, 1, 2]),\n    new Uint8Array([3, 4, 5]),\n    new Uint8Array([6, 7, 8])\n  ];\n\n  it(\"result has the same array type\", function() {\n\n    let result = concat(chunks);\n\n    assert.equal(result.constructor, Uint8Array);\n  });\n\n  it(\"concatenates arrays\", function() {\n\n    let result = concat(chunks);\n\n    assert.deepEqual(result, new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8]));\n\n  });\n\n  it(\"returns empty array on empty input\", function() {\n\n    let result = concat([]);\n\n    assert.equal(result.length, 0);\n\n  });\n\n});\n"
  },
  {
    "path": "4-binary/01-arraybuffer-binary-arrays/01-concat/solution.md",
    "content": ""
  },
  {
    "path": "4-binary/01-arraybuffer-binary-arrays/01-concat/task.md",
    "content": "\n# Concaténation de tableaux typés\n\nEn passant un tableau de `Uint8Array`, écrivez une fonction `concat(arrays)` qui retourne une concaténation dans un seul tableau.\n"
  },
  {
    "path": "4-binary/01-arraybuffer-binary-arrays/article.md",
    "content": "# ArrayBuffer, tableaux binaires\n\nDans le développement web, nous rencontrons des données binaires principalement lorsque l'on travaille avec des fichiers (création, envoi, téléchargement). Un autre cas d'utilisation est le traitement d'image.\n\nTout ceci est possible en JavaScript, et les opérations binaires sont très performantes.\n\nCependant, il y a de la confusion, car il y a beaucoup de classes disponibles.\nPour en nommer quelques unes:\n- `ArrayBuffer`, `Uint8Array`, `DataView`, `Blob`, `File`, etc.\n\nEn javascript, les données binaires sont implémentées de façon non standard, comparé à d'autres langages. Mais quand nous mettons de l'ordre dans tout ça, tout devient beaucoup plus simple.\n\n**L'objet binaire de base est un `ArrayBuffer` -- une référence à une zone contigüe de taille fixe de la mémoire.**\n\nNous le créons comme ceci:\n```js run\nlet buffer = new ArrayBuffer(16); // crée un Buffer de taille 16\nalert(buffer.byteLength); // 16\n```\n\nCela alloue une zone contigue de 16 octets dans la mémoire et la pré-remplie avec des zéros.\n\n```warn header=\"L'`ArrayBuffer` n'est pas un tableau de 'quelque chose'.\"\nCommençons par éliminer une possible source de confusion. `ArrayBuffer` n'a rien en commun avec `Array`:\n- Il possède une taille fixe, nous ne pouvons ni l'aggrandir, ni le réduire.\n- Il prend une taille spécifique en mémoire.\n- Pour accéder à des octets individuels, un autre objet de \"vue\" est nécessaire, on n'utilise pas `buffer[index]`.\n```\n\n`ArrayBuffer` est une zone de la mémoire. Qui y'a t'il à l'intérieur ? Juste une séquence d'octets.\n\n**Pour manipuler un `ArrayBuffer`, nous avons besoin d'utiliser un objet de \"vue\".**\n\nUn objet de \"vue\" ne stocke rien tout seul. Ce sont les lunettes qui donnent une interprétation des octets stockés dans l'`ArrayBuffer`.\n\nPar exemple:\n\n- **`Uint8Array`** -- Traite chaque octet dans l'`ArrayBuffer` comme un nombre unique, avec des valeurs possibles entre 0 jusqu'à 255 (Un octet est sur 8 bits, donc ça ne peut contenir que ça). On appelle ces valeurs des \"entiers non signés sur 8 bits\".\n- **`Uint16Array`** -- Traite par paquet de 2 octets en tant qu'entier, avec des valeurs possibles entre 0 jusqu'à 65535. On appelle ces valeurs des \"entiers non signés sur 16 bits\".\n- **`Uint32Array`** -- Traite par paquet de 4 octets en tant qu'entier, avec des valeurs possibles entre 0 jusqu'à 4294967295. On appelle ces valeurs des \"entiers non signés sur 32bits\".\n- **`Float64Array`** -- Traite par paquet de 8 octets en tant que nombre flottant avec des valeurs possibles entre <code>5.0x10<sup>-324</sup></code> et <code>1.8x10<sup>308</sup></code>.\n\nDonc, les données binaires dans un `ArrayBuffer` de 16 octets peuvent être interprétées comme 16 \"petits nombres\" , ou 8 grands nombres (2 octets chacun), ou 4 encore plus grands (4 octets chacun), ou 2 valeurs flottantes avec une haute précision (8 octets chacun).\n\n![](arraybuffer-views.svg)\n\n`ArrayBuffer` est l'objet central, le centre de tout, les données binaires brutes.\n\nMais si nous voulons écrire à l'intérieur, ou itérer dessus, pour n'importe quelle opération – nous devons utiliser une \"vue\", e.g:\n\n```js run\nlet buffer = new ArrayBuffer(16); // crée un buffer de taille 16\n\n*!*\nlet view = new Uint32Array(buffer); // Traite le buffer en une séquence d'entiers de 32 bits.\n\nalert(Uint32Array.BYTES_PER_ELEMENT); // 4 octets par entier.\n*/!*\n\nalert(view.length); // 4, il stocke cette quantité d'entiers.\nalert(view.byteLength); // 16, la taille en octets.\n\n// Ecrivons une valeur\nview[0] = 123456;\n\n// Itérons sur les valeurs\nfor(let num of view) {\n  alert(num); // 123456, puis 0, 0, 0 (4 valeurs au total)\n}\n\n```\n\n## TypedArray - tableau typé\n\nLe terme commun pour toutes ces vues (`Uint8Array`, `Uint32Array`, etc) est [TypedArray](https://tc39.github.io/ecma262/#sec-typedarray-objects). Elles partagent le même ensemble de méthodes et de propriétés.\n\nIl faut noter qu'il n'y a pas de construteur appelé `TypedArray`, Il s'agit d'un terme pour représenter une des vues par dessus un `ArrayBuffer`: `Int8Array`, `Uint8Array` etc. La liste entière va bientôt suivre.\n\nLorsque vous voyez quelque chose comme `new TypedArray`, Il s'agit de n'importe quoi parmi `new Int8Array`, `new Uint8Array`, etc.\n\nLes tableaux typés ressemblent à des tableaux classiques : ils ont des indexs et sont itérables.\n\nUn constructeur `TypedArray` (soit `Int8Array` ou `Float64Array`, peut importe) se comporte différement en fonction du type des arguments.\n\nIl y a 5 variantes d'arguments:\n\n```js\nnew TypedArray(buffer, [byteOffset], [length]);\nnew TypedArray(object);\nnew TypedArray(typedArray);\nnew TypedArray(length);\nnew TypedArray();\n```\n\n1. Si un `ArrayBuffer` est fourni, la vue est créée dessus. Nous avons déjà utilisé cette syntaxe.\n\n    Nous pouvons éventuellement fournir un décalage (`byteOffset`) pour commencer à partir de là (0 par défaut) et la longueur (`length`) (jusqu'à la fin du buffer par défaut), alors la vue ne va couvrir qu'une partie du `buffer`.\n\n2. Si c'est un `Array`, ou quelque chose ressemblant à un tableau qui est fourni, il crée un tableau typé de la même longueur et copie le contenu.\n\n    Nous pouvons l'utiliser pour pré-remplir le tableau avec les données:\n    ```js run\n    *!*\n    let arr = new Uint8Array([0, 1, 2, 3]);\n    */!*\n    alert( arr.length ); // 4, a créé une liste binaire de la même taille\n    alert( arr[1] ); // 1, remplit avec 4 octets (entiers non signés sur 8 bits) avec des valeurs données\n    ```\n3. Si un autre tableau typé est fourni, il fait la même chose: il crée un tableau typé de la même taille et copie le contenu. Les valeurs sont converties vers le nouveau type dans le processus si besoin.\n    ```js run\n    let arr16 = new Uint16Array([1, 1000]);\n    *!*\n    let arr8 = new Uint8Array(arr16);\n    */!*\n    alert( arr8[0] ); // 1\n    alert( arr8[1] ); // 232, 1000 ne rentre pas dans 8 bits (explications plus loin)\n    ```\n\n4. Si un argument `length` est fourni -- Il crée un tableau typé qui contient autant d'éléments. Sa taille en octets va être `length` multiplié par la taille en octets d'un seul élément `TypedArray.BYTES_PER_ELEMENT`:\n    ```js run\n    let arr = new Uint16Array(4); // Création d'un tableau typé de 4 entiers\n    alert( Uint16Array.BYTES_PER_ELEMENT ); // 2 octets par entier\n    alert( arr.byteLength ); // 8 (taille en octets)\n    ```\n\n5. Sans arguments, il crée un tableau typé de taille nulle.\n\nNous pouvons créer un tableau typé directement sans fournir un `ArrayBuffer`. Mais une vue ne peut pas exister sans, donc il sera créé automatiquement dans tous les cas, sauf le premier (quand il est passé en argument).\n\nPour accéder au `ArrayBuffer` sous-jacent, il existe les propriétés suivantes dans `TypedArray` :\n- `buffer` -- qui fait référence à l'`ArrayBuffer`.\n- `byteLength` -- qui correspond à la taille de l'`ArrayBuffer`.\n\nDonc nous pouvons toujours passer d'une vue à l'autre:\n```js\nlet arr8 = new Uint8Array([0, 1, 2, 3]);\n\n// Une autre vue avec les mêmes données\nlet arr16 = new Uint16Array(arr8.buffer);\n```\n\n\nVoici une liste de tableaux typés:\n\n- `Uint8Array`, `Uint16Array`, `Uint32Array` -- Pour les entiers de 8, 16 et 32 bits.\n  - `Uint8ClampedArray` -- Pour les entiers de 8 bits, avec une \"restriction\" à l'affectation (voir plus loin).\n- `Int8Array`, `Int16Array`, `Int32Array` -- Pour les nombres entiers signés (peuvent être négatifs).\n- `Float32Array`, `Float64Array` -- Pour les nombres flottants signés de 32 et 64 bits.\n\n```warn header=\"Pas de `int8` ou de types similaires\"\nMalgré la présence de noms tels que `Int8Array`, il n'y a pas de type comme `int` ou `int8` dans JavaScript. \n\nCar en effet `Int8Array` n'est pas un tableau de ces valeurs individuelles, mais plutôt une vue sur `ArrayBuffer`.\n```\n\n### Comportement hors limite\n\nQue se passe t'il lorsque nous essayons d'écrire des valeurs en dehors des limites dans un tableau typé ? Il n'y aura pas d'erreurs, mais les bits en trop seront supprimés.\n\nPar exemple, essayons d'ajouter 256 dans un `Uint8Array`. En binaire, 256 s'écrit `100000000` (9 bits), mais un `Uint8Array` ne permet que 8 bits par valeur, ce qui donne des valeurs possibles entre 0 et 255.\n\nPour les grands nombres, seuls les 8 bits les plus à droite (moins significatif) sont sauvegardés, et le reste est supprimé:\n\n![](8bit-integer-256.svg)\n\nDonc nous allons obtenir 0.\n\nPour 257, l'écriture binaire est `100000001` (9 bits), les 8 bits les plus à droite sont gardés, donc on aura un `1` dans notre tableau:\n\n![](8bit-integer-257.svg)\n\nEn d'autres termes, Le nombre modulo 2<sup>8</sup> est sauvegardé.\n\nDémonstration:\n\n```js run\nlet uint8array = new Uint8Array(16);\n\nlet num = 256;\nalert(num.toString(2)); // 100000000 (représentation binaire)\n\nuint8array[0] = 256;\nuint8array[1] = 257;\n\nalert(uint8array[0]); // 0\nalert(uint8array[1]); // 1\n```\n\n`Uint8ClampedArray` possède un comportement différent. Il garde 255 pour n'importe quel nombre qui est plus grand que 255, et 0 pour n'importe quel nombre négatif. Ce comportement est utile dans le traitement d'images.\n\n## Méthodes des tableaux typés\n\n`TypedArray` possède les méthodes de `Array`, avec quelques exceptions notables.\n\nNous pouvons itérer, `map`, `slice`, `find`, `reduce` etc.\n\nMais certaines choses ne sont pas possibles:\n\n- Pas de `splice` -- On ne peut pas supprimer une valeur, car les tableaux typés sont des vues sur un `buffer`, qui sont des zones fixes dans la mémoire. Tout ce que nous pouvons faire est de mettre un 0.\n- Pas de méthode `concat`.\n\nIl y a deux méthodes supplémentaires:\n\n- `arr.set(fromArr, [offset])` copie tous les éléments de `fromArr` vers `arr`, en commençant à partir de la position `offset` (0 par défaut).\n- `arr.subarray([begin, end])` crée une nouvelle vue du même type de `begin` jusqu'à `end` (non-inclus). C'est similaire à la méthode `slice` (qui est également disponible), mais elle ne copie rien -- il s'agit juste d'une création d'une nouvelle vue, pour travailler sur un certain morceau de données.\n\nLes méthodes nous permettent de copier des tableaux typés, de les mélanger, de créer des nouveaux tableaux depuis ceux existants, et bien d'autres choses.\n\n## DataView\n\n[DataView](mdn:/JavaScript/Reference/Global_Objects/DataView) est une vue spéciale \"non typée\" super flexible sur ʻArrayBuffer`. Il permet d'accéder aux données sur n'importe quel offset dans tous les formats.\n\n- Pour les tableaux typés, le constructeur détermine le format. Le tableau entier est supposé être uniforme. Le i-ème nombre est noté `arr[i]`.\n- Avec `DataView` nous accédons aux données avec des méthodes comme `.getUint8(i)` ou `.getUint16(i)`. Nous choisissons le format au moment de l'utilisation de la méthode au lieu du moment de la création.\n\nVoici la syntaxe:\n\n```js\nnew DataView(buffer, [byteOffset], [byteLength])\n```\n\n- **`buffer`** -- `ArrayBuffer`. Contrairement aux tableaux typés, `DataView` ne crée pas soit même un buffer. Nous avons besoin de le lui fournir directement.\n- **`byteOffset`** -- L'octet de départ de la vue (par défaut à 0).\n- **`byteLength`** -- La taille totale de la vue en octets (par défaut jusqu'à la fin de `buffer`).\n\nPour l'exemple, nous allons récupérer des nombres dans plusieurs formats avec le même buffer:\n\n```js run\n// Tableau binaire de 4 octets, tous ayant la valeur maximale - 255\nlet buffer = new Uint8Array([255, 255, 255, 255]).buffer;\n\nlet dataView = new DataView(buffer);\n\n// récupération d'un nombre en 8 bits avec un décalage de 0\nalert( dataView.getUint8(0) ); // 255\n\n// récupération d'un nombre en 16 bits avec un décalage de 0, soit 2 octets, qui sont interprétés ensemble en 65535\nalert( dataView.getUint16(0) ); // 65535 (Plus grand entier non signé en 16 bits)\n\n// récupération d'un nombre en 32 bits avec un décalage de 0\nalert( dataView.getUint32(0) ); // 4294967295 (Plus grand entier non signé en 32 bits)\n\ndataView.setUint32(0, 0); // Fixe le nombre sous 4 octets à 0, fixant ainsi tous les octets à 0\n```\n\n`DataView` est utile lorsque l'on met des données sous plusieurs formats dans le même buffer. Par exemple, on stocke une séquence de paires (16-bit integer, 32-bit float). `DataView` nous permettra d'y accéder facilement.\n\n## Résumé\n\n`ArrayBuffer` est l'objet au coeur de tout, c'est une référence à une zone de taille fixe dans la mémoire.\n\nPour faire presque n'importe quelle opération sur un `ArrayBuffer`, nous avons besoin d'une vue.\n\n- Il peut s'agir d'un tableau typé:\n    - `Uint8Array`, `Uint16Array`, `Uint32Array` -- pour les entiers non-signés de 8, 16, et 32 bits.\n    - `Uint8ClampedArray` -- pour les entiers de 8 bits, \"clamps\" them on assignment.\n    - `Int8Array`, `Int16Array`, `Int32Array` -- pour les entiers signés (peuvent être négatifs).\n    - `Float32Array`, `Float64Array` -- pour les nombres flottants signés de 32 et 64 bits.\n- Ou d'un `DataView` -- la vue qui utilise des méthodes pour spécifier un format, e.g. `getUint8(offset)`.\n\nDans la majorité des cas, on crée et on opère directement sur les tableaux typés, laissant `ArrayBuffer` en arrière. On peut toujours y accéder avec `.buffer` et faire une nouvelle vue si besoin.\n\nIl y a également 2 termes supplémentaires, qui sont utilisés dans les descriptions des méthodes pour travailler sur les données binaires:\n- `ArrayBufferView` qui est le terme pour tous les types de vues.\n- `BufferSource` qui est un terme désignant soit un `ArrayBuffer` ou un `ArrayBufferView`.\n\nNous verrons ces termes dans les prochains chapitres. `BufferSource` est l'un des termes les plus communs, qui veut dire \"toutes sortes de données binaires\" -- un `ArrayBuffer` ou une vue par dessus.\n\nVoici un cheatsheet :\n\n![](arraybuffer-view-buffersource.svg)\n"
  },
  {
    "path": "4-binary/02-text-decoder/article.md",
    "content": "# TextDecoder and TextEncoder\n\nQue faire si les données binaires sont en fait une chaîne de charactères ? Par exemple, nous avons reçu un fichier contenant des données textuelles.\n\nL'object interne [TextDecoder](https://encoding.spec.whatwg.org/#interface-textdecoder) permet de lire la valeur dans une chaîne JavaScript réelle, compte tenu du buffer et de l'encodage.\n\nNous devons d'abord le créer:\n```js\nlet decoder = new TextDecoder([label], [options]);\n```\n\n- **`label`** -- l'encodage, `utf-8` par défaut, mais `big5`, `windows-1251` et bien d'autres sont également pris en charge.\n- **`options`** -- objet optionnel :\n  - **`fatal`** -- boolean, si `true` une exception pour les caractères invalides (non décodables) est lancé, sinon (par défaut) remplacez-les par un caractère `\\uFFFD`.\n  - **`ignoreBOM`** -- boolean, si `true` ignore la BOM (une marque unicode facultative d'ordre des octets), rarement nécessaire.\n\n...Et puis décoder:\n\n```js\nlet str = decoder.decode([input], [options]);\n```\n\n- **`input`** -- `Source du buffer` à décoder.\n- **`options`** -- objet optionnel:\n  - **`stream`** -- vrai pour les flux de décodage, lorsque `decoder` est appelé à plusieurs reprises avec des blocs de données entrants. Dans ce cas, un caractère multi-octets peut parfois être divisé entre des morceaux. Cette option indique à `TextDecoder` de mémoriser les caractères\" inachevés \"et de les décoder lorsque le morceau suivant arrive.\n\nPar exemple:\n\n```js run\nlet uint8Array = new Uint8Array([72, 101, 108, 108, 111]);\n\nalert( new TextDecoder().decode(uint8Array) ); // Hello\n```\n\n\n```js run\nlet uint8Array = new Uint8Array([228, 189, 160, 229, 165, 189]);\n\nalert( new TextDecoder().decode(uint8Array) ); // 你好\n```\n\nNous pouvons décoder une partie du Buffer en créant une vue de sous-tableau pour celui-ci:\n\n\n```js run\nlet uint8Array = new Uint8Array([0, 72, 101, 108, 108, 111, 0]);\n\n// la chaîne de charactères est au milieu\n// créer une nouvelle vue, sans rien copier\nlet binaryString = uint8Array.subarray(1, -1);\n\nalert( new TextDecoder().decode(binaryString) ); // Hello\n```\n\n## TextEncoder\n\n[TextEncoder](https://encoding.spec.whatwg.org/#interface-textencoder) fait la chose inverse -- convertit une chaîne de charactères en bytes.\n\nLa syntaxe est:\n\n```js\nlet encoder = new TextEncoder();\n```\n\nLe seul encodage qu'il prend en charge est l'\"utf-8\".\n\nIl a deux méthodes:\n- **`encode(str)`** -- prend une chaîne de charactères et renvoie un `Uint8Array`.\n- **`encodeInto(str, destination)`** -- encode `str` dans `destination` (qui doit être un `Uint8Array`).\n\n```js run\nlet encoder = new TextEncoder();\n\nlet uint8Array = encoder.encode(\"Hello\");\nalert(uint8Array); // 72,101,108,108,111\n```\n"
  },
  {
    "path": "4-binary/03-blob/article.md",
    "content": "# Blob\n\n`ArrayBuffer` et les vues font partie de la norme ECMA, une partie de JavaScript.\n\nDans le navigateur, il existe d'autres objets de niveau supérieur, décrits dans [File API](https://www.w3.org/TR/FileAPI/), en particulier `Blob`.\n\n`Blob` consiste en une chaîne de caractères optionnelle `type` (un type de MIME habituellement), plus `blobParts` -- une séquence d'autres objets `Blob` , chaînes de caractères et `BufferSource`.\n\n![](blob.svg)\n\nLa syntaxe du constructeur est la suivante:\n\n```js\nnew Blob(blobParts, options);\n```\n\n- **`blobParts`** est un tableau de `Blob`/`BufferSource`/`String`.\n- **`options`** objet optionnel:\n  - **`type`** -- le type du `Blob`, généralement de type MIME, par exemple. `image/png`,\n  - **`endings`** -- s'il faut transformer la fin de ligne pour rendre le `Blob` correspondent aux nouvelles lignes de l'OS actuel (`\\r\\n` ou `\\n`). `\"transparent\"` Par défaut (ne fait rien), mais peut aussi être `\"native\"` (transformer).\n\nPar exemple:\n\n```js\n// créer un Blob à partir d'une chaîne\nlet blob = new Blob([\"<html>…</html>\"], {type: 'text/html'});\n// veuillez noter: le premier argument doit être un tableau [...]\n```\n\n```js\n// créer un objet blob à partir d'un tableau typés et de chaînes de caractères\nlet hello = new Uint8Array([72, 101, 108, 108, 111]); // \"Hello\" sous forme binaire\n\nlet blob = new Blob([hello, ' ', 'world'], {type: 'text/plain'});\n```\n\nNous pouvons extraire des parties du `Blob` avec :\n\n```js\nblob.slice([byteStart], [byteEnd], [contentType]);\n```\n\n- **`byteStart`** -- l'octet de départ, par défaut 0.\n- **`byteEnd`** -- le dernier octet (exclusif, par défaut jusqu'à la fin).\n- **`contentType`** -- Le `type` du nouvel objet blob, par défaut le même que la source.\n\nLes arguments sont similaires à `array.slice`, les nombres négatifs sont également autorisés.\n\n```smart header=\"Les objets `Blob` sont immuables\"\nNous ne pouvons pas modifier les données directement dans un `Blob`, mais nous pouvons découper des parties d'un `Blob`, créer de nouveaux objets `Blob` à partir d'eux, les mélanger dans un nouveau `Blob` et ainsi de suite.\n\nCe comportement est similaire aux chaînes de caractères JavaScript: nous ne pouvons pas changer un caractère dans une chaîne, mais nous pouvons créer une nouvelle chaîne corrigée.\n```\n\n## Blob comme URL\n\nUn Blob peut être facilement utilisé comme URL pour `<a>`, `<img>` ou d'autres balises, pour afficher son contenu.\n\nGrâce au `type`, nous pouvons également télécharger / uploader des objets `Blob`, et le `type` devient naturellement `Content-Type` dans les requêtes réseau.\n\nCommençons par un exemple simple. En cliquant sur un lien, vous téléchargez un `Blob` généré dynamiquement avec le contenu de `hello world` sous forme de fichier:\n\n```html run\n<!-- l'attribut de téléchargement force le navigateur à télécharger au lieu de naviguer -->\n<a download=\"hello.txt\" href='#' id=\"link\">Download</a>\n\n<script>\nlet blob = new Blob([\"Hello, world!\"], {type: 'text/plain'});\n\nlink.href = URL.createObjectURL(blob);\n</script>\n```\n\nOn peut aussi créer un lien dynamiquement en JavaScript et simuler un clic par `link.click()`, puis le téléchargement démarre automatiquement.\n\nVoici un code similaire qui oblige l'utilisateur à télécharger le `Blob` créé dynamiquement, sans aucun HTML :\n\n```js run\nlet link = document.createElement('a');\nlink.download = 'hello.txt';\n\nlet blob = new Blob(['Hello, world!'], {type: 'text/plain'});\n\nlink.href = URL.createObjectURL(blob);\n\nlink.click();\n\nURL.revokeObjectURL(link.href);\n```\n\n`URL.createObjectURL` prend un `Blob` et crée une URL unique pour celui-ci, sous la forme `blob:<origin>/<uuid>`.\n\nVoilà à quoi ressemble la valeur de `link.href`:\n\n```\nblob:https://javascript.info/1e67e00e-860d-40a5-89ae-6ab0cbee6273\n```\n\nPour chaque URL générée par `URL.createObjectURL`, le navigateur stocke en interne un mappage URL -> `Blob`. De telles URL sont donc courtes, mais permettent d'accéder au `Blob`.\n\nUne URL générée (et donc le lien avec elle) n'est valide que dans le document actuel, tant qu'il est ouvert. Et cela permet de référencer le `Blob` dans `<img>`, `<a>`, essentiellement tout autre objet qui attend une URL.\n\nUne URL générée (et donc le lien avec elle) n'est valide que dans le document actuel, tant qu'il est ouvert. Et cela permet de référencer le `Blob` dans `<img>`,`<a>`, ou tout autre objet qui attend une URL.\n\nIl y a cependant un effet secondaire. Bien qu'il y ait un mappage pour un `Blob`, le `Blob` lui-même réside dans la mémoire. Le navigateur ne peut pas le libérer.\n\nLe mappage est automatiquement effacé lors du déchargement du document, les objets `Blob` sont alors libérés. Mais si une application dure longtemps, cela ne se produit pas de sitôt.\n\n**Donc, si nous créons une URL, ce `Blob` restera en mémoire, même s'il n'est plus nécessaire.**\n\n`URL.revokeObjectURL(url)` supprime la référence du mappage interne, permettant ainsi de supprimer le `Blob` (s'il n'y a pas d'autres références), et de libérer la mémoire.\n\nDans le dernier exemple, nous voulons que le `Blob` ne soit utilisé qu'une seule fois, pour un téléchargement instantané, donc nous appelons `URL.revokeObjectURL(link.href)` immédiatement.\n\nDans l'exemple précédent avec le lien HTML cliquable, nous n'appelons pas `URL.revokeObjectURL(link.href)`, car cela rendrait l'url `Blob` invalide. Après la révocation, comme le mappage est supprimé, l'URL ne fonctionne plus.\n\n## Blob en base64\n\nUne alternative à `URL.createObjectURL` est de convertir un `Blob` en une chaîne de caractères encodée en base64.\n\nCet encodage représente des données binaires sous la forme d'une chaîne de caractères \"lisibles\" ultra-sûrs avec des codes ASCII de 0 à 64. Et ce qui est plus important - nous pouvons utiliser cet encodage dans \"data-urls\".\n\nUne [URL de données](mdn:/http/Data_URIs) a la forme `data:[<mediatype>][;base64],<data>`. Nous pouvons utiliser de telles URL partout, au même titre que les URL \"ordinaires\".\n\nPar exemple, voici un smiley:\n\n```html\n<img src=\"data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7\">\n```\n\nLe navigateur décodera la chaîne de caractères et affichera l'image: <img src=\"data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7\">\n\nPour transformer un `Blob` en base64, nous utiliserons l'objet `FileReader` intégré. Il peut lire les données des Blobs dans plusieurs formats. Dans le [chapitre suivant](info:file) nous le couvrirons plus en détail.\n\nVoici la démo du téléchargement d'un blob, maintenant via base-64:\n\n```js run\nlet link = document.createElement('a');\nlink.download = 'hello.txt';\n\nlet blob = new Blob(['Hello, world!'], {type: 'text/plain'});\n\n*!*\nlet reader = new FileReader();\nreader.readAsDataURL(blob); // convertit le blob en base64 et appelle onload\n*/!*\n\nreader.onload = function() {\n  link.href = reader.result; // URL de données\n  link.click();\n};\n```\n\nLes deux manières de créer une URL d'un `Blob` sont utilisables. Mais généralement `URL.createObjectURL(blob)` est plus simple et plus rapide.\n\n```compare title-plus=\"URL.createObjectURL(blob)\" title-minus=\"Blob vers l'URL de données\"\n+ Nous devons les révoquer si nous nous soucions de la mémoire.\n+ Accès direct au blob, pas d'\"encodage / décodage\"\n- Pas besoin de révoquer quoi que ce soit.\n- Perte de performances et de mémoire sur les gros objets `Blob` pour l'encodage.\n```\n\n## Image à blob\n\nNous pouvons créer un `Blob` d'une image, une partie d'image, ou même faire une capture d'écran de page. C'est pratique pour le télécharger quelque part.\n\nLes opérations sur les images se font via l'élément `<canvas>` :\n\n1. Dessinez une image (ou sa partie) sur le canevas en utilisant [canvas.drawImage](mdn:/api/CanvasRenderingContext2D/drawImage).\n2. Appeler la méthode canvas [.toBlob(callback, format, quality)](mdn:/api/HTMLCanvasElement/toBlob) qui crée un `Blob` et exécute `callback` avec lui une fois terminé.\n\nDans l'exemple ci-dessous, une image est simplement copiée, mais nous pourrions la couper ou la transformer sur un canevas avant de créer un blob :\n\n```js run\n// prendre n'importe quelle image\nlet img = document.querySelector('img');\n\n// rendre <canvas> de la même taille\nlet canvas = document.createElement('canvas');\ncanvas.width = img.clientWidth;\ncanvas.height = img.clientHeight;\n\nlet context = canvas.getContext('2d');\n\n// copier l'image dessus (cette méthode permet de couper l'image)\ncontext.drawImage(img, 0, 0);\n// nous pouvons context.rotate(), et faire beaucoup d'autres choses sur canvas\n\n// toBlob est une opération asynchrone, le callback est appelé lorsque c’est terminé\ncanvas.toBlob(function(blob) {\n  // blob prêt, téléchargez-le\n  let link = document.createElement('a');\n  link.download = 'example.png';\n\n  link.href = URL.createObjectURL(blob);\n  link.click();\n\n  // supprimer la référence blob interne, pour laisser le navigateur en effacer la mémoire\n  URL.revokeObjectURL(link.href);\n}, 'image/png');\n```\n\nSi nous préférons `async/await` au lieu de callbacks :\n\n```js\nlet blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));\n```\n\nPour faire une capture d'écran d'une page, nous pouvons utiliser une bibliothèque telle que <https://github.com/niklasvh/html2canvas>. Ce qu'elle fait, c'est simplement parcourir la page et la dessiner sur un `<canvas>`. Ensuite, nous pouvons en obtenir un `Blob` de la même manière que ci-dessus.\n\n## De Blob à ArrayBuffer\n\nLe constructeur `Blob` permet de créer un blob à partir de presque tout, y compris de n'importe quel `BufferSource`.\n\nMais si nous devons effectuer un traitement de bas niveau, nous pouvons obtenir le `ArrayBuffer` de plus bas niveau à partir de `blob.arrayBuffer()` :\n\n```js\n// obtenir arrayBuffer à partir de blob\nconst bufferPromise = await blob.arrayBuffer();\n\n// or\nblob.arrayBuffer().then(buffer => /* process the ArrayBuffer */);\n```\n\n## From Blob to stream\n\nWhen we read and write to a blob of more than `2 GB`, the use of `arrayBuffer` becomes more memory intensive for us. At this point, we can directly convert the blob to a stream.\n\nA stream is a special object that allows to read from it (or write into it) portion by portion. It's outside of our scope here, but here's an example, and you can read more at <https://developer.mozilla.org/en-US/docs/Web/API/Streams_API>. Streams are convenient for data that is suitable for processing piece-by-piece.\n\nThe `Blob` interface's `stream()` method returns a `ReadableStream` which upon reading returns the data contained within the `Blob`.\n\nThen we can read from it, like this:\n\n```js\n// get readableStream from blob\nconst readableStream = blob.stream();\nconst stream = readableStream.getReader();\n\nwhile (true) {\n  // for each iteration: value is the next blob fragment\n  let { done, value } = await stream.read();\n  if (done) {\n    // no more data in the stream\n    console.log('all blob processed.');\n    break;\n  }\n\n   // do something with the data portion we've just read from the blob\n  console.log(value);\n}\n```\n\n## Résumé\n\nAlors qu'`ArrayBuffer`, `Uint8Array` et autres `BufferSource` sont des \"données binaires\", un [Blob](https://www.w3.org/TR/FileAPI/#dfn-Blob) représente des \"données binaires de type\".\n\nCela rend les Blobs pratiques pour les opérations de téléchargement (upload / download), qui sont courantes dans le navigateur.\n\nLes méthodes qui effectuent des requêtes Web, telles que [XMLHttpRequest](info:xmlhttprequest), [fetch](info:fetch) et ainsi de suite, peuvent fonctionner avec `Blob` de manière native, ainsi qu'avec d'autres types binaires.\n\nNous pouvons facilement convertir les types de données binaires `Blob` et de bas niveau :\n\n- Nous pouvons créer un `Blob` à partir d'un tableau typé en utilisant le constructeur `new Blob(...)`.\n- Nous pouvons récupérer `ArrayBuffer` à partir d'un Blob en utilisant `blob.arrayBuffer()`, puis créer une vue dessus pour le traitement binaire de bas niveau.\n\nLes flux de conversion sont très utiles lorsque nous devons gérer de gros blob. Vous pouvez facilement créer un `ReadableStream` à partir d'un blob. La méthode `stream()` de l'interface `Blob` renvoie un `ReadableStream` qui, lors de la lecture, renvoie les données contenues dans le blob.\n"
  },
  {
    "path": "4-binary/04-file/article.md",
    "content": "# File and FileReader\n\nL'object [File](https://www.w3.org/TR/FileAPI/#dfn-file) hérite de `Blob` et est étendu avec des capacités liées au système de fichiers.\n\nIl y a deux façons de l'obtenir.\n\nPremièrement, il y a un constructeur, similaire à `Blob`:\n\n```js\nnew File(fileParts, fileName, [options])\n```\n\n- **`fileParts`** -- est un tableau de valeurs Blob / BufferSource / Chaîne de caractères.\n- **`fileName`** -- chaînes de caractères contenant le nom du fichier.\n- **`options`** -- objet optionnel:\n    - **`lastModified`** -- l'horodatage (date entière) de la dernière modification.\n\nDeuxièmement, le plus souvent, nous obtenons un fichier avec `<input type=\"file\">`, en glisser-déposer ou d'autres interfaces de navigateur. Dans ce cas, le fichier obtient ces informations du système d'exploitation.\n\nComme `File` hérite de `Blob`, les objets `File` ont les mêmes propriétés, plus:\n- `name` -- le nom du fichier,\n- `lastModified` -- l'horodatage de la dernière modification.\n\nVoici comment nous pouvons obtenir un objet `File` depuis `<input type=\"file\">`:\n\n```html run\n<input type=\"file\" onchange=\"showFile(this)\">\n\n<script>\nfunction showFile(input) {\n  let file = input.files[0];\n\n  alert(`File name: ${file.name}`); // e.g my.png\n  alert(`Last modified: ${file.lastModified}`); // e.g 1552830408824\n}\n</script>\n```\n\n```smart\nL'entrée peut sélectionner plusieurs fichiers, donc `input.files` est un objet de type tableau. Ici, nous n'avons qu'un seul fichier, donc nous prenons juste `input.files[0]`.\n```\n\n## FileReader\n\n[FileReader](https://www.w3.org/TR/FileAPI/#dfn-filereader) est un objet dont le seul but est de lire les données des objets `Blob` (et donc aussi `File`).\n\nIl fournit les données à l'aide d'événements, car la lecture à partir du disque peut prendre du temps.\n\nLe constructeur:\n\n```js\nlet reader = new FileReader(); // aucun argument\n```\n\nLes méthodes principales:\n\n- **`readAsArrayBuffer(blob)`** -- lit les données au format binaire `ArrayBuffer`.\n- **`readAsText(blob, [encoding])`** -- lit les données sous forme de chaîne de caractères avec l'encodage donné (`utf-8` par défaut).\n- **`readAsDataURL(blob)`** -- lit les données binaires et les encode en base64 comme URL de données.\n- **`abort()`** -- annule l'opération.\n\nLe choix de la méthode `read*` dépend du format que nous préférons, comment nous allons utiliser les données.\n\n- `readAsArrayBuffer` -- pour les fichiers binaires, pour effectuer des opérations binaires de bas niveau. Pour les opérations de haut niveau, comme le découpage, `File` hérite de `Blob`, nous pouvons donc les appeler directement, sans lire.\n- `readAsText` -- pour les fichiers texte, lorsque nous souhaitons obtenir une chaîne de caractères.\n- `readAsDataURL` -- quand nous utilisons ces données dans `src` pour `img` ou une autre balise. Il existe une alternative à la lecture d'un fichier, comme expliqué dans le chapitre <info:blob>:`URL.createObjectURL(file)`.\n\nAu fur et à mesure de la lecture, il y a des événements:\n- `loadstart` -- chargement commencé.\n- `progress` -- se produit pendant la lecture.\n- `load` -- aucune erreur, lecture terminée.\n- `abort` -- `abort()` est appelée.\n- `error` -- une erreur est survenue.\n- `loadend` -- lecture terminée avec succès ou échec.\n\nLorsque la lecture est terminée, nous pouvons accéder au résultat comme:\n- `reader.result` est le résultat (en cas de succès)\n- `reader.error` est l'erreur (en cas d'échec).\n\nLes événements les plus utilisés sont `load` et `error`.\n\nVoici un exemple de lecture d'un fichier:\n\n```html run\n<input type=\"file\" onchange=\"readFile(this)\">\n\n<script>\nfunction readFile(input) {\n  let file = input.files[0];\n\n  let reader = new FileReader();\n\n  reader.readAsText(file);\n\n  reader.onload = function() {\n    console.log(reader.result);\n  };\n\n  reader.onerror = function() {\n    console.log(reader.error);\n  };\n\n}\n</script>\n```\n\n```smart header=\"`FileReader` pour les objets blob\"\nComme mentionné dans le chapitre <info:blob>, `FileReader` peut lire non seulement les fichiers, mais tous les objets blob.\n\nNous pouvons l'utiliser pour convertir un blob dans un autre format :\n\n- `readAsArrayBuffer(blob)` -- en `ArrayBuffer`,\n- `readAsText(blob, [encoding])` -- en chaîne de caractères (une alternative à `TextDecoder`),\n- `readAsDataURL(blob)` -- en URL des données encoder en base64.\n```\n\n\n```smart header=\"`FileReaderSync` est disponible dans Web Workers\"\nPour les Web Workers, il existe également une variante synchrone de `FileReader`, appelée [FileReaderSync](https://www.w3.org/TR/FileAPI/#FileReaderSync).\n\nSes méthodes de lecture `read*` ne génèrent pas d'événements, mais renvoient plutôt un résultat, comme le font les fonctions régulières.\n\nCependant, ce ne fonctionne qu'à l'intérieur d'un Web Worker, car les retards dans les appels synchrones qui sont possibles lors de la lecture à partir de fichiers, sont moins importants dans les Web Workers . Ils n'affectent pas la page.\n```\n\n## Résumé\n\nLes objets `File` héritent de `Blob`.\n\nEn plus des méthodes et propriétés `Blob`, les objets `File` ont également les propriétés `name` et `lastModified`, ainsi que la capacité interne de lire à partir du système de fichiers. Nous obtenons généralement des objets `File` à partir de l'entrée utilisateur, comme un `<input>` ou l'événement Drag'n'Drop (`ondragend`).\n\n les objets `FileReader` peuvent lire à partir d'un fichier ou d'un objet blob, dans l'un des trois formats :\n\n- Chaînes de caractères (`readAsText`).\n- `ArrayBuffer` (`readAsArrayBuffer`).\n- URL des données, encodé en base64 (`readAsDataURL`).\n\nDans de nombreux cas cependant, nous n'avons pas à lire le contenu du fichier. Tout comme nous l'avons fait avec les blobs, nous pouvons créer une URL courte avec `URL.createObjectURL(fichier)` et l'assigner à `<a>` ou `<img>`. De cette façon, le fichier peut être téléchargé ou affiché sous forme d'image, comme partie de canevas, etc..\n\nEt si nous voulons envoyer un `File` sur un réseau, c'est aussi simple: une API réseau comme `XMLHttpRequest` ou `fetch` accepte nativement les objets `File`.\n"
  },
  {
    "path": "4-binary/index.md",
    "content": "# Les données binaires et les fichiers\n\nManipulation des données binaires et les fichiers en JavaScript.\n"
  },
  {
    "path": "5-network/01-fetch/01-fetch-users/_js.view/solution.js",
    "content": "\nasync function getUsers(names) {\n  let jobs = [];\n\n  for(let name of names) {\n    let job = fetch(`https://api.github.com/users/${name}`).then(\n      successResponse => {\n        if (successResponse.status != 200) {\n          return null;\n        } else {\n          return successResponse.json();\n        }\n      },\n      failResponse => {\n        return null;\n      }\n    );\n    jobs.push(job);\n  }\n\n  let results = await Promise.all(jobs);\n\n  return results;\n}\n"
  },
  {
    "path": "5-network/01-fetch/01-fetch-users/_js.view/source.js",
    "content": "\nasync function getUsers(names) {\n  /* your code */\n}\n"
  },
  {
    "path": "5-network/01-fetch/01-fetch-users/_js.view/test.js",
    "content": "describe(\"getUsers\", function() {\n\n  it(\"gets users from GitHub\", async function() {\n    let users = await getUsers(['iliakan', 'remy', 'no.such.users']);\n    assert.equal(users[0].login, 'iliakan');\n    assert.equal(users[1].login, 'remy');\n    assert.equal(users[2], null);\n  });\n\n});\n"
  },
  {
    "path": "5-network/01-fetch/01-fetch-users/solution.md",
    "content": "\nPour récupérer un utilisateur, nous avons besoin de : `fetch('https://api.github.com/users/USERNAME')`.\n\nSi la réponse a le statut `200`, appelons `.json()` pour lire l'objet JS.\n\nSinon, si un `fetch` échoue, ou si la réponse a un statut différent de 200, nous renvoyons simplement `null` dans le tableau de résutats.\n\nVoici donc le code :\n\n```js demo\nasync function getUsers(names) {\n  let jobs = [];\n\n  for(let name of names) {\n    let job = fetch(`https://api.github.com/users/${name}`).then(\n      successResponse => {\n        if (successResponse.status != 200) {\n          return null;\n        } else {\n          return successResponse.json();\n        }\n      },\n      failResponse => {\n        return null;\n      }\n    );\n    jobs.push(job);\n  }\n\n  let results = await Promise.all(jobs);\n\n  return results;\n}\n```\n\nVeuillez noter : l'appel `.then` est directement attaché à `fetch`, de sorte que lorsque nous avons la réponse, il n'attend pas d'autres fetches, mais commence à lire `.json()` immédiatement.\n\nSi nous avions utilisé `await Promise.all(names.map(name => fetch(...)))`, et appelé `.json()` sur les résultats, il aurait attendu que tous les fetches répondent. En ajoutant `.json()` directement à chaque `fetch`, nous nous assurons que les fetches individuels commencent à lire les données en JSON sans s'attendre les uns les autres.\n\nC'est un exemple de la façon dont l'API Promise de bas niveau peut toujours être utile même si nous utilisons principalement `async/wait`.\n"
  },
  {
    "path": "5-network/01-fetch/01-fetch-users/task.md",
    "content": "# Récupérer des utilisateurs depuis GitHub\n\nCréez une fonction asynchrone `getUsers(names)`, qui obtient un tableau de connexions GitHub, récupère les utilisateurs de GitHub et renvoie un tableau d'utilisateurs GitHub.\n\nL'URL GitHub avec les informations utilisateur pour la donnée `USERNAME` est : `https://api.github.com/users/USERNAME`.\n\nIl y a un exemple de test dans la sandbox.\n\nDétails importants :\n\n1. Il devrait y avoir une requête `fetch` par utilisateur.\n2. Les demandes ne doivent pas s’attendre les unes les autres. Pour que les données arrivent le plus tôt possible.\n3. Si une requête échoue, ou si l'utilisateur n'existe pas, la fonction doit retourner `null` dans le tableau de résultats.\n"
  },
  {
    "path": "5-network/01-fetch/article.md",
    "content": "\n# Fetch\n\nJavaScript peut envoyer des requêtes réseau au serveur et charger de nouvelles informations chaque fois que nécessaire.\n\nPar exemple, nous pouvons utiliser une requête réseau pour :\n\n- Soumettre une commande,\n- Charger des informations utilisateur,\n- Recevoir les dernières mises à jour du serveur,\n- ...etc.\n\n... Et tout cela sans recharger la page !\n\nIl y a un terme générique \"AJAX\" (abrégé de <b>A</b>synchronous <b>J</b>avaScript <b>A</b>nd <b>X</b>ML) pour les requêtes réseau à partir de JavaScript. Cependant nous n'avons pas besoin d'utiliser XML : le terme vient de l'ancien temps, c'est pourquoi ce mot est là. Vous avez peut-être déjà entendu ce terme.\n\nIl existe plusieurs façons d'envoyer une requête réseau et d'obtenir des informations du serveur.\n\nLa méthode `fetch()` est moderne et polyvalente, nous allons donc commencer avec celle-ci. Elle n'est pas prise en charge par les anciens navigateurs (peut être polyfilled), mais très bien prise en charge par les navigateurs modernes.\n\nLa syntaxe de base est :\n\n```js\nlet promise = fetch(url, [options])\n```\n\n- **`url`** -- l'URL cible.\n- **`options`** -- paramètres facultatifs : méthode, en-têtes, etc...\n\nSans `options`, c'est une simple requête GET, téléchargeant le contenu de l'`url`.\n\nLe navigateur démarre la requête immédiatement et renvoie une promesse que le code appelant devrait utiliser pour obtenir le résultat.\n\nObtenir une réponse est généralement un processus en deux étapes.\n\n**Premièrement, la `promise`, renvoyée par `fetch`, se résout avec un objet de la classe intégrée [Response](https://fetch.spec.whatwg.org/#response-class) dès que le serveur répond avec des en-têtes.**\n\nÀ ce stade, nous pouvons vérifier l'état HTTP, pour voir s'il est réussi ou non, vérifier les en-têtes, mais nous ne disposons pas encore du corps.\n\nLa promesse rejette si le `fetch` n'a pas pu faire de requête HTTP, par exemple problèmes de réseau, ou si l'adresse n'existe pas. Les statuts HTTP anormaux, tels que 404 ou 500, ne provoquent pas d'erreur.\n\nNous pouvons voir l'état HTTP dans les propriétés de réponse :\n\n- **`status`** -- Code d'état HTTP, par exemple 200.\n- **`ok`** -- booléen, `true` si le code d'état HTTP est 200-299.\n\nPar exemple :\n\n```js\nlet response = await fetch(url);\n\nif (response.ok) { // if HTTP-status is 200-299\n  // obtenir le corps de réponse (la méthode expliquée ci-dessous)\n  let json = await response.json();\n} else {\n  alert(\"HTTP-Error: \" + response.status);\n}\n```\n\n**Deuxièmement, pour obtenir le corps de la réponse, nous devons utiliser un appel de méthode supplémentaire.**\n\n`Response` fournit plusieurs méthodes basées sur les promesses pour accéder au corps dans différents formats :\n\n- **`response.text()`** -- lit la réponse et retourne sous forme de texte,\n- **`response.json()`** -- analyse la réponse en JSON,\n- **`response.formData()`** -- retourne la réponse en tant que objet `FormData` (expliqué dans le [chapitre suivant](info:formdata)),\n- **`response.blob()`** -- retourne la réponse en tant que [Blob](info:blob) (donnée binaire avec type),\n- **`response.arrayBuffer()`** -- retourne la réponse en tant que [ArrayBuffer](info:arraybuffer-binary-arrays) (représentation de bas niveau de donnée binaire),\n- aditionellement, `response.body` est un objet [ReadableStream](https://streams.spec.whatwg.org/#rs-class), qui permet de lire le corps morceau par morceau, nous verrons un exemple plus tard.\n\nPar exemple, obtenons un objet JSON avec les derniers commits de GitHub :\n\n```js run async\nlet url = 'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits';\nlet response = await fetch(url);\n\n*!*\nlet commits = await response.json(); // lire le corps de réponse et analyser en JSON\n*/!*\n\nalert(commits[0].author.login);\n```\n\nOu, la même chose sans `await`, en utilisant la syntaxe des promesses pures :\n\n```js run\nfetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits')\n  .then(response => response.json())\n  .then(commits => alert(commits[0].author.login));\n```\n\nPour obtenir la réponse en texte, `await response.text()` au lieu de `.json()` :\n\n```js run async\nlet response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits');\n\nlet text = await response.text(); // lire le corps de la réponse sous forme de texte\n\nalert(text.slice(0, 80) + '...');\n```\n\nEn tant que vitrine pour la lecture au format binaire, récupérons et affichons une image du logo de [\"fetch\" specification](https://fetch.spec.whatwg.org) (voir le chapitre [Blob](info:blob) pour plus de détails sur les opérations de `Blob`):\n\n```js async run\nlet response = await fetch('/article/fetch/logo-fetch.svg');\n\n*!*\nlet blob = await response.blob(); // télécharger en tant qu'objet Blob\n*/!*\n\n// create <img> for it\nlet img = document.createElement('img');\nimg.style = 'position:fixed;top:10px;left:10px;width:100px';\ndocument.body.append(img);\n\n// l'afficher\nimg.src = URL.createObjectURL(blob);\n\nsetTimeout(() => { // le cacher après 3 secondes\n  img.remove();\n  URL.revokeObjectURL(img.src);\n}, 3000);\n```\n\n````warn\nNous ne pouvons choisir qu'une seule méthode de lecture du corps.\n\nSi nous avons déjà la réponse avec `response.text()`, alors `response.json()` ne fonctionnera pas, car le contenu du corps a déjà été traité.\n\n```js\nlet text = await response.text(); // corps de la réponse consommé\nlet parsed = await response.json(); // echec (déjà consommé)\n```\n\n## En-têtes de réponse\n\nLes en-têtes de réponse sont disponibles dans un objet d'en-têtes de type Map-like `response.headers`.\n\nCe n'est pas exactement un Map, mais il a des méthodes similaires pour obtenir des en-têtes individuels par nom ou les parcourir :\n\n```js run async\nlet response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits');\n\n// get one header\nalert(response.headers.get('Content-Type')); // application/json; charset=utf-8\n\n// iterate over all headers\nfor (let [key, value] of response.headers) {\n  alert(`${key} = ${value}`);\n}\n```\n\n## En-têtes de requêtes\n\nPour définir un en-tête de requête dans `fetch`, nous pouvons utiliser l'option `headers`. Il a un objet avec des en-têtes sortants, comme ceci :\n\n```js\nlet response = fetch(protectedUrl, {\n  headers: {\n    Authentication: 'secret'\n  }\n});\n```\n\n... Mais il y a une liste d'[en-têtes HTTP interdits](https://fetch.spec.whatwg.org/#forbidden-header-name) que nous ne pouvons pas définir :\n\n- `Accept-Charset`, `Accept-Encoding`\n- `Access-Control-Request-Headers`\n- `Access-Control-Request-Method`\n- `Connection`\n- `Content-Length`\n- `Cookie`, `Cookie2`\n- `Date`\n- `DNT`\n- `Expect`\n- `Host`\n- `Keep-Alive`\n- `Origin`\n- `Referer`\n- `TE`\n- `Trailer`\n- `Transfer-Encoding`\n- `Upgrade`\n- `Via`\n- `Proxy-*`\n- `Sec-*`\n\nCes en-têtes assurent un HTTP correct et sûr, ils sont donc contrôlés exclusivement par le navigateur.\n\n## Requêtes POST\n\nPour faire une requête `POST`, ou une requête avec une autre méthode, nous devons utiliser les options `fetch` :\n\n- **`method`** -- HTTP-method, par exemple `POST`,\n- **`body`** -- le corps de la requête, un parmi ceux-ci :\n  - une chaîne de caractères (par exemple encodé en JSON),\n  - un objet `FormData`, pour soumettre les données en tant que `multipart/form-data`,\n  - `Blob`/`BufferSource` pour envoyer des données binaires,\n  - [URLSearchParams](info:url), pour soumettre les données au format `x-www-form-urlencoded`, rarement utilisé.\n\nLe format JSON est utilisé la plupart du temps.\n\nPar exemple, ce code soumet l'objet `user` en JSON :\n\n```js run async\nlet user = {\n  name: 'John',\n  surname: 'Smith'\n};\n\n*!*\nlet response = await fetch('/article/fetch/post/user', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json;charset=utf-8'\n  },\n  body: JSON.stringify(user)\n});\n*/!*\n\nlet result = await response.json();\nalert(result.message);\n```\n\nVeuillez noter que si la requête `body` est une chaîne de caractères, alors l'en-tête `Content-Type` est défini sur `text/plain;charset=UTF-8` par défaut.\n\nMais, si nous envoyons du JSON, nous utiliserons à la place l'option `headers` pour envoyer `application/json`, le bon `Content-Type` pour les données encodées en JSON.\n\n## Envoi d'une image\n\nNous pouvons également soumettre des données binaires avec `fetch` en utilisant des objets `Blob` ou `BufferSource`.\n\nDans cet exemple, il y a un `<canvas>` où nous pouvons dessiner en déplaçant une souris dessus. Un clic sur le bouton \"submit\" envoie l'image au serveur :\n\n```html run autorun height=\"90\"\n<body style=\"margin:0\">\n  <canvas id=\"canvasElem\" width=\"100\" height=\"80\" style=\"border:1px solid\"></canvas>\n\n  <input type=\"button\" value=\"Submit\" onclick=\"submit()\">\n\n  <script>\n    canvasElem.onmousemove = function(e) {\n      let ctx = canvasElem.getContext('2d');\n      ctx.lineTo(e.clientX, e.clientY);\n      ctx.stroke();\n    };\n\n    async function submit() {\n      let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));\n      let response = await fetch('/article/fetch/post/image', {\n        method: 'POST',\n        body: blob\n      });\n\n      // le serveur répond avec confirmation et la taille de l'image\n      let result = await response.json();\n      alert(result.message);\n    }\n\n  </script>\n</body>\n```\n\nVeuillez noter qu'ici, nous ne définissons pas l'en-tête `Content-Type` manuellement, car un objet `Blob` a un type intégré (ici `image/png`, tel que généré par `toBlob`). Pour les objets `Blob`, ce type devient la valeur de `Content-Type`.\n\nLa fonction `submit()` peut être réécrite sans `async/await` comme ceci :\n\n```js\nfunction submit() {\n  canvasElem.toBlob(function(blob) {        \n    fetch('/article/fetch/post/image', {\n      method: 'POST',\n      body: blob\n    })\n      .then(response => response.json())\n      .then(result => alert(JSON.stringify(result, null, 2)))\n  }, 'image/png');\n}\n```\n\n## Résumé\n\nUne requête fetch typique se compose de deux appels `await` :\n\n```js\nlet response = await fetch(url, options); // se résout avec des en-têtes de réponse\nlet result = await response.json(); // lit le corps en tant que JSON\n```\n\nOu, sans `await` :\n\n```js\nfetch(url, options)\n  .then(response => response.json())\n  .then(result => /* process result */)\n```\n\nPropriétés de réponse :\n- `response.status` -- Code HTTP de la réponse,\n- `response.ok` -- `true` est le statut 200-299.\n- `response.headers` -- objet Map-like avec en-têtes HTTP.\n\nMéthodes pour obtenir le corps de réponse :\n- **`response.text()`** -- retourne la réponse sous forme de texte,\n- **`response.json()`** -- analyse la réponse en tant qu'objet JSON,\n- **`response.formData()`** -- retourne la réponse en tant qu'objet `FormData` (encodage `multipart/form-data`, voir le chapitre suivant),\n- **`response.blob()`** -- retourne la réponse en tant que [Blob](info:blob) (données binaires avec type),\n- **`response.arrayBuffer()`** -- retourne la réponse en tant que [ArrayBuffer](info:arraybuffer-binary-arrays) (données binaires de bas niveau),\n\nOptions de fetch jusque là :\n- `method` -- Méthode HTTP,\n- `headers` -- un objet avec en-têtes de requête (aucun en-tête n'est autorisé),\n- `body` -- les données à envoyer (corps de la demande) en tant que `string`, `FormData`, `BufferSource`, `Blob` ou objet `UrlSearchParams`.\n\nDans les chapitres suivants, nous verrons plus d'options et de cas d'utilisation de `fetch`.\n"
  },
  {
    "path": "5-network/01-fetch/post.view/server.js",
    "content": "const Koa = require('koa');\nconst app = new Koa();\nconst bodyParser = require('koa-bodyparser');\nconst getRawBody = require('raw-body')\nconst Router = require('koa-router');\n\nlet router = new Router();\n\nrouter.post('/user', async (ctx) => {\n  ctx.body = {\n    message: \"User saved.\"\n  };\n});\n\nrouter.post('/image', async (ctx) => {\n  let body = await getRawBody(ctx.req, {\n    limit: '1mb'\n  });\n  ctx.body = {\n    message: `Image saved, size:${body.length}.`\n  };\n});\n\napp\n  .use(bodyParser())\n  .use(router.routes())\n  .use(router.allowedMethods());\n\n\nif (!module.parent) {\n  http.createServer(app.callback()).listen(8080);\n} else {\n  exports.accept = app.callback();\n}\n"
  },
  {
    "path": "5-network/02-formdata/article.md",
    "content": "\n# FormData\n\nCe chapitre concerne l'envoi de formulaires HTML: avec ou sans fichiers, avec des champs supplémentaires, etc...\n\nLes objets [FormData](https://xhr.spec.whatwg.org/#interface-formdata) peuvent nous aider pour cela. Comme vous l'avez peut-être deviné, c'est l'objet pour représenter les données du formulaire HTML.\n\nLe constructeur est :\n```js\nlet formData = new FormData([form]);\n```\n\nSi un élément HTML `form` est fourni, il capture automatiquement ses champs.\n\nLa particularité de `FormData` est que les méthodes réseau, telles que `fetch`, peuvent accepter un objet `FormData` en tant que corps. Il est encodé et envoyé avec `Content-Type: multipart/form-data`.\n\nDu point de vue du serveur, cela ressemble à une soumission de formulaire habituelle.\n\n## Envoi d'un formulaire simple\n\nEnvoyons d'abord un formulaire simple.\n\nComme vous pouvez le voir, c'est presque une ligne :\n\n```html run autorun\n<form id=\"formElem\">\n  <input type=\"text\" name=\"name\" value=\"John\">\n  <input type=\"text\" name=\"surname\" value=\"Smith\">\n  <input type=\"submit\">\n</form>\n\n<script>\n  formElem.onsubmit = async (e) => {\n    e.preventDefault();\n\n    let response = await fetch('/article/formdata/post/user', {\n      method: 'POST',\n*!*\n      body: new FormData(formElem)\n*/!*\n    });\n\n    let result = await response.json();\n\n    alert(result.message);\n  };\n</script>\n```\n\nDans cet exemple, le code du serveur n'est pas présenté, car il dépasse notre portée. Le serveur accepte la requête POST et répond \"User saved\".\n\n## Méthodes FormData\n\nNous pouvons modifier les champs dans `FormData` avec des méthodes :\n\n- `formData.append(name, value)` - ajoute un champ de formulaire avec le `name` et `value` donnés,\n- `formData.append(name, blob, fileName)` - ajoute un champ comme s'il était `<input type=\"file\">`, le troisième argument `fileName` définit le nom du fichier (pas le nom du champ de formulaire), comme s'il s'agissait d'un nom du fichier dans le système de fichiers de l'utilisateur,\n- `formData.delete(name)` - supprimer le champ avec le `name` donné,\n- `formData.get(name)` - obtient la valeur du champ avec le `name` donné,\n- `formData.has(name)` - s'il existe un champ avec le `name` donné, retourne `true`, sinon `false`\n\nUn formulaire est techniquement autorisé à avoir plusieurs champs avec le même `name`, donc plusieurs appels à `append` ajoute d'autres champs portant le même nom.\n\nIl existe également la méthode `set`, avec la même syntaxe que `append`. La différence est que `.set` supprime tous les champs avec le `name` donné, puis ajoute un nouveau champ. Il s'assure donc qu'il n'y a qu'un seul champ avec ce genre de `name`, le reste est comme `append` :\n\n- `formData.set(name, value)`,\n- `formData.set(name, blob, fileName)`.\n\nNous pouvons également parcourir les champs formData en utilisant la boucle `for..of` :\n\n```js run\nlet formData = new FormData();\nformData.append('key1', 'value1');\nformData.append('key2', 'value2');\n\n// List key/value pairs\nfor(let [name, value] of formData) {\n  alert(`${name} = ${value}`); // key1 = value1, ensuite key2 = value2\n}\n```\n\n## Envoi d'un formulaire avec un fichier\n\nLe formulaire est toujours envoyé en tant que `Content-Type: multipart/form-data`, cet encodage permet d'envoyer des fichiers. Ainsi, les champs `<input type=\"file\">` sont également envoyés, comme pour une soumission de formulaire habituelle.\n\nVoici un exemple avec ce genre de formulaire :\n\n```html run autorun\n<form id=\"formElem\">\n  <input type=\"text\" name=\"firstName\" value=\"John\">\n  Picture: <input type=\"file\" name=\"picture\" accept=\"image/*\">\n  <input type=\"submit\">\n</form>\n\n<script>\n  formElem.onsubmit = async (e) => {\n    e.preventDefault();\n\n    let response = await fetch('/article/formdata/post/user-avatar', {\n      method: 'POST',\n*!*\n      body: new FormData(formElem)\n*/!*\n    });\n\n    let result = await response.json();\n\n    alert(result.message);\n  };\n</script>\n```\n\n## Envoi d'un formulaire avec des données Blob\n\nComme nous l'avons vu dans le chapitre <info:fetch>, il est facile d'envoyer des données binaires générées dynamiquement, par exemple une image en tant que `Blob`. Nous pouvons le fournir directement en tant que paramètre `body` de `fetch`.\n\nEn pratique cependant, il est souvent plus commode d'envoyer une image non pas séparément, mais en tant que partie du formulaire, avec des champs supplémentaires, tels que \"name\" et autres métadonnées.\n\nEn outre, les serveurs sont généralement plus adaptés pour accepter des formulaires encodés en plusieurs parties, plutôt que des données binaires brutes.\n\nCet exemple soumet une image à partir de `<canvas>`, ainsi que d'autres champs, sous forme de formulaire, en utilisant `FormData` :\n\n```html run autorun height=\"90\"\n<body style=\"margin:0\">\n  <canvas id=\"canvasElem\" width=\"100\" height=\"80\" style=\"border:1px solid\"></canvas>\n\n  <input type=\"button\" value=\"Submit\" onclick=\"submit()\">\n\n  <script>\n    canvasElem.onmousemove = function(e) {\n      let ctx = canvasElem.getContext('2d');\n      ctx.lineTo(e.clientX, e.clientY);\n      ctx.stroke();\n    };\n\n    async function submit() {\n      let imageBlob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));\n\n*!*\n      let formData = new FormData();\n      formData.append(\"firstName\", \"John\");\n      formData.append(\"image\", imageBlob, \"image.png\");\n*/!*    \n\n      let response = await fetch('/article/formdata/post/image-form', {\n        method: 'POST',\n        body: formData\n      });\n      let result = await response.json();\n      alert(result.message);\n    }\n\n  </script>\n</body>\n```\n\nVeuillez noter comment l'image `Blob` est ajoutée :\n\n```js\nformData.append(\"image\", imageBlob, \"image.png\");\n```\n\nC'est comme s'il y avait `<input type=\"file\" name=\"image\">` dans le formulaire, et le visiteur a soumis un fichier nommé `\"image.png\"` (3ème argument) avec les données `imageBlob` (2ème argument) de son système de fichiers.\n\nLe serveur lit les données du formulaire et le fichier, comme s'il s'agissait d'une soumission régulière de formulaire.\n\n## Résumé\n\nLes objets [FormData](https://xhr.spec.whatwg.org/#interface-formdata) sont utilisés pour capturer le formulaire HTML et le soumettre en utilisant `fetch` ou une autre méthode réseau.\n\nNous pouvons soit créer un `new FormData(form)` à partir d'un formulaire HTML, soit créer un objet sans aucun formulaire, puis ajouter des champs avec des méthodes :\n\n- `formData.append(name, value)`\n- `formData.append(name, blob, fileName)`\n- `formData.set(name, value)`\n- `formData.set(name, blob, fileName)`\n\nNotons ici deux particularités :\n\n1. La méthode `set` supprime les champs du même nom, contrairement à `append`. C'est la seule différence entre eux.\n2. Pour envoyer un fichier, une syntaxe à 3 arguments est nécessaire, le dernier argument est un nom de fichier, qui est normalement extrait du système de fichiers utilisateur pour `<input type=\"file\">`.\n\nLes autres méthodes sont :\n\n- `formData.delete(name)`\n- `formData.get(name)`\n- `formData.has(name)`\n\nC'est tout !\n"
  },
  {
    "path": "5-network/02-formdata/post.view/server.js",
    "content": "const Koa = require('koa');\nconst app = new Koa();\nconst bodyParser = require('koa-bodyparser');\nconst getRawBody = require('raw-body')\nconst busboy = require('async-busboy');\nconst Router = require('koa-router');\n\nlet router = new Router();\n\nrouter.post('/user', async (ctx) => {\n  ctx.body = {\n    message: \"User saved\"\n  };\n});\n\nrouter.post('/image-form', async (ctx) => {\n\n  let files = [];\n  const { fields } = await busboy(ctx.req, {\n    onFile(fieldname, file, filename, encoding, mimetype) {\n      // read all file stream to continue\n      let length = 0;\n      file.on('data', function(data) {\n        length += data.length;\n      });\n      file.on('end', () => {\n        files.push({\n          fieldname,\n          filename,\n          length\n        });\n      });\n    }\n  });\n\n  ctx.body = {\n    message: `Image saved, firstName: ${fields.firstName}, Image size:${files[0].length}, fileName: ${files[0].filename}`\n  };\n});\n\n\nrouter.post('/user-avatar', async (ctx) => {\n\n  let files = [];\n  const { fields } = await busboy(ctx.req, {\n    onFile(fieldname, file, filename, encoding, mimetype) {\n      // read all file stream to continue\n      let length = 0;\n      file.on('data', function(data) {\n        length += data.length;\n      });\n      file.on('end', () => {\n        files.push({\n          fieldname,\n          filename,\n          length\n        });\n      });\n\n    }\n  });\n\n  ctx.body = {\n    message: `User with picture, firstName: ${fields.firstName}, picture size:${files[0].length}`\n  };\n});\n\napp\n  .use(bodyParser())\n  .use(router.routes())\n  .use(router.allowedMethods());\n\n\nif (!module.parent) {\n  http.createServer(app.callback()).listen(8080);\n} else {\n  exports.accept = app.callback();\n}\n"
  },
  {
    "path": "5-network/03-fetch-progress/article.md",
    "content": "\n# Fetch: Download progress\n\nLa méthode `fetch` permet de suivre la progression du *téléchargement*.\n\nVeuillez noter: il n'y a actuellement aucun moyen pour `fetch` de suivre la progression du *téléchargement*. À cette fin, veuillez utiliser [XMLHttpRequest](info:xmlhttprequest), nous le couvrirons plus tard.\n\nPour suivre la progression du téléchargement, nous pouvons utiliser la propriété `response.body`. C'est `ReadableStream` - un objet spécial qui fournit le corps morceau par morceau, comme il vient. Les flux lisibles sont décrits dans la spécification [Streams API](https://streams.spec.whatwg.org/#rs-class).\n\nContrairement à `response.text()`, `response.json()` et à d'autres méthodes, `response.body` donne un contrôle total sur le processus de lecture, et nous pouvons compter la quantité consommée à tout moment.\n\nVoici l'esquisse de code qui lit la réponse de `response.body` :\n\n```js\n// au lieu de response.json() et d'autres méthodes\nconst reader = response.body.getReader();\n\n// boucle infinie pendant le téléchargement du corps\nwhile(true) {\n  // done is true for the last chunk\n  // value is Uint8Array of the chunk bytes\n  const {done, value} = await reader.read();\n\n  if (done) {\n    break;\n  }\n\n  console.log(`Received ${value.length} bytes`)\n}\n```\n\nLe résultat de l'appel `await reader.read()` est un objet avec deux propriétés :\n- **`done`** -- `true` lorsque la lecture est terminée, sinon `false`.\n- **`value`** -- un tableau typé d'octets : `Uint8Array`.\n\n```smart\nL'API Streams décrit également l'itération asynchrone sur `ReadableStream` avec la boucle `for wait..of`, mais elle n'est pas encore largement prise en charge (voir les [problèmes de navigateurs](https://github.com/whatwg/streams/issues/778#issuecomment-461341033)), nous utilisons donc la boucle `while`.\n```\n\nNous recevons des morceaux de réponse dans la boucle, jusqu'à ce que le chargement se termine, c'est-à-dire: jusqu'à ce que `done` devienne `true`.\n\nPour enregistrer la progression, nous avons juste besoin d'ajouter la longueur de la `value` de chaque fragment reçu au compteur.\n\nVoici l'exemple de travail complet qui obtient la réponse et enregistre la progression dans la console, plus d'explications à suivre :\n\n```js run async\n// Étape 1: démarrez la récupération et obtenir un lecteur\nlet response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits?per_page=100');\n\nconst reader = response.body.getReader();\n\n// Étape 2: obtenir la longueur totale\nconst contentLength = +response.headers.get('Content-Length');\n\n// Étape 3: lecture des données\nlet receivedLength = 0; // reçu autant d'octets en ce moment\nlet chunks = []; // tableau de morceaux binaires reçus (comprend le corps)\nwhile(true) {\n  const {done, value} = await reader.read();\n\n  if (done) {\n    break;\n  }\n\n  chunks.push(value);\n  receivedLength += value.length;\n\n  console.log(`Received ${receivedLength} of ${contentLength}`)\n}\n\n// Étape 4: concaténer des morceaux en un seul Uint8Array\nlet chunksAll = new Uint8Array(receivedLength); // (4.1)\nlet position = 0;\nfor(let chunk of chunks) {\n\tchunksAll.set(chunk, position); // (4.2)\n\tposition += chunk.length;\n}\n\n// Étape 5: décoder en une chaîne de caractères\nlet result = new TextDecoder(\"utf-8\").decode(chunksAll);\n\n// Nous avons fini !\nlet commits = JSON.parse(result);\nalert(commits[0].author.login);\n```\n\nExpliquons cela étape par étape :\n\n1. Nous effectuons un `fetch` comme d'habitude, mais au lieu d'appeler `response.json() `, nous obtenons un lecteur de flux `response.body.getReader()`.\n\n    Veuillez noter que nous ne pouvons pas utiliser ces deux méthodes pour lire la même réponse : utilisez un lecteur ou une méthode de réponse pour obtenir le résultat.\n2. Avant la lecture, nous pouvons déterminer la longueur complète de la réponse à partir de l'en-tête `Content-Length`.\n\n    Il peut être absent pour les requêtes cross-origin (voir le chapitre <info:fetch-crossorigin>), techniquement, un serveur n'a pas à le configurer. Mais généralement, c'est à sa place.\n3. Appel de `await reader.read()` jusqu'à ce que ce soit fait.\n\n    Nous rassemblons des blocs de réponse dans le tableau `chunks`. C'est important, car une fois la réponse consommée, nous ne pourrons pas la \"relire\" à l'aide de `response.json()` ou d'une autre manière (vous pouvez essayer, il y aura une erreur).\n4. À la fin, nous avons `chunks` -- un tableau de morceaux d'octets `Uint8Array`. Nous devons les joindre en un seul résultat. Malheureusement, il n'y a pas de méthode unique qui les concatène, donc il y a du code pour le faire :\n    1. Nous créons `chunksAll = new Uint8Array(receivedLength)` -- un tableau de même type avec la longueur combinée.\n    2. Ensuite nous utilisons la méthode `.set(chunk, position)` pour copier chaque `chunk` l'un après l'autre.\n5. Nous avons le résultat dans `chunksAll`. C'est un tableau d'octets cependant, pas une chaîne de caractères.\n\n    Pour créer une chaîne de caractères, nous devons interpréter ces octets. Le [TextDecoder](info:text-decoder) intégré fait exactement cela. Ensuite, nous pouvons `JSON.parse`, si nécessaire.\n\n    Et si nous avions besoin d'un contenu binaire au lieu d'une chaîne de caractères ? C'est encore plus simple. Remplacez les étapes 4 et 5 par une seule ligne qui crée un `Blob` à partir de tous les morceaux :\n    ```js\n    let blob = new Blob(chunks);\n    ```\n\nÀ la fin, nous avons le résultat (sous forme de chaîne de caractères ou d'objet blob, selon ce qui est pratique) et le suivi des progrès dans le processus.\n\nEncore une fois, veuillez noter que ce n'est pas pour la progression en *upload* (pas de possibilité actuellement avec `fetch`), seulement pour la progression en *download*.\n\nDe plus, si la taille est inconnue, nous devrions vérifier `receiveLength` dans la boucle et la briser une fois qu'elle atteint une certaine limite. Pour que les `chunks` ne débordent pas de mémoire.\n"
  },
  {
    "path": "5-network/03-fetch-progress/progress.view/index.html",
    "content": "<!doctype html>\n<script>\n(async () {\n\t\n\tconst response = await fetch('long.txt');\n\tconst reader = response.body.getReader();\n\n\tconst contentLength = +response.headers.get('Content-Length');\n\tlet receivedLength = 0;\n\tlet chunks = [];\n\twhile(true) {\n\t\tconst chunk = await reader.read();\n\n\t\tif (chunk.done) {\n\t\t\tconsole.log(\"done!\");\n\t\t\tbreak;\n\t\t}\n\n\t\tchunks.push(chunk.value);\n\t\treceivedLength += chunk.value.length;\n\t\tconsole.log(`${receivedLength}/${contentLength} received`)\n\t}\n\n\n\tlet chunksMerged = new Uint8Array(receivedLength);\n\tlet length = 0;\n\tfor(let chunk of chunks) {\n\t\tchunksMerged.set(chunk, length);\n\t\tlength += chunk.length;\n\t}\n\n\tlet result = new TextDecoder(\"utf-8\").decode(chunksMerged);\n\tconsole.log(result);\n})();\n\n</script>\n"
  },
  {
    "path": "5-network/03-fetch-progress/progress.view/long.txt",
    "content": "A long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\n"
  },
  {
    "path": "5-network/04-fetch-abort/article.md",
    "content": "\n# Fetch: Abort\n\nComme nous le savons, `fetch` renvoie une promesse. Et JavaScript n'a généralement aucun concept d'\"annulation\" d'une promesse. Alors, comment pouvons-nous annuler un `fetch` en cours ? Par exemple. si les actions de l'utilisateur sur notre site indiquent que le `fetch` n'est plus nécessaire.\n\nIl existe un objet intégré spécial dédié : `AbortController`, qui peut être utilisé pour abandonner non seulement un `fetch`, mais aussi d'autres tâches asynchrones.\n\nL'utilisation est assez simple :\n\n## L'objet AbortController\n\nCréez un contrôleur :\n\n```js\nlet controller = new AbortController();\n```\n\nUn contrôleur est un objet extrêmement simple.\n\n- Il a une seule méthode `abort()`,\n- Et une seule propriété `signal` qui permet de définir des écouteurs d'événements dessus.\n\nQuand `abort()` est appelé :\n- `controller.signal` émet l'événement `\"abort\"`.\n- La propriété `controller.signal.aborted` devient `true`.\n\nGenerally, we have two parties in the process:\n1. The one that performs a cancelable operation, it sets a listener on `controller.signal`.\n2. The one that cancels: it calls `controller.abort()` when needed.\n\nVoici l'exemple complet (sans `fetch` encore) :\n\n```js run\nlet controller = new AbortController();\nlet signal = controller.signal;\n\n// The party that performs a cancelable operation\n// gets the \"signal\" object\n// and sets the listener to trigger when controller.abort() is called\nsignal.addEventListener('abort', () => alert(\"abort!\"));\n\n// The other party, that cancels (at any point later):\ncontroller.abort(); // abort!\n\n// L'événement se déclenche et signal.aborted devient vrai\nalert(signal.aborted); // true\n```\n\nAs we can see, `AbortController` is just a mean to pass `abort` events when `abort()` is called on it.\n\nNous pourrions implémenter le même type d'écoute d'événement dans notre code par nous-mêmes, sans objet `AbortController`.\n\nMais ce qui est précieux, c'est que `fetch` sait comment travailler avec l'objet `AbortController`, il est intégré avec lui.\n\n## Utilisation avec fetch\n\nPour pouvoir annuler `fetch`, passez la propriété `signal` d'un `AbortController` comme une option `fetch` :\n\n```js\nlet controller = new AbortController();\nfetch(url, {\n  signal: controller.signal\n});\n```\n\nLa méthode `fetch` sait comment travailler avec `AbortController`. Il écoutera les événements `abort` sur `signal`.\n\nMaintenant, pour abandonner, appelons `controller.abort()` :\n\n```js\ncontroller.abort();\n```\n\nNous avons terminé: `fetch` récupère l'événement de `signal` et abandonne la requête.\n\nLorsqu'une extraction est abandonnée, sa promesse est rejetée avec une erreur `AbortError`, nous devons donc la gérer, par ex. dans `try..catch`.\n\nVoici l'exemple complet avec `fetch` abandonné après 1 seconde :\n\n```js run async\n// abandonner en 1 seconde\nlet controller = new AbortController();\nsetTimeout(() => controller.abort(), 1000);\n\ntry {\n  let response = await fetch('/article/fetch-abort/demo/hang', {\n    signal: controller.signal\n  });\n} catch(err) {\n  if (err.name == 'AbortError') { // gère abort()\n    alert(\"Aborted!\");\n  } else {\n    throw err;\n  }\n}\n```\n\n## AbortController est évolutif\n\n`AbortController` est évolutif, il permet d'annuler plusieurs récupérations à la fois.\n\nVoici une esquisse de code qui récupère de nombreuses `urls` en parallèle et utilise un seul contrôleur pour toutes les abandonner:\n\n```js\nlet urls = [...]; // une liste d'urls à récupérer en parallèle\n\nlet controller = new AbortController();\n\n// an array of fetch promises\nlet fetchJobs = urls.map(url => fetch(url, {\n  signal: controller.signal\n}));\n\nlet results = await Promise.all(fetchJobs);\n\n// si controller.abort() est appelée d'ailleurs,\n// elle interrompt tous les fetches\n```\n\nSi nous avons nos propres tâches asynchrones, différentes de `fetch`, nous pouvons utiliser un seul `AbortController` pour les arrêter, avec des fetches.\n\nNous avons juste besoin d'écouter son événement `abort` dans nos tâches :\n\n```js\nlet urls = [...];\nlet controller = new AbortController();\n\nlet ourJob = new Promise((resolve, reject) => { // notre tâche\n  ...\n  controller.signal.addEventListener('abort', reject);\n});\n\nlet fetchJobs = urls.map(url => fetch(url, { // fetches\n  signal: controller.signal\n}));\n\n// Wait for fetches and our task in parallel\nlet results = await Promise.all([...fetchJobs, ourJob]);\n\n// si controller.abort() est appelée d'ailleurs,\n// elle interrompt tous les fetches et ourJob\n```\n\n## Résumé\n\n- `AbortController` est un objet simple qui génère un événement `abort` sur sa propriété `signal` lorsque la méthode `abort()` est appelée (et définit également `signal.aborted` sur `true`).\n- `fetch` s'intègre avec lui : nous passons la propriété `signal` comme option, puis `fetch` l'écoute, il devient donc possible d'annuler le `fetch`.\n- Nous pouvons utiliser `AbortController` dans notre code. L'interaction \"appeler `abort()`\" -> \"écouter l'événement `abort`\" est simple et universelle. Nous pouvons l'utiliser même sans `fetch`.\n"
  },
  {
    "path": "5-network/04-fetch-abort/demo.view/server.js",
    "content": "const Koa = require('koa');\nconst app = new Koa();\n\nconst Router = require('koa-router');\n\nlet router = new Router();\n\nrouter.get('/hang', async (ctx) => {\n  await new Promise(() => {});\n});\n\napp\n  .use(router.routes())\n  .use(router.allowedMethods());\n\n\nif (!module.parent) {\n  http.createServer(app.callback()).listen(8080);\n} else {\n  exports.accept = app.callback();\n}\n"
  },
  {
    "path": "5-network/05-fetch-crossorigin/1-do-we-need-origin/solution.md",
    "content": "Nous avons besoin de `Origin`, car parfois `Referer` est absent. Par exemple, lorsque nous faisons un `fetch` de la page HTTP à partir de la page HTTPS (accès moins sécurisé de plus sécurisé), il n'y a pas de `Referer`.\n\nLe [Content Security Policy](http://en.wikipedia.org/wiki/Content_Security_Policy) peut interdire l'envoi d'un `Referer`.\n\nComme nous le verrons, `fetch` a des options qui empêchent d'envoyer le `Referer` et permettent même de le changer (dans le même site).\n\nPar spécification, `Referer` est un en-tête HTTP facultatif.\n\nPrécisément parce que `Referer` n'est pas fiable, `Origin` a été inventé. Le navigateur garantit une origine correcte pour les requêtes cross-origin.\n"
  },
  {
    "path": "5-network/05-fetch-crossorigin/1-do-we-need-origin/task.md",
    "content": "importance: 5\n\n---\n\n# Pourquoi avons-nous besoin d'Origin ?\n\nComme vous le savez probablement, il y a un en-tête HTTP `Referer`, qui contient généralement une URL de la page qui a initié une requête réseau.\n\nPar exemple, lors de la récupération de `http://google.com` à partir de `http://javascript.info/some/url`, les en-têtes ressemblent à ceci :\n\n```\nAccept: */*\nAccept-Charset: utf-8\nAccept-Encoding: gzip,deflate,sdch\nConnection: keep-alive\nHost: google.com\n*!*\nOrigin: http://javascript.info\nReferer: http://javascript.info/some/url\n*/!*\n```\n\nComme vous pouvez le voir, `Referer` et `Origin` sont présents.\n\nQuestions :\n\n1. Pourquoi `Origin` est nécessaire, si `Referer` a encore plus d'informations ?\n2. Est-il possible qu'il n'y ait pas de `Referer` ou `Origin`, ou est-ce incorrect ?\n"
  },
  {
    "path": "5-network/05-fetch-crossorigin/article.md",
    "content": "# Fetch: Requêtes Cross-Origin \n\nSi nous envoyons une requête `fetch` à un autre site Web, elle échouera probablement.\n\nPar exemple, essayons de récupérer `http://example.com` :\n\n```js run async\ntry {\n  await fetch('http://example.com');\n} catch(err) {\n  alert(err); // Échec de la récupération\n}\n```\n\nLa récupération échoue, comme prévu.\n\nLe concept principal ici est *origin* - un triplet domaine/port/protocole.\n\nLes requêtes cross-origin - celles envoyées vers un autre domaine (même un sous-domaine) ou un protocole ou un port - nécessitent des en-têtes spéciaux du côté distant.\n\nCette politique est appelée \"CORS\": Cross-Origin Resource Sharing.\n\n## Pourquoi CORS est-il nécessaire ? Un bref historique\n\nCORS existe pour protéger Internet des diaboliques pirates.\n\nPlus sérieusement, faisons une très brève digression historique.\n\n**Pendant de nombreuses années, un script d'un site n'a pas pu accéder au contenu d'un autre site.**\n\nCette règle simple mais puissante a été le fondement de la sécurité Internet. Par exemple. un script malveillant du site Web `hacker.com` ne pouvait pas accéder à la boîte aux lettres de l'utilisateur sur le site Web `gmail.com`. Les gens se sentaient en sécurité.\n\nJavaScript n'avait pas non plus de méthodes spéciales pour effectuer des requêtes réseau à l'époque. C'était un langage pour s'amuser à décorer une page Web.\n\nMais les développeurs Web ont demandé plus de puissance. Diverses astuces ont été inventées pour contourner la limitation et faire des demandes à d'autres sites Web.\n\n### Utilisation de formulaires\n\nUne façon de communiquer avec un autre serveur était de soumettre un `<form>`. Les gens l'ont soumis dans un `<iframe>`, juste pour rester sur la page actuelle, comme ceci :\n\n```html\n<!-- depuis la cible -->\n*!*\n<iframe name=\"iframe\"></iframe>\n*/!*\n\n<!-- un formulaire pourrait être généré dynamiquement et soumis par JavaScript -->\n*!*\n<form target=\"iframe\" method=\"POST\" action=\"http://another.com/…\">\n*/!*\n  ...\n</form>\n```\n\nAinsi, il était possible de faire une demande GET/POST vers un autre site, même sans méthodes de mise en réseau, car les formulaires peuvent envoyer des données n'importe où. Mais comme il est interdit d'accéder au contenu d'un `<iframe>` depuis un autre site, il n'était pas possible de lire la réponse.\n\nPour être précis, il y avait en fait des astuces pour cela, ils nécessitaient des scripts spéciaux à la fois sur l'iframe et sur la page. La communication avec l'iframe était donc techniquement possible. Pour le moment, il est inutile d'entrer dans les détails, laissons ces dinosaures reposer en paix.\n\n### Utilisation de scripts\n\nUne autre astuce consistait à utiliser une balise `script`. Un script peut avoir n'importe quel `src`, avec n'importe quel domaine, comme `<script src=\"http://another.com/…\">`. Il est possible d'exécuter un script à partir de n'importe quel site Web.\n\nSi un site Web, par exemple `another.com` avait pour but d'exposer des données pour ce type d'accès, alors un protocole dit \"JSONP (JSON with padding)\" aurait été utilisé.\n\nVoici comment cela fonctionnait.\n\nDisons que nous, sur notre site, devons obtenir les données de `http://another.com`, telles que la météo :\n\n1. Tout d'abord, en amont, nous déclarons une fonction globale pour accepter les données, par exemple `gotWeather`.\n\n    ```js\n    // 1. Déclarons la fonction pour traiter les données météorologiques\n    function gotWeather({ temperature, humidity }) {\n      alert(`temperature: ${temperature}, humidity: ${humidity}`);\n    }\n    ```\n2. Ensuite, nous écrivons une balise `<script>` avec `src=\"http://another.com/weather.json?callback=gotWeather\"`, en utilisant le nom de notre fonction comme paramètre d'URL `callback`.\n\n    ```js\n    let script = document.createElement('script');\n    script.src = `http://another.com/weather.json?callback=gotWeather`;\n    document.body.append(script);\n    ```\n3. Le serveur distant `another.com` génère dynamiquement un script qui appelle `gotWeather(...)` avec les données qu'il souhaite que nous recevions.\n    ```js\n    // La réponse attendue du serveur ressemble à ceci :\n    gotWeather({\n      temperature: 25,\n      humidity: 78\n    });\n    ```\n4. Lorsque le script distant se charge et s'exécute, `gotWeather` s'exécute et, comme c'est notre fonction, nous avons les données.\n\nCela fonctionne et ne viole pas la sécurité, car les deux parties ont convenu de transmettre les données de cette façon. Et, lorsque les deux parties sont d'accord, ce n'est certainement pas un hack. Il existe encore des services qui offrent ce genre d'accès, car cela fonctionne même pour les très anciens navigateurs.\n\nAprès un certain temps, les méthodes de mise en réseau en JavaScript sont apparues dans le navigateur.\n\nAu début, les requêtes cross-origin étaient interdites. Mais à la suite de longues discussions, les requêtes cross-origin ont été autorisées, mais avec de nouvelles capacités nécessitant une autorisation explicite du serveur, exprimées en en-têtes spéciaux.\n\n## Requêtes sécurisées\n\nIl existe deux types de requêtes cross-origin :\n\n1. Les requêtes sécurisées\n2. Toutes les autres.\n\nLes requêtes sécurisées sont bien plus simples à faire, alors commençons par elles.\n\nUne requête est sécurisée si elle remplit deux conditions :\n\n1. [Méthode sécurisée](https://fetch.spec.whatwg.org/#cors-safelisted-method) : GET, POST ou HEAD\n2. [En-têtes simples](https://fetch.spec.whatwg.org/#cors-safelisted-request-header) -- les seuls en-têtes personnalisés autorisés sont :\n    - `Accept`,\n    - `Accept-Language`,\n    - `Content-Language`,\n    - `Content-Type` avec la valeur `application/x-www-form-urlencoded`, `multipart/form-data` ou `text/plain`.\n\nToute autre demande est considérée comme \"non sécurisée\". Par exemple, une demande avec la méthode `PUT` ou avec un en-tête `API-Key` ne correspond pas aux limitations.\n\n**La différence essentielle est qu’une requête sécurisée peut être faite avec un `<form>` ou un `<script>`, sans méthodes spéciales.**\n\nAinsi, même un très ancien serveur doit être prêt à accepter une demande sécurisée.\n\nContrairement à cela, les requêtes avec des en-têtes non standard ou par exemple la méthode `DELETE` ne peut pas être créée de cette façon. Pendant longtemps, JavaScript n'a pas pu faire de telles requêtes. Un ancien serveur peut donc supposer que de telles requêtes proviennent d'une source privilégiée, \"car une page Web ne peut pas les envoyer\".\n\nLorsque nous essayons de faire une requête non sécurisée, le navigateur envoie une demande spéciale de \"contrôle en amont\" qui demande au serveur -- accepte-t-il ou non ces requêtes cross-origin ?\n\nEt, à moins que le serveur ne confirme explicitement cela avec ces en-têtes, une demande non sécurisée n'est pas envoyée.\n\nNous allons maintenant entrer dans les détails.\n\n## CORS pour des requêtes sécurisées\n\nSi une requête est une cross-origin, le navigateur lui ajoute toujours l'en-tête `Origin`.\n\nPar exemple, si nous requêtons `https://anywhere.com/request` depuis `https://javascript.info/page`, les en-têtes ressembleront à cela :\n\n```http\nGET /request\nHost: anywhere.com\n*!*\nOrigin: https://javascript.info\n*/!*\n...\n```\n\nComme vous pouvez le voir, l'en-tête `Origin` contient exactement l'origine (domaine/protocole/port), sans chemin.\n\nLe serveur peut inspecter le `Origin` et, s'il est convenu d'accepter une telle demande, ajoute un en-tête spécial `Access-Control-Allow-Origin` à la réponse. Cet en-tête doit contenir l'origine autorisée (dans notre cas `https://javascript.info`), ou une étoile `*`. Ensuite, la réponse est réussie, sinon c'est une erreur.\n\nLe navigateur joue ici le rôle d'un médiateur de confiance :\n1. Il garantit que l'`Origin` correcte est envoyée avec une requête cross-origin.\n2. Il vérifie pour permettre `Access-Control-Allow-Origin` dans la réponse, si elle existe, alors JavaScript est autorisé à accéder à la réponse, sinon il échoue avec une erreur.\n\n![](xhr-another-domain.svg)\n\nVoici un exemple de réponse de serveur permissive :\n```http\n200 OK\nContent-Type:text/html; charset=UTF-8\n*!*\nAccess-Control-Allow-Origin: https://javascript.info\n*/!*\n```\n\n## En-têtes de réponse\n\nPour les requêtes cross-origin, par défaut, JavaScript ne peut accéder qu'aux en-têtes de réponse dits \"sécurisés\" :\n\n- `Cache-Control`\n- `Content-Language`\n- `Content-Length`\n- `Content-Type`\n- `Expires`\n- `Last-Modified`\n- `Pragma`\n\nL'accès à tout autre en-tête de réponse provoque une erreur.\n\nPour accorder un accès JavaScript à tout autre en-tête de réponse, le serveur doit envoyer l'en-tête `Access-Control-Expose-Headers`. Il contient une liste séparée par des virgules de noms d'en-tête non sécurisés qui doivent être rendus accessibles.\n\nPar exemple :\n\n```http\n200 OK\nContent-Type:text/html; charset=UTF-8\nContent-Length: 12345\nContent-Encoding: gzip\nAPI-Key: 2c9de507f2c54aa1\nAccess-Control-Allow-Origin: https://javascript.info\n*!*\nAccess-Control-Expose-Headers: Content-Encoding,API-Key\n*/!*\n```\nAvec ce genre d'en-tête `Access-Control-Expose-Headers`, le script est autorisé à lire les en-têtes `Content-Encoding` et `API-Key` de la réponse.\n\n## Requêtes \"Non-sécurisées\" \n\nNous pouvons utiliser n'importe quelle méthode HTTP : pas seulement `GET/POST`, mais aussi `PATCH`, `DELETE` et d'autres.\n\nIl y a quelque temps, personne ne pouvait même imaginer qu'une page Web puisse faire de telles demandes. Il peut donc toujours exister des services web qui traitent une méthode non standard comme un signal : \"Ce n'est pas un navigateur\". Ils peuvent en tenir compte lors de la vérification des droits d'accès.\n\nAinsi, pour éviter les malentendus, toute demande \"non sûre\" - qui ne pouvait pas être faite par le passé, le navigateur ne fait pas de telles demandes tout de suite. Avant d'envoyer une demande préliminaire, dite de \"contrôle en amont\", demandant l'autorisation.\n\nUne demande de contrôle en amont utilise la méthode `OPTIONS`, pas de corps et deux en-têtes :\n\n\n- L’en-tête `Access-Control-Request-Method` contient la méthode de la requête non sécurisée.\n- L’en-tête `Access-Control-Request-Headers` fournit une liste séparée par des virgules de ses en-têtes HTTP non sécurisés.\n- L’en-tête `Origin` indique d'où vient la demande. (comme `https://javascript.info`)\n\nSi le serveur accepte de répondre aux requêtes, il doit répondre avec un corps vide, le statut 200 et des en-têtes :\n\n- `Access-Control-Allow-Origin` doit être soit `*` soit l'origine de la demande, telle que `https://javascript.info`, pour l'autoriser.\n- `Access-Control-Allow-Methods` doit avoir la méthode autorisée.\n- `Access-Control-Allow-Headers` doit avoir une liste d'en-têtes autorisés.\n- De plus, l'en-tête `Access-Control-Max-Age` peut spécifier un nombre de secondes pour mettre en cache les autorisations. Ainsi, le navigateur n'aura pas à envoyer de contrôle en amont pour les requêtes ultérieures qui satisfont aux autorisations données.\n\n![](xhr-preflight.svg)\n\nVoyons comment cela fonctionne étape par étape, par exemple, pour une requête cross-origin `PATCH` (cette méthode est souvent utilisée pour mettre à jour les données) :\n\n```js\nlet response = await fetch('https://site.com/service.json', {\n  method: 'PATCH',\n  headers: {\n    'Content-Type': 'application/json',\n    'API-Key': 'secret'\n  }\n});\n```\n\nIl y a trois raisons pour lesquelles la demande n'est pas sécurisée (une suffit) :\n- La méthode `PATCH`\n- `Content-Type` ne fait pas partie de : `application/x-www-form-urlencoded`, `multipart/form-data`, `text/plain`.\n  - Entête \"Non-sécurisée\" `API-Key`.\n\n### Étape 1 (requête de contrôle en amont)\n\nAvant d'envoyer une telle requête, le navigateur envoie lui-même une requête de contrôle en amont qui ressemble à ceci :\n\n```http\nOPTIONS /service.json\nHost: site.com\nOrigin: https://javascript.info\nAccess-Control-Request-Method: PATCH\nAccess-Control-Request-Headers: Content-Type,API-Key\n```\n\n- La méthode : `OPTIONS`.\n- Le chemin -- exactement le même que la requête principale : `/service.json`.\n- En-têtes Cross-origin spéciaux :\n    - `Origin` -- l'origine de la source.\n    - `Access-Control-Request-Method` -- méthode demandée.\n    - `Access-Control-Request-Headers` -- une liste d'en-têtes \"non sécurisés\" séparés par des virgules.\n\n### Étape 2 (réponse en amont)\n\nLe serveur doit répondre avec le statut 200 et les en-têtes :\n- `Access-Control-Allow-Origin: https://javascript.info`\n- `Access-Control-Allow-Methods: PATCH`\n- `Access-Control-Allow-Headers: Content-Type,API-Key`.\n\nCela permet une communication future, sinon une erreur est déclenchée.\n\nSi le serveur attend d'autres méthodes et en-têtes à l'avenir, il est logique de les autoriser à l'avance en les ajoutant à la liste. \n\nPar exemple, cette réponse autorise également `PUT`, `DELETE` et des en-têtes supplémentaires :\n\n```http\n200 OK\nAccess-Control-Allow-Origin: https://javascript.info\nAccess-Control-Allow-Methods: PUT,PATCH,DELETE\nAccess-Control-Allow-Headers: API-Key,Content-Type,If-Modified-Since,Cache-Control\nAccess-Control-Max-Age: 86400\n```\n\nMaintenant, le navigateur peut voir que `PATCH` est dans `Access-Control-Allow-Methods` et `Content-Type,API-Key` sont dans la liste `Access-Control-Allow-Headers`, il envoie donc la requête principale.\n\nS'il y a un en-tête `Access-Control-Max-Age` avec un certain nombre de secondes, alors les autorisations de contrôle en amont sont mises en cache pour le temps donné. La réponse ci-dessus sera mise en cache pendant 86400 secondes (un jour). Dans ce délai, les demandes ultérieures n'entraîneront pas de contrôle en amont. En supposant qu'ils correspondent aux quotas mis en cache, ils seront envoyés directement.\n\n### Étape 3 (requête réelle)\n\nLorsque le contrôle en amont réussit, le navigateur fait maintenant la requête principale. Ici, le processus est le même que pour les requêtes sécurisées.\n\nLa requête principale a un en-tête `Origin` (car il s'agit d'une cross-origin) :\n\n```http\nPATCH /service.json\nHost: site.com\nContent-Type: application/json\nAPI-Key: secret\nOrigin: https://javascript.info\n```\n\n### Étape 4 (réponse réelle)\n\nLe serveur ne doit pas oublier d'ajouter `Access-Control-Allow-Origin` à la réponse principale. Un contrôle en amont réussi ne dispense pas de cela :\n\n```http\nAccess-Control-Allow-Origin: https://javascript.info\n```\n\nEnsuite, JavaScript est capable de lire la réponse du serveur principal.\n\n```smart\nLa requête de contrôle en amont se produit \"dans les coulisses\", elle est invisible pour JavaScript.\n\nJavaScript n'obtient la réponse à la requête principale ou une erreur que s'il n'y a pas d'autorisation de serveur.\n```\n\n## Identifiants\n\nUne requête cross-origin initiée par du code JavaScript par défaut n'apporte pas d'informations d'identification (cookies ou authentification HTTP).\n\nC'est rare pour les requêtes HTTP. Habituellement, une requête pour `http://site.com` est accompagnée de tous les cookies de ce domaine. Mais les requêtes cross-origin faites par des méthodes JavaScript sont une exception.\n\nPar exemple, `fetch('http://another.com')` n'envoie aucun cookie, même ceux(!) qui appartiennent au domaine `another.com`.\n\nPourquoi ?\n\nEn effet, une requête avec des informations d'identification est beaucoup plus puissante que sans. Si cela est autorisé, il accorde à JavaScript le pouvoir d'agir au nom de l'utilisateur et d'accéder à des informations sensibles à l'aide de ses informations d'identification.\n\nLe serveur fait-il vraiment autant confiance au script ? Ensuite, il doit explicitement autoriser les requêtes avec des informations d'identification avec un en-tête supplémentaire.\n\nPour envoyer les informations d'identification dans `fetch`, nous devons ajouter l'option `credentials: \"include\"`, comme ceci :\n\n```js\nfetch('http://another.com', {\n  credentials: \"include\"\n});\n```\n\nMaintenant, `fetch` envoie des cookies provenant de `another.com` avec requête à ce site.\n\nSi le serveur est configuré pour accepter une requête *avec des informations d'identification*, il doit ajouter un en-tête `Access-Control-Allow-Credentials: true` à la réponse, en plus de `Access-Control-Allow-Origin`.\n\nPar exemple :\n\n```http\n200 OK\nAccess-Control-Allow-Origin: https://javascript.info\nAccess-Control-Allow-Credentials: true\n```\n\nVeuillez noter : `Access-Control-Allow-Origin` est interdit d'utiliser une étoile `*` pour les demandes avec des informations d'identification. Comme indiqué ci-dessus, il doit y fournir l'origine exacte. C'est une mesure de sécurité supplémentaire, pour garantir que le serveur sait vraiment à qui il fait confiance pour effectuer de telles demandes.\n\n## Résumé\n\nDu point de vue du navigateur, il existe deux types de requêtes cross-origin : \"sécurisées\" et toutes les autres.\n\nLes requêtes simples doivent satisfaire aux conditions suivantes :\n- Méthode : GET, POST ou HEAD.\n- En-têtes - nous ne pouvons définir que :\n    - `Accept`\n    - `Accept-Language`\n    - `Content-Language`\n    - `Content-Type` pour la valeur `application/x-www-form-urlencoded`, `multipart/form-data` ou `text/plain`.\n\nLa différence essentielle est que les requêtes sécurisées étaient réalisables depuis très longtemps en utilisant des balises `<form>` ou `<script>`, alors que les requêtes non sécurisées étaient impossibles pour les navigateurs pendant longtemps.\n\nAinsi, la différence pratique est que les demandes sécurisées sont envoyées immédiatement, avec l'en-tête `Origin`, tandis que pour les autres, le navigateur fait une requête préliminaire de \"contrôle en amont\", demandant la permission.\n\n**Pour les requêtes sécurisées :**\n\n- → Le navigateur envoie l'en-tête `Origin` avec l'origine.\n- ← Pour les requêtes sans informations d'identification (non envoyées par défaut), le serveur doit définir :\n    - `Access-Control-Allow-Origin` avec `*` ou la même valeur que `Origin`\n- ← Pour les requêtes avec informations d'identification, le serveur doit définir :\n    - `Access-Control-Allow-Origin` avec la même valeur que `Origin`\n    - `Access-Control-Allow-Credentials` à `true`\n\nEn outre, pour accorder un accès JavaScript à tous les en-têtes de réponse, sauf `Cache-Control`,  `Content-Language`, `Content-Type`, `Expires`, `Last-Modified` ou `Pragma`, le serveur doit répertorier ceux autorisés dans l'en-tête `Access-Control-Expose-Headers`.\n\n**Pour les requêtes non sécurisées, une demande préalable de \"contrôle en amont\" est émise avant celle demandée :**\n\n- → Le navigateur envoie la requête `OPTIONS` à la même URL, avec en-têtes :\n    - `Access-Control-Request-Method` a demandé la méthode.\n    - `Access-Control-Request-Headers` répertorie les en-têtes non sécurisés demandés.\n- ← Le serveur doit répondre avec le statut 200 et les en-têtes :\n    - `Access-Control-Allow-Methods` avec une liste de méthodes autorisées,\n    - `Access-Control-Allow-Headers` avec une liste des en-têtes autorisés,\n    - `Access-Control-Max-Age` avec un certain nombre de secondes pour mettre en cache les autorisations.\n- Ensuite, la requête réelle est envoyée, le schéma \"sécurisé\" précédent est appliqué.\n"
  },
  {
    "path": "5-network/06-fetch-api/article.md",
    "content": "\n# API Fetch\n\nJusqu'à présent, nous en savons pas mal sur `fetch`.\n\nVoyons le reste de l'API, pour couvrir toutes ses capacités.\n\n```smart\nRemarque: la plupart de ces options sont rarement utilisées. Vous pouvez ignorer ce chapitre et continuer à utiliser `fetch` correctement.\n\nPourtant, il est bon de savoir ce que `fetch` peut faire, donc si le besoin s'en fait sentir, vous pouvez revenir et lire les détails.\n```\n\nVoici la liste complète de toutes les options possibles de `fetch` avec leurs valeurs par défaut (alternatives dans les commentaires) :\n\n```js\nlet promise = fetch(url, {\n  method: \"GET\", // POST, PUT, DELETE, etc.\n  headers: {\n    // la valeur de l'en-tête du type de contenu est généralement définie automatiquement\n    // selon la requête du body\n    \"Content-Type\": \"text/plain;charset=UTF-8\"\n  },\n  body: undefined, // string, FormData, Blob, BufferSource, ou URLSearchParams\n  referrer: \"about:client\", // ou \"\" to send no Referer header,\n  // or an url from the current origin\n  referrerPolicy: \"strict-origin-when-cross-origin\", // no-referrer-when-downgrade, no-referrer, origin, same-origin...\n  mode: \"cors\", // same-origin, no-cors\n  credentials: \"same-origin\", // omit, include\n  cache: \"default\", // no-store, reload, no-cache, force-cache, or only-if-cached\n  redirect: \"follow\", // manual, error\n  integrity: \"\", // un hash, comme \"sha256-abcdef1234567890\"\n  keepalive: false, // true\n  signal: undefined, // AbortController pour annuler la requête\n  window: window // null\n});\n```\n\nUne liste impressionnante, non ?\n\nNous avons entièrement couvert `method`, `headers` et `body` dans le chapitre <info:fetch>.\n\nL'option `signal` est couverte dans <info:fetch-abort>.\n\nExplorons maintenant le reste des capacités.\n\n## referrer, referrerPolicy\n\nCes options régissent la façon dont `fetch` définit l'en-tête HTTP `Referer`.\n\nHabituellement, cet en-tête est défini automatiquement et contient l'url de la page à l'origine de la requête. Dans la plupart des scénarios, ce n'est pas important du tout, parfois, pour des raisons de sécurité, il est logique de le supprimer ou de le raccourcir.\n\n**L'option `referer` permet de définir n'importe quel `Referer` dans l'origine actuelle) ou de le supprimer.**\n\nPour n'envoyer aucun referer, définissez une chaîne de caractères vide :\n```js\nfetch('/page', {\n*!*\n  referrer: \"\" // pas de header Referer\n*/!*\n});\n```\n\nPour définir une autre URL dans l'origine actuelle :\n\n```js\nfetch('/page', {\n  // en supposant que nous sommes sur https://javascript.info\n  // nous pouvons définir n'importe quel en-tête Referer, mais uniquement dans l'origine actuelle\n*!*\n  referrer: \"https://javascript.info/anotherpage\"\n*/!*\n});\n```\n\n**L'option `referrerPolicy` établit des règles générales pour `Referer`.**\n\nLes requêtes sont divisées en 3 types :\n\n1. Requête à la même origine.\n2. Requête à une autre origine.\n3. Requête de HTTPS à HTTP (du protocole sûr au protocole non sécurisé).\n\nContrairement à l'option `referrer` qui permet de définir la valeur exacte de `Referer`, `referrerPolicy` indique les règles générales du navigateur pour chaque type de requête.\n\nLes valeurs possibles sont décrites dans la [spécification Referrer Policy](https://w3c.github.io/webappsec-referrer-policy/):\n\n- **`\"strict-origin-when-cross-origin\"`** -- la valeur par défaut : pour la même origine, envoie le `Referer` complet, pour l'origine croisée, envoie uniquement l'origine, à moins qu'il ne s'agisse d'une requête HTTPS→HTTP , puis n'envoie rien.\n- **`\"no-referrer-when-downgrade\"`** -- le `Referer` complet est toujours envoyé, sauf si nous envoyons une requête de HTTPS à HTTP (vers le protocole le moins sécurisé).\n- **`\"no-referrer\"`** -- n'envoie jamais le `Referer`.\n- **`\"origine\"`** -- n'envoie que l'origine dans le `Referer`, pas l'URL complète de la page, par ex. uniquement `http://site.com` au lieu de `http://site.com/path`.\n- **`\"origin-when-cross-origin\"`** -- envoie le `Referer` complet à la même origine, mais uniquement la partie d'origine pour les requêtes cross-origin (comme ci-dessus).\n- **`\"same-origin\"`** -- envoie le `Referer` complet à la même origine, mais pas de `Referer` pour les requêtes cross-origin.\n- **`\"strict-origin\"`** -- envoie uniquement l'origine, pas le `Referer` pour les requêtes HTTPS→HTTP.\n- **`\"unsafe-url\"`** -- envoie toujours l'url complète dans `Referer`, même pour les requêtes HTTPS→HTTP.\n\nVoici un tableau avec toutes les combinaisons :\n\n| Valeur | Pour la même origine | Pour une autre origine | HTTPS→HTTP |\n|-------|----------------|-------------------|------------|\n| `\"no-referrer\"` | - | - | - |\n| `\"no-referrer-when-downgrade\"` | full | full | - |\n| `\"origin\"` | origin | origin | origin |\n| `\"origin-when-cross-origin\"` | full | origin | origin |\n| `\"same-origin\"` | full | - | - |\n| `\"strict-origin\"` | origin | origin | - |\n| `\"strict-origin-when-cross-origin\"` or `\"\"` (default) | full | origin | - |\n| `\"unsafe-url\"` | full | full | full |\n\nDisons que nous avons une zone d'administration avec une structure d'URL qui ne devrait pas être connue de l'extérieur du site.\n\nSi nous envoyons un `fetch`, alors par défaut, il envoie toujours l'en-tête `Referer` avec l'url complète de notre page (sauf lorsque nous demandons de HTTPS à HTTP, alors pas de `Referer`).\n\nPar exemple : `Referer: https://javascript.info/admin/secret/paths`.\n\nSi nous souhaitons que d'autres sites Web connaissent uniquement la partie origin, pas le chemin URL, nous pouvons définir l'option :\n\n```js\nfetch('https://another.com/page', {\n  // ...\n  referrerPolicy: \"origin-when-cross-origin\" // Referer: https://javascript.info\n});\n```\n\nNous pouvons le mettre à tous les appels `fetch`, peut-être l'intégrer dans la bibliothèque JavaScript de notre projet qui fait toutes les requêtes et utilise `fetch` à l'intérieur.\n\nSa seule différence par rapport au comportement par défaut est que pour les requêtes vers une autre origine, `fetch` envoie uniquement la partie origine de l'URL (par exemple `https://javascript.info`, sans le chemin). Pour les requêtes à notre origine, nous obtenons toujours le `Referer` complet (peut-être utile à des fins de débogage).\n\n```smart header=\"La Referrer policy n'est pas seulement pour `fetch`\"\nLa Referrer policy, décrite dans la [spécification](https://w3c.github.io/webappsec-referrer-policy/), n'est pas seulement pour `fetch`, mais plus globale.\n\nPlus particulièrement, il est possible de définir la politique par défaut pour toute la page en utilisant l'en-tête HTTP `Referrer-Policy`, ou par lien, avec `<a rel=\"noreferrer\">`.\n```\n\n## mode\n\nL'option `mode` est un garde-fou qui empêche les requêtes cross-origin occasionnelles :\n\n- **`\"cors\"`** -- par défaut, les requêtes cross-origin sont autorisées, comme décrit dans <info:fetch-crossorigin>,\n- **`\"same-origin\"`** -- les requêtes cross-origin requests sont interdites,\n- **`\"no-cors\"`** -- seules les requêtes cross-origin sécurisées sont autorisées.\n\nCette option peut être utile lorsque l'URL de `fetch` provient d'un tiers, et nous voulons un \"interrupteur de mise hors tension\" pour limiter les capacités de cross-origin.\n\n## credentials\n\nL'option `credentials` spécifie si `fetch` doit envoyer des cookies et des en-têtes d'autorisation HTTP avec la requête.\n\n- **`\"same-origin\"`** -- par défaut, n'envoyez pas de requêtes cross-origin,\n- **`\"include\"`** -- toujours envoyer, nécessite `Accept-Control-Allow-Credentials` du serveur cross-origin pour que JavaScript accède à la réponse, qui a été traitée dans le chapitre <info:fetch-crossorigin>,\n- **`\"omit\"`** -- ne jamais envoyer, même pour des requêtes cross-origin.\n\n## cache\n\nPar défaut, les requêtes `fetch` utilisent la mise en cache HTTP standard. Autrement dit, il honore les en-têtes `Expires` et `Cache-Control`, envoie `If-Modified-Since`, et ainsi de suite. Tout comme les requêtes HTTP régulières.\n\nLes options `cache` permettent d'ignorer le cache HTTP ou d'affiner son utilisation :\n\n- **`\"default\"`** -- `fetch` utilise des règles et des en-têtes de cache HTTP standard,\n- **`\"no-store\"`** -- ignore totalement le cache HTTP, ce mode devient la valeur par défaut si nous définissons un en-tête `If-Modified-Since`, `If-None-Match`, `If-Unmodified-Since`, `If-Match`, ou `If-Range`,\n- **`\"reload\"`** -- ne prenez pas le résultat du cache HTTP (le cas échéant), mais remplissez le cache avec la réponse (si les en-têtes de réponse le permettent),\n- **`\"no-cache\"`** -- créer une requête conditionnelle s'il y a une réponse mise en cache, et sinon une requête normale. Remplissez le cache HTTP avec la réponse,\n- **`\"force-cache\"`** -- utilise une réponse du cache HTTP, même si elle est périmée. S'il n'y a pas de réponse dans le cache HTTP, fait une requête HTTP régulière, se comporte normalement,\n- **`\"only-if-cached\"`** -- utilise une réponse du cache HTTP, même si elle est périmée. S'il n'y a pas de réponse dans le cache HTTP, alors erreur. Fonctionne uniquement lorsque le `mode` est sur `\"same-origin\"`.\n\n## redirect\n\nNormalement, `fetch` suit de manière transparente les redirections HTTP, comme 301, 302 etc.\n\nL'option `redirect` permet de changer cela :\n\n- **`\"follow\"`** -- par défaut, suit les redirections HTTP,\n- **`\"error\"`** -- erreur en cas de redirection HTTP,\n- **`\"manual\"`** -- permet de traiter manuellement les redirections HTTP. En cas de redirection, nous obtiendrons un objet de réponse spécial, avec `response.hype=\"opaqueredirect\"` et un statut zéro/vide ainsi que la plupart des autres propriétés.\n\n## integrity\n\nL'option `intégrité` permet de vérifier si la réponse correspond à la somme de contrôle connue à l'avance.\n\nComme décrit dans la [spécification](https://w3c.github.io/webappsec-subresource-integrity/), les fonctions de hachage prises en charge sont SHA-256, SHA-384 et SHA-512, il peut y en avoir d'autres en fonction du navigateur.\n\nPar exemple, nous téléchargeons un fichier et nous savons que sa somme de contrôle SHA-256 est \"abcdef\" (une vraie somme de contrôle est plus longue, bien sûr).\n\nNous pouvons le mettre dans l'option `integrity`, comme ceci :\n\n```js\nfetch('http://site.com/file', {\n  integrity: 'sha256-abcdef'\n});\n```\n\nEnsuite, `fetch` calculera SHA-256 seul et le comparera avec notre chaîne de caractères. En cas de non-concordance, une erreur est déclenchée.\n\n## keepalive\n\nL'option `keepalive` indique que la demande peut \"survivre\" à la page Web qui l'a initiée.\n\nPar exemple, nous recueillons des statistiques sur la façon dont le visiteur actuel utilise notre page (clics de souris, fragments de page qu'il consulte), pour analyser et améliorer l'expérience utilisateur.\n\nLorsque le visiteur quitte notre page -- nous aimerions enregistrer les données sur notre serveur.\n\nNous pouvons utiliser l'événement `window.onunload` pour cela :\n\n```js run\nwindow.onunload = function() {\n  fetch('/analytics', {\n    method: 'POST',\n    body: \"statistics\",\n*!*\n    keepalive: true\n*/!*\n  });\n};\n```\n\nNormalement, lorsqu'un document est déchargé, toutes les requêtes réseau associées sont abandonnées. Mais l'option `keepalive` indique au navigateur d'exécuter la requête en arrière-plan, même après avoir quitté la page. Cette option est donc essentielle à la réussite de notre demande.\n\nElle a quelques limitations :\n\n- Nous ne pouvons pas envoyer des mégaoctets : la limite de corps pour les requêtes `keepalive` est de 64kb.\n    - Si nous avons besoin de rassembler beaucoup de statistiques sur la visite, nous devons les envoyer régulièrement par paquets, afin qu'il ne reste plus grand-chose pour la dernière requête `onunload`.\n    - Cette limite s'applique à toutes les demandes `keepalive` ensemble. En d'autres termes, nous pouvons effectuer plusieurs requêtes `keepalive` en parallèle, mais la somme de leurs longueurs de corps ne doit pas dépasser 64 Kb.\n- Nous ne pouvons pas gérer la réponse du serveur si le document est déchargé. Donc, dans notre exemple, `fetch` réussira grâce à `keepalive`, mais les fonctions suivantes ne fonctionneront pas.\n    - Dans la plupart des cas, comme l'envoi de statistiques, ce n'est pas un problème, car le serveur accepte simplement les données et envoie généralement une réponse vide à de telles demandes.\n"
  },
  {
    "path": "5-network/06-fetch-api/post.view/index.html",
    "content": "<!doctype html>\n<script>\n(async () {\n\t\n\tconst response = await fetch('long.txt');\n\tconst reader = response.body.getReader();\n\n\tconst contentLength = +response.headers.get('Content-Length');\n\tlet receivedLength = 0;\n\tlet chunks = [];\n\twhile(true) {\n\t\tconst chunk = await reader.read();\n\n\t\tif (chunk.done) {\n\t\t\tconsole.log(\"done!\");\n\t\t\tbreak;\n\t\t}\n\n\t\tchunks.push(chunk.value);\n\t\treceivedLength += chunk.value.length;\n\t\tconsole.log(`${receivedLength}/${contentLength} received`)\n\t}\n\n\n\tlet chunksMerged = new Uint8Array(receivedLength);\n\tlet length = 0;\n\tfor(let chunk of chunks) {\n\t\tchunksMerged.set(chunk, length);\n\t\tlength += chunk.length;\n\t}\n\n\tlet result = new TextDecoder(\"utf-8\").decode(chunksMerged);\n\tconsole.log(result);\n})();\n\n</script>\n"
  },
  {
    "path": "5-network/06-fetch-api/post.view/server.js",
    "content": "let http = require('http');\nlet url = require('url');\nlet querystring = require('querystring');\nlet static = require('node-static');\nlet file = new static.Server('.', {\n  cache: 0\n});\n\n\nfunction accept(req, res) {\n\n  if (req.method == 'POST') {\n    let chunks = [];\n    let length = 0;\n\n    req.on('data', function (data) {\n      chunks.push(data);\n      length += data.length;\n\n      // Too much POST data, kill the connection!\n      if (length > 1e6) {\n        request.connection.destroy();\n      }\n    });\n\n    req.on('end', function() {\n      // let post = JSON.parse(chunks.join(''));\n\n      if (req.url == '/user') {\n        res.writeHead(200, { 'Content-Type': 'application/json' });\n        res.end(JSON.stringify({ message: 'User saved' }));\n      } else if (req.url == '/image') {\n        res.writeHead(200, { 'Content-Type': 'application/json' });\n        res.end(JSON.stringify({ message: \"Image saved\", imageSize: length }));\n      } else {\n        res.writeHead(404);\n        res.end(\"Not found\");\n      }\n    });\n\n\n  } else {\n    file.serve(req, res);\n  }\n\n}\n\n\n// ------ запустить сервер -------\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n} else {\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/07-url/article.md",
    "content": "\n# Les objets URL\n\nLa classe d'[URL](https://url.spec.whatwg.org/#api) intégrée fournit une interface pratique pour créer et analyser des URL.\n\nIl n'y a pas de méthodes de mise en réseau qui nécessitent exactement un objet `URL`, les chaînes de caractères sont assez bonnes. Donc, techniquement, nous n'avons pas à utiliser `URL`. Mais parfois, cela peut être très utile.\n\n## Création d'une URL\n\nLa syntaxe pour créer un nouvel objet `URL` :\n\n```js\nnew URL(url, [base])\n```\n\n- **`url`** -- l'URL complète ou le seul chemin (si la base est définie, voir ci-dessous),\n- **`base`** -- une URL de base facultative : si définie et que l'argument `url` n'a que le chemin, l'URL est générée par rapport à la `base`.\n\nPar exemple :\n\n```js\nlet url = new URL('https://javascript.info/profile/admin');\n```\n\nCes deux URL sont identiques :\n\n```js run\nlet url1 = new URL('https://javascript.info/profile/admin');\nlet url2 = new URL('/profile/admin', 'https://javascript.info');\n\nalert(url1); // https://javascript.info/profile/admin\nalert(url2); // https://javascript.info/profile/admin\n```\n\nNous pouvons facilement créer une nouvelle URL basée sur le chemin d'accès par rapport à une URL existante :\n\n```js run\nlet url = new URL('https://javascript.info/profile/admin');\nlet newUrl = new URL('tester', url);\n\nalert(newUrl); // https://javascript.info/profile/tester\n```\n\nL'objet `URL` nous permet immédiatement d'accéder à ses composants, c'est donc une bonne façon d'analyser l'url, par exemple :\n\n```js run\nlet url = new URL('https://javascript.info/url');\n\nalert(url.protocol); // https:\nalert(url.host);     // javascript.info\nalert(url.pathname); // /url\n```\n\nVoici le cheatsheet pour les composants URL :\n\n![](url-object.svg)\n\n- `href` est l'URL complète, identique à `url.toString()`\n- `protocol` se termine par le caractère deux-points `:`\n- `search` - une chaîne de caractères de paramètres, commence par le point d'interrogation `?`\n- `hash` commence par le caractère de hachage `#`\n- il peut y avoir aussi les propriétés `user` et `password` si l'authentification HTTP est présente : `http://login:password@site.com` (pas expliqué ci-dessus, très peu utilisé)\n\n\n```smart header=\"Nous pouvons passer des objets `URL` aux méthodes de mise en réseau (et à la plupart des autres) au lieu d'une chaîne de caractères\"\nNous pouvons utiliser un objet `URL` dans `fetch` ou `XMLHttpRequest`, presque partout où une chaîne de caractères URL est attendue.\n\nGénéralement, l'objet `URL` peut être passé à n'importe quelle méthode au lieu d'une chaîne de caractères, car la plupart des méthodes effectueront la conversion de chaîne de caractères, qui transforme un objet `URL` en une chaîne de caractères avec une URL complète.\n```\n\n## SearchParams \"?...\"\n\nDisons que nous voulons créer une URL avec des paramètres de recherche donnés, par exemple `https://google.com/search?query=JavaScript`.\n\nNous pouvons les fournir dans la chaîne de caractères URL :\n\n```js\nnew URL('https://google.com/search?query=JavaScript')\n```\n\n… Mais les paramètres doivent être encodés s'ils contiennent des espaces, des lettres non latines, etc. (plus à ce sujet ci-dessous).\n\nIl y a donc une propriété URL pour cela : `url.searchParams`, un objet de type [URLSearchParams](https://url.spec.whatwg.org/#urlsearchparams).\n\nElle fournit des méthodes pratiques pour les paramètres de recherche :\n\n- **`append(name, value)`** -- ajouter le paramètre par `name`,\n- **`delete(name)`** -- supprime le paramètre par `name`,\n- **`get(name)`** -- obtenir le paramètre par `name`,\n- **`getAll(name)`** -- obtient tous les paramètres avec le même `name` (c'est possible, par exemple `?user=John&user=Pete`),\n- **`has(name)`** -- vérifie l'existence du paramètre par `name`,\n- **`set(name, value)`** -- défini/remplace le paramètre,\n- **`sort()`** -- trie les paramètres par nom, rarement nécessaire,\n- … et elle est également itérable, semblable à `Map`.\n\nUn exemple avec des paramètres contenant des espaces et des signes de ponctuation :\n\n```js run\nlet url = new URL('https://google.com/search');\n\nurl.searchParams.set('q', 'test me!'); // paramètre ajouté avec un espace et !\n\nalert(url); // https://google.com/search?q=test+me%21\n\nurl.searchParams.set('tbs', 'qdr:y'); // paramètre ajouté avec deux points :\n\n// les paramètres sont automatiquement encodés\nalert(url); // https://google.com/search?q=test+me%21&tbs=qdr%3Ay\n\n// itérer sur les paramètres de recherche (décodés)\nfor(let [name, value] of url.searchParams) {\n  alert(`${name}=${value}`); // q=test me!, ensuite tbs=qdr:y\n}\n```\n\n\n## Encodage\n\nIl y a une norme [RFC3986](https://tools.ietf.org/html/rfc3986) qui définit quels caractères sont autorisés dans les URL et lesquels ne le sont pas.\n\nCeux qui ne sont pas autorisés doivent être encodés, par exemple les lettres et les espaces non latins - remplacés par leurs codes UTF-8, préfixés par `%`, tels que `%20` (un espace peut être encodé par `+`, pour des raisons historiques, mais c'est une exception).\n\nLa bonne nouvelle est que les objets `URL` gèrent tout cela automatiquement. Nous fournissons simplement tous les paramètres non codés, puis convertissons l'URL en chaîne de caractères :\n\n```js run\n// en utilisant des caractères cyrilliques pour cet exemple\n\nlet url = new URL('https://ru.wikipedia.org/wiki/Тест');\n\nurl.searchParams.set('key', 'ъ');\nalert(url); //https://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%81%D1%82?key=%D1%8A\n```\n\nComme vous pouvez le voir, `Тест` dans le chemin de l'url et `ъ` dans le paramètre sont encodés.\n\nL'URL est devenue plus longue, car chaque lettre cyrillique est représentée avec deux octets en UTF-8, il y a donc deux entités `%..`.\n\n### Encodage de chaîne de caractères\n\nAutrefois, avant que les objets `URL` n'apparaissent, les gens utilisaient des chaînes de caractères pour les URL.\n\nPour l'instant, les objets `URL` sont souvent plus pratiques, mais les chaînes de caractères peuvent toujours être utilisées également. Dans de nombreux cas, l'utilisation d'une chaîne de caractères raccourcit le code.\n\nSi nous utilisons une chaîne de caractères, nous devons encoder/décoder les caractères spéciaux manuellement.\n\nIl existe des fonctions intégrées pour cela :\n\n- [encodeURI](mdn:/JavaScript/Reference/Global_Objects/encodeURI) - encode l'URL dans son ensemble.\n- [decodeURI](mdn:/JavaScript/Reference/Global_Objects/decodeURI) - la décode de nouveau.\n- [encodeURIComponent](mdn:/JavaScript/Reference/Global_Objects/encodeURIComponent) - encode un composant URL, tel qu'un paramètre de recherche, un hachage ou un chemin d'accès.\n- [decodeURIComponent](mdn:/JavaScript/Reference/Global_Objects/decodeURIComponent) - la décode de nouveau.\n\nUne question naturelle est: \"Quelle est la différence entre `encodeURIComponent` et `encodeURI` ? Quand devrions-nous utiliser l'une ou l'autre ?\"\n\nC'est facile à comprendre si nous regardons l'URL, qui est divisée en composants dans l'image ci-dessus :\n\n```\nhttps://site.com:8080/path/page?p1=v1&p2=v2#hash\n```\n\nComme nous pouvons le voir, les caractères tels que `:`, `?`, `=`, `&`, `#` sont autorisés dans l'URL.\n\n… D'un autre côté, si nous regardons un seul composant URL, tel qu'un paramètre de recherche, ces caractères doivent être encodés, pour ne pas casser la mise en forme.\n\n- `encodeURI` encode uniquement les caractères totalement interdits dans l'URL.\n- `encodeURIComponent` encode les mêmes caractères et, en plus d'eux, les caractères `#`, `$`, `&`, `+`, `,`, `/`, `:`, `;`, `=`, `?` et `@`.\n\nDonc, pour une URL entière, nous pouvons utiliser `encodeURI` :\n\n```js run\n// en utilisant des caractères cyrilliques dans le chemin de l'url\nlet url = encodeURI('http://site.com/привет');\n\nalert(url); // http://site.com/%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82\n```\n\n… Alors que pour les paramètres d'URL, nous devrions plutôt utiliser `encodeURIComponent` :\n\n```js run\nlet music = encodeURIComponent('Rock&Roll');\n\nlet url = `https://google.com/search?q=${music}`;\nalert(url); // https://google.com/search?q=Rock%26Roll\n```\n\nComparons-le avec `encodeURI` :\n\n```js run\nlet music = encodeURI('Rock&Roll');\n\nlet url = `https://google.com/search?q=${music}`;\nalert(url); // https://google.com/search?q=Rock&Roll\n```\n\nComme nous pouvons le voir, `encodeURI` n'encode pas `&`, car il s'agit d'un caractère légitime dans l'URL.\n\nMais nous devons encoder `&` à l'intérieur d'un paramètre de recherche, sinon, nous obtenons `q=Rock&Roll` - qui est en fait `q=Rock` plus un paramètre obscur `Roll`. Pas comme prévu.\n\nNous devons donc utiliser uniquement `encodeURIComponent` pour chaque paramètre de recherche, pour l'insérer correctement dans la chaîne de caractères URL. Le plus sûr est d'encoder à la fois le nom et la valeur, à moins que nous ne soyons absolument sûrs qu'il n'a que des caractères autorisés.\n\n````smart header=\"Différence d'encodage par rapport à `URL`\"\nLes classes [URL](https://url.spec.whatwg.org/#url-class) et [URLSearchParams](https://url.spec.whatwg.org/#interface-urlsearchparams) sont basés sur la dernière spécification d'URI : [RFC3986](https://tools.ietf.org/html/rfc3986), tandis que les fonctions `encode*` sont basées sur la version obsolète [RFC2396](https://www.ietf.org/rfc/rfc2396.txt).\n\nIl y a peu de différences, par exemple, Les adresses IPv6 sont encodées différemment :\n\n```js run\n// URL valide avec adresse IPv6\nlet url = 'http://[2607:f8b0:4005:802::1007]/';\n\nalert(encodeURI(url)); // http://%5B2607:f8b0:4005:802::1007%5D/\nalert(new URL(url)); // http://[2607:f8b0:4005:802::1007]/\n```\n\nComme nous pouvons le voir, `encodeURI` a remplacé les crochets `[...]`, ce qui n'est pas correct, la raison est que les URL IPv6 n'existaient pas au moment de la RFC2396 (août 1998).\n\nDe tels cas sont rares, les fonctions `encode*` fonctionnent bien la plupart du temps.\n````\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/article.md",
    "content": "# XMLHttpRequest\n\n`XMLHttpRequest` est un objet intégré du navigateur qui permet de faire des requêtes HTTP en JavaScript.\n\nBien qu'il ait le mot \"XML\" dans son nom, il peut fonctionner sur toutes les données, pas seulement au format XML. Nous pouvons upload/download des fichiers, suivre les progrès et bien plus encore.\n\nÀ l'heure actuelle, il existe une autre méthode, plus moderne, `fetch`, qui déprécie quelque peu `XMLHttpRequest`.\n\nDans le développement Web moderne, `XMLHttpRequest` est utilisé pour trois raisons :\n\n1. Raisons historiques : nous devons prendre en charge les scripts existants avec `XMLHttpRequest`.\n2. Nous devons prendre en charge les anciens navigateurs et nous ne voulons pas de polyfills (par exemple pour garder les scripts minuscules).\n3. Nous avons besoin de quelque chose que `fetch` ne peut pas encore faire, par exemple pour suivre la progression de l'upload.\n\nCela vous semble-t-il familier ? Si oui, alors d'accord, continuez avec `XMLHttpRequest`. Sinon, rendez-vous sur <info:fetch>.\n\n## Les bases\n\n`XMLHttpRequest` a deux modes de fonctionnement : synchrone et asynchrone.\n\nVoyons d'abord l'asynchrone, car il est utilisé dans la majorité des cas.\n\nPour faire la requête, nous avons besoin de 3 étapes :\n\n1. Créer `XMLHttpRequest`:\n    ```js\n    let xhr = new XMLHttpRequest();\n    ```\n    Le constructeur n'a aucun argument.\n\n2. L'initialiser, généralement juste après `new XMLHttpRequest` :\n    ```js\n    xhr.open(method, URL, [async, user, password])\n    ```\n\n    Cette méthode spécifie les principaux paramètres de la requête :\n\n    - `method` -- Méthode HTTP. Habituellement `\"GET\"` ou `\"POST\"`.\n    - `URL` -- l'URL à demander, une chaîne de caractères, peut être l'objet [URL](info:url).\n    - `async` -- si explicitement défini sur `false`, alors la demande est synchrone, nous couvrirons cela un peu plus tard.\n    - `user`, `password` -- identifiant et mot de passe pour l'authentification HTTP de base (si nécessaire).\n\n    Veuillez noter que l'appel `open`, contrairement à son nom, n'ouvre pas la connexion. Il configure uniquement la demande, mais l'activité réseau ne démarre qu'avec l'appel de `send`.\n\n3. L'envoyer.\n\n    ```js\n    xhr.send([body])\n    ```\n\n    Cette méthode ouvre la connexion et envoie la demande au serveur. Le paramètre facultatif `body` contient le corps de la requête.\n\n    Certaines méthodes de requête comme `GET` n'ont pas de corps. Et certains d'entre eux comme `POST` utilisent `body` pour envoyer les données au serveur. Nous verrons des exemples de cela plus tard.\n\n4. Écouter les événements `xhr` pour obtenir une réponse.\n\n    Ces trois événements sont les plus utilisés :\n    - `load` -- lorsque la requête est terminée (même si l'état HTTP est de type 400 ou 500) et que la réponse est entièrement téléchargée.\n    - `error` -- lorsque la requête n'a pas pu être faite, par exemple réseau en panne ou URL non valide.\n    - `progress` -- se déclenche périodiquement pendant le téléchargement de la réponse, indique combien a été téléchargé.\n\n    ```js\n    xhr.onload = function() {\n      alert(`Loaded: ${xhr.status} ${xhr.response}`);\n    };\n\n    xhr.onerror = function() { // ne se déclenche que si la demande n'a pas pu être faite du tout\n      alert(`Network Error`);\n    };\n\n    xhr.onprogress = function(event) { // se déclenche périodiquement\n      // event.loaded - combien d'octets téléchargés\n      // event.lengthComputable = true si le serveur a envoyé l'en-tête Content-Length\n      // event.total - nombre total d'octets (si lengthComputable)\n      alert(`Received ${event.loaded} of ${event.total}`);\n    };\n    ```\n\nVoici un exemple complet. Le code ci-dessous charge l'URL vers `/article/xmlhttprequest/example/load` depuis le serveur et affiche la progression :\n\n```js run\n// 1. Créer un nouvel objet XMLHttpRequest\nlet xhr = new XMLHttpRequest();\n\n// 2. Le configure : GET-request pour l'URL /article/.../load\nxhr.open('GET', '/article/xmlhttprequest/example/load');\n\n// 3. Envoyer la requête sur le réseau\nxhr.send();\n\n// 4. Ceci sera appelé après la réception de la réponse\nxhr.onload = function() {\n  if (xhr.status != 200) { // analyse l'état HTTP de la réponse\n    alert(`Error ${xhr.status}: ${xhr.statusText}`); // e.g. 404: Not Found\n  } else { // show the result\n    alert(`Done, got ${xhr.response.length} bytes`); // response est la réponse du serveur\n  }\n};\n\nxhr.onprogress = function(event) {\n  if (event.lengthComputable) {\n    alert(`Received ${event.loaded} of ${event.total} bytes`);\n  } else {\n    alert(`Received ${event.loaded} bytes`); // pas de Content-Length\n  }\n\n};\n\nxhr.onerror = function() {\n  alert(\"Request failed\");\n};\n```\n\nUne fois que le serveur a répondu, nous pouvons recevoir le résultat dans les propriétés `xhr` suivantes :\n\n`status`\n: Code d'état HTTP (un nombre): `200`, `404`, `403` et ainsi de suite, peut être `0` en cas d'échec non-HTTP.\n\n`statusText`\n: Message d'état HTTP (une chaîne de caractères): généralement `OK` pour `200`, `Not Found` pour `404`, `Forbidden` pour `403` et ainsi de suite.\n\n`response` (les anciens scripts peuvent utiliser `responseText`)\n: Le corps de réponse du serveur.\n\nNous pouvons également spécifier un délai d'expiration en utilisant la propriété correspondante :\n\n```js\nxhr.timeout = 10000; // délai d'attente en ms, 10 secondes\n```\n\nSi la demande échoue dans le délai imparti, elle est annulée et l'événement `timeout` se déclenche.\n\n````smart header=\"Paramètres de recherche d'URL\"\nPour ajouter des paramètres à l'URL, comme `?name=value`, et assurer le bon encodage, nous pouvons utiliser l'objet [URL](info:url) :\n\n```js\nlet url = new URL('https://google.com/search');\nurl.searchParams.set('q', 'test me!');\n\n// le paramètre 'q' est encodé\nxhr.open('GET', url); // https://google.com/search?q=test+me%21\n```\n\n````\n\n## Type de réponse\n\nNous pouvons utiliser la propriété `xhr.responseType` pour définir le format de réponse :\n\n- `\"\"` (default) -- obtenir en tant que chaîne de caractères,\n- `\"text\"` -- obtenir en tant que chaîne de caractères,\n- `\"arraybuffer\"` -- obtenir en tant que `ArrayBuffer` (pour les données binaires, voir le chapitre <info:arraybuffer-binary-arrays>),\n- `\"blob\"` -- obtenir en tant que `Blob` (pour les données binaires, voir le chapitre <info:blob>),\n- `\"document\"` -- obtenir en tant que document XML (peut utiliser XPath et d'autres méthodes XML) ou document HTML (basé sur le type MIME des données reçues),\n- `\"json\"` -- obtenir en tant que JSON (analysé automatiquement).\n\nPar exemple, obtenons la réponse en JSON :\n\n```js run\nlet xhr = new XMLHttpRequest();\n\nxhr.open('GET', '/article/xmlhttprequest/example/json');\n\n*!*\nxhr.responseType = 'json';\n*/!*\n\nxhr.send();\n\n// la réponse est {\"message\": \"Hello, world!\"}\nxhr.onload = function() {\n  let responseObj = xhr.response;\n  alert(responseObj.message); // Hello, world!\n};\n```\n\n```smart\nDans les anciens scripts, vous pouvez également trouver des propriétés `xhr.responseText` et même `xhr.responseXML`.\n\nIls existent pour des raisons historiques, pour obtenir une chaîne de caractères ou un document XML. De nos jours, nous devons définir le format dans `xhr.responseType` et obtenir `xhr.response` comme illustré ci-dessus.\n```\n\n## États prêts\n\n`XMLHttpRequest` change entre les états au fur et à mesure de sa progression. L'état actuel est accessible en tant que `xhr.readyState`.\n\nTous les États, comme dans [la spécification](https://xhr.spec.whatwg.org/#states):\n\n```js\nUNSENT = 0; // état initial\nOPENED = 1; // open appelé\nHEADERS_RECEIVED = 2; // en-têtes de réponse reçus\nLOADING = 3; // la réponse est en cours de chargement (une donnée empaquetée est reçue)\nDONE = 4; // requête terminée\n```\n\nUn objet `XMLHttpRequest` voyagent dans l'ordre `0` -> `1` -> `2` -> `3` -> ... -> `3` -> `4`. L'état `3` se répète chaque fois qu'un paquet de données est reçu sur le réseau.\n\nNous pouvons les suivre en utilisant l'événement `readystatechange` :\n\n```js\nxhr.onreadystatechange = function() {\n  if (xhr.readyState == 3) {\n    // chargement\n  }\n  if (xhr.readyState == 4) {\n    // requête terminée\n  }\n};\n```\n\nVous pouvez trouver des écouteurs `readystatechange` dans un code très ancien, il est là pour des raisons historiques, car il fut un temps où il n'y avait pas de `load` et d'autres événements. De nos jours, les gestionnaires `load/error/progress` le déprécient.\n\n## Abandon de la requête\n\nNous pouvons mettre fin à la requête à tout moment. L'appel à `xhr.abort()` fait cela :\n\n```js\nxhr.abort(); // met fin à la requête\n```\n\nCela déclenche l'événement `abort` et `xhr.status` devient `0`.\n\n## Requêtes synchrones\n\nSi dans la méthode `open` le troisième paramètre `async` est réglé sur `false`, la demande est faite de manière synchrone.\n\nEn d'autres termes, l'exécution de JavaScript s'interrompt à `send()` et reprend lorsque la réponse est reçue. Un peu comme les commandes `alert` ou `prompt`.\n\nVoici l'exemple réécrit, le 3ème paramètre de `open` est `false` :\n\n```js\nlet xhr = new XMLHttpRequest();\n\nxhr.open('GET', '/article/xmlhttprequest/hello.txt', *!*false*/!*);\n\ntry {\n  xhr.send();\n  if (xhr.status != 200) {\n    alert(`Error ${xhr.status}: ${xhr.statusText}`);\n  } else {\n    alert(xhr.response);\n  }\n} catch(err) { // en cas d'erreur\n  alert(\"Request failed\");\n}\n```\n\nCela peut sembler correct, mais les appels synchrones sont rarement utilisés, car ils bloquent le JavaScript dans la page jusqu'à la fin du chargement. Dans certains navigateurs, il devient impossible de faire défiler. Si un appel synchrone prend trop de temps, le navigateur peut suggérer de fermer la page Web \"suspendue\".\n\nDe nombreuses capacités avancées de `XMLHttpRequest`, comme la requête d'un autre domaine ou la spécification d'un délai d'expiration, ne sont pas disponibles pour les demandes synchrones. De plus, comme vous pouvez le voir, aucune indication de progression.\n\nÀ cause de tout cela, les requêtes synchrones sont utilisées avec parcimonie, pour ainsi dire presque jamais. Nous n'en parlerons plus.\n\n## En-têtes HTTP\n\n`XMLHttpRequest` permet à la fois d'envoyer des en-têtes personnalisés et de lire les en-têtes à partir de la réponse.\n\nIl existe 3 méthodes pour les en-têtes HTTP :\n\n`setRequestHeader(name, value)`\n: Définit l'en-tête de demande avec le `name` donné et la `value`.\n\n    Par exemple :\n\n    ```js\n    xhr.setRequestHeader('Content-Type', 'application/json');\n    ```\n\n    ```warn header=\"Limites des en-têtes\"\n    Plusieurs en-têtes sont gérés exclusivement par le navigateur, par exemple `Referer` et `Host`.\n    La liste complète est [dans la spécification](https://xhr.spec.whatwg.org/#the-setrequestheader()-method).\n\n    `XMLHttpRequest` n'est pas autorisé à les modifier, pour la sécurité des utilisateurs et l'exactitude de la requête.\n    ```\n\n    ````warn header=\"Impossible de supprimer un en-tête\"\n    Une autre particularité de `XMLHttpRequest` est qu'on ne peut pas annuler `setRequestHeader`.\n\n    Une fois l'en-tête défini, il est défini. Des appels supplémentaires ajoutent des informations à l'en-tête, ils ne les écrasent pas.\n\n    Par exemple :\n\n    ```js\n    xhr.setRequestHeader('X-Auth', '123');\n    xhr.setRequestHeader('X-Auth', '456');\n\n    // l'en-tête sera :\n    // X-Auth: 123, 456\n    ```\n    ````\n\n`getResponseHeader(name)`\n: Obtient l'en-tête de réponse avec le `name` donné (sauf `Set-Cookie` et `Set-Cookie2`).\n\n    Par exemple :\n\n    ```js\n    xhr.getResponseHeader('Content-Type')\n    ```\n\n`getAllResponseHeaders()`\n: Renvoie tous les en-têtes de réponse, à l'exception de `Set-Cookie` et `Set-Cookie2`.\n\n    Les en-têtes sont renvoyés sur une seule ligne, par exemple :\n\n    ```http\n    Cache-Control: max-age=31536000\n    Content-Length: 4260\n    Content-Type: image/png\n    Date: Sat, 08 Sep 2012 16:53:16 GMT\n    ```\n\n    Le saut de ligne entre les en-têtes est toujours `\"\\r\\n\"` (ne dépend pas du système d'exploitation), nous pouvons donc facilement le diviser en en-têtes individuels. Le séparateur entre le nom et la valeur est toujours un deux-points suivi d'un espace `\": \"`. C'est fixé dans la spécification.\n\n    Donc, si nous voulons obtenir un objet avec des paires nom/valeur, nous devons ajouter un peu de JS.\n\n    Comme ceci (en supposant que si deux en-têtes ont le même nom, alors le dernier écrase l'ancien) :\n\n    ```js\n    let headers = xhr\n      .getAllResponseHeaders()\n      .split('\\r\\n')\n      .reduce((result, current) => {\n        let [name, value] = current.split(': ');\n        result[name] = value;\n        return result;\n      }, {});\n\n    // headers['Content-Type'] = 'image/png'\n    ```\n\n## POST, FormData\n\nPour faire une requête POST, nous pouvons utiliser l'objet intégrée [FormData](mdn:api/FormData).\n\nLa syntaxe :\n\n```js\nlet formData = new FormData([form]); // crée un objet, éventuellement remplir à partir de <form>\nformData.append(name, value); // ajoute un champ\n```\n\nNous le créons, remplissons éventuellement à partir d'un formulaire, ajoutons d'autres champs si nécessaire, puis :\n\n1. `xhr.open('POST', ...)` – utilise la méthode `POST`.\n2. `xhr.send(formData)` pour soumettre le formulaire au serveur.\n\nPar exemple :\n\n```html run refresh\n<form name=\"person\">\n  <input name=\"name\" value=\"John\">\n  <input name=\"surname\" value=\"Smith\">\n</form>\n\n<script>\n  // pré-remplir FormData du formulaire\n  let formData = new FormData(document.forms.person);\n\n  // ajouter un champ de plus\n  formData.append(\"middle\", \"Lee\");\n\n  // l'envoie\n  let xhr = new XMLHttpRequest();\n  xhr.open(\"POST\", \"/article/xmlhttprequest/post/user\");\n  xhr.send(formData);\n\n  xhr.onload = () => alert(xhr.response);\n</script>\n```\n\nLe formulaire est envoyé avec un encodage `multipart/form-data`.\n\nOu, si nous aimons davantage JSON, alors `JSON.stringify` et l'envoyer sous forme de chaîne de caractères.\n\nN'oubliez juste pas de définir l'en-tête `Content-Type: application/json`, de nombreux frameworks côté serveur décodent automatiquement JSON avec :\n\n```js\nlet xhr = new XMLHttpRequest();\n\nlet json = JSON.stringify({\n  name: \"John\",\n  surname: \"Smith\"\n});\n\nxhr.open(\"POST\", '/submit')\nxhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');\n\nxhr.send(json);\n```\n\nLa méthode `.send(body)` est assez omnivore. Il peut envoyer presque n'importe quel `body`, y compris les objets `Blob` et `BufferSource`.\n\n## Progression de l'upload\n\nL'événement `progress` se déclenche uniquement à l'étape du téléchargement.\n\nC'est-à-dire: si nous envoyons via `POST` quelque chose, `XMLHttpRequest` upload d'abord nos données (le corps de la requête), puis télécharge la réponse.\n\nSi nous uploadons quelque chose de gros, alors nous sommes sûrement plus intéressés à suivre la progression de l'envoi. Mais `xhr.onprogress` n'aide pas ici.\n\nIl existe un autre objet, sans méthodes, exclusivement pour suivre les événements de l'envoi : `xhr.upload`.\n\nIl génère des événements, similaires à `xhr`, mais `xhr.upload` les déclenche uniquement lors de l'upload :\n\n- `loadstart` -- upload démarré.\n- `progress` -- se déclenche périodiquement pendant l'upload.\n- `abort` -- upload annulé.\n- `error` -- erreur non-HTTP.\n- `load` -- upload terminé avec succès.\n- `timeout` -- upload expiré (si la propriété `timeout` est définie).\n- `loadend` -- upload terminé avec succès ou erreur.\n\nExemple de gestionnaires :\n\n```js\nxhr.upload.onprogress = function(event) {\n  alert(`Uploaded ${event.loaded} of ${event.total} bytes`);\n};\n\nxhr.upload.onload = function() {\n  alert(`Upload finished successfully.`);\n};\n\nxhr.upload.onerror = function() {\n  alert(`Error during the upload: ${xhr.status}`);\n};\n```\n\nVoici un exemple réel : upload de fichier avec indication de progression :\n\n```html run\n<input type=\"file\" onchange=\"upload(this.files[0])\">\n\n<script>\nfunction upload(file) {\n  let xhr = new XMLHttpRequest();\n\n  // suivre la progression de l'upload\n*!*\n  xhr.upload.onprogress = function(event) {\n    console.log(`Uploaded ${event.loaded} of ${event.total}`);\n  };\n*/!*\n\n  // suivi de l'envoi : réussi ou non\n  xhr.onloadend = function() {\n    if (xhr.status == 200) {\n      console.log(\"success\");\n    } else {\n      console.log(\"error \" + this.status);\n    }\n  };\n\n  xhr.open(\"POST\", \"/article/xmlhttprequest/post/upload\");\n  xhr.send(file);\n}\n</script>\n```\n\n## Requêtes Cross-origin\n\n`XMLHttpRequest` peut faire des requêtes cross-origin, en utilisant la même politique CORS que [fetch](info:fetch-crossorigin).\n\nTout comme `fetch`, elle n'envoie pas de cookies et d'autorisation HTTP à une autre origine par défaut. Pour les activer, définissez `xhr.withCredentials` sur `true` :\n\n```js\nlet xhr = new XMLHttpRequest();\n*!*\nxhr.withCredentials = true;\n*/!*\n\nxhr.open('POST', 'http://anywhere.com/request');\n...\n```\n\nVoir le chapitre <info:fetch-crossorigin> pour plus de détails sur les en-têtes cross-origin.\n\n\n## Résumé\n\nCode typique de la requête GET avec `XMLHttpRequest` :\n\n```js\nlet xhr = new XMLHttpRequest();\n\nxhr.open('GET', '/my/url');\n\nxhr.send();\n\nxhr.onload = function() {\n  if (xhr.status != 200) { // HTTP error?\n    // handle error\n    alert( 'Error: ' + xhr.status);\n    return;\n  }\n\n  // obtenir la réponse de xhr.response\n};\n\nxhr.onprogress = function(event) {\n  // report progress\n  alert(`Loaded ${event.loaded} of ${event.total}`);\n};\n\nxhr.onerror = function() {\n  // gérer les erreurs non HTTP (par exemple, panne de réseau)\n};\n```\n\nIl y a en fait plus d'événements, la [spécification moderne](https://xhr.spec.whatwg.org/#events) les répertorie (dans l'ordre du cycle de vie) :\n\n- `loadstart` -- la requête a commencé.\n- `progress` -- un paquet de données de la réponse est arrivé, tout le corps de la réponse est actuellement dans `response`.\n- `abort` -- la requête a été annulée par l'appel `xhr.abort()`.\n- `error` -- une erreur de connexion s'est produite, par exemple nom de domaine incorrect. Ne se produit pas pour les erreurs HTTP comme 404.\n- `load` -- la requête s'est terminée avec succès.\n- `timeout` -- la requête a été annulée en raison du délai d'attente (ne se produit que si elle a été définie).\n- `loadend` -- se déclenche après `load`, `error`, `timeout` ou `abort`.\n\nLes événements `error`, `abort`, `timeout`, et `load` s'excluent mutuellement. Un seul d'entre eux peut se produire.\n\nLes événements les plus utilisés sont la progression du chargement (`load`), l'échec du chargement (`error`), ou nous pouvons utiliser un seul gestionnaire `loadend` et vérifier les propriétés de l'objet de requête `xhr` pour voir ce qui s'est passé.\n\nNous avons déjà vu un autre événement : `readystatechange`. Historiquement, il est apparu il y a longtemps, avant que la spécification ne soit réglée. De nos jours, il n'est pas nécessaire de l'utiliser, nous pouvons le remplacer par des événements plus récents, mais il peut souvent être trouvé dans des scripts plus anciens.\n\nSi nous devons suivre spécifiquement l'uplaod, alors nous devons écouter les mêmes événements sur l'objet `xhr.upload`.\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/example.view/index.html",
    "content": "<!DOCTYPE HTML>\n<script>\nfunction run() {\n\n  let xhr = new XMLHttpRequest();\n  write(`readyState=${xhr.readyState}`);\n\n  xhr.open('GET', 'digits');\n  write(`readyState=${xhr.readyState}`);\n\n  xhr.onreadystatechange = function() {\n    write(`readyState=${xhr.readyState}, responseText.length=${xhr.responseText.length}`);\n  };\n\n  xhr.onprogress = function() {\n    write(`readyState=${xhr.readyState}, responseText.length=${xhr.responseText.length}`);\n  };\n\n  xhr.send();\n}\n\nfunction write(text) {\n  let li = log.appendChild(document.createElement('li'));\n  li.innerHTML = text;\n}\n</script>\n\n<button onclick=\"run()\">Load digits</button>\n\n<ul id=\"log\"></ul>\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/example.view/server.js",
    "content": "let http = require('http');\nlet url = require('url');\nlet querystring = require('querystring');\nlet static = require('node-static');\nlet file = new static.Server('.');\n\nfunction accept(req, res) {\n\n  if (req.url == '/load') {\n\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Cache-Control': 'no-cache',\n      'Content-Length': 90000\n    });\n\n    let i = 0;\n\n    let timer = setInterval(write, 1000);\n    write();\n\n    function write() {\n      res.write(String(i).repeat(10000));\n      i++;\n      if (i == 9) {\n        clearInterval(timer);\n        res.end();\n      }\n\n    }\n  } else if (req.url == '/json') {\n    res.writeHead(200, {\n      // 'Content-Type': 'application/json;charset=utf-8',\n      'Cache-Control': 'no-cache'\n    });\n\n    res.write(JSON.stringify({message: \"Hello, world!\"}));\n    res.end();\n  } else {\n    file.serve(req, res);\n  }\n}\n\n\n\n// ----- запуск accept как сервера из консоли или как модуля ------\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n} else {\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/hello.txt",
    "content": "Hello from the server!\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/phones-async.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n\n  <button onclick=\"loadPhones()\" id=\"button\">Load phones.json!</button>\n\n  <script>\n    function loadPhones() {\n\n      let xhr = new XMLHttpRequest();\n\n      xhr.open('GET', 'phones.json');\n\n\n      xhr.send();\n\n\n      xhr.onreadystatechange = function() {\n        if (xhr.readyState != 4) return;\n\n        button.innerHTML = 'Complete!';\n\n        if (xhr.status != 200) {\n          // handle error\n          alert(xhr.status + ': ' + xhr.statusText);\n        } else {\n          // show result\n          alert(xhr.responseText);\n        }\n\n      }\n\n      button.innerHTML = 'Loading...';\n      button.disabled = true;\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/phones-async.view/phones.json",
    "content": "[\n    {\n        \"age\": 0, \n        \"id\": \"motorola-xoom-with-wi-fi\", \n        \"imageUrl\": \"img/phones/motorola-xoom-with-wi-fi.0.jpg\", \n        \"name\": \"Motorola XOOM\\u2122 with Wi-Fi\", \n        \"snippet\": \"The Next, Next Generation\\r\\n\\r\\nExperience the future with Motorola XOOM with Wi-Fi, the world's first tablet powered by Android 3.0 (Honeycomb).\"\n    }, \n    {\n        \"age\": 1, \n        \"id\": \"motorola-xoom\", \n        \"imageUrl\": \"img/phones/motorola-xoom.0.jpg\", \n        \"name\": \"MOTOROLA XOOM\\u2122\", \n        \"snippet\": \"The Next, Next Generation\\n\\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb).\"\n    }, \n    {\n        \"age\": 2, \n        \"carrier\": \"AT&amp;T\", \n        \"id\": \"motorola-atrix-4g\", \n        \"imageUrl\": \"img/phones/motorola-atrix-4g.0.jpg\", \n        \"name\": \"MOTOROLA ATRIX\\u2122 4G\", \n        \"snippet\": \"MOTOROLA ATRIX 4G the world's most powerful smartphone.\"\n    }, \n    {\n        \"age\": 3, \n        \"id\": \"dell-streak-7\", \n        \"imageUrl\": \"img/phones/dell-streak-7.0.jpg\", \n        \"name\": \"Dell Streak 7\", \n        \"snippet\": \"Introducing Dell\\u2122 Streak 7. Share photos, videos and movies together. It\\u2019s small enough to carry around, big enough to gather around.\"\n    }, \n    {\n        \"age\": 4, \n        \"carrier\": \"Cellular South\", \n        \"id\": \"samsung-gem\", \n        \"imageUrl\": \"img/phones/samsung-gem.0.jpg\", \n        \"name\": \"Samsung Gem\\u2122\", \n        \"snippet\": \"The Samsung Gem\\u2122 brings you everything that you would expect and more from a touch display smart phone \\u2013 more apps, more features and a more affordable price.\"\n    }, \n    {\n        \"age\": 5, \n        \"carrier\": \"Dell\", \n        \"id\": \"dell-venue\", \n        \"imageUrl\": \"img/phones/dell-venue.0.jpg\", \n        \"name\": \"Dell Venue\", \n        \"snippet\": \"The Dell Venue; Your Personal Express Lane to Everything\"\n    }, \n    {\n        \"age\": 6, \n        \"carrier\": \"Best Buy\", \n        \"id\": \"nexus-s\", \n        \"imageUrl\": \"img/phones/nexus-s.0.jpg\", \n        \"name\": \"Nexus S\", \n        \"snippet\": \"Fast just got faster with Nexus S. A pure Google experience, Nexus S is the first phone to run Gingerbread (Android 2.3), the fastest version of Android yet.\"\n    }, \n    {\n        \"age\": 7, \n        \"carrier\": \"Cellular South\", \n        \"id\": \"lg-axis\", \n        \"imageUrl\": \"img/phones/lg-axis.0.jpg\", \n        \"name\": \"LG Axis\", \n        \"snippet\": \"Android Powered, Google Maps Navigation, 5 Customizable Home Screens\"\n    }, \n    {\n        \"age\": 8, \n        \"id\": \"samsung-galaxy-tab\", \n        \"imageUrl\": \"img/phones/samsung-galaxy-tab.0.jpg\", \n        \"name\": \"Samsung Galaxy Tab\\u2122\", \n        \"snippet\": \"Feel Free to Tab\\u2122. The Samsung Galaxy Tab\\u2122 brings you an ultra-mobile entertainment experience through its 7\\u201d display, high-power processor and Adobe\\u00ae Flash\\u00ae Player compatibility.\"\n    }, \n    {\n        \"age\": 9, \n        \"carrier\": \"Cellular South\", \n        \"id\": \"samsung-showcase-a-galaxy-s-phone\", \n        \"imageUrl\": \"img/phones/samsung-showcase-a-galaxy-s-phone.0.jpg\", \n        \"name\": \"Samsung Showcase\\u2122 a Galaxy S\\u2122 phone\", \n        \"snippet\": \"The Samsung Showcase\\u2122 delivers a cinema quality experience like you\\u2019ve never seen before. Its innovative 4\\u201d touch display technology provides rich picture brilliance, even outdoors\"\n    }, \n    {\n        \"age\": 10, \n        \"carrier\": \"Verizon\", \n        \"id\": \"droid-2-global-by-motorola\", \n        \"imageUrl\": \"img/phones/droid-2-global-by-motorola.0.jpg\", \n        \"name\": \"DROID\\u2122 2 Global by Motorola\", \n        \"snippet\": \"The first smartphone with a 1.2 GHz processor and global capabilities.\"\n    }, \n    {\n        \"age\": 11, \n        \"carrier\": \"Verizon\", \n        \"id\": \"droid-pro-by-motorola\", \n        \"imageUrl\": \"img/phones/droid-pro-by-motorola.0.jpg\", \n        \"name\": \"DROID\\u2122 Pro by Motorola\", \n        \"snippet\": \"The next generation of DOES.\"\n    }, \n    {\n        \"age\": 12, \n        \"carrier\": \"AT&amp;T\", \n        \"id\": \"motorola-bravo-with-motoblur\", \n        \"imageUrl\": \"img/phones/motorola-bravo-with-motoblur.0.jpg\", \n        \"name\": \"MOTOROLA BRAVO\\u2122 with MOTOBLUR\\u2122\", \n        \"snippet\": \"An experience to cheer about.\"\n    }, \n    {\n        \"age\": 13, \n        \"carrier\": \"T-Mobile\", \n        \"id\": \"motorola-defy-with-motoblur\", \n        \"imageUrl\": \"img/phones/motorola-defy-with-motoblur.0.jpg\", \n        \"name\": \"Motorola DEFY\\u2122 with MOTOBLUR\\u2122\", \n        \"snippet\": \"Are you ready for everything life throws your way?\"\n    }, \n    {\n        \"age\": 14, \n        \"carrier\": \"T-Mobile\", \n        \"id\": \"t-mobile-mytouch-4g\", \n        \"imageUrl\": \"img/phones/t-mobile-mytouch-4g.0.jpg\", \n        \"name\": \"T-Mobile myTouch 4G\", \n        \"snippet\": \"The T-Mobile myTouch 4G is a premium smartphone designed to deliver blazing fast 4G speeds so that you can video chat from practically anywhere, with or without Wi-Fi.\"\n    }, \n    {\n        \"age\": 15, \n        \"carrier\": \"US Cellular\", \n        \"id\": \"samsung-mesmerize-a-galaxy-s-phone\", \n        \"imageUrl\": \"img/phones/samsung-mesmerize-a-galaxy-s-phone.0.jpg\", \n        \"name\": \"Samsung Mesmerize\\u2122 a Galaxy S\\u2122 phone\", \n        \"snippet\": \"The Samsung Mesmerize\\u2122 delivers a cinema quality experience like you\\u2019ve never seen before. Its innovative 4\\u201d touch display technology provides rich picture brilliance,even outdoors\"\n    }, \n    {\n        \"age\": 16, \n        \"carrier\": \"Sprint\", \n        \"id\": \"sanyo-zio\", \n        \"imageUrl\": \"img/phones/sanyo-zio.0.jpg\", \n        \"name\": \"SANYO ZIO\", \n        \"snippet\": \"The Sanyo Zio by Kyocera is an Android smartphone with a combination of ultra-sleek styling, strong performance and unprecedented value.\"\n    }, \n    {\n        \"age\": 17, \n        \"id\": \"samsung-transform\", \n        \"imageUrl\": \"img/phones/samsung-transform.0.jpg\", \n        \"name\": \"Samsung Transform\\u2122\", \n        \"snippet\": \"The Samsung Transform\\u2122 brings you a fun way to customize your Android powered touch screen phone to just the way you like it through your favorite themed \\u201cSprint ID Service Pack\\u201d.\"\n    }, \n    {\n        \"age\": 18, \n        \"id\": \"t-mobile-g2\", \n        \"imageUrl\": \"img/phones/t-mobile-g2.0.jpg\", \n        \"name\": \"T-Mobile G2\", \n        \"snippet\": \"The T-Mobile G2 with Google is the first smartphone built for 4G speeds on T-Mobile's new network. Get the information you need, faster than you ever thought possible.\"\n    }, \n    {\n        \"age\": 19, \n        \"id\": \"motorola-charm-with-motoblur\", \n        \"imageUrl\": \"img/phones/motorola-charm-with-motoblur.0.jpg\", \n        \"name\": \"Motorola CHARM\\u2122 with MOTOBLUR\\u2122\", \n        \"snippet\": \"Motorola CHARM fits easily in your pocket or palm.  Includes MOTOBLUR service.\"\n    }\n]\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/phones-async.view/server.js",
    "content": "var http = require('http');\nvar url = require('url');\nvar querystring = require('querystring');\nvar static = require('node-static');\nvar file = new static.Server('.', {\n  cache: 0\n});\n\n\nfunction accept(req, res) {\n\n  if (req.url == '/phones.json') {\n    // искусственная задержка для наглядности\n    setTimeout(function() {\n      file.serve(req, res);\n    }, 2000);\n  } else {\n    file.serve(req, res);\n  }\n\n}\n\n\n// ------ запустить сервер -------\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n} else {\n  exports.accept = accept;\n}"
  },
  {
    "path": "5-network/08-xmlhttprequest/phones.json",
    "content": "[\n    {\n        \"age\": 0,\n        \"id\": \"motorola-xoom-with-wi-fi\",\n        \"imageUrl\": \"img/phones/motorola-xoom-with-wi-fi.0.jpg\",\n        \"name\": \"Motorola XOOM\\u2122 with Wi-Fi\",\n        \"snippet\": \"The Next, Next Generation\\r\\n\\r\\nExperience the future with Motorola XOOM with Wi-Fi, the world's first tablet powered by Android 3.0 (Honeycomb).\"\n    },\n    {\n        \"age\": 1,\n        \"id\": \"motorola-xoom\",\n        \"imageUrl\": \"img/phones/motorola-xoom.0.jpg\",\n        \"name\": \"MOTOROLA XOOM\\u2122\",\n        \"snippet\": \"The Next, Next Generation\\n\\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb).\"\n    },\n    {\n        \"age\": 2,\n        \"carrier\": \"AT&amp;T\",\n        \"id\": \"motorola-atrix-4g\",\n        \"imageUrl\": \"img/phones/motorola-atrix-4g.0.jpg\",\n        \"name\": \"MOTOROLA ATRIX\\u2122 4G\",\n        \"snippet\": \"MOTOROLA ATRIX 4G the world's most powerful smartphone.\"\n    },\n    {\n        \"age\": 3,\n        \"id\": \"dell-streak-7\",\n        \"imageUrl\": \"img/phones/dell-streak-7.0.jpg\",\n        \"name\": \"Dell Streak 7\",\n        \"snippet\": \"Introducing Dell\\u2122 Streak 7. Share photos, videos and movies together. It\\u2019s small enough to carry around, big enough to gather around.\"\n    },\n    {\n        \"age\": 4,\n        \"carrier\": \"Cellular South\",\n        \"id\": \"samsung-gem\",\n        \"imageUrl\": \"img/phones/samsung-gem.0.jpg\",\n        \"name\": \"Samsung Gem\\u2122\",\n        \"snippet\": \"The Samsung Gem\\u2122 brings you everything that you would expect and more from a touch display smart phone \\u2013 more apps, more features and a more affordable price.\"\n    },\n    {\n        \"age\": 5,\n        \"carrier\": \"Dell\",\n        \"id\": \"dell-venue\",\n        \"imageUrl\": \"img/phones/dell-venue.0.jpg\",\n        \"name\": \"Dell Venue\",\n        \"snippet\": \"The Dell Venue; Your Personal Express Lane to Everything\"\n    },\n    {\n        \"age\": 6,\n        \"carrier\": \"Best Buy\",\n        \"id\": \"nexus-s\",\n        \"imageUrl\": \"img/phones/nexus-s.0.jpg\",\n        \"name\": \"Nexus S\",\n        \"snippet\": \"Fast just got faster with Nexus S. A pure Google experience, Nexus S is the first phone to run Gingerbread (Android 2.3), the fastest version of Android yet.\"\n    },\n    {\n        \"age\": 7,\n        \"carrier\": \"Cellular South\",\n        \"id\": \"lg-axis\",\n        \"imageUrl\": \"img/phones/lg-axis.0.jpg\",\n        \"name\": \"LG Axis\",\n        \"snippet\": \"Android Powered, Google Maps Navigation, 5 Customizable Home Screens\"\n    },\n    {\n        \"age\": 8,\n        \"id\": \"samsung-galaxy-tab\",\n        \"imageUrl\": \"img/phones/samsung-galaxy-tab.0.jpg\",\n        \"name\": \"Samsung Galaxy Tab\\u2122\",\n        \"snippet\": \"Feel Free to Tab\\u2122. The Samsung Galaxy Tab\\u2122 brings you an ultra-mobile entertainment experience through its 7\\u201d display, high-power processor and Adobe\\u00ae Flash\\u00ae Player compatibility.\"\n    },\n    {\n        \"age\": 9,\n        \"carrier\": \"Cellular South\",\n        \"id\": \"samsung-showcase-a-galaxy-s-phone\",\n        \"imageUrl\": \"img/phones/samsung-showcase-a-galaxy-s-phone.0.jpg\",\n        \"name\": \"Samsung Showcase\\u2122 a Galaxy S\\u2122 phone\",\n        \"snippet\": \"The Samsung Showcase\\u2122 delivers a cinema quality experience like you\\u2019ve never seen before. Its innovative 4\\u201d touch display technology provides rich picture brilliance, even outdoors\"\n    },\n    {\n        \"age\": 10,\n        \"carrier\": \"Verizon\",\n        \"id\": \"droid-2-global-by-motorola\",\n        \"imageUrl\": \"img/phones/droid-2-global-by-motorola.0.jpg\",\n        \"name\": \"DROID\\u2122 2 Global by Motorola\",\n        \"snippet\": \"The first smartphone with a 1.2 GHz processor and global capabilities.\"\n    },\n    {\n        \"age\": 11,\n        \"carrier\": \"Verizon\",\n        \"id\": \"droid-pro-by-motorola\",\n        \"imageUrl\": \"img/phones/droid-pro-by-motorola.0.jpg\",\n        \"name\": \"DROID\\u2122 Pro by Motorola\",\n        \"snippet\": \"The next generation of DOES.\"\n    },\n    {\n        \"age\": 12,\n        \"carrier\": \"AT&amp;T\",\n        \"id\": \"motorola-bravo-with-motoblur\",\n        \"imageUrl\": \"img/phones/motorola-bravo-with-motoblur.0.jpg\",\n        \"name\": \"MOTOROLA BRAVO\\u2122 with MOTOBLUR\\u2122\",\n        \"snippet\": \"An experience to cheer about.\"\n    },\n    {\n        \"age\": 13,\n        \"carrier\": \"T-Mobile\",\n        \"id\": \"motorola-defy-with-motoblur\",\n        \"imageUrl\": \"img/phones/motorola-defy-with-motoblur.0.jpg\",\n        \"name\": \"Motorola DEFY\\u2122 with MOTOBLUR\\u2122\",\n        \"snippet\": \"Are you ready for everything life throws your way?\"\n    },\n    {\n        \"age\": 14,\n        \"carrier\": \"T-Mobile\",\n        \"id\": \"t-mobile-mytouch-4g\",\n        \"imageUrl\": \"img/phones/t-mobile-mytouch-4g.0.jpg\",\n        \"name\": \"T-Mobile myTouch 4G\",\n        \"snippet\": \"The T-Mobile myTouch 4G is a premium smartphone designed to deliver blazing fast 4G speeds so that you can video chat from practically anywhere, with or without Wi-Fi.\"\n    },\n    {\n        \"age\": 15,\n        \"carrier\": \"US Cellular\",\n        \"id\": \"samsung-mesmerize-a-galaxy-s-phone\",\n        \"imageUrl\": \"img/phones/samsung-mesmerize-a-galaxy-s-phone.0.jpg\",\n        \"name\": \"Samsung Mesmerize\\u2122 a Galaxy S\\u2122 phone\",\n        \"snippet\": \"The Samsung Mesmerize\\u2122 delivers a cinema quality experience like you\\u2019ve never seen before. Its innovative 4\\u201d touch display technology provides rich picture brilliance,even outdoors\"\n    },\n    {\n        \"age\": 16,\n        \"carrier\": \"Sprint\",\n        \"id\": \"sanyo-zio\",\n        \"imageUrl\": \"img/phones/sanyo-zio.0.jpg\",\n        \"name\": \"SANYO ZIO\",\n        \"snippet\": \"The Sanyo Zio by Kyocera is an Android smartphone with a combination of ultra-sleek styling, strong performance and unprecedented value.\"\n    },\n    {\n        \"age\": 17,\n        \"id\": \"samsung-transform\",\n        \"imageUrl\": \"img/phones/samsung-transform.0.jpg\",\n        \"name\": \"Samsung Transform\\u2122\",\n        \"snippet\": \"The Samsung Transform\\u2122 brings you a fun way to customize your Android powered touch screen phone to just the way you like it through your favorite themed \\u201cSprint ID Service Pack\\u201d.\"\n    },\n    {\n        \"age\": 18,\n        \"id\": \"t-mobile-g2\",\n        \"imageUrl\": \"img/phones/t-mobile-g2.0.jpg\",\n        \"name\": \"T-Mobile G2\",\n        \"snippet\": \"The T-Mobile G2 with Google is the first smartphone built for 4G speeds on T-Mobile's new network. Get the information you need, faster than you ever thought possible.\"\n    },\n    {\n        \"age\": 19,\n        \"id\": \"motorola-charm-with-motoblur\",\n        \"imageUrl\": \"img/phones/motorola-charm-with-motoblur.0.jpg\",\n        \"name\": \"Motorola CHARM\\u2122 with MOTOBLUR\\u2122\",\n        \"snippet\": \"Motorola CHARM fits easily in your pocket or palm.  Includes MOTOBLUR service.\"\n    }\n]\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/phones.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n\n  <button onclick=\"loadPhones()\">Load phones.json!</button>\n\n  <script>\n    function loadPhones() {\n      let xhr = new XMLHttpRequest();\n\n      xhr.open('GET', 'phones.json', false);\n      xhr.send();\n\n      if (xhr.status != 200) {\n        // handle error\n        alert('Error ' + xhr.status + ': ' + xhr.statusText);\n      } else {\n        // show result\n        alert(xhr.responseText);\n      }\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/phones.view/phones.json",
    "content": "[\n    {\n        \"age\": 0, \n        \"id\": \"motorola-xoom-with-wi-fi\", \n        \"imageUrl\": \"img/phones/motorola-xoom-with-wi-fi.0.jpg\", \n        \"name\": \"Motorola XOOM\\u2122 with Wi-Fi\", \n        \"snippet\": \"The Next, Next Generation\\r\\n\\r\\nExperience the future with Motorola XOOM with Wi-Fi, the world's first tablet powered by Android 3.0 (Honeycomb).\"\n    }, \n    {\n        \"age\": 1, \n        \"id\": \"motorola-xoom\", \n        \"imageUrl\": \"img/phones/motorola-xoom.0.jpg\", \n        \"name\": \"MOTOROLA XOOM\\u2122\", \n        \"snippet\": \"The Next, Next Generation\\n\\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb).\"\n    }, \n    {\n        \"age\": 2, \n        \"carrier\": \"AT&amp;T\", \n        \"id\": \"motorola-atrix-4g\", \n        \"imageUrl\": \"img/phones/motorola-atrix-4g.0.jpg\", \n        \"name\": \"MOTOROLA ATRIX\\u2122 4G\", \n        \"snippet\": \"MOTOROLA ATRIX 4G the world's most powerful smartphone.\"\n    }, \n    {\n        \"age\": 3, \n        \"id\": \"dell-streak-7\", \n        \"imageUrl\": \"img/phones/dell-streak-7.0.jpg\", \n        \"name\": \"Dell Streak 7\", \n        \"snippet\": \"Introducing Dell\\u2122 Streak 7. Share photos, videos and movies together. It\\u2019s small enough to carry around, big enough to gather around.\"\n    }, \n    {\n        \"age\": 4, \n        \"carrier\": \"Cellular South\", \n        \"id\": \"samsung-gem\", \n        \"imageUrl\": \"img/phones/samsung-gem.0.jpg\", \n        \"name\": \"Samsung Gem\\u2122\", \n        \"snippet\": \"The Samsung Gem\\u2122 brings you everything that you would expect and more from a touch display smart phone \\u2013 more apps, more features and a more affordable price.\"\n    }, \n    {\n        \"age\": 5, \n        \"carrier\": \"Dell\", \n        \"id\": \"dell-venue\", \n        \"imageUrl\": \"img/phones/dell-venue.0.jpg\", \n        \"name\": \"Dell Venue\", \n        \"snippet\": \"The Dell Venue; Your Personal Express Lane to Everything\"\n    }, \n    {\n        \"age\": 6, \n        \"carrier\": \"Best Buy\", \n        \"id\": \"nexus-s\", \n        \"imageUrl\": \"img/phones/nexus-s.0.jpg\", \n        \"name\": \"Nexus S\", \n        \"snippet\": \"Fast just got faster with Nexus S. A pure Google experience, Nexus S is the first phone to run Gingerbread (Android 2.3), the fastest version of Android yet.\"\n    }, \n    {\n        \"age\": 7, \n        \"carrier\": \"Cellular South\", \n        \"id\": \"lg-axis\", \n        \"imageUrl\": \"img/phones/lg-axis.0.jpg\", \n        \"name\": \"LG Axis\", \n        \"snippet\": \"Android Powered, Google Maps Navigation, 5 Customizable Home Screens\"\n    }, \n    {\n        \"age\": 8, \n        \"id\": \"samsung-galaxy-tab\", \n        \"imageUrl\": \"img/phones/samsung-galaxy-tab.0.jpg\", \n        \"name\": \"Samsung Galaxy Tab\\u2122\", \n        \"snippet\": \"Feel Free to Tab\\u2122. The Samsung Galaxy Tab\\u2122 brings you an ultra-mobile entertainment experience through its 7\\u201d display, high-power processor and Adobe\\u00ae Flash\\u00ae Player compatibility.\"\n    }, \n    {\n        \"age\": 9, \n        \"carrier\": \"Cellular South\", \n        \"id\": \"samsung-showcase-a-galaxy-s-phone\", \n        \"imageUrl\": \"img/phones/samsung-showcase-a-galaxy-s-phone.0.jpg\", \n        \"name\": \"Samsung Showcase\\u2122 a Galaxy S\\u2122 phone\", \n        \"snippet\": \"The Samsung Showcase\\u2122 delivers a cinema quality experience like you\\u2019ve never seen before. Its innovative 4\\u201d touch display technology provides rich picture brilliance, even outdoors\"\n    }, \n    {\n        \"age\": 10, \n        \"carrier\": \"Verizon\", \n        \"id\": \"droid-2-global-by-motorola\", \n        \"imageUrl\": \"img/phones/droid-2-global-by-motorola.0.jpg\", \n        \"name\": \"DROID\\u2122 2 Global by Motorola\", \n        \"snippet\": \"The first smartphone with a 1.2 GHz processor and global capabilities.\"\n    }, \n    {\n        \"age\": 11, \n        \"carrier\": \"Verizon\", \n        \"id\": \"droid-pro-by-motorola\", \n        \"imageUrl\": \"img/phones/droid-pro-by-motorola.0.jpg\", \n        \"name\": \"DROID\\u2122 Pro by Motorola\", \n        \"snippet\": \"The next generation of DOES.\"\n    }, \n    {\n        \"age\": 12, \n        \"carrier\": \"AT&amp;T\", \n        \"id\": \"motorola-bravo-with-motoblur\", \n        \"imageUrl\": \"img/phones/motorola-bravo-with-motoblur.0.jpg\", \n        \"name\": \"MOTOROLA BRAVO\\u2122 with MOTOBLUR\\u2122\", \n        \"snippet\": \"An experience to cheer about.\"\n    }, \n    {\n        \"age\": 13, \n        \"carrier\": \"T-Mobile\", \n        \"id\": \"motorola-defy-with-motoblur\", \n        \"imageUrl\": \"img/phones/motorola-defy-with-motoblur.0.jpg\", \n        \"name\": \"Motorola DEFY\\u2122 with MOTOBLUR\\u2122\", \n        \"snippet\": \"Are you ready for everything life throws your way?\"\n    }, \n    {\n        \"age\": 14, \n        \"carrier\": \"T-Mobile\", \n        \"id\": \"t-mobile-mytouch-4g\", \n        \"imageUrl\": \"img/phones/t-mobile-mytouch-4g.0.jpg\", \n        \"name\": \"T-Mobile myTouch 4G\", \n        \"snippet\": \"The T-Mobile myTouch 4G is a premium smartphone designed to deliver blazing fast 4G speeds so that you can video chat from practically anywhere, with or without Wi-Fi.\"\n    }, \n    {\n        \"age\": 15, \n        \"carrier\": \"US Cellular\", \n        \"id\": \"samsung-mesmerize-a-galaxy-s-phone\", \n        \"imageUrl\": \"img/phones/samsung-mesmerize-a-galaxy-s-phone.0.jpg\", \n        \"name\": \"Samsung Mesmerize\\u2122 a Galaxy S\\u2122 phone\", \n        \"snippet\": \"The Samsung Mesmerize\\u2122 delivers a cinema quality experience like you\\u2019ve never seen before. Its innovative 4\\u201d touch display technology provides rich picture brilliance,even outdoors\"\n    }, \n    {\n        \"age\": 16, \n        \"carrier\": \"Sprint\", \n        \"id\": \"sanyo-zio\", \n        \"imageUrl\": \"img/phones/sanyo-zio.0.jpg\", \n        \"name\": \"SANYO ZIO\", \n        \"snippet\": \"The Sanyo Zio by Kyocera is an Android smartphone with a combination of ultra-sleek styling, strong performance and unprecedented value.\"\n    }, \n    {\n        \"age\": 17, \n        \"id\": \"samsung-transform\", \n        \"imageUrl\": \"img/phones/samsung-transform.0.jpg\", \n        \"name\": \"Samsung Transform\\u2122\", \n        \"snippet\": \"The Samsung Transform\\u2122 brings you a fun way to customize your Android powered touch screen phone to just the way you like it through your favorite themed \\u201cSprint ID Service Pack\\u201d.\"\n    }, \n    {\n        \"age\": 18, \n        \"id\": \"t-mobile-g2\", \n        \"imageUrl\": \"img/phones/t-mobile-g2.0.jpg\", \n        \"name\": \"T-Mobile G2\", \n        \"snippet\": \"The T-Mobile G2 with Google is the first smartphone built for 4G speeds on T-Mobile's new network. Get the information you need, faster than you ever thought possible.\"\n    }, \n    {\n        \"age\": 19, \n        \"id\": \"motorola-charm-with-motoblur\", \n        \"imageUrl\": \"img/phones/motorola-charm-with-motoblur.0.jpg\", \n        \"name\": \"Motorola CHARM\\u2122 with MOTOBLUR\\u2122\", \n        \"snippet\": \"Motorola CHARM fits easily in your pocket or palm.  Includes MOTOBLUR service.\"\n    }\n]\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/phones.view/server.js",
    "content": "let http = require('http');\nlet url = require('url');\nlet querystring = require('querystring');\nlet static = require('node-static');\nlet file = new static.Server('.', {\n  cache: 0\n});\n\n\nfunction accept(req, res) {\n\n  if (req.url == '/phones.json') {\n    // stall a bit to let \"loading\" message show up\n    setTimeout(function() {\n      file.serve(req, res);\n    }, 2000);\n  } else {\n    file.serve(req, res);\n  }\n\n}\n\n\n// ------ запустить сервер -------\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n} else {\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/post.view/index.html",
    "content": "<!doctype html>\n<script>\n(async () {\n\t\n\tconst response = await fetch('long.txt');\n\tconst reader = response.body.getReader();\n\n\tconst contentLength = +response.headers.get('Content-Length');\n\tlet receivedLength = 0;\n\tlet chunks = [];\n\twhile(true) {\n\t\tconst chunk = await reader.read();\n\n\t\tif (chunk.done) {\n\t\t\tconsole.log(\"done!\");\n\t\t\tbreak;\n\t\t}\n\n\t\tchunks.push(chunk.value);\n\t\treceivedLength += chunk.value.length;\n\t\tconsole.log(`${receivedLength}/${contentLength} received`)\n\t}\n\n\n\tlet chunksMerged = new Uint8Array(receivedLength);\n\tlet length = 0;\n\tfor(let chunk of chunks) {\n\t\tchunksMerged.set(chunk, length);\n\t\tlength += chunk.length;\n\t}\n\n\tlet result = new TextDecoder(\"utf-8\").decode(chunksMerged);\n\tconsole.log(result);\n})();\n\n</script>\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/post.view/server.js",
    "content": "let http = require('http');\nlet url = require('url');\nlet querystring = require('querystring');\nlet static = require('node-static');\nlet file = new static.Server('.', {\n  cache: 0\n});\n\n\nfunction accept(req, res) {\n\n  if (req.method == 'POST') {\n    let chunks = [];\n    let length = 0;\n\n    req.on('data', function (data) {\n      chunks.push(data);\n      length += data.length;\n\n      // More than 10mb, kill the connection!\n      if (length > 1e8) {\n        req.connection.destroy();\n      }\n    });\n\n    req.on('end', function() {\n      // let post = JSON.parse(chunks.join(''));\n\n      if (req.url == '/user') {\n        res.writeHead(200, { 'Content-Type': 'application/json' });\n        res.end(JSON.stringify({ message: 'User saved' }));\n      } else if (req.url == '/image') {\n        res.writeHead(200, { 'Content-Type': 'application/json' });\n        res.end(JSON.stringify({ message: \"Image saved\", imageSize: length }));\n      } else if (req.url == '/upload') {\n        res.writeHead(200, { 'Content-Type': 'application/json' });\n        res.end(JSON.stringify({ message: \"Upload complete\", size: length }));\n      } else {\n        res.writeHead(404);\n        res.end(\"Not found\");\n      }\n    });\n\n\n  } else {\n    file.serve(req, res);\n  }\n\n}\n\n\n// ------ запустить сервер -------\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n} else {\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/09-resume-upload/article.md",
    "content": "# Upload pouvant être repris\n\nAvec la méthode `fetch`, il est assez facile de upload un fichier.\n\nComment reprendre l'upload après une perte de connexion ? Il n'y a pas d'option intégrée pour cela, mais nous avons les pièces pour l'implémenter.\n\nLes uploads pouvant être repris devraient être accompagnés d'une indication de progression, puisque nous pouvons nous attendre à de gros fichiers (au cas où on devrait reprendre). Donc, comme `fetch` ne permet pas de suivre la progression du téléchargement, nous utiliserons [XMLHttpRequest](info:xmlhttprequest).\n\n## Événement de progression pas si utile\n\nPour reprendre l'upload, nous devons savoir combien a été uploadé jusqu'à ce que la connexion soit perdue.\n\nIl y a `xhr.upload.onprogress` pour suivre la progression de l'upload.\n\nMalheureusement, cela ne nous aidera pas à reprendre l'upload ici, car cela ne se déclenche que lorsque les données sont *envoyées*, mais est-ce que le serveur l'a reçu ? Le navigateur ne sait pas.\n\nPeut-être que cela a été mis en mémoire tampon par un proxy de réseau local, ou peut-être que le processus du serveur distant vient de mourir et n'a pas pu les traiter, ou cela a juste été perdu au milieu et n'a pas atteint le destinataire.\n\nC'est pourquoi cet événement n'est utile que pour afficher une belle barre de progression.\n\nPour reprendre l'upload, nous devons connaître *exactement* le nombre d'octets reçus par le serveur. Et seul le serveur peut le dire, nous ferons donc une demande supplémentaire.\n\n## Algorithme\n\n1. Créer d'abord un identifiant de fichier pour identifier de manière unique le fichier que nous allons uploader :\n    ```js\n    let fileId = file.name + '-' + file.size + '-' + file.lastModified;\n    ```\n    Cela est nécessaire pour reprendre l'upload, pour indiquer au serveur ce que nous reprenons.\n\n    Si le nom ou la taille ou la dernière date de modification change, alors il y aura un autre `fileId`.\n\n2. Envoyer une demande au serveur, lui demandant combien d'octets il possède déjà, comme ceci :\n    ```js\n    let response = await fetch('status', {\n      headers: {\n        'X-File-Id': fileId\n      }\n    });\n\n    // Le serveur a autant d'octets\n    let startByte = +await response.text();\n    ```\n\n    Cela suppose que le serveur effectue le suivi des uploads de fichiers par l'en-tête `X-File-Id`. Doit être implémenté côté serveur.\n\n    Si le fichier n'existe pas encore sur le serveur, la réponse du serveur doit être `0`.\n\n3. Ensuite, nous pouvons utiliser un `Blob` par la méhtode `slice` pour envoyer le fichier depuis `startByte` :\n    ```js\n    xhr.open(\"POST\", \"upload\");\n\n    // Identifiant du fichier, afin que le serveur sache quel fichier nous uploadons\n    xhr.setRequestHeader('X-File-Id', fileId);\n\n    // L'octet à partir duquel nous reprenons, donc le serveur sait que nous reprenons\n    xhr.setRequestHeader('X-Start-Byte', startByte);\n\n    xhr.upload.onprogress = (e) => {\n      console.log(`Uploaded ${startByte + e.loaded} of ${startByte + e.total}`);\n    };\n\n    // le fichier peut provenir de input.files[0] ou d'une autre source\n    xhr.send(file.slice(startByte));\n    ```\n\n    Ici, nous envoyons au serveur à la fois l'ID du fichier en tant que `X-File-Id`, afin qu'il sache quel fichier nous uploadons, et l'octet de départ en tant que `X-Start-Byte`, afin qu'il sache que nous ne l'uploadons pas de zéro, mais en reprenant.\n\n    Le serveur doit vérifier ses enregistrements et s'il y a eu un upload de ce fichier et que la taille actuellement téléchargée est exactement `X-Start-Byte`, alors il y ajoute les données.\n\n\nVoici la démo avec le code client et serveur, écrite sur Node.js.\n\nCela ne fonctionne que partiellement sur ce site, car Node.js est derrière un autre serveur nommé Nginx, qui met en mémoire tampon les uploads, en les transmettant à Node.js que lorsqu'il est complètement terminé.\n\nMais vous pouvez le télécharger et l'exécuter localement pour la démonstration complète :\n\n[codetabs src=\"upload-resume\" height=200]\n\nComme nous pouvons le voir, les méthodes de mise en réseau modernes sont proches des gestionnaires de fichiers dans leurs capacités - contrôle des en-têtes, indicateur de progression, envoi de parties de fichier, etc...\n\nNous pouvons implémenter un upload pouvant être repris et bien plus encore.\n"
  },
  {
    "path": "5-network/09-resume-upload/upload-resume.view/index.html",
    "content": "<!DOCTYPE HTML>\n\n<script src=\"uploader.js\"></script>\n\n<form name=\"upload\" method=\"POST\" enctype=\"multipart/form-data\" action=\"/upload\">\n  <input type=\"file\" name=\"myfile\">\n  <input type=\"submit\" name=\"submit\" value=\"Upload (Resumes automatically)\">\n</form>\n\n<button onclick=\"uploader.stop()\">Stop upload</button>\n\n\n<div id=\"log\">Progress indication</div>\n\n<script>\n  function log(html) {\n    document.getElementById('log').innerHTML = html;\n    console.log(html);\n  }\n\n  function onProgress(loaded, total) {\n    log(\"progress \" + loaded + ' / ' + total);\n  }\n\n  let uploader;\n\n  document.forms.upload.onsubmit = async function(e) {\n    e.preventDefault();\n\n    let file = this.elements.myfile.files[0];\n    if (!file) return;\n\n    uploader = new Uploader({file, onProgress});\n\n    try {\n      let uploaded = await uploader.upload();\n\n      if (uploaded) {\n        log('success');\n      } else {\n        log('stopped');\n      }\n\n    } catch(err) {\n      console.error(err);\n      log('error');\n    }\n  };\n\n</script>\n"
  },
  {
    "path": "5-network/09-resume-upload/upload-resume.view/server.js",
    "content": "let http = require('http');\nlet static = require('node-static');\nlet fileServer = new static.Server('.');\nlet path = require('path');\nlet fs = require('fs');\nlet debug = require('debug')('example:resume-upload');\n\nlet uploads = Object.create(null);\n\nfunction onUpload(req, res) {\n\n  let fileId = req.headers['x-file-id'];\n  let startByte = +req.headers['x-start-byte'];\n\n  if (!fileId) {\n    res.writeHead(400, \"No file id\");\n    res.end();\n  }\n\n  // we'll files \"nowhere\"\n  let filePath = '/dev/null';\n  // could use a real path instead, e.g.\n  // let filePath = path.join('/tmp', fileId);\n\n  debug(\"onUpload fileId: \", fileId);\n\n  // initialize a new upload\n  if (!uploads[fileId]) uploads[fileId] = {};\n  let upload = uploads[fileId];\n\n  debug(\"bytesReceived:\" + upload.bytesReceived + \" startByte:\" + startByte)\n\n  let fileStream;\n\n  // if startByte is 0 or not set, create a new file, otherwise check the size and append to existing one\n  if (!startByte) {\n    upload.bytesReceived = 0;\n    fileStream = fs.createWriteStream(filePath, {\n      flags: 'w'\n    });\n    debug(\"New file created: \" + filePath);\n  } else {\n    // we can check on-disk file size as well to be sure\n    if (upload.bytesReceived != startByte) {\n      res.writeHead(400, \"Wrong start byte\");\n      res.end(upload.bytesReceived);\n      return;\n    }\n    // append to existing file\n    fileStream = fs.createWriteStream(filePath, {\n      flags: 'a'\n    });\n    debug(\"File reopened: \" + filePath);\n  }\n\n\n  req.on('data', function(data) {\n    debug(\"bytes received\", upload.bytesReceived);\n    upload.bytesReceived += data.length;\n  });\n\n  // send request body to file\n  req.pipe(fileStream);\n\n  // when the request is finished, and all its data is written\n  fileStream.on('close', function() {\n    if (upload.bytesReceived == req.headers['x-file-size']) {\n      debug(\"Upload finished\");\n      delete uploads[fileId];\n\n      // can do something else with the uploaded file here\n\n      res.end(\"Success \" + upload.bytesReceived);\n    } else {\n      // connection lost, we leave the unfinished file around\n      debug(\"File unfinished, stopped at \" + upload.bytesReceived);\n      res.end();\n    }\n  });\n\n  // in case of I/O error - finish the request\n  fileStream.on('error', function(err) {\n    debug(\"fileStream error\");\n    res.writeHead(500, \"File error\");\n    res.end();\n  });\n\n}\n\nfunction onStatus(req, res) {\n  let fileId = req.headers['x-file-id'];\n  let upload = uploads[fileId];\n  debug(\"onStatus fileId:\", fileId, \" upload:\", upload);\n  if (!upload) {\n    res.end(\"0\")\n  } else {\n    res.end(String(upload.bytesReceived));\n  }\n}\n\n\nfunction accept(req, res) {\n  if (req.url == '/status') {\n    onStatus(req, res);\n  } else if (req.url == '/upload' && req.method == 'POST') {\n    onUpload(req, res);\n  } else {\n    fileServer.serve(req, res);\n  }\n\n}\n\n\n\n\n// -----------------------------------\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n  console.log('Server listening at port 8080');\n} else {\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/09-resume-upload/upload-resume.view/uploader.js",
    "content": "class Uploader {\n\n  constructor({file, onProgress}) {\n    this.file = file;\n    this.onProgress = onProgress;\n\n    // create fileId that uniquely identifies the file\n    // we could also add user session identifier (if had one), to make it even more unique\n    this.fileId = file.name + '-' + file.size + '-' + file.lastModified;\n  }\n\n  async getUploadedBytes() {\n    let response = await fetch('status', {\n      headers: {\n        'X-File-Id': this.fileId\n      }\n    });\n\n    if (response.status != 200) {\n      throw new Error(\"Can't get uploaded bytes: \" + response.statusText);\n    }\n\n    let text = await response.text();\n\n    return +text;\n  }\n\n  async upload() {\n    this.startByte = await this.getUploadedBytes();\n\n    let xhr = this.xhr = new XMLHttpRequest();\n    xhr.open(\"POST\", \"upload\", true);\n\n    // send file id, so that the server knows which file to resume\n    xhr.setRequestHeader('X-File-Id', this.fileId);\n    // send the byte we're resuming from, so the server knows we're resuming\n    xhr.setRequestHeader('X-Start-Byte', this.startByte);\n\n    xhr.upload.onprogress = (e) => {\n      this.onProgress(this.startByte + e.loaded, this.startByte + e.total);\n    };\n\n    console.log(\"send the file, starting from\", this.startByte);\n    xhr.send(this.file.slice(this.startByte));\n\n    // return\n    //   true if upload was successful,\n    //   false if aborted\n    // throw in case of an error\n    return await new Promise((resolve, reject) => {\n\n      xhr.onload = xhr.onerror = () => {\n        console.log(\"upload end status:\" + xhr.status + \" text:\" + xhr.statusText);\n\n        if (xhr.status == 200) {\n          resolve(true);\n        } else {\n          reject(new Error(\"Upload failed: \" + xhr.statusText));\n        }\n      };\n\n      // onabort triggers only when xhr.abort() is called\n      xhr.onabort = () => resolve(false);\n\n    });\n\n  }\n\n  stop() {\n    if (this.xhr) {\n      this.xhr.abort();\n    }\n  }\n\n}\n"
  },
  {
    "path": "5-network/10-long-polling/article.md",
    "content": "# L'interrogation longue \n\nL'interrogation longue est le moyen le plus simple d'avoir une connexion persistante avec le serveur, qui n'utilise aucun protocole spécifique comme WebSocket ou Server Side Events.\n\nÉtant très facile à mettre en œuvre, elle est également assez bonne dans de nombreux cas.\n\n## Interrogation régulière\n\nLe moyen le plus simple d'obtenir de nouvelles informations du serveur est l'interrogation périodique. Autrement dit, des requêtes régulières au serveur : \"Bonjour, je suis là, avez-vous des informations pour moi ?\". Par exemple, une fois toutes les 10 secondes.\n\nEn réponse, le serveur se signale d'abord à lui-même que le client est en ligne, et deuxièmement - envoie un paquet de messages qu'il a reçu jusqu'à ce moment.\n\nCela fonctionne, mais il y a des inconvénients :\n1. Les messages sont transmis avec un délai allant jusqu'à 10 secondes (entre les requêtes).\n2. Même s'il n'y a pas de messages, le serveur est bombardé de requêtes toutes les 10 secondes, même si l'utilisateur est passé ailleurs ou est endormi. C'est une charge à gérer, en termes de performances.\n\nDonc, si nous parlons d'un très petit service, l'approche peut être viable, mais en général, elle doit être améliorée.\n\n## Interrogation longue\n\n\"L'interrogation longue\" est une bien meilleure façon d'interroger le serveur.\n\nElle est également très facile à mettre en œuvre et délivre des messages sans délai.\n\nLe flux :\n\n1. Une requête est envoyée au serveur.\n2. Le serveur ne ferme pas la connexion tant qu'il n'a pas de message à envoyer.\n3. Lorsqu'un message apparaît - le serveur répond à la requête avec lui.\n4. Le navigateur fait immédiatement une nouvelle requête.\n\nCette situation, dans laquelle le navigateur a envoyé une requête et maintient une connexion en attente avec le serveur, est standard pour cette méthode. Ce n'est que lorsqu'un message est délivré que la connexion est fermée et rétablie.\n\n![](long-polling.svg)\n\nSi la connexion est perdue, en raison, par exemple, d'une erreur de réseau, le navigateur envoie immédiatement une nouvelle demande.\n\nUne esquisse de la fonction `subscribe` côté client qui fait de longues requêtes :\n\n```js\nasync function subscribe() {\n  let response = await fetch(\"/subscribe\");\n\n  if (response.status == 502) {\n    // Le statut 502 est une erreur de dépassement de délai de connexion,\n    // peut se produire lorsque la connexion est en attente depuis trop longtemps,\n    // et le serveur distant ou un proxy l'a fermé\n    // reconnectons-nous\n    await subscribe();\n  } else if (response.status != 200) {\n    // Une erreur - affichons-la\n    showMessage(response.statusText);\n    // Reconnexion en une seconde\n    await new Promise(resolve => setTimeout(resolve, 1000));\n    await subscribe();\n  } else {\n    // Obtenons et affichons le message\n    let message = await response.text();\n    showMessage(message);\n    // Appelons à nouveau subscribe() pour recevoir le message suivant\n    await subscribe();\n  }\n}\n\nsubscribe();\n```\n\nComme vous pouvez le voir, la fonction `subscribe` effectue une extraction, puis attend la réponse, la gère et se rappelle.\n\n```warn header=\"Le serveur devrait être ok avec de nombreuses connexions en attente\"\nL'architecture du serveur doit pouvoir fonctionner avec de nombreuses connexions en attente.\n\nCertaines architectures de serveur exécutent un processus par connexion ; résultant en autant de processus que de connexions, alors que chaque processus consomme pas mal de mémoire. Donc, trop de connexions consommeront tout.\n\nC'est souvent le cas pour les backends écrits dans des langages comme PHP et Ruby.\n\nLes serveurs écrits avec Node.js n'ont généralement pas ce genres de problèmes.\n\nCela dit, ce n'est pas un problème de langage de programmation. La plupart des langages modernes, y compris PHP et Ruby, permettent d'implémenter un backend approprié. Assurez-vous simplement que l'architecture de votre serveur fonctionne correctement avec de nombreuses connexions simultanées.\n```\n\n## Démo: un tchat\n\nVoici un tchat de démonstration, vous pouvez également le télécharger et l'exécuter localement (si vous connaissez Node.js et pouvez installer des modules) :\n\n[codetabs src=\"longpoll\" height=500]\n\nLe code du navigateur est dans `browser.js`.\n\n## Zone d'utilisation\n\nL'interrogation longue fonctionne très bien dans les situations où les messages sont rares.\n\nSi les messages arrivent très souvent, alors le tableau des messages de demande de réception, schématisé ci-dessus, ressemble à une scie.\n\nChaque message est une requête distincte, fournie avec des en-têtes, une surcharge d'authentification, etc.\n\nDonc, dans ce cas, une autre méthode est préférée, comme [Websocket](info:websocket) ou [Événements envoyés par le serveur](info:server-sent-events).\n"
  },
  {
    "path": "5-network/10-long-polling/longpoll.view/browser.js",
    "content": "// Sending messages, a simple POST\nfunction PublishForm(form, url) {\n\n  function sendMessage(message) {\n    fetch(url, {\n      method: 'POST',\n      body: message\n    });\n  }\n\n  form.onsubmit = function() {\n    let message = form.message.value;\n    if (message) {\n      form.message.value = '';\n      sendMessage(message);\n    }\n    return false;\n  };\n}\n\n// Receiving messages with long polling\nfunction SubscribePane(elem, url) {\n\n  function showMessage(message) {\n    let messageElem = document.createElement('div');\n    messageElem.append(message);\n    elem.append(messageElem);\n  }\n\n  async function subscribe() {\n    let response = await fetch(url);\n\n    if (response.status == 502) {\n      // Connection timeout\n      // happens when the connection was pending for too long\n      // let's reconnect\n      await subscribe();\n    } else if (response.status != 200) {\n      // Show Error\n      showMessage(response.statusText);\n      // Reconnect in one second\n      await new Promise(resolve => setTimeout(resolve, 1000));\n      await subscribe();\n    } else {\n      // Got message\n      let message = await response.text();\n      showMessage(message);\n      await subscribe();\n    }\n  }\n\n  subscribe();\n\n}\n"
  },
  {
    "path": "5-network/10-long-polling/longpoll.view/index.html",
    "content": "<!DOCTYPE html>\n<script src=\"browser.js\"></script>\n\nAll visitors of this page will see messages of each other.\n\n<form name=\"publish\">\n  <input type=\"text\" name=\"message\" />\n  <input type=\"submit\" value=\"Send\" />\n</form>\n\n<div id=\"subscribe\">\n</div>\n\n<script>\n  new PublishForm(document.forms.publish, 'publish');\n  // random url parameter to avoid any caching issues\n  new SubscribePane(document.getElementById('subscribe'), 'subscribe?random=' + Math.random());\n</script>\n"
  },
  {
    "path": "5-network/10-long-polling/longpoll.view/server.js",
    "content": "let http = require('http');\nlet url = require('url');\nlet querystring = require('querystring');\nlet static = require('node-static');\n\nlet fileServer = new static.Server('.');\n\nlet subscribers = Object.create(null);\n\nfunction onSubscribe(req, res) {\n  let id = Math.random();\n\n  res.setHeader('Content-Type', 'text/plain;charset=utf-8');\n  res.setHeader(\"Cache-Control\", \"no-cache, must-revalidate\");\n\n  subscribers[id] = res;\n\n  req.on('close', function() {\n    delete subscribers[id];\n  });\n\n}\n\nfunction publish(message) {\n\n  for (let id in subscribers) {\n    let res = subscribers[id];\n    res.end(message);\n  }\n\n  subscribers = Object.create(null);\n}\n\nfunction accept(req, res) {\n  let urlParsed = url.parse(req.url, true);\n\n  // new client wants messages\n  if (urlParsed.pathname == '/subscribe') {\n    onSubscribe(req, res);\n    return;\n  }\n\n  // sending a message\n  if (urlParsed.pathname == '/publish' && req.method == 'POST') {\n    // accept POST\n    req.setEncoding('utf8');\n    let message = '';\n    req.on('data', function(chunk) {\n      message += chunk;\n    }).on('end', function() {\n      publish(message); // publish it to everyone\n      res.end(\"ok\");\n    });\n\n    return;\n  }\n\n  // the rest is static\n  fileServer.serve(req, res);\n\n}\n\nfunction close() {\n  for (let id in subscribers) {\n    let res = subscribers[id];\n    res.end();\n  }\n}\n\n// -----------------------------------\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n  console.log('Server running on port 8080');\n} else {\n  exports.accept = accept;\n\n  if (process.send) { \n     process.on('message', (msg) => {\n       if (msg === 'shutdown') {\n         close();\n       }\n     });\n  }\n\n  process.on('SIGINT', close);\n}\n"
  },
  {
    "path": "5-network/11-websocket/article.md",
    "content": "# WebSocket\n\nLe protocole `WebSocket`, décrit dans la spécification [RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455) fournit un moyen d'échanger des données entre le navigateur et le serveur via une connexion persistante. Les données peuvent être transmises dans les deux sens sous forme de \"paquets\", sans interrompre la connexion et de requêtes HTTP supplémentaires.\n\nWebSocket est particulièrement adapté aux services qui nécessitent un échange de données continu, par exemple jeux en ligne, systèmes de trading en temps réel, etc.\n\n## Un exemple simple\n\nPour ouvrir une connexion websocket, nous devons créer `new WebSocket` en utilisant le protocole spécial `ws` dans l'url :\n\n```js\nlet socket = new WebSocket(\"*!*ws*/!*://javascript.info\");\n```\n\nIl existe également un protocole chiffré `wss://`. C'est comme HTTPS mais pour les websockets.\n\n```smart header=\"Toujours préférer `wss://`\"\nLe protocole `wss://` est non seulement chiffré, mais également plus fiable.\n\nC'est parce que les données `ws://` ne sont pas chiffrées, par conséquent visibles pour tout intermédiaire. Les anciens serveurs proxy ne connaissent pas WebSocket, ils peuvent voir des en-têtes \"étranges\" et abandonner la connexion.\n\nD'un autre côté, `wss://` est WebSocket sur TLS, (comme HTTPS est HTTP sur TLS), la couche de sécurité de transport chiffre les données à l'expéditeur et déchiffre au récepteur. Les paquets de données sont donc transmis chiffrés via des proxys. Ils ne peuvent pas voir ce qu'il y a à l'intérieur et les laisser passer.\n```\n\nUne fois le socket créé, nous devons écouter les événements qu'il contient. Il y a au total 4 événements :\n- **`open`** -- Connection établie,\n- **`message`** -- Donnée reçue,\n- **`error`** -- erreur websocket,\n- **`close`** -- connexion fermée.\n\n… Et si nous souhaitons envoyer quelque chose, alors `socket.send(data)` fera cela.\n\nVoici un exemple :\n\n```js run\nlet socket = new WebSocket(\"wss://javascript.info/article/websocket/demo/hello\");\n\nsocket.onopen = function(e) {\n  alert(\"[open] Connection established\");\n  alert(\"Sending to server\");\n  socket.send(\"My name is John\");\n};\n\nsocket.onmessage = function(event) {\n  alert(`[message] Data received from server: ${event.data}`);\n};\n\nsocket.onclose = function(event) {\n  if (event.wasClean) {\n    alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);\n  } else {\n    // par exemple : processus serveur arrêté ou réseau en panne\n    // event.code est généralement 1006 dans ce cas\n    alert('[close] Connection died');\n  }\n};\n\nsocket.onerror = function(error) {\n  alert(`[error]`);\n};\n```\n\nÀ des fins de démonstration, il y a un petit serveur [server.js](demo/server.js) écrit en Node.js, pour l'exemple ci-dessus, en cours d'exécution. Il répond par \"Bonjour du serveur, John\", puis attend 5 secondes et ferme la connexion.\n\nVous verrez donc les événements `open` -> `message` -> `close`.\n\nOn peut déjà parler de WebSocket. C'est assez simple, non ?\n\nApprofondissont maintenant.\n\n## Ouvrir un websocket\n\nLorsque `new WebSocket(url)` est créé, il se connecte immédiatement.\n\nLors de la connexion, le navigateur (à l'aide des en-têtes) demande au serveur: \"Prenez-vous en charge Websocket ?\" Et si le serveur répond \"oui\", alors la conversation se poursuit dans le protocole WebSocket, qui n'est pas du tout HTTP.\n\n![](websocket-handshake.svg)\n\nVoici un exemple d'en-têtes de navigateur pour une demande faite par `new WebSocket(\"wss://javascript.info/chat\")`.\n\n```\nGET /chat\nHost: javascript.info\nOrigin: https://javascript.info\nConnection: Upgrade\nUpgrade: websocket\nSec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q==\nSec-WebSocket-Version: 13\n```\n\n- `Origin` -- l'origine de la page client, par exemple `https://javascript.info`. Les objets WebSocket sont cross-origin par nature. Il n'y a pas d'en-têtes spéciaux ou d'autres limitations. Les anciens serveurs ne sont pas en mesure de gérer WebSocket de toute façon, il n'y a donc pas de problème de compatibilité. Mais l'en-tête `Origin` est important, car il permet au serveur de décider de discuter ou non en WebSocket avec ce site Web.\n- `Connection: Upgrade` -- indique que le client souhaite modifier le protocole.\n- `Upgrade: websocket` -- le protocole demandé est \"websocket\".\n- `Sec-WebSocket-Key` -- une clé générée aléatoirement par le navigateur pour la sécurité.\n- `Sec-WebSocket-Version` -- Version du protocole WebSocket, 13 est la version actuelle.\n\n```smart header=\"Le handshake WebSocket ne peut pas être émulé\"\nNous ne pouvons pas utiliser `XMLHttpRequest` ou `fetch` pour effectuer ce type de requête HTTP, car JavaScript n'est pas autorisé à définir ces en-têtes.\n```\n\nSi le serveur accepte de passer à WebSocket, il doit envoyer le code de réponse 101 :\n\n```\n101 Switching Protocols\nUpgrade: websocket\nConnection: Upgrade\nSec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g=\n```\n\nIci `Sec-WebSocket-Accept` est `Sec-WebSocket-Key`, recodé à l'aide d'un algorithme spécial. En le voyant, le navigateur comprend que le serveur prend réellement en charge le protocole WebSocket.\n\nEnsuite, les données sont transférées en utilisant le protocole WebSocket, nous verrons bientôt sa structure (\"frames\"). Et ce n'est pas du tout HTTP.\n\n### Extensions et sous-protocoles\n\nIl peut y avoir des en-têtes supplémentaires `Sec-WebSocket-Extensions` et `Sec-WebSocket-Protocol` qui décrivent les extensions et les sous-protocoles.\n\nPar exemple :\n\n- `Sec-WebSocket-Extensions: deflate-frame` signifie que le navigateur prend en charge la compression des données. Une extension est liée au transfert des données, c'est une fonctionnalité qui étend le protocole WebSocket. L'en-tête `Sec-WebSocket-Extensions` est envoyé automatiquement par le navigateur, avec la liste de toutes les extensions qu'il prend en charge.\n\n- `Sec-WebSocket-Protocol: soap, wamp` signifie que nous aimerions transférer non seulement toutes les données, mais les données [SOAP](http://en.wikipedia.org/wiki/SOAP) ou les protocoles WAMP (\"The WebSocket Application Messaging Protocol\"). Les sous-protocoles WebSocket sont enregistrés dans le [catalogue IANA](http://www.iana.org/assignments/websocket/websocket.xml). Donc, cet en-tête décrit les formats de données que nous allons utiliser.\n\n    Cet en-tête facultatif est défini à l'aide du deuxième paramètre de `new WebSocket`. C'est le tableau des sous-protocoles, par exemple si nous souhaitons utiliser SOAP ou WAMP :\n\n    ```js\n    let socket = new WebSocket(\"wss://javascript.info/chat\", [\"soap\", \"wamp\"]);\n    ```\n\nLe serveur doit répondre avec une liste de protocoles et d'extensions qu'il accepte d'utiliser.\n\nPar exemple, la requête :\n\n```\nGET /chat\nHost: javascript.info\nUpgrade: websocket\nConnection: Upgrade\nOrigin: https://javascript.info\nSec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q==\nSec-WebSocket-Version: 13\n*!*\nSec-WebSocket-Extensions: deflate-frame\nSec-WebSocket-Protocol: soap, wamp\n*/!*\n```\n\nRéponse :\n\n```\n101 Switching Protocols\nUpgrade: websocket\nConnection: Upgrade\nSec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g=\n*!*\nSec-WebSocket-Extensions: deflate-frame\nSec-WebSocket-Protocol: soap\n*/!*\n```\n\nIci, le serveur répond qu'il prend en charge l'extension \"deflate-frame\", et uniquement SOAP des sous-protocoles demandés.\n\n## Transfert de données\n\nLa communication WebSocket se compose de \"frames\" -- des fragments de données, qui peuvent être envoyés de chaque côté et peuvent être de plusieurs types :\n\n- \"text frames\" -- contiennent des données textuelles que les parties s'envoient mutuellement.\n- \"binary data frames\" -- contiennent des données binaires que les parties s'envoient mutuellement.\n- \"ping/pong frames\" sont utilisés pour vérifier la connexion, envoyée par le serveur, le navigateur y répond automatiquement.\n- il y a aussi la \"connection close frame\" et quelques autres frames de service.\n\nDans le navigateur, nous travaillons directement uniquement avec du texte ou des frames binaires.\n\n**La méthode WebSocket `.send()` peut envoyer du texte ou des données binaires.**\n\nUn appel `socket.send(body)` autorise `body` dans une chaîne de caractères ou un format binaire, y compris `Blob`, `ArrayBuffer`, etc. Aucun paramètre requis : il suffit de l'envoyer dans n'importe quel format.\n\n**Lorsque nous recevons les données, le texte vient toujours sous forme de chaîne de caractères. Et pour les données binaires, nous pouvons choisir entre les formats `Blob` et `ArrayBuffer`.**\n\nCela est défini par la propriété `socket.binaryType`, c'est `\"blob\"` par défaut, donc les données binaires viennent en tant qu'objets `Blob`.\n\n[Blob](info:blob) est un objet binaire de haut niveau, il s'intègre directement avec `<a>`, `<img>` et d'autres balises, c'est donc un défaut sain. Mais pour le traitement binaire, pour accéder aux octets de données individuels, nous pouvons le changer en `\"arraybuffer\"` :\n\n```js\nsocket.binaryType = \"arraybuffer\";\nsocket.onmessage = (event) => {\n  // event.data est soit une chaîne de caractères (si du texte), soit un arraybuffer (si binaire)\n};\n```\n\n## Limitation de débit\n\nImaginez, notre application génère beaucoup de données à envoyer. Mais l'utilisateur a une connexion réseau lente, peut-être sur un Internet mobile, en dehors d'une ville.\n\nNous pouvons appeler `socket.send (data)` encore et encore. Mais les données seront mises en mémoire tampon (stockées) en mémoire et envoyées uniquement aussi rapidement que la vitesse du réseau le permet.\n\nLa propriété `socket.bufferedAmount` stocke le nombre d'octets qui sont mis en mémoire tampon à ce moment, en attente d'être envoyés sur le réseau.\n\nNous pouvons l'examiner pour voir si le socket est réellement disponible pour la transmission.\n\n```js\n// toutes les 100 ms examine le socket et envoi plus de données\n// uniquement si toutes les données existantes ont été envoyées\nsetInterval(() => {\n  if (socket.bufferedAmount == 0) {\n    socket.send(moreData());\n  }\n}, 100);\n```\n\n## Connexion fermée\n\nNormalement, lorsqu'une partie souhaite fermer la connexion (le navigateur et le serveur ont les mêmes droits), ils envoient un \"connection close frame\" avec un code numérique et une raison textuelle.\n\nLa méthode pour cela est :\n\n```js\nsocket.close([code], [reason]);\n```\n\n- `code` est un code de fermeture WebSocket spécial (facultatif)\n- `reason` est une chaîne de caractères qui décrit la raison de la fermeture (facultatif)\n\nEnsuite, l'autre partie du gestionnaire d'événements `close` obtient le code et la raison, par exemple :\n\n```js\n// partie fermante :\nsocket.close(1000, \"Work complete\");\n\n// l'autre partie\nsocket.onclose = event => {\n  // event.code === 1000\n  // event.reason === \"Work complete\"\n  // event.wasClean === true (clean close)\n};\n```\n\nValeurs de code les plus courantes :\n\n- `1000` -- la closure normale par défaut (utilisée si aucun `code` n'est fourni),\n- `1006` -- aucun moyen pour utiliser ce code manuellement, indique que la connexion a été perdue (pas de frame de fermeture).\n\nIl existe d'autres codes comme :\n\n- `1001` -- la partie s'en va, par exemple le serveur s'arrête ou un navigateur quitte la page,\n- `1009` -- le message est trop gros pour être traité,\n- `1011` -- erreur inattendue sur le serveur,\n- … etc.\n\nLa liste complète se trouve dans le [RFC6455, §7.4.1](https://tools.ietf.org/html/rfc6455#section-7.4.1).\n\nLes codes WebSocket sont un peu comme les codes HTTP, mais différents. En particulier, tous les codes inférieurs à `1000` sont réservés, il y aura une erreur si nous essayons de définir un tel code.\n\n```js\n// en cas de rupture de connexion\nsocket.onclose = event => {\n  // event.code === 1006\n  // event.reason === \"\"\n  // event.wasClean === false (no closing frame)\n};\n```\n\n## État de connexion\n\nPour obtenir l'état de la connexion, il existe en outre la propriété `socket.readyState` avec des valeurs :\n\n- **`0`** -- \"CONNECTING\": la connexion n'a pas encore été établie,\n- **`1`** -- \"OPEN\": communicante,\n- **`2`** -- \"CLOSING\": la connexion se ferme,\n- **`3`** -- \"CLOSED\": la connexion est fermée.\n\n## Exemple de tchat\n\nPassons en revue un exemple de discussion à l'aide de l'API WebSocket du navigateur et du module Node.js WebSocket <https://github.com/websockets/ws>. Nous porterons notre attention principalement sur le côté client, mais le serveur est également simple.\n\nHTML: nous avons besoin d'un `<form>` pour envoyer des messages et d'un `<div>` pour les messages entrants :\n\n```html\n<!-- message form -->\n<form name=\"publish\">\n  <input type=\"text\" name=\"message\">\n  <input type=\"submit\" value=\"Send\">\n</form>\n\n<!-- div with messages -->\n<div id=\"messages\"></div>\n```\n\nDe JavaScript, nous voulons trois choses :\n\n1. Ouvrir la connexion.\n2. Lors de la soumission du formulaire - `socket.send(message)` pour le message.\n3. Sur le message entrant - l'ajouter à `div#messages`.\n\nVoici le code :\n\n```js\nlet socket = new WebSocket(\"wss://javascript.info/article/websocket/chat/ws\");\n\n// envoyer un message depuis le formulaire\ndocument.forms.publish.onsubmit = function() {\n  let outgoingMessage = this.message.value;\n\n  socket.send(outgoingMessage);\n  return false;\n};\n\n// message reçu - affiche le message dans div#messages\nsocket.onmessage = function(event) {\n  let message = event.data;\n\n  let messageElem = document.createElement('div');\n  messageElem.textContent = message;\n  document.getElementById('messages').prepend(messageElem);\n}\n```\n\nLe code côté serveur dépasse un peu notre portée. Ici, nous utiliserons Node.js, mais ce n'est pas obligatoire. D'autres plateformes ont également leurs moyens de travailler avec WebSocket.\n\nL'algorithme côté serveur sera :\n\n1. Créer `clients = new Set()` -- un ensemble de sockets.\n2. Pour chaque websocket accepté, l'ajouter à l'ensemble `clients.add (socket)` et configurer l'écouteur d'événements `message` pour obtenir ses messages.\n3. Lorsqu'un message est reçu : parcourir les clients et l'envoyer à tout le monde.\n4. Lorsqu'une connexion est fermée : `clients.delete(socket)`.\n\n```js\nconst ws = new require('ws');\nconst wss = new ws.Server({noServer: true});\n\nconst clients = new Set();\n\nhttp.createServer((req, res) => {\n  // ici, nous ne gérons que les connexions websocket\n  // dans un projet réel, nous aurions un autre code ici pour gérer les demandes non Websocket\n  wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect);\n});\n\nfunction onSocketConnect(ws) {\n  clients.add(ws);\n\n  ws.on('message', function(message) {\n    message = message.slice(0, 50); // la longueur maximale des messages sera de 50\n\n    for(let client of clients) {\n      client.send(message);\n    }\n  });\n\n  ws.on('close', function() {\n    clients.delete(ws);\n  });\n}\n```\n\nVoici l'exemple fonctionnel :\n\n[iframe src=\"chat\" height=\"100\" zip]\n\nVous pouvez également le télécharger (bouton en haut à droite dans l'iframe) et l'exécuter localement. N'oubliez pas d'installer [Node.js](https://nodejs.org/en/) et `npm install ws` avant l'exécution.\n\n## Résumé\n\nWebSocket est un moyen moderne d'avoir des connexions navigateur-serveur persistantes.\n\n- WebSockets n'ont pas de limites cross-origin.\n- Ils sont bien pris en charge dans les navigateurs.\n- Peut envoyer/recevoir des chaînes de caractères et des données binaires.\n\nL'API est simple.\n\nLes méthodes :\n\n- `socket.send(data)`,\n- `socket.close([code], [reason])`.\n\nLes événements :\n\n- `open`,\n- `message`,\n- `error`,\n- `close`.\n\nWebSocket ne comprend pas à lui seul la reconnexion, l'authentification et de nombreux autres mécanismes de haut niveau. Il existe donc des bibliothèques client / serveur pour cela, et il est également possible d'implémenter ces capacités manuellement.\n\nParfois, pour intégrer WebSocket dans un projet existant, les gens exécutent le serveur WebSocket en parallèle avec le serveur HTTP principal et partagent une seule base de données. Les requêtes à WebSocket utilisent `wss://ws.site.com`, un sous-domaine qui mène au serveur WebSocket, tandis que `https://site.com` va au serveur HTTP principal.\n\nCertes, d'autres modes d'intégration sont également possibles.\n"
  },
  {
    "path": "5-network/11-websocket/chat.view/index.html",
    "content": "<!doctype html>\n<form name=\"publish\">\n  <input type=\"text\" name=\"message\" maxlength=\"50\"/>\n  <input type=\"submit\" value=\"Send\"/>\n</form>\n\n<div id=\"messages\"></div>\n\n<script>\nlet url = location.host == 'localhost' ?\n  'ws://localhost:8080/ws' : location.host == 'javascript.local' ?\n  `ws://javascript.local/article/websocket/chat/ws` : // dev integration with local site\n  `wss://javascript.info/article/websocket/chat/ws`; // prod integration with javascript.info\n\nlet socket = new WebSocket(url);\n\n// send message from the form\ndocument.forms.publish.onsubmit = function() {\n  let outgoingMessage = this.message.value;\n\n  socket.send(outgoingMessage);\n  return false;\n};\n\n// handle incoming messages\nsocket.onmessage = function(event) {\n  let incomingMessage = event.data;\n  showMessage(incomingMessage);\n};\n\nsocket.onclose = event => console.log(`Closed ${event.code}`);\n\n// show message in div#messages\nfunction showMessage(message) {\n  let messageElem = document.createElement('div');\n  messageElem.textContent = message;\n  document.getElementById('messages').prepend(messageElem);\n}\n</script>\n"
  },
  {
    "path": "5-network/11-websocket/chat.view/server.js",
    "content": "/**\nBefore running:\n> npm install ws\nThen:\n> node server.js\n> open http://localhost:8080 in the browser\n*/\n\nconst http = require('http');\nconst fs = require('fs');\nconst ws = new require('ws');\n\nconst wss = new ws.Server({noServer: true});\n\nconst clients = new Set();\n\nfunction accept(req, res) {\n\n  if (req.url == '/ws' && req.headers.upgrade &&\n      req.headers.upgrade.toLowerCase() == 'websocket' &&\n      // can be Connection: keep-alive, Upgrade\n      req.headers.connection.match(/\\bupgrade\\b/i)) {\n    wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect);\n  } else if (req.url == '/') { // index.html\n    fs.createReadStream('./index.html').pipe(res);\n  } else { // page not found\n    res.writeHead(404);\n    res.end();\n  }\n}\n\nfunction onSocketConnect(ws) {\n  clients.add(ws);\n  log(`new connection`);\n\n  ws.on('message', function(message) {\n    log(`message received: ${message}`);\n\n    message = message.slice(0, 50); // max message length will be 50\n\n    for(let client of clients) {\n      client.send(message);\n    }\n  });\n\n  ws.on('close', function() {\n    log(`connection closed`);\n    clients.delete(ws);\n  });\n}\n\nlet log;\nif (!module.parent) {\n  log = console.log;\n  http.createServer(accept).listen(8080);\n} else {\n  // to embed into javascript.info\n  log = function() {};\n  // log = console.log;\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/11-websocket/demo.view/server.js",
    "content": "const http = require('http');\nconst ws = require('ws');\n\nconst wss = new ws.Server({noServer: true});\n\nfunction accept(req, res) {\n  // all incoming requests must be websockets\n  if (!req.headers.upgrade || req.headers.upgrade.toLowerCase() != 'websocket') {\n    res.end();\n    return;\n  }\n\n  // can be Connection: keep-alive, Upgrade\n  if (!req.headers.connection.match(/\\bupgrade\\b/i)) {\n    res.end();\n    return;\n  }\n\n  wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onConnect);\n}\n\nfunction onConnect(ws) {\n  ws.on('message', function (message) {\n    message = message.toString();\n    let name = message.match(/([\\p{Alpha}\\p{M}\\p{Nd}\\p{Pc}\\p{Join_C}]+)$/gu) || \"Guest\";\n    ws.send(`Hello from server, ${name}!`);\n\n    setTimeout(() => ws.close(1000, \"Bye!\"), 5000);\n  });\n}\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n} else {\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/12-server-sent-events/article.md",
    "content": "# Server Sent Events\n\nLa spécification [Server-Sent Events](https://html.spec.whatwg.org/multipage/comms.html#the-eventsource-interface) décrit une classe intégrée `EventSource`, qui maintient la connexion avec le serveur et permet de recevoir des événements de celui-ci.\n\nSimilaire à `WebSocket`, la connexion est persistante.\n\nMais il existe plusieurs différences importantes :\n\n| `WebSocket`                                                             | `EventSource`                                       |\n|-------------------------------------------------------------------------|-----------------------------------------------------|\n| Bi-directionnel : le client et le serveur peuvent échanger des messages | Unidirectionnel: seul le serveur envoie des données |\n| Données binaires et texte                                               | Texte uniquement                                    |\n| Protocol WebSocket                                              | HTTP régulier                                       |\n\n`EventSource` est un moyen de communication avec le serveur moins puissant que `WebSocket`.\n\nDans ce cas pourquoi devrait-on l'utiliser ?\n\nLa raison principale : c'est plus simple. Dans de nombreuses applications, la puissance de `WebSocket` est un peu trop importante.\n\nNous devons recevoir un flux de données du serveur: peut-être des messages de chat ou des prix du marché, ou autre chose. C'est à cela qu'`EventSource` est bon. Il prend également en charge la reconnexion automatique, quelque chose que nous devons implémenter manuellement avec `WebSocket`. De plus, c'est un vieux HTTP simple, pas un nouveau protocole.\n\n## Recevoir des messages\n\nPour commencer à recevoir des messages, il suffit de créer `new EventSource(url)`.\n\nLe navigateur se connectera à `url` et gardera la connexion ouverte, en attendant les événements.\n\nLe serveur doit répondre avec le statut 200 et l'en-tête `Content-Type: text/event-stream`, puis conserver la connexion et y écrire des messages au format spécial, comme ceci :\n\n```\ndata: Message 1\n\ndata: Message 2\n\ndata: Message 3\ndata: of two lines\n```\n\n- Un texte de message va après `data:`, l'espace après les deux points est facultatif.\n- Les messages sont délimités par des doubles sauts de ligne `\\n\\n`.\n- Pour envoyer un saut de ligne `\\n`, nous pouvons immédiatement envoyer une autre `data:` (3e message ci-dessus).\n\nEn pratique, les messages complexes sont généralement envoyés au format JSON. Les sauts de ligne sont codés en tant que `\\n`, donc les messages multilignes de `data:` ne sont pas nécessaires.\n\nPar exemple :\n\n```js\ndata: {\"user\":\"John\",\"message\":\"First line*!*\\n*/!* Second line\"}\n```\n\n… On peut donc supposer qu'une `data:` contient exactement un message.\n\nPour chacun de ces messages, l'événement `message` est généré :\n\n```js\nlet eventSource = new EventSource(\"/events/subscribe\");\n\neventSource.onmessage = function(event) {\n  console.log(\"New message\", event.data);\n  // se connectera 3 fois pour le flux de données ci-dessus\n};\n\n// or eventSource.addEventListener('message', ...)\n```\n\n### Requêtes Cross-origin \n\n`EventSource` prend en charge les requêtes cross-origin, comme `fetch` toute autre méthode de mise en réseau. Nous pouvons utiliser n'importe quelle URL :\n\n```js\nlet source = new EventSource(\"https://another-site.com/events\");\n```\n\nLe serveur distant obtiendra l'en-tête `Origin` et doit répondre avec `Access-Control-Allow-Origin` pour continuer.\n\nPour transmettre les informations d'identification, nous devons définir l'option supplémentaire `withCredentials`, comme ceci :\n\n```js\nlet source = new EventSource(\"https://another-site.com/events\", {\n  withCredentials: true\n});\n```\n\nVeuillez consulter le chapitre <info:fetch-crossorigin> pour plus de détails sur les en-têtes cross-origin.\n\n\n## Reconnexion\n\nLors de la création, `new EventSource` se connecte au serveur, et si la connexion est rompue -- se reconnecte.\n\nC'est très pratique, car nous n'avons pas à nous en soucier.\n\nIl y a un petit délai entre les reconnexions, quelques secondes par défaut.\n\nLe serveur peut définir le délai recommandé en utilisant `retry:` en réponse (en millisecondes) :\n\n```js\nretry: 15000\ndata: Hello, I set the reconnection delay to 15 seconds\n```\n\nLe `retry:` peut venir à la fois avec certaines données, ou en tant que message autonome.\n\nLe navigateur doit attendre autant de millisecondes avant de se reconnecter. Ou plus, par exemple si le navigateur sait (depuis le système d'exploitation) qu'il n'y a pas de connexion réseau pour le moment, il peut attendre que la connexion apparaisse, puis réessayer.\n\n- Si le serveur souhaite que le navigateur cesse de se reconnecter, il doit répondre avec l'état HTTP 204.\n- Si le navigateur souhaite fermer la connexion, il doit appeler `eventSource.close()`:\n\n```js\nlet eventSource = new EventSource(...);\n\neventSource.close();\n```\n\nDe plus, il n'y aura pas de reconnexion si la réponse a un `Content-Type` incorrect ou si son état HTTP diffère de 301, 307, 200 et 204. Dans de tels cas, l'événement `\"error\"` sera émis et le navigateur ne se reconnecte pas.\n\n```smart\nLorsqu'une connexion est finalement fermée, il n'y a aucun moyen de la \"rouvrir\". Si nous souhaitons nous reconnecter, créez simplement un nouveau `EventSource`.\n```\n\n## ID du message\n\nLorsqu'une connexion est interrompue en raison de problèmes de réseau, les deux parties ne peuvent pas savoir quels messages ont été reçus et lesquels ne l'ont pas été.\n\nPour reprendre correctement la connexion, chaque message doit avoir un champ `id`, comme ceci :\n\n```\ndata: Message 1\nid: 1\n\ndata: Message 2\nid: 2\n\ndata: Message 3\ndata: of two lines\nid: 3\n```\n\nLorsqu'un message avec `id:` est reçu, le navigateur :\n\n- Définit la propriété `eventSource.lastEventId` sur sa valeur.\n- Lors de la reconnexion, l'en-tête `Last-Event-ID` est envoyé avec cet `id`, afin que le serveur puisse renvoyer les messages suivants.\n\n```smart header=\"Mettre `id:` après `data:`\"\nVeuillez noter : l'`id` est ajouté sous le message `data` par le serveur, pour s'assurer que `lastEventId` est mis à jour après la réception du message.\n```\n\n## État de connexion : readyState\n\nL'objet `EventSource` a la propriété `readyState`, qui a l'une des trois valeurs :\n\n```js no-beautify\nEventSource.CONNECTING = 0; // connexion ou reconnexion\nEventSource.OPEN = 1;       // connecté\nEventSource.CLOSED = 2;     // connexion fermée\n```\n\nLorsqu'un objet est créé ou que la connexion est interrompue, il s'agit toujours de `EventSource.CONNECTING` (égal à `0`).\n\nNous pouvons interroger cette propriété pour connaître l'état de `EventSource`.\n\n## Types d'événements\n\nPar défaut, l'objet `EventSource` génère trois événements :\n\n- `message` -- un message reçu, disponible en tant que `event.data`.\n- `open` -- la connexion est ouverte.\n- `error` -- la connexion n'a pas pu être établie, par exemple le serveur a renvoyé le statut HTTP 500.\n\nLe serveur peut spécifier un autre type d'événement avec `event: ...` au début de l'événement.\n\nPar exemple :\n\n```\nevent: join\ndata: Bob\n\ndata: Hello\n\nevent: leave\ndata: Bob\n```\n\nPour gérer des événements personnalisés, nous devons utiliser `addEventListener`, pas `onmessage` :\n\n```js\neventSource.addEventListener('join', event => {\n  alert(`Joined ${event.data}`);\n});\n\neventSource.addEventListener('message', event => {\n  alert(`Said: ${event.data}`);\n});\n\neventSource.addEventListener('leave', event => {\n  alert(`Left ${event.data}`);\n});\n```\n\n## Exemple complet\n\nVoici le serveur qui envoie des messages avec `1`, `2`, `3`, puis `bye` et rompt la connexion.\n\nEnsuite, le navigateur se reconnecte automatiquement.\n\n[codetabs src=\"eventsource\"]\n\n## Résumé\n\nL'objet `EventSource` établit automatiquement une connexion persistante et permet au serveur d'envoyer des messages par-dessus.\n\nCela offre :\n- Reconnexion automatique, avec délai d'attente de `retry` réglable.\n- Identifiants des messages pour reprendre les événements, le dernier identifiant reçu est envoyé dans l'en-tête `Last-Event-ID` lors de la reconnexion.\n- L'état actuel est dans la propriété `readyState`.\n\nCela fait de `EventSource` une alternative viable à `WebSocket`, car il est plus bas niveau et manque de telles fonctionnalités intégrées (bien qu'elles puissent être implémentées).\n\nDans de nombreuses applications réelles, la puissance de `EventSource` est juste suffisante.\n\nPris en charge dans tous les navigateurs modernes (pas IE).\n\nLa syntaxe est :\n\n```js\nlet source = new EventSource(url, [credentials]);\n```\n\nLe deuxième argument n'a qu'une seule option possible: `{withCredentials: true}`, il permet d'envoyer des informations d'identification cross-origin.\n\nLa sécurité globale de cross-origin est la même que pour `fetch` et d'autres méthodes réseau.\n\n### Propriétés d'un objet `EventSource`\n\n`readyState`\n: L'état de connexion actuel : soit `EventSource.CONNECTING (=0)`, `EventSource.OPEN (=1)` ou `EventSource.CLOSED (=2)`.\n\n`lastEventId`\n: Le dernier `id` reçu. Lors de la reconnexion, le navigateur l'envoie dans l'en-tête `Last-Event-ID`.\n\n### Les méthodes\n\n`close()`\n: Ferme la connexion.\n\n### Les événements\n\n`message`\n: Message reçu, les données sont dans `event.data`.\n\n`open`\n: La connexion est établie.\n\n`error`\n: En cas d'erreur, y compris la perte de connexion (se reconnectera automatiquement) et les erreurs fatales. Nous pouvons vérifier `readyState` pour voir si la reconnexion est tentée.\n\nLe serveur peut définir un nom d'événement personnalisé dans `event:`. De tels événements doivent être gérés en utilisant `addEventListener`, pas `on<event>`.\n\n### Format de réponse du serveur\n\nLe serveur envoie des messages, délimités par `\\n\\n`.\n\nUn message peut contenir les champs suivants :\n\n- `data:` -- corps du message, une séquence de plusieurs `data` est interprétée comme un seul message, avec `\\n` entre les parties.\n- `id:` -- renouvelle `lastEventId`, envoyé dans `Last-Event-ID` lors de la reconnexion.\n- `retry:` -- recommande un délai de relance pour les reconnexions en ms. Il n'y a aucun moyen de le définir à partir de JavaScript.\n- `event:` -- nom de l'événement, doit précéder `data:`.\n\nUn message peut inclure un ou plusieurs champs dans n'importe quel ordre, mais `id:` va généralement en dernier.\n"
  },
  {
    "path": "5-network/12-server-sent-events/eventsource.view/index.html",
    "content": "<!DOCTYPE html>\n<script>\nlet eventSource;\n\nfunction start() { // when \"Start\" button pressed\n  if (!window.EventSource) {\n    // IE or an old browser\n    alert(\"The browser doesn't support EventSource.\");\n    return;\n  }\n\n  eventSource = new EventSource('digits');\n\n  eventSource.onopen = function(e) {\n    log(\"Event: open\");\n  };\n\n  eventSource.onerror = function(e) {\n    log(\"Event: error\");\n    if (this.readyState == EventSource.CONNECTING) {\n      log(`Reconnecting (readyState=${this.readyState})...`);\n    } else {\n      log(\"Error has occured.\");\n    }\n  };\n\n  eventSource.addEventListener('bye', function(e) {\n    log(\"Event: bye, data: \" + e.data);\n  });\n\n  eventSource.onmessage = function(e) {\n    log(\"Event: message, data: \" + e.data);\n  };\n}\n\nfunction stop() { // when \"Stop\" button pressed\n  eventSource.close();\n  log(\"eventSource.close()\");\n}\n\nfunction log(msg) {\n  logElem.innerHTML += msg + \"<br>\";\n  document.documentElement.scrollTop = 99999999;\n}\n</script>\n\n<button onclick=\"start()\">Start</button> Press the \"Start\" to begin.\n<div id=\"logElem\" style=\"margin: 6px 0\"></div>\n\n<button onclick=\"stop()\">Stop</button> \"Stop\" to finish.\n"
  },
  {
    "path": "5-network/12-server-sent-events/eventsource.view/server.js",
    "content": "let http = require('http');\nlet url = require('url');\nlet querystring = require('querystring');\nlet static = require('node-static');\nlet fileServer = new static.Server('.');\n\nfunction onDigits(req, res) {\n  res.writeHead(200, {\n    'Content-Type': 'text/event-stream; charset=utf-8',\n    'Cache-Control': 'no-cache'\n  });\n\n  let i = 0;\n\n  let timer = setInterval(write, 1000);\n  write();\n\n  function write() {\n    i++;\n\n    if (i == 4) {\n      res.write('event: bye\\ndata: bye-bye\\n\\n');\n      clearInterval(timer);\n      res.end();\n      return;\n    }\n\n    res.write('data: ' + i + '\\n\\n');\n\n  }\n}\n\nfunction accept(req, res) {\n\n  if (req.url == '/digits') {\n    onDigits(req, res);\n    return;\n  }\n\n  fileServer.serve(req, res);\n}\n\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n} else {\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/index.md",
    "content": "# Requêtes réseau\n"
  },
  {
    "path": "6-data-storage/01-cookie/article.md",
    "content": "# Cookies, document.cookie\n\nLes cookies sont des donnés stockées sous forme de petites chaîne de caractères directement dans le navigateur. Ils font parti du protocole HTTP, ils sont définis par la spécification [RFC 6265](https://tools.ietf.org/html/rfc6265).\n\nLes cookies sont en général définis par le serveur web en utilisant l'entête HTTP `Set-Cookie`. Alors, le navigateur les ajoutent automatiquement à (presque) toutes les requêtes provenant du même domaine en utilisant l'entête HTTP `Cookie`.\n\nL'un des cas d'utilisation les plus répandus est l'authentification :\n\n1. Une fois connecté, le serveur utilise l'entête HTTP `Set-Cookie` dans la réponse pour définir un cookie avec un \"identifiant de session\" unique.\n2. La prochaine fois lorsque la requête est envoyée au même domaine, le navigateur envoie le cookie sur le réseau en utilisant l'entête HTTP `Cookie`.\n3. Alors le serveur sait qui a fait la requête.\n\nNous pouvons aussi accéder aux cookies depuis le navigateur, en utilisant la propriété `document.cookie`.\n\nIl y a beaucoup de chose malignes à faire à propos des cookies et leurs options. Dans ce chapitre nous les couvrirons en détail.\n\n## Lire depuis document.cookie\n\n```online\nVotre navigateur stocke t-il des cookies depuis ce site ? Voyons voir :\n```\n\n```offline\nEn considérant que vous êtes sur un site web, il est possible de voir ses cookies, comme ceci :\n```\n\n```js run\n// Sur javascript.info, nous utilisons Google Analytics pour les statistiques,\n// Donc il devrait y avoir quelques cookies\nalert( document.cookie ); // cookie1=value1; cookie2=value2;...\n```\n\nLa valeur de `document.cookie` consiste en des paires `name=value`, délimitées par `; `. Chacune étant un cookie séparé.\n\nPour trouver un cookie en particulier, nous pouvons diviser `document.cookie` par `; `, et donc trouver le bon nom. Nous pouvons utiliser soit une expression régulière (regex) ou des fonctions de tableau pour faire cela.\n\nNous laissons cela en tant qu'exercice pour le lecteur. Aussi, à la fin du chapitre vous trouverez des fonctions utilitaires pour manipuler les cookies.\n\n## Écrire dans document.cookie\n\nNous pouvons écrire dans `document.cookie`. Mais ce n'est pas une propriété de données, c'est un [accesseur (getter/setter)](info:property-accessors). Une affectation à ce dernier est traitée spécialement.\n\n**Une opération d'écriture dans `document.cookie` met à jour seulement les cookies mentionnés dedans, mais ne touche pas les autres cookies.**\n\nPar exemple, cet appel définit un cookie avec le nom `user` et la valeur `John` :\n\n```js run\ndocument.cookie = \"user=John\"; // Met à jour uniquement le cookie nommé 'user'\nalert(document.cookie); // Affiche tous les cookies\n```\n\nSi vous exécutez cela, vous verrez probablement plusieurs cookies. Car l'opération `document.cookie=` ne réécrit pas tous les cookies. Elle définit uniquement le cookie `user` mentionné.\n\nTechniquement, le nom et la valeur peuvent être n'importe quel caractère. Pour garder le formattage valide, ils devraient être échappés en utilisant la fonction integrée `encodeURIComponent` :\n\n```js run\n// Les caractères spéciaux ont besoin d'encodage\nlet name = \"my name\";\nlet value = \"John Smith\"\n\n// Encode le cookie en tant que my%20name=John%20Smith\ndocument.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value);\n\nalert(document.cookie); // ...; my%20name=John%20Smith\n```\n\n```warn header=\"Limitations\"\nIl y a quelques limites :\n- La paire `name=value`, après `encodeURIComponent`, ne peut pas excéder 4KB. Donc on ne peut pas stocker quelque chose de trop lourd sur un cookie.\n- Le nombre total de cookie par domaine est limité à ~ 20+, la limite exacte dépend du navigateur.\n```\n\nLes cookies ont plusieurs options, beaucoup d'entre elles sont importantes et devraient être définies.\n\nLes options sont listées après `key=value`, délimité par `;`, comme ceci :\n\n```js run\ndocument.cookie = \"user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT\"\n```\n\n## path\n\n- **`path=/mypath`**\n\nLe préfix du chemin de l'URL doit être absolu. Ça rend le cookie accessible pour les pages du même chemin. Par défaut, il s'agit du chemin courant.\n\nSi un cookie est défini avec `path=/admin`, il est visible aux pages `/admin` et `/admin/something`, mais pas à `/home` ou `/adminpage`.\n\nGénéralement, nous devons définir `path` à la racine `path=/` pour rendre le cookie accessible depuis toutes les pages du site.\n\n## domain\n\n- **`domain=site.com`**\n\nUn domaine définit par où le cookie est accessible. Cependant en pratique, il y a des limites. Nous ne pouvons pas définir n'importe quel domaine.\n\n**Il n'y a pas de moyen de laisser un cookie être accessible depuis un domaine de second niveau, donc `other.com` ne recevra jamais un cookie défini à `site.com`**\n\nIl s'agit d'une restriction de sécurité, pour nous permettre de stocker des données sensibles dans nos cookies qui ne seront disponibles que sur un site.\n\nPar défaut, un cookie est accessible uniquement depuis le domaine qui l'a défini.\n\nVeuillez noter, par défaut un cookie n'est pas partagé avec un sous-domaine, tel que `forum.site.com`.\n\n```js\n// Si nous définissons un cookie sur site.com\ndocument.cookie = \"user=John\"\n\n// ...Nous ne le verrons pas depuis forum.site.com\nalert(document.cookie); // no user\n```\n\n...Mais cela peut changer. Si nous aimerions permettre aux sous-domaines comme `forum.site.com` de récupérer un cookie défini par `site.com`, c'est possible.\n\nPour que cela arrive, quand nous definissons un cookie depuis `site.com`, nous pouvons définir l'option `domain` à la racine du domaine : `domain=site.com`. Alors tous les sous-domaines verront un tel cookie.\n\nPar exemple :\n\n```js\n// Depuis site.com\n// Rendre le cookie accessible à tous les sous-domaines *.site.com:\ndocument.cookie = \"user=John; *!*domain=site.com*/!*\"\n\n// Plus tard\n\n// Depuis forum.site.com\nalert(document.cookie); // Le cookie user=John existe\n```\n\nPour des raisons historiques, `domain=.site.con` (avec un point avant `site.com`) fonctionne de la même manière, permettant l'accés au cookie depuis les sous-domaines. C'est une vielle notation et devrait être utilisée si nous avons besoin de prendre en charge les très vieux navigateurs.\n\nPour résumer, l'option `domain` permet de rendre un cookie accessible aux sous-domaines.\n\n## expires, max-age\n\nPar défaut, si un cookie n'a pas ces options, il disparait quand le navigateur est fermé. De tels cookies sont appellés \"cookies de session\"\n\nPour laisser les cookies survivre à la fermeture du navigateur, nous pouvons soit définir soit l'option `expires` ou `max-age`.\n\n- **`expires=Tue, 19 Jan 2038 03:14:07 GMT`**\n\nLa date d'expiration du cookie définit la date, à laquelle le navigateur le supprimera automatiquement.\n\nLa date doit être exactement dans ce format, en timezone GMT. Nous pouvons utiliser `date.toUTCString` pour le récupérer. Par exemple, nous pouvons définir le cookie pour qu'il expire dans 1 jour :\n\n```js\n// +1 jour depuis maintenant\nlet date = new Date(Date.now() + 86400e3);\ndate = date.toUTCString();\ndocument.cookie = \"user=John; expires=\" + date;\n```\n\nSi nous définissons `expires` à une date dans le passé, le cookie est supprimé.\n\n- **`max-age=3600`**\n\nIl s'agit d'une alternative à `expires` et elle spécifie l'expiration du cookie en seconde à partir de l'instant.\n\nSi elle est définie à zero ou une valeur négative, le cookie sera supprimé :\n\n```js\n// Le cookie mourra dans +1 heure à partir de maintenant\ndocument.cookie = \"user=John; max-age=3600\";\n\n// Supprime le cookie (le laisser expirer tout de suite)\ndocument.cookie = \"user=John; max-age=0\";\n```\n\n## secure\n\n- **`secure`**\n\nLe cookie devrait être transféré seulement avec HTTPS.\n\n**Par défaut, si nous définissons un cookie à `http://site.com`, alors il apparaitra aussi à `https://site.com` et vice versa.**\n\nLes cookies sont \"domain-based\", ils ne sont pas distinguables entre les protocoles.\n\nAvec cette option, si un cookie est défini par `https://site.com`, alors il n'apparait pas quand le même site est accédé par HTTP, comme `http://site.com`. Donc si un cookie a un contenu sensible il ne devrait pas être envoyé sur HTTP qui n'est chiffré, le flag `secure` est la bonne chose.\n\n```js\n// Considérons que nous soyons sur https:// maintenant\n// Définit le cookie pour être sécurisé (seulement accessible par HTTPS)\ndocument.cookie = \"user=John; secure\";\n```\n\n## samesite\n\nIl s'agit d'un nouvel attribut de sécurité `samesite`. Il a été conçu pour protéger de ce qu'on appelle attaques XSRF (cross-site request forgery).\n\n## L'attaque XSRF\n\nImaginez, vous êtes connecté sur le site `bank.com`. Ce qui signifie : que vous avez un cookie d'authentification sur ce site. Votre navigateur l'envoie à `bank.com` à chaque requête, donc il vous reconnait et effectue toutes les opérations financières sensibles.\n\nMaintenant, pendant que vous naviguez sur le web dans une autre fenêtre, vous arrivez accidentellement sur un autre site `evil.com`. Ce site a du code JavaScript qui soumet un formulaire `<form action=\"https://bank.com/pay\">` à `bank.com` avec les champs qui initient une transaction avec le compte du hacker.\n\nLe navigateur envoie des cookies à chaque fois que vous visitez le site `bank.com`, même si le formulaire a été envoyé depuis `evil.com`. Donc la banque vous reconnait et effectue le paiement.\n\n![](cookie-xsrf.svg)\n\nC'est ce qu'on appelle une attaque \"Cross-Site Request Forgery\" (XSRF en plus court).\n\nLes vraies banques en sont évidemment protégées. Tous les formulaires générés par `bank.com` ont un champ spécial, un certain \"XSRF protection token\", qu'une page malveillante ne peut pas générer ou extraire de la page distante. Elle peut y soumettre un formulaire, mais pas récupérer les données. Le site `bank.com` vérifie ce genre de token dans tous les formulaires qu'il reçoit.\n\nUne telle protection prend du temps à mettre en œuvre cependant. Nous avons besoin de nous assurer que tous les formulaires ont le champ de token requis, et nous devons aussi vérifier toutes les requêtes.\n\n### Entrer un cookie avec l'option samesite\n\nL'option `samesite` de cookie fournit un autre moyen de se protéger de telles attaques, cela ne devrait (en théorie) pas nécessiter de \"tokens de protections xsrf\".\n\nElle a deux valeurs possible :\n\n- **`samesite=strict` (pareil que `samesite` sans valeur)**\n\nUn cookie avec `samesite=strict` n'est jamais envoyé si un utilisateur vient d'en dehors du même site.\n\nEn d'autres termes, qu'importe que l'utilisateur suive un lien de ses mails ou soumette un formulaire provenant d'`evil.com`, ou qu'il fasse des opérations provenants d'un autre domaine, le cookie n'est pas envoyé.\n\nSi le cookie d'authentification a l'option `samesite`, alors l'attaque XSRF n'a aucune chance de se solder par un succés, car une soumission depuis `evil.com` ne vient pas avec les cookies. Donc `bank.com` ne reconnaitra pas l'utilisateur et ne procédera pas au paiement.\n\nLa protection est plutôt fiable. Seules les opérations provenants de `bank.com` vont envoyer le cookie `samesite`, e.g. une soumission de formulaire depuis une autre page à `bank.com`.\n\nBien que, il y ait un petit inconvénient.\n\nQuand un utilisateur suit un lien légitime vers `bank.com`, comme depuis ses propres notes, il sera surpris que `bank.com` ne le reconnaisse pas. En effet, les cookies `samesite=strict` ne sont pas envoyés dans ce cas.\n\nNous pouvons contourner ça avec deux cookies : une pour la \"reconnaissance générale\", uniquement dans le but de dire : \"Salut, John\", et un autre pour les opérations de changements de données avec `samesite=strict`. Alors, une personne venant de l'extérieur du site verra un message de bienvenue, mais les paiements devront être initiés depuis le site de la banque, pour que le second cookie soit envoyé.\n\n- **`samesite=lax`**\n\nUne approche plus relax qui protège aussi des XSRF et qui n'entrave pas l'expérience utilisateur.\n\nLe mode lax, tout comme `strict`, interdit au navigateur d'envoyer des cookies quand venu de l'extérieur du site, mais ajoute une exception.\n\nUn cookie `samesite=lax` est envoyé lorsque deux conditions sont réunies :\n\n1. La méthode HTTP est \"safe\" (e.g. GET, mais pas POST).\n\n  La liste complète des méthodes HTTP safes est dans la [spécification RFC7231](https://tools.ietf.org/html/rfc7231). Basiquement ce sont des méthodes qui peuvent être utilisées pour lire, mais pas pour écrire de données. Elles ne doivent pas effectuer d'opérations de modifications de données. Suivre un lien c'est toujours du GET, la méthode safe.\n\n2. L'opération effectue une navigation de haut niveau (change l'URL dans la barre d'adresse).\n\n  C'est généralement vrai, mais si la navigation est effectuée dans une `<iframe>`, alors ce n'est pas du haut-niveau. Aussi, les méthodes JavaScript n'effectuent aucune navigation, alors elles ne conviennent pas.\n\nDonc, que fait `samesite=lax`, il permet les opérations basiques \"va à l'URL\" à avoir des cookies. E.g. ouvrir un lien depuis des notes satisfait ces conditions.\n\nMais quelque chose de plus compliqué, comme une requête depuis un autre site ou une soumission de formulaire, perd les cookies.\n\nSi cela vous convient, alors ajouter `samesite=lax` n'entravera probablement pas l'expérience utilisateur et ajoutera une protection.\n\nDans l'ensemble, `samesite` est une bonne option.\n\nIl y a un inconvénient :\n\n- `samesite` est ignoré (non supporté) par les très vieux navigateurs, datant de 2017 et avant.\n\n**Donc si nous comptions uniquement sur `samesite` pour fournir une protection, alors les anciens navigateurs seraient vulnérables.**\n\nMais nous pouvons assurément utiliser `samesite` avec d'autres mesures de protections, comme les tokens xsrf, pour ajouter une couche de défense additionnelle et donc, dans le futur, quand les anciens navigateurs mourront, nous pourrons probablement nous passer des tokens xsrf.\n\n## httpOnly\n\nCette option n'a rien à voir avec JavaScript, mais nous devons la mentionner pour des raisons d'exhaustivité.\n\nLe serveur web utilise l'entête `Set-Cookie` pour définir un cookie. Aussi, il peut définir l'option `httpOnly`.\n\nCette option interdit à JavaScript d'accéder au cookie. Nous ne pouvons pas voir de tels cookies ou les manipuler en utilisant `document.cookie`.\n\nC'est utilisé en tant que précaution, pour protéger de certaines attaques quand un hacker injecte son propre code JavaScript dans une page et attend qu'un utilisateur visite la page. Ça ne devrait pas être possible du tout, les hackers ne devraient pas être capable d'injecter leur code dans votre site, mais il peut y avoir des bugs qui les laisserai le faire.\n\nNormalement, si ce genre de chose arrive, et qu'un utilisateur visite une page web avec le code JavaScript d'un hacker, alors le code s'exécute et obtient l'accès à `document.cookie` avec les cookies de l'utilisateur contenant les informations d'authentification. C'est mauvais.\n\nMais si un cookie est `httpOnly`, alors `document.cookie` ne le voit pas, donc il est protégé.\n\n## Annexe : Les fonctions du cookie\n\nIci un petit ensemble de fonctions qui fonctionnent avec les cookies, plus pratiques que des modifications manuelles de `document.cookie`.\n\nIl existe beaucoup de librairies de cookie pour ça, celles là sont à but démonstratifs. Elles fonctionnent complétement cependant.\n\n### getCookie(name)\n\nLe moyen le plus court d'accéder à un cookie est d'utiliser une [expression régulière](info:regular-expressions).\n\nLa fonction `getCookie(name)` retourne un cookie avec le nom donné :\n\n```js\n// Retourne le cookie correspondant au nom donné,\n// ou undefined si non trouvé\nfunction getCookie(name) {\n  let matches = document.cookie.match(new RegExp(\n    \"(?:^|; )\" + name.replace(/([\\.$?*|{}\\(\\)\\[\\]\\\\\\/\\+^])/g, '\\\\$1') + \"=([^;]*)\"\n  ));\n  return matches ? decodeURIComponent(matches[1]) : undefined;\n}\n```\n\nIci `new RegExp` est généré dynamiquement, pour faire correspondre `; name=<value>`.\n\nVeuillez noter qu'un cookie a une valeur encodée, donc `getCookie` utilise une fonction `decodeURIComponent` intégrée pour la décoder.\n\n### setCookie(name, value, options)\n\nDéfinit le cookie `name` à la valeur `valeur` avec `path=/` par défaut (peut être modifié pour ajouter d'autres valeurs par défaut) :\n\n```js run\nfunction setCookie(name, value, options = {}) {\n\n  options = {\n    path: '/',\n    // Ajoute d'autres valeurs par défaut si nécessaire\n    ...options\n  };\n\n  if (options.expires instanceof Date) {\n    options.expires = options.expires.toUTCString();\n  }\n\n  let updatedCookie = encodeURIComponent(name) + \"=\" + encodeURIComponent(value);\n\n  for (let optionKey in options) {\n    updatedCookie += \"; \" + optionKey;\n    let optionValue = options[optionKey];\n    if (optionValue !== true) {\n      updatedCookie += \"=\" + optionValue;\n    }\n  }\n\n  document.cookie = updatedCookie;\n}\n\n// Exemple d'utilisation :\nsetCookie('user', 'John', {secure: true, 'max-age': 3600});\n```\n\n### deleteCookie(name)\n\nPour supprimer un cookie, nous pouvons l'appeler avec une date d'expiration négative :\n\n```js\nfunction deleteCookie(name) {\n  setCookie(name, \"\", {\n    'max-age': -1\n  })\n}\n```\n\n```warn header=\"Mettre à jour ou supprimer doivent utiliser le même path et domain\"\nVeuillez noter : quand nous mettons à jour ou supprimons un cookie, nous devons utiliser exactement les mêmes options path et domain que lorsque nous l'avions défini\n```\n\nEnsemble : [cookie.js](cookie.js).\n\n## Annexe : Les cookies tiers\n\nUn cookie est appellé \"tiers\" s'il est placé par un domaine autre que la page que l'utilisateur visite.\n\nPar exemple :\n1. Une page à `site.com` charge une bannière depuis un autre site : `<img src=\"https://ads.com/banner.png\">`.\n2. Le long de la bannière, le serveur distant à `ads.com` peut définir un entête `Set-Cookie` avec un cookie type `id=1234`. Un tel cookie provenant du domaine `ads.com`, et ne sera visible que par `ads.com` :\n\n    ![](cookie-third-party.svg)\n\n3. La prochaine fois que `ads.com` est accéder, le serveur distant récupère le cookie `id` et reconnait l'utilisateur :\n \n    ![](cookie-third-party-2.svg)\n\n4. Le plus important c'est que, quand un utilisateur bouge depuis `site.com` vers un autre site `other.com`, qui a lui aussi une bannière, alors `ads.com` récupère le cookie, comme elle appartient à `ads.com`, de fait il reconnait le visiteur et le tracke puisqu'il a bougé entre les sites :\n\n    ![](cookie-third-party-3.svg)\n\nLes cookies tiers sont traditionnellement utilisés pour le tracking et les services publicitaires, en raison de leur nature. Ils sont liés au domaine dont ils proviennent, donc `ads.com` peut tracker le même utilisateur entre différents sites, s'ils y accédent tous.\n\nNaturellement, certaines personnes n'aiment pas être trackées, donc les navigateurs permettent de désactiver ce genre de cookie.\n\nAussi, les navigateur modernes mettent en place des politiques spéciales pour de tels cookies :\n- Safari ne permet pas du tout les cookies tiers\n- Firefox vient avec une \"black list\" de domaines tiers où sont bloqués les cookies tiers.\n\n```smart\nSi nous chargeons un script d'un domaine tiers, comme `<script src=\"https://google-analytics.com/analytics.js\">`, et que ce script utilise `document.cookie` pour définir un cookie, alors un tel cookie n'est pas un cookie tiers.\n\nSi un script définit un cookie, alors peu importe d'où vient le script -- le cookie appartient au domaine de la page courante.\n```\n\n## Annexe : RGPD\n\nCe sujet n'est pas lié à JavaScript du tout, il s'agit de quelque chose à garder à l'esprit quand nous définissons des cookies.\n\nIl y a une législation en Europe appellée RGPD, qui force les sites web à suivre un ensemble de règle pour respecter la vie privée de l'utilisateur. L'une de ces règles est de nécessiter une permission explicite de l'utilisateur pour les cookies de tracking.\n\nVeuillez noter, ça concerne seulement les cookies de tracking/identification/autorisation.\n\nDonc, si nous définissons un cookie pour sauvegarder certaines informations, mais sans tracker ni identifier l'utilisateur, nous sommes libre de le faire.\n\nMais si nous allons définir un cookie avec une session d'authentification ou un identifiant de tracking, alors l'utilisateur doit le permettre.\n\nLes sites web ont généralement deux variantes pour suivre le RGPD. Vous devez les avoir déjà vu sur le web :\n\n1. Si un site web veut définir des cookies de tracking uniquement pour les utilisateurs authentifiés.\n\n    Pour faire ça, le formulaire d'enregistrement doit avoir une case à cocher comme \"Accepter la politique sur la vie privée\" (qui décrit comment les cookies sont utilisés), l'utilisateur doit la cocher, et alors le site web est libre de définir des cookies d'authentification.\n\n2. Si un site web veut définir des cookies de tracking pour tout le monde.\n\n    Pour faire ça légalement, un site web affiche une fenêtre contextuelle \"de démarrage\" pour les nouveaux venus, et nécessite qu'ils acceptent les cookies. Alors le site web peut les définir et laisser les gens voir le contenu. Ça peut être dérangeant pour les nouveaux visiteurs cependant. Personne n'aime voir de tel fenêtre contextuelle \"doit cliquer\" plutôt que le contenu. Mais le RGPD requiert un accord explicite.\n\nLe RGPD ne concerne pas uniquement les cookies, ça concerne aussi les trucs d'ordres personnels. Mais ça va au delà de notre portée.\n\n## Résumé\n\n`document.cookie` fournit un accès aux cookies.\n- Les opérations d'écriture modifient uniquement les cookies qu'elles mentionnent.\n- Name/value doivent être encodés.\n- Un cookie ne peut pas excéder 4KB en taille. Le nombre de cookies permis sur un domaine est d'environ 20+ (variable selon le navigateur)\n\nLes options de cookies sont :\n\n- `path=/`, par défaut le chemin courant, rend les cookies visibles uniquement sous ce chemin.\n- `domain=site.com`, par défaut un cookie est visible seulement sur le domaine courant. Si le domaine est défini explicitement, le cookie devient visible depuis les sous domaines.\n- `expires` ou `max-age` définissent la date d'expiration du cookie. Sans eux le cookie meurt à la fermeture du navigateur.\n- `secure` empêche le navigateur d'envoyer un cookie avec les requêtes en dehors du site. Ça aide à prévenir des attaques XSRF.\n\nAdditionnellement :\n\n- Les cookies tiers peuvent être interdis par le navigateur, e.g. Safari le fait par défaut.\n- Quand nous définissons un cookie de tracking pour les citoyens Européens, le RGPD nécessite qu'on en demande la permissions.\n"
  },
  {
    "path": "6-data-storage/01-cookie/cookie.js",
    "content": "function getCookie(name) {\n  let matches = document.cookie.match(new RegExp(\n    \"(?:^|; )\" + name.replace(/([.$?*|{}()[\\]\\\\/+^])/g, '\\\\$1') + \"=([^;]*)\"\n  ));\n  return matches ? decodeURIComponent(matches[1]) : undefined;\n}\n\nfunction setCookie(name, value, options = {}) {\n\n  options = {\n    path: '/',\n    // Ajouter d'autres valeurs par défaut si nécessaire\n    ...options\n  };\n\n  if (options.expires instanceof Date) {\n    options.expires = options.expires.toUTCString();\n  }\n\n  let updatedCookie = encodeURIComponent(name) + \"=\" + encodeURIComponent(value);\n\n  for (let optionKey in options) {\n    updatedCookie += \"; \" + optionKey;\n    let optionValue = options[optionKey];\n    if (optionValue !== true) {\n      updatedCookie += \"=\" + optionValue;\n    }\n  }\n\n  document.cookie = updatedCookie;\n}\n\n\nfunction deleteCookie(name) {\n  setCookie(name, \"\", {\n    'max-age': -1\n  })\n}\n"
  },
  {
    "path": "6-data-storage/02-localstorage/1-form-autosave/solution.md",
    "content": "\n"
  },
  {
    "path": "6-data-storage/02-localstorage/1-form-autosave/solution.view/index.html",
    "content": "<!doctype html>\n<textarea style=\"width:200px; height: 60px;\" id=\"area\" placeholder=\"Write here\"></textarea>\n<br>\n<button onclick=\"localStorage.removeItem('area');area.value=''\">Clear</button>\n<script>\n    area.value = localStorage.getItem('area');\n    area.oninput = () => {\n      localStorage.setItem('area', area.value)\n    };\n</script>\n"
  },
  {
    "path": "6-data-storage/02-localstorage/1-form-autosave/source.view/index.html",
    "content": "<!doctype html>\n<textarea style=\"width:200px; height: 60px;\" id=\"area\"></textarea>\n"
  },
  {
    "path": "6-data-storage/02-localstorage/1-form-autosave/task.md",
    "content": "# Enregistrer automatiquement un champ de formulaire\n\nCréez un champ `textarea` qui \"enregistre automatiquement\" sa valeur à chaque modification.\n\nAinsi, si l'utilisateur ferme accidentellement la page et l'ouvre à nouveau, il retrouvera sa saisie inachevée à la place.\n\nComme ceci :\n\n[iframe src=\"solution\" height=120]\n"
  },
  {
    "path": "6-data-storage/02-localstorage/article.md",
    "content": "# LocalStorage, sessionStorage\n\nLes objets de stockage Web `localStorage` et `sessionStorage` permettent d'enregistrer les paires clé/valeur dans le navigateur.\n\nCe qui est intéressant à leur sujet, c'est que les données survivent à une actualisation de la page (pour `sessionStorage`) et même à un redémarrage complet du navigateur (pour `localStorage`). Nous verrons cela très bientôt.\n\nNous avons déjà des cookies. Pourquoi des objets supplémentaires ?\n\n- Contrairement aux cookies, les objets de stockage Web ne sont pas envoyés au serveur à chaque requête. Grâce à cela, nous pouvons stocker beaucoup plus. La plupart des navigateurs autorisent au moins 5 mégaoctets de données (ou plus) et ont des paramètres pour configurer cela.\n- Contrairement aux cookies également, le serveur ne peut pas manipuler les objets de stockage via les en-têtes HTTP. Tout se fait en JavaScript.\n- Le stockage est lié à l'origine (triplet domaine/protocole/port). Autrement dit, différents protocoles ou sous-domaines impliquent différents objets de stockage, ils ne peuvent pas accéder aux données les uns des autres.\n\nLes deux objets de stockage fournissent les mêmes méthodes et propriétés :\n\n- `setItem(key, value)` -- stocke la paire clé/valeur.\n- `getItem(key)` -- récupère la valeur par clé.\n- `removeItem(key)` -- supprime la clé avec sa valeur.\n- `clear()` -- supprime tout.\n- `key(index)` -- récupère la clé sur une position donnée.\n- `length` -- le nombre d'éléments stockés.\n\nComme vous pouvez le voir, c'est comme une collection `Map` (`setItem/getItem/removeItem`), mais permet également l'accès par index avec `key(index)`.\n\nVoyons voir comment ça fonctionne.\n\n## Démo localStorage\n\nLes principales caractéristiques de `localStorage` sont les suivantes :\n\n- Partagé entre tous les onglets et fenêtres d'une même origine.\n- Les données n'expirent pas. Il reste après le redémarrage du navigateur et même le redémarrage du système d'exploitation.\n\nPar exemple, si vous exécutez ce code...\n\n```js run\nlocalStorage.setItem('test', 1);\n```\n\n...Et fermez/ouvrez le navigateur ou ouvrez simplement la même page dans une autre fenêtre, alors vous pouvez l'obtenir comme ceci :\n\n```js run\nalert( localStorage.getItem('test') ); // 1\n```\n\nIl suffit d'être sur la même origine (domaine/port/protocole), le chemin de l'url peut être différent.\n\nLe `localStorage` est partagé entre toutes les fenêtres avec la même origine, donc si nous définissons les données dans une fenêtre, le changement devient visible dans une autre.\n\n## Accès de type objet\n\nNous pouvons également utiliser un objet simple pour obtenir/définir des clés, comme ceci :\n\n```js run\n// définir la clé\nlocalStorage.test = 2;\n\n// obtenir la clé\nalert( localStorage.test ); // 2\n\n// supprimer clé\ndelete localStorage.test;\n```\n\nC'est autorisé pour des raisons historiques, et fonctionne plus ou moins, mais généralement déconseillé, car :\n\n1. Si la clé est générée par l'utilisateur, elle peut être n'importe quoi, comme `length` ou `toString`, ou une autre méthode intégrée de `localStorage`. Dans ce cas, `getItem/setItem` fonctionne correctement, tandis que l'accès de type objet échoue :\n\n   ```js run\n   let key = 'length';\n   localStorage[key] = 5; // Erreur, impossible d'attribuer 'length'\n   ```\n\n2. Il y a un événement `storage`, il se déclenche lorsque nous modifions les données. Cet événement ne se produit pas pour un accès de type objet. Nous verrons cela plus loin dans ce chapitre.\n\n## Boucle sur les clés\n\nComme nous l'avons vu, les méthodes fournissent la fonctionnalité \"get/set/remove by key\". Mais comment obtenir toutes les valeurs ou clés enregistrées ?\n\nMalheureusement, les objets de stockage ne sont pas itérables.\n\nUne façon consiste à boucler sur eux comme sur un tableau :\n\n```js run\nfor (let i=0; i<localStorage.length; i++) {\n  let key = localStorage.key(i);\n  alert(`${key}: ${localStorage.getItem(key)}`);\n}\n```\n\nUne autre façon consiste à utiliser la boucle `for key in localStorage`, comme nous le faisons avec des objets normaux.\n\nIl itère sur les clés, mais génère également quelques champs intégrés dont nous n'avons pas besoin :\n\n```js run\n// mauvais essai\nfor(let key in localStorage) {\n  alert(key); // affiche getItem, setItem et d'autres éléments intégrés\n}\n```\n\n...Nous devons donc soit filtrer les champs du prototype avec la vérification `hasOwnProperty` :\n\n```js run\nfor(let key in localStorage) {\n  if (!localStorage.hasOwnProperty(key)) {\n    continue; // sauter des clés comme \"setItem\", \"getItem\" etc.\n  }\n  alert(`${key}: ${localStorage.getItem(key)}`);\n}\n```\n\n...Ou récupérez simplement les clés \"propres\" avec `Object.keys`, puis bouclez-les si nécessaire :\n\n```js run\nlet keys = Object.keys(localStorage);\nfor(let key of keys) {\n  alert(`${key}: ${localStorage.getItem(key)}`);\n}\n```\n\nCe dernier fonctionne, car `Object.keys` ne renvoie que les clés appartenant à l'objet, en ignorant le prototype.\n\n## Chaînes uniquement\n\nVeuillez noter que la clé et la valeur doivent être des chaînes.\n\nS'il s'agissait d'un autre type, comme un nombre ou un objet, il est automatiquement converti en chaîne de caractères :\n\n```js run\nlocalStorage.user = {name: \"John\"};\nalert(localStorage.user); // [object Object]\n```\n\nWe can use `JSON` to store objects though:\n\n```js run\nlocalStorage.user = JSON.stringify({name: \"John\"});\n\n// un peu plus tard\nlet user = JSON.parse( localStorage.user );\nalert( user.name ); // John\n```\n\nIl est également possible de transformer en chaîne l'ensemble de l'objet de stockage, par ex. à des fins de débogage :\n\n```js run\n// ajout d'options de formatage à JSON.stringify pour rendre l'objet plus beau\nalert( JSON.stringify(localStorage, null, 2) );\n```\n\n## sessionStorage\n\nL'objet `sessionStorage` est beaucoup moins utilisé que `localStorage`.\n\nLes propriétés et les méthodes sont les mêmes, mais c'est beaucoup plus limité :\n\n- Le `sessionStorage` n'existe que dans l'onglet actuel du navigateur.\n  - Un autre onglet avec la même page aura un stockage différent.\n  - Mais il est partagé entre les iframes du même onglet (en supposant qu'ils proviennent de la même origine).\n- Les données survivent à l'actualisation de la page, mais pas à la fermeture/ouverture de l'onglet.\n\nVoyons cela en action.\n\nExécutez ce code...\n\n```js run\nsessionStorage.setItem('test', 1);\n```\n\n...Puis actualisez la page. Maintenant, vous pouvez toujours obtenir les données :\n\n```js run\nalert( sessionStorage.getItem('test') ); // après rafraîchissement : 1\n```\n\n...Mais si vous ouvrez la même page dans un autre onglet et réessayez, le code ci-dessus renvoie `null`, ce qui signifie \"rien trouvé\".\n\nC'est exactement parce que `sessionStorage` est lié non seulement à l'origine, mais également à l'onglet du navigateur. Pour cette raison, `sessionStorage` est utilisé avec parcimonie.\n\n## Événement de stockage\n\nLorsque les données sont mises à jour dans `localStorage` ou `sessionStorage`, l'événement [storage](https://html.spec.whatwg.org/multipage/webstorage.html#the-storageevent-interface) se déclenche, avec les propriétés :\n\n- `key` – la clé qui a été changée (`null` si `.clear()` est appelé).\n- `oldValue` - l'ancienne valeur (`null` si la clé vient d'être ajoutée).\n- `newValue` - la nouvelle valeur (`null` si la clé est supprimée).\n- `url` - l'url du document où la mise à jour s'est produite.\n- `storageArea` - objet `localStorage` ou `sessionStorage` où la mise à jour s'est produite.\n\nL'important est que l'événement se déclenche sur tous les objets `window` où le stockage est accessible, sauf celui qui l'a provoqué.\n\nÉlaborons.\n\nImaginez, vous avez deux fenêtres avec le même site dans chacune. Donc `localStorage` est partagé entre eux.\n\n```en ligne\nVous pouvez ouvrir cette page dans deux fenêtres de navigateur pour tester le code ci-dessous.\n```\n\nSi les deux fenêtres écoutent `window.onstorage`, chacune réagira aux mises à jour qui se sont produites dans l'autre.\n\n```js run\n// se déclenche sur les mises à jour effectuées sur le même stockage à partir d'autres documents\nwindow.onstorage = (event) => {\n  // peut également utiliser window.addEventListener('storage', event => {\n  if (event.key != \"now\") return;\n  alert(event.key + \":\" + event.newValue + \" at \" + event.url);\n};\n\nlocalStorage.setItem('now', Date.now());\n```\n\nVeuillez noter que l'événement contient également : `event.url` -- l'url du document où les données ont été mises à jour.\n\nDe plus, `event.storageArea` contient l'objet de stockage -- l'événement est le même pour `sessionStorage` et `localStorage`, donc `event.storageArea` fait référence à celui qui a été modifié. On peut même vouloir y remettre quelque chose, \"répondre\" à un changement.\n\n**Cela permet à différentes fenêtres d'une même origine d'échanger des messages.**\n\nLes navigateurs modernes prennent également en charge [Broadcast channel API](mdn:/api/Broadcast_Channel_API), l'API spéciale pour la communication inter-fenêtre de même origine, elle est plus complète, mais moins prise en charge. Il existe des bibliothèques qui polyfill cette API, basée sur `localStorage`, qui la rendent disponible partout.\n\n## Résumé\n\nLes objets de stockage Web `localStorage` et `sessionStorage` permettent de stocker des paires clé/valeur dans le navigateur.\n\n- `clé` et `valeur` doivent être des chaînes.\n- La limite est de 5mb+, dépend du navigateur.\n- Ils n'expirent pas.\n- Les données sont liées à l'origine (domaine/port/protocole).\n\n| `localStorage`                                             | `sessionStorage`                                                               |\n| ---------------------------------------------------------- | ------------------------------------------------------------------------------ |\n| Partagé entre tous les onglets et fenêtres de même origine | Visible dans un onglet de navigateur, y compris les iframes de la même origine |\n| Survit au redémarrage du navigateur                        | Survit à l'actualisation de la page (mais pas à la fermeture de l'onglet)      |\n\nAPI :\n\n- `setItem(key, value)` -- stocke la paire clé/valeur.\n- `getItem(key)` -- récupère la valeur par clé.\n- `removeItem(key)` -- supprime la clé avec sa valeur.\n- `clear()` -- supprime tout.\n- `key(index)` -- récupère la clé sur une position donnée.\n- `length` -- le nombre d'éléments stockés.\n- Utilisez `Object.keys` pour obtenir toutes les clés.\n- Nous accédons aux clés en tant que propriétés d'objet, dans ce cas l'événement `storage` n'est pas déclenché.\n\nÉvénement de stockage :\n\n- Se déclenche sur les appels `setItem`, `removeItem`, `clear`.\n- Contient toutes les données sur l'opération (`key/oldValue/newValue`), le document `url` et l'objet de stockage `storageArea`.\n- Se déclenche sur tous les objets `window` qui ont accès au stockage sauf celui qui l'a généré (dans un onglet pour `sessionStorage`, globalement pour `localStorage`).\n"
  },
  {
    "path": "6-data-storage/02-localstorage/sessionstorage.view/iframe.html",
    "content": "<!doctype html>\n<script>\n  window.addEventListener('storage', event => {\n    alert(\"iframe.html: onstorage\");\n  });\n</script>\n<button onclick=\"sessionStorage.setItem('now', new Date())\">sessionStorage.setItem</button>\n</body>\n</html>\n"
  },
  {
    "path": "6-data-storage/02-localstorage/sessionstorage.view/index.html",
    "content": "<!doctype html>\n<script>\n  window.addEventListener('storage', event => {\n    alert(\"index.html: onstorage\");\n  });\n</script>\n<button onclick=\"sessionStorage.setItem('now', new Date())\">sessionStorage.setItem</button>\n<iframe src=\"iframe.html\" style=\"height:100px\"></iframe>\n</body>\n</html>\n"
  },
  {
    "path": "6-data-storage/03-indexeddb/article.md",
    "content": "libs:\n\n- 'https://cdn.jsdelivr.net/npm/idb@3.0.2/build/idb.min.js'\n\n---\n\n# IndexedDB\n\nIndexedDB est une base de données intégrée au navigateur, beaucoup plus puissant que `localStorage`.\n\n- Stocke presque tous types de valeurs par clés, plusieurs types de clés.\n- Prend en charge les transactions pour la fiabilité.\n- Prend en charge les requêtes de plage de clés, les index.\n- Peut stocker des volumes de données beaucoup plus importants que `localstorage`.\n\nCette puissance est généralement excessive pour les applications traditionnelles client-serveur. IndexedDB est destiné aux applications hors ligne, pour être combinées avec ServiceWorker et d'autres technologies.\n\nL'interface native à IndexedDB, décrite dans la spécification <https://www.w3.org/TR/IndexedDB>, est basée sur des événements.\n\nNous pouvons également utiliser `async/await` à l'aide d'un wrapper basé sur des promesses, comme <https://github.com/jakearchibald/idb> C'est assez pratique, mais le wrapper n'est pas parfait, il ne peut pas remplacer les événements pour tous les cas. Nous allons donc commencer par des événements, puis, après avoir compris IndexedDB, nous utiliserons le wrapper.\n\n```smart header=\"Où sont les données?\"\nTechniquement, les données sont généralement stockées dans le répertoire d'origine du visiteur, ainsi que les paramètres du navigateur, les extensions, etc.\n\nDifférents navigateurs et utilisateurs de niveau OS ont chacun leur propre stockage indépendant.\n```\n\n## Ouvrir une base de données\n\nPour commencer à travailler avec IndexedDB, nous devons d'abord \"ouvrir\" (connecter à) une base de données.\n\nLa syntaxe:\n\n```js\nlet openRequest = indexedDB.open(name, version);\n```\n\n- `name` - une chaîne, le nom de la base de données.\n- `version` - une version a nombre entier positif, par défaut `1` (expliqué ci-dessous).\n\nNous pouvons avoir de nombreuses bases de données avec des noms différents, mais tous existent dans l'origine actuelle (domaine/protocole/port). Différents sites Web ne peuvent pas accéder aux bases de données de l'autre.\n\nL'appel renvoie l'objet `openRequest`, nous devons écouter les événements suivants:\n\n- `success`: la base de données est prête, il y a \"l'objet de base de données\" dans `openRequest.result`, nous devons l'utiliser pour d'autres appels.\n- `error`: l'ouverture a échoué.\n- `upgradeneeded`: la base de données est prête, mais sa version est obsolète (voir ci-dessous).\n\n**IndexedDB a un mécanisme intégré de \"versionnage de schéma\", absent dans les bases de données côté serveur.**\n\nContrairement aux bases de données côté serveur, IndexedDB est côté client, les données sont stockées dans le navigateur, donc nous, développeurs, n'ayons pas accès à temps plein. Ainsi, lorsque nous avons publié une nouvelle version de notre application et que l'utilisateur visite notre page Web, nous devrons peut-être mettre à jour la base de données.\n\nSi la version de la base de données locale est inférieure à celle spécifiée dans `open`, un événement spécial `upgradeneeded` est déclenché, et nous pouvons comparer les versions et mettre à niveau les structures de données selon les besoins.\n\nL'événement `upgradeneeded` se déclenche également lorsque la base de données n'existe pas encore (techniquement, sa version est `0`), nous pouvons donc effectuer l'initialisation.\n\nDisons que nous avons publié la première version de notre application.\n\nEnsuite, nous pouvons ouvrir la base de données avec la version `1` et effectuer l'initialisation dans un gestionnaire de `upgradeneeded` comme ceci:\n\n```js\nlet openRequest = indexedDB.open(\"store\", *!*1*/!*);\n\nopenRequest.onupgradeneeded = function() {\n  // déclenche si le client n'avait pas de base de données\n  // ...effectuer l'initialisation...\n};\n\nopenRequest.onerror = function() {\n  console.error(\"Error\", openRequest.error);\n};\n\nopenRequest.onsuccess = function() {\n  let db = openRequest.result;\n  // continuez à travailler avec la base de données à l'aide de l'objet db\n};\n```\n\nEnsuite, plus tard, nous publions la 2ème version.\n\nNous pouvons l'ouvrir avec la version `2` et effectuer la mise à niveau comme ceci:\n\n```js\nlet openRequest = indexedDB.open(\"store\", *!*2*/!*);\n\nopenRequest.onupgradeneeded = function(event) {\n  // la version de la base de données existante est inférieure à 2 (ou elle n'existe pas)\n  let db = openRequest.result;\n  switch(event.oldVersion) { // version db existante\n    case 0:\n      // la version 0 signifie que le client n'avait pas de base de données\n      // effectuer l'initialisation\n    case 1:\n      // le client avait la version 1\n      // mettre à jour\n  }\n};\n```\n\nVeuillez noter : comme notre version actuelle est `2`, le gestionnaire `onupgradneeded` a une branche de code pour la version `0`, adaptée aux utilisateurs qui accéderont pour la première fois et n'ont pas de base de données, ainsi que pour la version `1`, pour les mises à niveau.\n\nEt puis, seulement si le gestionnaire `onupgradeededed` se termine sans erreurs, `openRequest.onsuccess` se déclanche et la base de données est considérée comme ouverte avec succès.\n\nPour supprimer une base de données:\n\n```js\nlet deleteRequest = indexedDB.deleteDatabase(name);\n// deleteRequest.onsuccess/onerror suit le résultat\n```\n\n```warn header=\"Nous ne pouvons pas ouvrir une base de données en utilisant une ancienne version de open\"\nSi la base de données de l'utilisateur actuel a une version plus élevée que dans l'appel `open`, par exemple la version de la DB existante est `3 ', et nous essayons `open(...2)`, c'est une erreur, `openRequest.onerror` se déclenche.\n\nC'est rare, mais une telle chose peut se produire lorsqu'un visiteur charge le code JavaScript obsolète, par exemple à partir d'un cache proxy. Le code est donc vieux, mais sa base de données est nouvelle.\n\nPour protéger des erreurs, nous devons vérifier `db.version` et suggérer un rechargement de page. Utilisez les en-têtes de mise en cache HTTP appropriés pour éviter de charger l'ancien code, afin que vous n'ayez jamais de tels problèmes.\n```\n\n### Problème de mise à jour parallèle\n\nComme nous parlons de versionnage, abordons un petit problème connexe.\n\nDisons:\n\n1. Un visiteur a ouvert notre site dans un onglet de navigateur, avec la version de base de données `1`.\n2. Ensuite, nous avons déployé une mise à jour, donc notre code est plus récent.\n3. Et puis le même visiteur ouvre notre site dans un autre onglet.\n\nIl y a donc un onglet avec une connexion ouverte à la version `1` de la base de données, tandis que le second tente de le mettre à jour vers la version `2` dans son gestionnaire `upgradeneeded`.\n\nLe problème est qu'une base de données est partagée entre deux onglets, car c'est le même site, même origine. Et il ne peut pas s'agir à la fois des versions '1' et '2'. Pour effectuer la mise à jour vers la version `2`, toutes les connexions à la version 1 doivent être fermées, y compris celle du premier onglet.\n\nAfin d'organiser cela, l'événement `versionchange` se déclenche sur l'objet de base de données \"obsolète\". Nous devrions écouter pour cela et fermer l'ancienne connexion à la base de données (et probablement suggérer un rechargement de page, pour charger le code mis à jour).\n\nSi nous n'écoutons pas l'événement `versionchange` et ne fermons pas l'ancienne connexion, la deuxième, nouvelle connexion, ne sera pas établie. L'objet `openRequest` émettra l'événement `blocked` au lieu de `success`. Donc le deuxième onglet ne fonctionnera pas.\n\nVoici le code pour gérer correctement la mise à jour parallèle. Il installe le gestionnaire `onversionchange`, qui se déclenche si la connexion actuelle à la base de données devient obsolète (la version de la base de données est mise à jour ailleurs) et ferme la connexion.\n\n```js\nlet openRequest = indexedDB.open(\"store\", 2);\n\nopenRequest.onupgradeneeded = ...;\nopenRequest.onerror = ...;\n\nopenRequest.onsuccess = function() {\n  let db = openRequest.result;\n\n  *!*\n  db.onversionchange = function() {\n    db.close();\n    alert(\"Database is outdated, please reload the page.\")\n  };\n  */!*\n\n  // ...la base de données est prête, utilisez-la...\n};\n\n*!*\nopenRequest.onblocked = function() {\n  // cet événement ne devrait pas se déclencher si nous gérons correctement onversionchange\n\n  // cela signifie qu'il existe une autre connexion ouverte à la même base de données\n  // et il n'a pas été fermé après le déclenchement de db.onversionchange\n};\n*/!*\n```\n\n...Autrement dit, ici on fait deux choses :\n\n1. L'écouteur `db.onversionchange` nous informe d'une tentative de mise à jour parallèle, si la version actuelle de la base de données devient obsolète.\n2. L'écouteur `openRequest.onblocked` nous informe de la situation inverse : il y a une connexion à une version obsolète ailleurs, et elle ne se ferme pas, donc la nouvelle connexion ne peut pas être établie.\n\nNous pouvons gérer les choses plus gracieusement dans `db.onversionchange`, inviter le visiteur à enregistrer les données avant la fermeture de la connexion, etc.\n\nOu, une approche alternative serait de ne pas fermer la base de données dans `db.onversionchange`, mais plutôt d'utiliser le gestionnaire `onblocked` (dans le nouvel onglet) pour alerter le visiteur, lui dire que la nouvelle version ne peut pas être chargée avant ils ferment d'autres onglets.\n\nCes collisions de mises à jour se produisent rarement, mais nous devrions au moins avoir une certaine gestion pour elles, au moins un gestionnaire `onblocked`, pour empêcher notre script de mourir silencieusement.\n\n## Magasin d'objets\n\nPour stocker quelque chose dans IndexedDB, nous avons besoin d'un *magasin d'objets*.\n\nUn magasin d'objets est un concept de base d'IndexedDB. Les équivalents dans d'autres bases de données sont appelés \"tables\" ou \"collections\". C'est là que les données sont stockées. Une base de données peut avoir plusieurs magasins : un pour les utilisateurs, un autre pour les biens, etc.\n\nBien qu'elles soient appelées \"magasin d'objets\", les primitives peuvent également être stockées.\n\n**Nous pouvons stocker presque n'importe quelle valeur, y compris des objets complexes.**\n\nIndexedDB utilise [l'algorithme standard de sérialisation](https://www.w3.org/TR/html53/infrastructure.html#section-structuredserializeforstorage) pour cloner et stocker un objet. C'est comme `JSON.stringify`, mais plus puissant, capable de stocker beaucoup plus de types de données.\n\nUn exemple d'objet qui ne peut pas être stocké : un objet avec des références circulaires. De tels objets ne sont pas sérialisables. `JSON.stringify` échoue également pour ces objets.\n\n**Il doit y avoir une `clé` unique pour chaque valeur du magasin.**\n\nUne clé doit être de l'un de ces types : nombre, date, chaîne, binaire ou tableau. C'est un identifiant unique, nous pouvons donc rechercher/supprimer/mettre à jour des valeurs par clé.\n\n![](indexeddb-structure.svg)\n\nComme nous le verrons très bientôt, nous pouvons fournir une clé lorsque nous ajoutons une valeur au magasin, similaire à `localStorage`. Mais lorsque nous stockons des objets, IndexedDB permet de configurer une propriété d'objet comme clé, ce qui est beaucoup plus pratique. Ou nous pouvons générer automatiquement des clés.\n\nMais nous devons d'abord créer un magasin d'objets.\n\nLa syntaxe pour créer un magasin d'objets :\n\n```js\ndb.createObjectStore(name, keyOptions);\n```\n\nVeuillez noter que l'opération est synchrone, `await` n'est nécessaire.\n\n- `name` est le nom du magasin, par ex. `\"books\"` pour les livres,\n- `keyOptions` est un objet facultatif avec l'une des deux propriétés :\n  - `keyPath` -- un chemin vers une propriété d'objet qu'IndexedDB utilisera comme clé, par ex. `id`.\n  - `autoIncrement` -- si `true`, alors la clé d'un objet nouvellement stocké est générée automatiquement, sous la forme d'un nombre toujours incrémenté.\n\nSi nous ne fournissons pas `keyOptions`, nous devrons fournir une clé explicitement plus tard, lors du stockage d'un objet.\n\nPar exemple, ce magasin d'objets utilise la propriété `id` comme clé :\n\n```js\ndb.createObjectStore('books', {keyPath: 'id'});\n```\n\n**Un magasin d'objets ne peut être créé/modifié que lors de la mise à jour de la version de la base de données, dans le gestionnaire `upgradeneeded`.**\n\nC'est une limitation technique. En dehors du gestionnaire, nous pourrons ajouter/supprimer/mettre à jour les données, mais les magasins d'objets ne peuvent être créés/supprimés/modifiés que lors d'une mise à jour de version.\n\nPour effectuer une mise à niveau de version de base de données, il existe deux approches principales :\n\n1. Nous pouvons implémenter des fonctions de mise à niveau par version : de 1 à 2, de 2 à 3, de 3 à 4, etc. Ensuite, dans `upgradeneeded`, nous pouvons comparer les versions (par exemple, l'ancienne 2, maintenant la 4) et exécuter des mises à niveau par version étape par étape, pour chaque version intermédiaire (2 à 3, puis 3 à 4).\n2. Ou nous pouvons simplement examiner la base de données : obtenez une liste des magasins d'objets existants sous le nom `db.objectStoreNames`. Cet objet est une [DOMStringList](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#domstringlist) qui fournit la méthode `contains(name)` pour vérifier l'existence. Et puis nous pouvons faire des mises à jour en fonction de ce qui existe et de ce qui n'existe pas.\n\nPour les petites bases de données, la deuxième variante peut être plus simple.\n\nVoici la démo de la deuxième approche :\n\n```js\nlet openRequest = indexedDB.open(\"db\", 2);\n\n// créer/mettre à jour la base de données sans vérification de version\nopenRequest.onupgradeneeded = function() {\n  let db = openRequest.result;\n  if (!db.objectStoreNames.contains('books')) {\n    // s'il n'y a pas de magasin \"books\"\n    db.createObjectStore('books', {keyPath: 'id'}); // créez-le\n  }\n};\n```\n\nPour supprimer un magasin d'objets :\n\n```js\ndb.deleteObjectStore('books');\n```\n\n## Transactions\n\nLe terme \"transaction\" est générique, utilisé dans de nombreux types de bases de données.\n\nUne transaction est un groupe d'opérations qui doivent toutes réussir ou toutes échouer.\n\nPar exemple, lorsqu'une personne achète quelque chose, nous devons :\n\n1. Soustrayez l'argent de leur compte.\n2. Ajoutez l'objet à son inventaire.\n\nCe serait plutôt mauvais si nous terminions la 1ère opération, puis quelque chose ne va pas, par ex. les lumières s'éteignent, et nous ne parvenons pas à faire le 2ème. Les deux doivent soit réussir (achat terminé, bon !) soit échouer (au moins la personne a gardé son argent, elle peut donc réessayer).\n\nLes transactions peuvent le garantir.\n\n**Toutes les opérations de données doivent être effectuées dans une transaction dans IndexedDB.**\n\nPour démarrer une transaction :\n\n```js\ndb.transaction(store, type);\n```\n\n- `store` est un nom de magasin auquel la transaction va accéder, par ex. `\"books\"`. Peut être un tableau de noms de magasins si nous allons accéder à plusieurs magasins.\n- `type` - un type de transaction, l'un des suivants :\n  - `readonly` -- peut uniquement lire, la valeur par défaut.\n  - `readwrite` -- ne peut que lire et écrire les données, mais pas créer/supprimer/modifier les magasins d'objets.\n\nIl y a aussi le type de transaction `versionchange` : ces transactions peuvent tout faire, mais nous ne pouvons pas les créer manuellement. IndexedDB crée automatiquement une transaction `versionchange` lors de l'ouverture de la base de données, pour le gestionnaire `upgradeneeded`. C'est pourquoi c'est un endroit unique où nous pouvons mettre à jour la structure de la base de données, créer/supprimer des magasins d'objets.\n\n```smart header=\"Pourquoi existe-t-il différents types de transactions ?\"\nLa performance est la raison pour laquelle les transactions doivent être étiquetées `readonly` et `readwrite`.\n\nDe nombreuses transactions en lecture seule peuvent accéder simultanément au même magasin, mais les transactions en lecture écriture ne le peuvent pas. Une transaction `readwrite` \"verrouille\" le magasin pour l'écriture. La prochaine transaction doit attendre que la précédente se termine avant d'accéder au même magasin.\n```\n\nUne fois la transaction créée, nous pouvons ajouter un article au magasin, comme ceci :\n\n```js\nlet transaction = db.transaction(\"books\", \"readwrite\"); // (1)\n\n// obtenir un magasin d'objets pour opérer dessus\n*!*\nlet books = transaction.objectStore(\"books\"); // (2)\n*/!*\n\nlet book = {\n  id: 'js',\n  price: 10,\n  created: new Date()\n};\n\n*!*\nlet request = books.add(book); // (3)\n*/!*\n\nrequest.onsuccess = function() { // (4)\n  console.log(\"Book added to the store\", request.result);\n};\n\nrequest.onerror = function() {\n  console.log(\"Error\", request.error);\n};\n```\n\nIl y avait essentiellement quatre étapes :\n\n1. Créez une transaction, en mentionnant tous les magasins auxquels elle va accéder, à `(1)`.\n2. Obtenez l'objet de magasin en utilisant `transaction.objectStore(name)`, à `(2)`.\n3. Effectuez la demande au magasin d'objets `books.add(book)`, à `(3)`.\n4. ...Gérer le succès/l'erreur de la demande `(4)`, puis nous pouvons faire d'autres demandes si nécessaire, etc.\n\nLes magasins d'objets prennent en charge deux méthodes pour stocker une valeur :\n\n- **put(value, [key])**\n  Ajoutez `value` au magasin. `key` n'est fournie que si le magasin d'objets n'a pas l'option `keyPath` ou `autoIncrement`. S'il existe déjà une valeur avec la même clé, elle sera remplacée.\n\n- **add(value, [key])**\n  Identique à `put`, mais s'il existe déjà une valeur avec la même clé, la requête échoue et une erreur portant le nom `\"ConstraintError\"` est générée.\n\nSemblable à l'ouverture d'une base de données, nous pouvons envoyer une requête : `books.add(book)`, puis attendre les événements `success/error`.\n\n- Le `request.result` pour `add` est la clé du nouvel objet.\n- L'erreur est dans `request.error` (le cas échéant).\n\n## Validation automatique des transactions\n\nDans l'exemple ci-dessus, nous avons démarré la transaction et effectué une requête `add`. Mais comme nous l'avons indiqué précédemment, une transaction peut avoir plusieurs requêtes associées, qui doivent toutes réussir ou toutes échouer. Comment marquons-nous la transaction comme terminée, sans plus de demandes à venir ?\n\nLa réponse courte est : nous ne le faisons pas.\n\nDans la prochaine version 3.0 de la spécification, il y aura probablement un moyen manuel de terminer la transaction, mais pour le moment dans la version 2.0, il n'y en a pas.\n\n**Lorsque toutes les requêtes de transaction sont terminées et que la [file d'attente de microtâches](info:microtask-queue) est vide, elle est validée automatiquement.**\n\nHabituellement, nous pouvons supposer qu'une transaction est validée lorsque toutes ses requêtes sont terminées et que le code actuel se termine.\n\nAinsi, dans l'exemple ci-dessus, aucun appel spécial n'est nécessaire pour terminer la transaction.\n\nLe principe de validation automatique des transactions a un effet secondaire important. Nous ne pouvons pas insérer une opération asynchrone comme `fetch`, `setTimeout` au milieu d'une transaction. IndexedDB ne maintiendra pas la transaction en attente jusqu'à ce que celles-ci soient terminées.\n\nDans le code ci-dessous, `request2` à la ligne `(*)` échoue, car la transaction est déjà validée et ne peut y faire aucune requête :\n\n```js\nlet request1 = books.add(book);\n\nrequest1.onsuccess = function() {\n  fetch('/').then(response => {\n*!*\n    let request2 = books.add(anotherBook); // (*)\n*/!*\n    request2.onerror = function() {\n      console.log(request2.error.name); // TransactionInactiveError\n    };\n  });\n};\n```\n\nC'est parce que `fetch` est une opération asynchrone, une macrotâche. Les transactions sont fermées avant que le navigateur ne commence à effectuer des macrotâches.\n\nLes auteurs de la spécification IndexedDB pensent que les transactions doivent être de courte durée. Principalement pour des raisons de performances.\n\nNotamment, les transactions `readwrite` \"verrouillent\" les magasins pour l'écriture. Donc, si une partie de l'application a lancé `readwrite` sur le magasin d'objets `books`, alors une autre partie qui veut faire de même doit attendre : la nouvelle transaction \"se bloque\" jusqu'à ce que la première soit terminée. Cela peut entraîner des retards étranges si les transactions prennent beaucoup de temps.\n\nAlors que faire?\n\nDans l'exemple ci-dessus, nous pourrions créer une nouvelle `db.transaction` juste avant la nouvelle requête `(*)`.\n\nMais ce sera encore mieux si nous souhaitons conserver les opérations ensemble, en une seule transaction, pour séparer les transactions IndexedDB et les \"autres\" éléments asynchrones.\n\nTout d'abord, faites `fetch`, préparez les données si nécessaire, puis créez une transaction et effectuez toutes les requêtes de base de données, cela fonctionnera alors.\n\nPour détecter le moment de l'achèvement réussi, nous pouvons écouter l'événement `transaction.oncomplete` :\n\n```js\nlet transaction = db.transaction(\"books\", \"readwrite\");\n\n// ...effectuer des opérations...\n\ntransaction.oncomplete = function() {\n  console.log(\"Transaction is complete\");\n};\n```\n\nSeul `complete` garantit que la transaction est enregistrée dans son ensemble. Les requêtes individuelles peuvent réussir, mais l'opération d'écriture finale peut mal tourner (par exemple, une erreur d'I/O ou autre).\n\nPour abandonner manuellement la transaction, appelez :\n\n```js\ntransaction.abort();\n```\n\nCela annule toutes les modifications apportées par les requêtes qu'il contient et déclenche l'événement `transaction.onabort`.\n\n## La gestion des erreurs\n\nLes demandes d'écriture peuvent échouer.\n\nC'est normal, non seulement en raison d'éventuelles erreurs de notre part, mais aussi pour des raisons non liées à la transaction elle-même. Par exemple, le quota de stockage peut être dépassé. Nous devons donc être prêts à gérer un tel cas.\n\n**Une requête ayant échoué annule automatiquement la transaction, annulant toutes ses modifications.**\n\nDans certaines situations, nous pouvons vouloir gérer l'échec (par exemple, essayer une autre requête), sans annuler les modifications existantes, et continuer la transaction. C'est possible. Le gestionnaire `request.onerror` est capable d'empêcher l'abandon de la transaction en appelant `event.preventDefault()`.\n\nDans l'exemple ci-dessous, un nouveau livre est ajouté avec la même clé (`id`) que celui existant. La méthode `store.add` génère une `\"ConstraintError\"` dans ce cas. Nous le traitons sans annuler la transaction :\n\n```js\nlet transaction = db.transaction(\"books\", \"readwrite\");\n\nlet book = { id: 'js', price: 10 };\n\nlet request = transaction.objectStore(\"books\").add(book);\n\nrequest.onerror = function(event) {\n  // ConstraintError se produit lorsqu'un objet avec le même identifiant existe déjà\n  if (request.error.name == \"ConstraintError\") {\n    console.log(\"Book with such id already exists\"); // gérer l'erreur\n    event.preventDefault(); // ne pas interrompre la transaction\n    // utiliser une autre clé pour le livre ?\n  } else {\n    // erreur inattendue, impossible à gérer\n    // la transaction sera annulée\n  }\n};\n\ntransaction.onabort = function() {\n  console.log(\"Error\", transaction.error);\n};\n```\n\n### Délégation d'événements\n\nAvons-nous besoin de onerror/onsuccess pour chaque requête ? Pas à chaque fois. Nous pouvons utiliser la délégation d'événements à la place.\n\n**Les événements d,IndexedDB se propagent : `request` -> `transaction` -> `database`.**\n\nTous les événements sont des événements DOM, avec capture et propagation, mais généralement, seule la phase de propagation est utilisée.\n\nNous pouvons donc intercepter toutes les erreurs à l'aide du gestionnaire `db.onerror`, à des fins de rapport ou à d'autres fins :\n\n```js\ndb.onerror = function(event) {\n  let request = event.target; // la requête à l'origine de l'erreur\n\n  console.log(\"Error\", request.error);\n};\n```\n\n...Mais que se passe-t-il si une erreur est entièrement gérée ? Nous ne voulons pas le signaler dans ce cas.\n\nNous pouvons arrêter la propagation et donc `db.onerror` en utilisant `event.stopPropagation()` dans `request.onerror`.\n\n```js\nrequest.onerror = function(event) {\n  if (request.error.name == \"ConstraintError\") {\n    console.log(\"Book with such id already exists\"); // gérer l'erreur\n    event.preventDefault(); // ne pas interrompre la transaction\n    event.stopPropagation(); // ne propagez pas l'erreur, \"mâchez-la\"\n  } else {\n    // ne fais rien\n    // la transaction sera abandonnée\n    // nous pouvons nous occuper de l'erreur dans transaction.onabort\n  }\n};\n```\n\n## Recherche\n\nIl existe deux principaux types de recherche dans un magasin d'objets :\n\n1. Par une valeur de clé ou une plage de clés. Dans notre stockage \"books\", ce serait une valeur ou une plage de valeurs de `book.id`.\n2. Par un autre champ d'objet, par ex. `book.price`. Cela nécessitait une structure de données supplémentaire, nommée \"index\".\n\n### Par clé\n\nTraitons d'abord le premier type de recherche : par clé.\n\nLes méthodes de recherche prennent en charge à la fois les valeurs de clé exactes et les \"plages de valeurs\" -- Les objets [IDBKeyRange](https://www.w3.org/TR/IndexedDB/#keyrange) spécifient une \"plage de clés\" acceptable.\n\nLes objets `IDBKeyRange` sont créés à l'aide des appels suivants :\n\n- `IDBKeyRange.lowerBound(lower, [open])` signifie : `≥lower` (ou `>lower` si `open` est true)\n- `IDBKeyRange.upperBound(upper, [open])` signifie : `≤upper` (ou `<upper` si `open` est true)\n- `IDBKeyRange.bound(lower, upper, [lowerOpen], [upperOpen])` signifie : entre `lower` et `upper`. Si les drapeaux open sont true, la clé correspondante n'est pas incluse dans la plage.\n- `IDBKeyRange.only(key)` -- une plage composée d'une seule `key`, rarement utilisée.\n\nNous verrons très prochainement des exemples concrets d'utilisation.\n\nPour effectuer la recherche proprement dite, il existe les méthodes suivantes. Ils acceptent un argument `query` qui peut être une clé exacte ou une plage de clés :\n\n- `store.get(query)` -- recherche la première valeur par une clé ou une plage.\n- `store.getAll([query], [count])` -- recherche toutes les valeurs, limite par `count` si donné.\n- `store.getKey(query)` -- recherche la première clé qui satisfait la requête, généralement une plage.\n- `store.getAllKeys([query], [count])` -- recherche toutes les clés qui satisfont la requête, généralement une plage, jusqu'à `count` si donné.\n- `store.count([query])` -- obtient le nombre total de clés qui satisfont la requête, généralement une plage.\n\nPar exemple, nous avons beaucoup de livres dans notre magasin. N'oubliez pas que le champ `id` est la clé, donc toutes ces méthodes peuvent rechercher par `id`.\n\nExemples de requête :\n\n```js\n// obtenir un livre\nbooks.get('js');\n\n// obtenir des livres avec 'css' <= id <= 'html'\nbooks.getAll(IDBKeyRange.bound('css', 'html'));\n\n// obtenir des livres avec id < 'html'\nbooks.getAll(IDBKeyRange.upperBound('html', true));\n\n// obtenir tous les livres\nbooks.getAll();\n\n// obtenir toutes les clés, où id > 'js'\nbooks.getAllKeys(IDBKeyRange.lowerBound('js', true));\n```\n\n```smart header=\"Le magasin d'objets est toujours trié\"\nUn magasin d'objets trie les valeurs par clé en interne.\n\nAinsi, les requêtes qui renvoient de nombreuses valeurs les renvoient toujours triées par ordre de clé.\n```\n\n### Par un champ utilisant un index\n\nPour rechercher par d'autres champs d'objets, nous devons créer une structure de données supplémentaire nommée \"index\".\n\nUn index est un \"module complémentaire\" au magasin qui suit un champ d'objet donné. Pour chaque valeur de ce champ, il stocke une liste de clés pour les objets qui ont cette valeur. Il y aura une image plus détaillée ci-dessous.\n\nLa syntaxe :\n\n```js\nobjectStore.createIndex(name, keyPath, [options]);\n```\n\n- **`name`** -- nom de l'index,\n- **`keyPath`** -- chemin vers le champ de l'objet que l'index doit suivre (nous allons chercher par ce champ),\n- **`option`** -- un objet optionnel avec les propriétés :\n  - **`unique`** -- si true, alors il ne peut y avoir qu'un seul objet dans le magasin avec la valeur donnée au `keyPath`. L'index appliquera cela en générant une erreur si nous essayons d'ajouter un doublon.\n  - **`multiEntry`** -- utilisé uniquement si la valeur de `keyPath` est un tableau. Dans ce cas, par défaut, l'index traitera le tableau entier comme clé. Mais si `multiEntry` est true, alors l'index conservera une liste d'objets de magasin pour chaque valeur de ce tableau.Ainsi, les membres du tableau deviennent des clés d'index.\n\nDans notre exemple, nous stockons des livres dont la clé est `id`.\n\nDisons que nous voulons effectuer une recherche par `price`.\n\nTout d'abord, nous devons créer un index. Cela doit être fait dans `upgradeneeded`, tout comme un magasin d'objets :\n\n```js\nopenRequest.onupgradeneeded = function() {\n  // il faut créer l'index ici, dans la transaction versionchange\n  let books = db.createObjectStore('books', {keyPath: 'id'});\n*!*\n  let index = books.createIndex('price_idx', 'price');\n*/!*\n};\n```\n\n- L'index suivra le champ `price`.\n- Le prix n'est pas unique, il peut y avoir plusieurs livres avec le même prix, nous ne définissons donc pas l'option `unique`.\n- Le prix n'est pas un tableau, donc le drapeau `multiEntry` n'est pas applicable.\n\nImaginez que notre `inventory` compte 4 livres. Voici l'image qui montre exactement ce qu'est l'`index` :\n\n![](indexeddb-index.svg)\n\nComme indiqué, l'index pour chaque valeur de `price` (deuxième argument) conserve la liste des clés qui ont ce prix.\n\nL'index se tient automatiquement à jour, nous n'avons pas à nous en soucier.\n\nDésormais, lorsque nous voulons rechercher un prix donné, nous appliquons simplement les mêmes méthodes de recherche à l'index :\n\n```js\nlet transaction = db.transaction(\"books\"); // readonly\nlet books = transaction.objectStore(\"books\");\nlet priceIndex = books.index(\"price_idx\");\n\n*!*\nlet request = priceIndex.getAll(10);\n*/!*\n\nrequest.onsuccess = function() {\n  if (request.result !== undefined) {\n    console.log(\"Books\", request.result); // tableau de livres avec price=10\n  } else {\n    console.log(\"No such books\");\n  }\n};\n```\n\nNous pouvons également utiliser `IDBKeyRange` pour créer des gammes et rechercher des livres bon marché/chers :\n\n```js\n// trouver des livres où price <= 5\nlet request = priceIndex.getAll(IDBKeyRange.upperBound(5));\n```\n\nLes index sont triés en interne par le champ d'objet suivi, `price` dans notre cas. Ainsi, lorsque nous effectuons la recherche, les résultats sont également triés par `price`.\n\n## Supprimer du magasin\n\nLa méthode `delete` recherche les valeurs à supprimer par une requête, le format d'appel est similaire à `getAll` :\n\n- **`delete(query)`** -- supprime les valeurs correspondantes par requête.\n\nPar exemple:\n\n```js\n// supprimer le livre où id='js'\nbooks.delete('js');\n```\n\nSi nous souhaitons supprimer des livres en fonction d'un prix ou d'un autre champ d'objet, nous devons d'abord rechercher la clé dans l'index, puis appeler `delete` :\n\n```js\n// trouver la clé où price = 5\nlet request = priceIndex.getKey(5);\n\nrequest.onsuccess = function() {\n  let id = request.result;\n  let deleteRequest = books.delete(id);\n};\n```\n\nPour tout supprimer :\n\n```js\nbooks.clear(); // effacer le stockage.\n```\n\n## Curseurs\n\nDes méthodes comme `getAll/getAllKeys` retournent un tableau de clés/valeurs.\n\nMais un magasin d'objets peut être énorme, plus grand que la mémoire disponible. Alors `getAll` échouera à obtenir tous les enregistrements sous forme de tableau.\n\nQue faire?\n\nLes curseurs permettent de contourner cela.\n\n**Un *curseur* est un objet spécial qui traverse le magasin d'objets, étant donné une requête, et renvoie une clé/valeur à la fois, économisant ainsi de la mémoire.**\n\nComme un magasin d'objets est trié en interne par clé, un curseur parcourt le magasin dans l'ordre des clés (ascendant par défaut).\n\nLa syntaxe :\n\n```js\n// comme getAll, mais avec un curseur :\nlet request = store.openCursor(query, [direction]);\n\n// pour obtenir des clés, pas des valeurs (comme getAllKeys): store.openKeyCursor\n```\n\n- **`query`** est une clé ou une plage de clés, comme pour `getAll`.\n- **`direction`** est un argument facultatif, dans quel ordre utiliser :\n  - `\"next\"` -- la valeur par défaut, le curseur remonte à partir de l'enregistrement avec la clé la plus basse.\n  - `\"prev\"` -- l'ordre inverse : vers le bas à partir de l'enregistrement avec la plus grande clé.\n  - `\"nextunique\"`, `\"prevunique\"` -- comme ci-dessus, mais ignore les enregistrements avec la même clé (uniquement pour les curseurs sur les index, par exemple pour plusieurs livres avec price=5, seul le premier sera renvoyé).\n\n**La principale différence du curseur est que `request.onsuccess` se déclenche plusieurs fois : une fois pour chaque résultat.**\n\nVoici un exemple d'utilisation d'un curseur :\n\n```js\nlet transaction = db.transaction(\"books\");\nlet books = transaction.objectStore(\"books\");\n\nlet request = books.openCursor();\n\n// appelé pour chaque livre trouvé par le curseur\nrequest.onsuccess = function() {\n  let cursor = request.result;\n  if (cursor) {\n    let key = cursor.key; // clé de livre (champ id)\n    let value = cursor.value; // objet livre\n    console.log(key, value);\n    cursor.continue();\n  } else {\n    console.log(\"No more books\");\n  }\n};\n```\n\nLes principales méthodes de curseur sont :\n\n- `advance(count)` -- avance le curseur `count` fois, en sautant les valeurs.\n- `continue([key])` -- avance le curseur à la valeur suivante dans la correspondance de plage (ou immédiatement après `key` si elle est donnée).\n\nQu'il y ait plus de valeurs correspondant au curseur ou non - `onsuccess` est appelé, puis dans `result` nous pouvons faire pointer le curseur vers l'enregistrement suivant, ou `undefined`.\n\nDans l'exemple ci-dessus, le curseur a été créé pour le magasin d'objets.\n\nMais nous pouvons aussi placer un curseur sur un index. Rappelons que les index permettent de rechercher par champ objet. Les curseurs sur les index font exactement la même chose que sur les magasins d'objets - ils économisent de la mémoire en renvoyant une valeur à la fois.\n\nPour les curseurs sur les index, `cursor.key` est la clé d'index (par exemple, le prix), et nous devrions utiliser la propriété `cursor.primaryKey` pour la clé d'objet :\n\n```js\nlet request = priceIdx.openCursor(IDBKeyRange.upperBound(5));\n\n// appelé pour chaque enregistrement\nrequest.onsuccess = function() {\n  let cursor = request.result;\n  if (cursor) {\n    let primaryKey = cursor.primaryKey; // prochaine clé de magasin d'objets (champ id)\n    let value = cursor.value; // prochain objet de magasin d'objets (objet livre)\n    let key = cursor.key; // clé d'index suivante (price)\n    console.log(key, value);\n    cursor.continue();\n  } else {\n    console.log(\"No more books\");\n  }\n};\n```\n\n## Promise wrapper\n\nAjouter `onsuccess/onerror` à chaque requête est une tâche assez lourde. Parfois, nous pouvons nous faciliter la vie en utilisant la délégation d'événements, par ex. définir des gestionnaires sur l'ensemble des transactions, mais `async/wait` est beaucoup plus pratique.\n\nUtilisons un wrapper de promesses léger <https://github.com/jakearchibald/idb> plus loin dans ce chapitre. Il crée un objet `idb` global avec les méthodes IndexedDB en tant que [promesse](info:promisify).\n\nEnsuite, au lieu de `onsuccess/onerror` nous pouvons écrire comme ceci :\n\n```js\nlet db = await idb.openDB('store', 1, db => {\n  if (db.oldVersion == 0) {\n    // effectuer l'initialisation\n    db.createObjectStore('books', {keyPath: 'id'});\n  }\n});\n\nlet transaction = db.transaction('books', 'readwrite');\nlet books = transaction.objectStore('books');\n\ntry {\n  await books.add(...);\n  await books.add(...);\n\n  await transaction.complete;\n\n  console.log('jsbook saved');\n} catch(err) {\n  console.log('error', err.message);\n}\n```\n\nNous avons donc tous les trucs sympas de \"code asynchrone simple\" et \"try..catch\".\n\n### La gestion des erreurs\n\nSi nous n'interceptons pas d'erreur, alors elle tombe à travers, jusqu'au `try..catch` extérieur le plus proche.\n\nUne erreur non interceptée devient un événement \"unhandled promise rejection\" sur l'objet `window`.\n\nNous pouvons gérer de telles erreurs comme ceci :\n\n```js\nwindow.addEventListener('unhandledrejection', event => {\n  let request = event.target; // objet de requête natif IndexedDB\n  let error = event.reason; //  objet d'erreur non géré, comme request.error\n  ...report about the error...\n});\n```\n\n### Le piège de \"Inactive transaction\"\n\nComme nous le savons déjà, une transaction s'auto-valide dès que le navigateur a terminé avec le code et les microtâches actuels. Donc, si nous plaçons une *macrotask* comme `fetch` au milieu d'une transaction, alors la transaction n'attendra pas qu'elle se termine. Il s'auto-valide simplement. Ainsi, la prochaine requête échouerait.\n\nPour un wrapper de promesse et `async/wait` la situation est la même.\n\nVoici un exemple de `fetch` au milieu de la transaction :\n\n```js\nlet transaction = db.transaction(\"inventory\", \"readwrite\");\nlet inventory = transaction.objectStore(\"inventory\");\n\nawait inventory.add({ id: 'js', price: 10, created: new Date() });\n\nawait fetch(...); // (*)\n\nawait inventory.add({ id: 'js', price: 10, created: new Date() }); // Erreur\n```\n\nLe prochain `inventory.add` après `fetch` `(*)` échoue avec une erreur \"inactive transaction\", car la transaction est déjà validée et fermée à ce moment-là.\n\nLa solution de contournement est la même que lorsque vous travaillez avec IndexedDB natif : faites une nouvelle transaction ou séparez simplement les éléments.\n\n1. Préparez les données et récupérez tout ce qui est nécessaire en premier.\n2. Enregistrez ensuite dans la base de données.\n\n### Obtenir des objets natifs\n\nEn interne, le wrapper exécute une requête IndexedDB native, en y ajoutant `onerror/onsuccess`, et renvoie une promesse qui rejette/résout avec le résultat.\n\nCela fonctionne bien la plupart du temps. Les exemples sont sur la page lib <https://github.com/jakearchibald/idb>.\n\nDans quelques rares cas, lorsque nous avons besoin de l'objet `request` d'origine, nous pouvons y accéder en tant que propriété `promise.request` de la promesse :\n\n```js\nlet promise = books.add(book); // obtenir une promesse (n'attendez pas son résultat)\n\nlet request = promise.request; // objet de requête natif\nlet transaction = request.transaction; // objet de transaction natif\n\n// ...faites du vaudou IndexedDB natif...\n\nlet result = await promise; // s'il faut encore\n```\n\n## Résumé\n\nIndexedDB peut être considéré comme un \"localStorage sur des stéroïdes\". Il s'agit d'une simple base de données clé-valeur, suffisamment puissante pour les applications hors ligne, mais simple à utiliser.\n\nLe meilleur manuel est la spécification, [l'actuelle](https://www.w3.org/TR/IndexedDB-2/) est 2.0, mais quelques des méthodes de [3.0](https://w3c.github.io/IndexedDB/) (ce n'est pas très différent) sont partiellement pris en charge.\n\nL'utilisation de base peut être décrite en quelques phrases :\n\n1. Obtenez un wrapper de promesses comme [idb](https://github.com/jakearchibald/idb).\n2. Ouvrez une base de données : `idb.openDb(name, version, onupgradeneeded)`\n   - Créez des stockages d'objets et des index dans le gestionnaire `onupgradeneeded` ou effectuez une mise à jour de version si nécessaire.\n3. Pour les demandes :\n   - Créer la transaction `db.transaction('books')` (readwrite si nécessaire).\n   - Récupérez le magasin d'objets `transaction.objectStore('books')`.\n4. Ensuite, pour effectuer une recherche par clé, appelez directement les méthodes du magasin d'objets.\n   - Pour effectuer une recherche par champ d'objet, créez un index.\n5. Si les données ne rentrent pas en mémoire, utilisez un curseur.\n\nVoici une petite application de démonstration :\n\n[codetabs src=\"books\" current=\"index.html\"]\n"
  },
  {
    "path": "6-data-storage/03-indexeddb/books.view/index.html",
    "content": "<!doctype html>\n<script src=\"https://cdn.jsdelivr.net/npm/idb@3.0.2/build/idb.min.js\"></script>\n\n<button onclick=\"addBook()\">Add a book</button>\n<button onclick=\"clearBooks()\">Clear books</button>\n\n<p>Books list:</p>\n\n<ul id=\"listElem\"></ul>\n\n<script>\nlet db;\n\ninit();\n\nasync function init() {\n  db = await idb.openDb('booksDb', 1, db => {\n    db.createObjectStore('books', {keyPath: 'name'});\n  });\n\n  list();\n}\n\nasync function list() {\n  let tx = db.transaction('books');\n  let bookStore = tx.objectStore('books');\n\n  let books = await bookStore.getAll();\n\n  if (books.length) {\n    listElem.innerHTML = books.map(book => `<li>\n        name: ${book.name}, price: ${book.price}\n      </li>`).join('');\n  } else {\n    listElem.innerHTML = '<li>No books yet. Please add books.</li>'\n  }\n\n\n}\n\nasync function clearBooks() {\n  let tx = db.transaction('books', 'readwrite');\n  await tx.objectStore('books').clear();\n  await list();\n}\n\nasync function addBook() {\n  let name = prompt(\"Book name?\");\n  let price = +prompt(\"Book price?\");\n\n  let tx = db.transaction('books', 'readwrite');\n\n  try {\n    await tx.objectStore('books').add({name, price});\n    await list();\n  } catch(err) {\n    if (err.name == 'ConstraintError') {\n      alert(\"Such book exists already\");\n      await addBook();\n    } else {\n      throw err;\n    }\n  }\n}\n\nwindow.addEventListener('unhandledrejection', event => {\n  alert(\"Error: \" + event.reason.message);\n});\n\n</script>\n"
  },
  {
    "path": "6-data-storage/index.md",
    "content": "# Stockage des données dans le navigateur\n"
  },
  {
    "path": "7-animation/1-bezier-curve/article.md",
    "content": "# Courbe de Bézier\n\nLes courbes de Bézier sont utilisées en infographie pour dessiner des formes, pour l'animation CSS et dans bien d'autres domaines.\n\nIls sont très simples, il suffit de les étudier une fois pour se sentir à l'aise dans le monde des graphiques vectoriels et des animations avancées.\n\n```smart header=\"Un peu de théorie s'il vous plait\"\nCet article fournit un aperçu théorique, mais très nécessaire, de ce que sont les courbes de Bézier, tandis que [le suivant](info:css-animations#bezier-curve) montre comment nous pouvons les utiliser pour les animations CSS.\n\nVeuillez prendre votre temps pour lire et comprendre le concept, il vous servira bien.\n```\n\n## Points de contrôle\n\nUne [courbe de Bézier](https://fr.wikipedia.org/wiki/Courbe_de_B%C3%A9zier) est défini par des points de contrôle.\n\nIl peut y en avoir 2, 3, 4 ou plus.\n\nPar exemple, la courbe de deux points:\n\n![](bezier2.svg)\n\nCourbe à trois points:\n\n![](bezier3.svg)\n\nCourbe à quatre points:\n\n![](bezier4.svg)\n\nSi vous regardez attentivement ces courbes, vous pouvez immédiatement remarquer:\n\n1. **Les points ne sont pas toujours sur la courbe.** C'est tout à fait normal, nous verrons plus tard comment la courbe est construite.\n2. **L'ordre de la courbe est égal au nombre de points moins un**.\nPour deux points, nous avons une courbe linéaire (c'est une ligne droite), pour trois points -- une courbe quadratique (parabolique), pour quatre points -- une courbe cubique.\n3. **Une courbe est toujours à l'intérieur de l'[enveloppe convexe](https://fr.wikipedia.org/wiki/Enveloppe_convexe) de points de contrôle:**\n\n    ![](bezier4-e.svg) ![](bezier3-e.svg)\n\nGrâce à cette dernière propriété, il est possible, en infographie, d'optimiser les tests d'intersection. Si les enveloppes convexes ne se croisent pas, les courbes ne se croisent pas non plus. Ainsi, vérifier l'intersection des enveloppes convexes en premier lieu peut donner un résultat très rapide \"pas d'intersection\". La vérification de l'intersection des enveloppes convexes est beaucoup plus facile, car il s'agit de rectangles, de triangles, etc. (voir l'image ci-dessus), des figures beaucoup plus simples que la courbe.\n\n**La principale valeur des courbes de Bézier pour le dessin -- en déplaçant les points, la courbe change *d'une manière intuitive et évidente*.**\n\nEssayez de déplacer les points de contrôle à l'aide d'une souris dans l'exemple ci-dessous:\n\n[iframe src=\"demo.svg?nocpath=1&p=0,0,0.5,0,0.5,1,1,1\" height=370]\n\n**Comme vous pouvez le remarquer, la courbe s'étire le long des lignes tangentielles 1 -> 2 et 3 -> 4.**\n\nAprès un peu de pratique, il devient évident de placer les points pour obtenir la courbe voulue. Et en reliant plusieurs courbes, on peut obtenir pratiquement n'importe quoi.\n\nVoici quelques exemples:\n\n![](bezier-car.svg) ![](bezier-letter.svg) ![](bezier-vase.svg)\n\n## L'algorithme de De Casteljau\n\nIl existe une formule mathématique pour les courbes de Bézier, mais nous y reviendrons un peu plus tard, parce que\n[l'algorithme de De Casteljau](https://fr.wikipedia.org/wiki/Algorithme_de_Casteljau) est identique à la définition mathématique et montre visuellement comment elle est construite.\n\nVoyons tout d'abord l'exemple de 3-points.\n\nVoici la démo, et l'explication qui suit.\n\nLes points de contrôle (1,2 et 3) peuvent être déplacés par la souris. Appuyez sur le bouton \"play\" pour l'exécuter.\n\n[iframe src=\"demo.svg?p=0,0,0.5,1,1,0&animate=1\" height=370]\n\n**L'algorithme de De Casteljau pour la construction de la courbe de Bézier à 3 points:**\n\n1. Dessinez des points de contrôle. Dans la démo ci-dessus, ils sont intitulés: `1`, `2`, `3`.\n2. Construisez des segments entre les points de contrôle 1 -> 2 -> 3. Dans la démonstration ci-dessus, ils sont <span style=\"color:#825E28\">bruns</span>.\n3. Le paramètre `t` passe de `0` à `1`. Dans l'exemple ci-dessus, l'étape `0.05` est atteinte: la boucle parcourt `0, 0.05, 0.1, 0.15, .... 0.95, 1`.\n\n    Pour chacune de ces valeurs de `t`:\n\n    - Sur chaque segment <span style=\"color:#825E28\">brun</span> on prend un point situé sur la distance proportionnelle à `t` de son début. Comme il y a deux segments, nous avons deux points.\n\n        Par exemple, pour `t=0` -- les deux points seront au début des segments, et pour `t=0.25` -- sur les 25% de la longueur du segment depuis le début, pour `t=0.5` -- 50%(le milieu), pour `t=1` -- à la fin des segments.\n\n    - Reliez les points. Sur l'image ci-dessous, le segment de connexion est peint en <span style=\"color:#167490\">bleu</span>.\n\n\n| Pour `t=0.25`             | Pour `t=0.5`            |\n| ------------------------ | ---------------------- |\n| ![](bezier3-draw1.svg)   | ![](bezier3-draw2.svg) |\n\n4. Maintenant, dans le segment <span style=\"color:#167490\">bleu</span> prenez un point sur la distance proportionnelle à la même valeur de `t`. C'est-à-dire que pour `t=0.25` (l'image de gauche) nous avons un point à l'extrémité du quart gauche du segment, et pour `t=0.5` (l'image de droite) -- au milieu du segment. Sur les images ci-dessus, ce point est <span style=\"color:red\">rouge</span>.\n\n5. Comme `t` va de `0` à `1`, chaque valeur de `t` ajoute un point à la courbe. L'ensemble de ces points forme la courbe de Bézier. Elle est rouge et parabolique sur les images ci-dessus.\n\nC'était un processus pour 3 points. Mais c'est la même chose pour 4 points.\n\nLa démo pour 4 points (les points peuvent être déplacés à la souris):\n\n[iframe src=\"demo.svg?p=0,0,0.5,0,0.5,1,1,1&animate=1\" height=370]\n\nL'algorithme pour 4 points:\n\n- Relier les points de contrôle par des segments: 1 -> 2, 2 -> 3, 3 -> 4. Il y aura 3 segments <span style=\"color:#825E28\">bruns</span>.\n- Pour chaque `t` dans l'intervalle de `0` à `1`:\n    - On prend des points sur ces segments sur la distance proportionnelle à `t` depuis le début. Ces points sont reliés, de sorte que nous avons deux <span style=\"color:#0A0\">segments verts</span>.\n    - Sur ces segments, nous prenons des points proportionnels à `t`. Nous obtenons un <span style=\"color:#167490\">segment bleu</span>.\n    - Sur le segment bleu, on prend un point proportionnel à `t`. Dans l'exemple ci-dessus, c'est <span style=\"color:red\">le point rouge</span>.\n- Ces points forment ensemble la courbe.\n\nL'algorithme est récursif et peut être généralisé pour un nombre quelconque de points de contrôle.\n\nÉtant donné N de points de contrôle:\n\n1. Nous les connectons pour obtenir initialement N-1 segments.\n2. Puis pour chaque `t` de `0` à `1`, on prend un point sur chaque segment sur la distance proportionnelle à `t` et on les relie. Il y aura N-2 segments.\n3. Répétez l'étape 2 jusqu'à ce qu'il n'y ait plus qu'un seul point.\n\nCes points forment la courbe.\n\n```online\n**Exécutez et mettez en pause les exemples pour voir clairement les segments et la façon dont la courbe est construite.**\n```\n\n\nUne courbe qui ressemble à `y=1/t`:\n\n[iframe src=\"demo.svg?p=0,0,0,0.75,0.25,1,1,1&animate=1\" height=370]\n\nLes points de contrôle en zig-zag fonctionnent également très bien:\n\n[iframe src=\"demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1\" height=370]\n\nFaire une boucle est possible:\n\n[iframe src=\"demo.svg?p=0,0,1,0.5,0,1,0.5,0&animate=1\" height=370]\n\nune courbe de Bézier non lisse (oui, c'est possible aussi):\n\n[iframe src=\"demo.svg?p=0,0,1,1,0,1,1,0&animate=1\" height=370]\n\n```online\nSi quelque chose n'est pas clair dans la description de l'algorithme, veuillez consulter les exemples en direct ci-dessus pour voir comment la courbe est construite.\n```\n\nComme l'algorithme est récursif, nous pouvons construire des courbes de Bézier de n'importe quel ordre, c'est-à-dire en utilisant 5, 6 ou plus de points de contrôle. Mais en pratique, de nombreux points sont moins utiles. En général, nous prenons 2 ou 3 points, et pour les lignes complexes, nous collons plusieurs courbes ensemble. C'est plus simple à développer et à calculer.\n\n```smart header=\"Comment dessiner une courbe par des points donnés ?\"\nPour spécifier une courbe de Bézier, on utilise des points de contrôle. Comme nous pouvons le voir, ils ne sont pas sur la courbe, sauf le premier et le dernier.\n\nParfois, nous avons une autre tâche : dessiner une courbe *par plusieurs points*, de manière à ce qu'ils se trouvent tous sur une seule courbe lisse. Cette tâche s'appelle l'[interpolation numérique](https://fr.wikipedia.org/wiki/Interpolation_num%C3%A9rique), et ici nous ne le traitons pas.\n\nIl existe des formules mathématiques pour de telles courbes, par exemple le [polynôme de Lagrange](https://fr.wikipedia.org/wiki/Interpolation_lagrangienne). En infographie l'[interpolation spline](https://en.wikipedia.org/wiki/Spline_interpolation) est souvent utilisé pour construire des courbes lisses qui relient plusieurs points.\n```\n\n\n## Mathématiques\n\nUne courbe de Bézier peut être décrite à l'aide d'une formule mathématique.\n\nComme nous l'avons vu, il n'est pas nécessaire de le savoir, la plupart des gens se contentent de dessiner la courbe en déplaçant des points avec la souris. Mais si vous aimez les maths, voici la solution.\n\nÉtant donné les coordonnées des points de contrôle <code>P<sub>i</sub></code>: le premier point de contrôle a des coordonnées <code>P<sub>1</sub> = (x<sub>1</sub>, y<sub>1</sub>)</code>, le second: <code>P<sub>2</sub> = (x<sub>2</sub>, y<sub>2</sub>)</code>, et ainsi de suite, les coordonnées de la courbe sont décrites par l'équation qui dépend du paramètre `t` du segment `[0,1]`.\n\n- La formule pour une courbe à 2 points:\n\n    <code>P = (1-t)P<sub>1</sub> + tP<sub>2</sub></code>\n- Pour 3 points de contrôle:\n\n    <code>P = (1−t)<sup>2</sup>P<sub>1</sub> + 2(1−t)tP<sub>2</sub> + t<sup>2</sup>P<sub>3</sub></code>\n- Pour 4 points de contrôle:\n\n    <code>P = (1−t)<sup>3</sup>P<sub>1</sub> + 3(1−t)<sup>2</sup>tP<sub>2</sub>  +3(1−t)t<sup>2</sup>P<sub>3</sub> + t<sup>3</sup>P<sub>4</sub></code>\n\n\nCe sont des équations vectorielles. En d'autres termes, nous pouvons mettre `x` et `y` à la place de `P` pour obtenir les coordonnées correspondantes.\n\nPar exemple, la courbe à 3 points est formée par les points `(x,y)` calculés comme suit:\n\n- <code>x = (1−t)<sup>2</sup>x<sub>1</sub> + 2(1−t)tx<sub>2</sub> + t<sup>2</sup>x<sub>3</sub></code>\n- <code>y = (1−t)<sup>2</sup>y<sub>1</sub> + 2(1−t)ty<sub>2</sub> + t<sup>2</sup>y<sub>3</sub></code>\n\nAu lieu de <code>x<sub>1</sub>, y<sub>1</sub>, x<sub>2</sub>, y<sub>2</sub>, x<sub>3</sub>, y<sub>3</sub></code> on devrait mettre les coordonnées de 3 points de contrôle, et ensuite quand `t` se déplace de `0` à `1`, pour chaque valeur de `t` on aura `(x,y)` de la courbe.\n\nPar exemple, si les points de contrôle sont `(0,0)`, `(0.5, 1)` et `(1, 0)`, les équations deviennent:\n\n- <code>x = (1−t)<sup>2</sup> * 0 + 2(1−t)t * 0.5 + t<sup>2</sup> * 1 = (1-t)t + t<sup>2</sup> = t</code>\n- <code>y = (1−t)<sup>2</sup> * 0 + 2(1−t)t * 1 + t<sup>2</sup> * 0 = 2(1-t)t = –2t<sup>2</sup> + 2t</code>\n\nMaintenant que `t` va de `0` à `1`, l'ensemble des valeurs `(x,y)` pour chaque `t` forme la courbe pour ces points de contrôle.\n\n## Résumé\n\nLes courbes de Bézier sont définies par leurs points de contrôle.\n\nNous avons vu deux définitions des courbes de Bézier:\n\n1. Un procédé de dessin : L'algorithme de De Casteljau.\n2. Une formule mathématique.\n\nPropriétés intéréssantes des courbes de Bézier:\n\n- Nous pouvons dessiner des lignes lisses avec une souris en déplaçant les points de contrôle.\n- Les formes complexes peuvent être constituées de plusieurs courbes de Bezier.\n\nUtilisation:\n\n- En infographie, modélisation, éditeurs de graphiques vectoriels. Les polices sont décrites par des courbes de Bézier.\n- Dans le domaine du développement web -- pour les graphiques sur Canvas et au format SVG. À ce propos, les exemples \"en direct\" ci-dessus sont écrits en SVG. Il s'agit en fait d'un seul document SVG auquel on donne différents points comme paramètres. Vous pouvez l'ouvrir dans une fenêtre distincte et voir la source: [demo.svg](demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1).\n- Dans les animations CSS, pour décrire la trajectoire et la vitesse de l'animation.\n"
  },
  {
    "path": "7-animation/2-css-animations/1-animate-logo-css/solution.md",
    "content": "\nCSS to animate both `width` and `height`:\n```css\n/* original class */\n\n#flyjet {\n  transition: all 3s;\n}\n\n/* JS adds .growing */\n#flyjet.growing {\n  width: 400px;\n  height: 240px;\n}\n```\n\nPlease note that `transitionend` triggers two times -- once for every property. So if we don't perform an additional check then the message would show up 2 times.\n"
  },
  {
    "path": "7-animation/2-css-animations/1-animate-logo-css/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    img {\n      cursor: pointer;\n    }\n  </style>\n  <style>\n    #flyjet {\n      width: 40px;\n      height: 24px;\n      transition: all 3s;\n    }\n\n    #flyjet.growing {\n      width: 400px;\n      height: 240px;\n    }\n  </style>\n</head>\n\n<body>\n\n  <img id=\"flyjet\" src=\"https://en.js.cx/clipart/flyjet.jpg\">\n\n  <script>\n    let ended = false; // will change to true after the animation finishes\n\n    flyjet.onclick = function() {\n\n      flyjet.addEventListener('transitionend', function() {\n        if (!ended) { // check to show the message only once\n          ended = true;\n          alert('Done!');\n        }\n      });\n\n      flyjet.classList.add('growing');\n    }\n  </script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/1-animate-logo-css/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    img {\n      cursor: pointer;\n    }\n  </style>\n  <style>\n    #flyjet {\n      width: 40px;\n      /* -> 400px */\n\n      height: 24px;\n      /* -> 240px */\n    }\n  </style>\n</head>\n\n<body>\n\n  <img id=\"flyjet\" src=\"https://en.js.cx/clipart/flyjet.jpg\">\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/1-animate-logo-css/task.md",
    "content": "importance: 5\n\n---\n\n# Animate a plane (CSS)\n\nShow the animation like on the picture below (click the plane):\n\n[iframe src=\"solution\" height=300]\n\n- The picture grows on click from `40x24px` to `400x240px` (10 times larger).\n- The animation takes 3 seconds.\n- At the end output: \"Done!\".\n- During the animation process, there may be more clicks on the plane. They shouldn't \"break\" anything.\n"
  },
  {
    "path": "7-animation/2-css-animations/2-animate-logo-bezier-css/solution.md",
    "content": "We need to choose the right Bezier curve for that animation. It should have `y>1` somewhere for the plane to \"jump out\".\n\nFor instance, we can take both control points with `y>1`, like: `cubic-bezier(0.25, 1.5, 0.75, 1.5)`.\n\nThe graph:\n\n![](bezier-up.svg)\n"
  },
  {
    "path": "7-animation/2-css-animations/2-animate-logo-bezier-css/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    img {\n      display: block;\n      cursor: pointer;\n    }\n  </style>\n  <style>\n    #flyjet {\n      width: 40px;\n      height: 24px;\n      transition: all 3s cubic-bezier(0.25, 1.5, 0.75, 1.5);\n    }\n\n    #flyjet.growing {\n      width: 400px;\n      height: 240px;\n    }\n  </style>\n</head>\n\n<body>\n\n  <img id=\"flyjet\" src=\"https://en.js.cx/clipart/flyjet.jpg\">\n\n  <script>\n    flyjet.onclick = function() {\n      flyjet.classList.add('growing');\n    };\n  </script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/2-animate-logo-bezier-css/task.md",
    "content": "importance: 5\n\n---\n\n# Animate the flying plane (CSS)\n\nModify the solution of the previous task <info:task/animate-logo-css> to make the plane grow more than its original size 400x240px (jump out), and then return to that size.\n\nHere's how it should look (click on the plane):\n\n[iframe src=\"solution\" height=350]\n\nTake the solution of the previous task as the source.\n"
  },
  {
    "path": "7-animation/2-css-animations/3-animate-circle/solution.md",
    "content": ""
  },
  {
    "path": "7-animation/2-css-animations/3-animate-circle/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    .circle {\n      transition-property: width, height;\n      transition-duration: 2s;\n      position: fixed;\n      transform: translateX(-50%) translateY(-50%);\n      background-color: red;\n      border-radius: 50%;\n    }\n  </style>\n</head>\n\n<body>\n\n  <button onclick=\"showCircle(150, 150, 100)\">showCircle(150, 150, 100)</button>\n\n  <script>\n  function showCircle(cx, cy, radius) {\n    let div = document.createElement('div');\n    div.style.width = 0;\n    div.style.height = 0;\n    div.style.left = cx + 'px';\n    div.style.top = cy + 'px';\n    div.className = 'circle';\n    document.body.append(div);\n\n    setTimeout(() => {\n      div.style.width = radius * 2 + 'px';\n      div.style.height = radius * 2 + 'px';\n    }, 0);\n  }\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/3-animate-circle/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    .circle {\n      transition-property: width, height;\n      transition-duration: 2s;\n      position: fixed;\n      transform: translateX(-50%) translateY(-50%);\n      background-color: red;\n      border-radius: 50%;\n\n      width: 200px;\n      height: 200px;\n      top: 150px;\n      left: 150px;\n    }\n  </style>\n</head>\n\n<body>\n\n  <div class=\"circle\"></div>\n\n</body>\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/3-animate-circle/task.md",
    "content": "importance: 5\n\n---\n\n# Animated circle\n\nCreate a function `showCircle(cx, cy, radius)` that shows an animated growing circle.\n\n- `cx,cy` are window-relative coordinates of the center of the circle,\n- `radius` is the radius of the circle.\n\nClick the button below to see how it should look like:\n\n[iframe src=\"solution\" height=260]\n\nThe source document has an example of a circle with right styles, so the task is precisely to do the animation right.\n"
  },
  {
    "path": "7-animation/2-css-animations/4-animate-circle-callback/solution.md",
    "content": ""
  },
  {
    "path": "7-animation/2-css-animations/4-animate-circle-callback/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    .message-ball {\n      font-size: 20px;\n      line-height: 200px;\n      text-align: center;\n    }\n    .circle {\n      transition-property: width, height;\n      transition-duration: 2s;\n      position: fixed;\n      transform: translateX(-50%) translateY(-50%);\n      background-color: red;\n      border-radius: 50%;\n    }\n  </style>\n</head>\n\n<body>\n\n  <button onclick=\"go()\">Click me</button>\n\n  <script>\n\n  function go() {\n    showCircle(150, 150, 100, div => {\n      div.classList.add('message-ball');\n      div.append(\"Hello, world!\");\n    });\n  }\n\n  function showCircle(cx, cy, radius, callback) {\n    let div = document.createElement('div');\n    div.style.width = 0;\n    div.style.height = 0;\n    div.style.left = cx + 'px';\n    div.style.top = cy + 'px';\n    div.className = 'circle';\n    document.body.append(div);\n\n    setTimeout(() => {\n      div.style.width = radius * 2 + 'px';\n      div.style.height = radius * 2 + 'px';\n\n      div.addEventListener('transitionend', function handler() {\n        div.removeEventListener('transitionend', handler);\n        callback(div);\n      });\n    }, 0);\n  }\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/4-animate-circle-callback/task.md",
    "content": "\n# Animated circle with callback\n\nIn the task <info:task/animate-circle> an animated growing circle is shown.\n\nNow let's say we need not just a circle, but to show a message inside it. The message should appear *after* the animation is complete (the circle is fully grown), otherwise it would look ugly.\n\nIn the solution of the task, the function `showCircle(cx, cy, radius)` draws the circle, but gives no way to track when it's ready.\n\nAdd a callback argument: `showCircle(cx, cy, radius, callback)` to be called when the animation is complete. The `callback` should receive the circle `<div>` as an argument.\n\nHere's the example:\n\n```js\nshowCircle(150, 150, 100, div => {\n  div.classList.add('message-ball');\n  div.append(\"Hello, world!\");\n});\n```\n\nDemo:\n\n[iframe src=\"solution\" height=260]\n\nTake the solution of the task <info:task/animate-circle> as the base.\n"
  },
  {
    "path": "7-animation/2-css-animations/article.md",
    "content": "# CSS-animations\n\nCSS animations make it possible to do simple animations without JavaScript at all.\n\nJavaScript can be used to control CSS animations and make them even better, with little code.\n\n## CSS transitions [#css-transition]\n\nThe idea of CSS transitions is simple. We describe a property and how its changes should be animated. When the property changes, the browser paints the animation.\n\nThat is, all we need is to change the property, and the fluid transition will be done by the browser.\n\nFor instance, the CSS below animates changes of `background-color` for 3 seconds:\n\n```css\n.animated {\n  transition-property: background-color;\n  transition-duration: 3s;\n}\n```\n\nNow if an element has `.animated` class, any change of `background-color` is animated during 3 seconds.\n\nClick the button below to animate the background:\n\n```html run autorun height=60\n<button id=\"color\">Click me</button>\n\n<style>\n  #color {\n    transition-property: background-color;\n    transition-duration: 3s;\n  }\n</style>\n\n<script>\n  color.onclick = function() {\n    this.style.backgroundColor = 'red';\n  };\n</script>\n```\n\nThere are 4 properties to describe CSS transitions:\n\n- `transition-property`\n- `transition-duration`\n- `transition-timing-function`\n- `transition-delay`\n\nWe'll cover them in a moment, for now let's note that the common `transition` property allows declaring them together in the order: `property duration timing-function delay`, as well as animating multiple properties at once.\n\nFor instance, this button animates both `color` and `font-size`:\n\n```html run height=80 autorun no-beautify\n<button id=\"growing\">Click me</button>\n\n<style>\n#growing {\n*!*\n  transition: font-size 3s, color 2s;\n*/!*\n}\n</style>\n\n<script>\ngrowing.onclick = function() {\n  this.style.fontSize = '36px';\n  this.style.color = 'red';\n};\n</script>\n```\n\nNow, let's cover animation properties one by one.\n\n## transition-property\n\nIn `transition-property`, we write a list of properties to animate, for instance: `left`, `margin-left`, `height`, `color`. Or we could write `all`, which means \"animate all properties\".\n\nDo note that, there are properties which can not be animated. However, [most of the generally used properties are animatable](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties).\n\n## transition-duration\n\nIn `transition-duration` we can specify how long the animation should take. The time should be in [CSS time format](https://www.w3.org/TR/css3-values/#time): in seconds `s` or milliseconds `ms`.\n\n## transition-delay\n\nIn `transition-delay` we can specify the delay *before* the animation. For instance, if `transition-delay` is `1s` and `transition-duration` is `2s`, then the animation starts 1 second after the property change and the total duration will be 2 seconds.\n\nNegative values are also possible. Then the animation is shown immediately, but the starting point of the animation will be after given value (time). For example, if `transition-delay` is `-1s` and `transition-duration` is `2s`, then animation starts from the halfway point and total duration will be 1 second.\n\nHere the animation shifts numbers from `0` to `9` using CSS `translate` property:\n\n[codetabs src=\"digits\"]\n\nThe `transform` property is animated like this:\n\n```css\n#stripe.animate {\n  transform: translate(-90%);\n  transition-property: transform;\n  transition-duration: 9s;\n}\n```\n\nIn the example above JavaScript adds the class `.animate` to the element -- and the animation starts:\n\n```js\nstripe.classList.add('animate');\n```\n\nWe could also start it from somewhere in the middle of the transition, from an exact number, e.g. corresponding to the current second, using a negative `transition-delay`.\n\nHere if you click the digit -- it starts the animation from the current second:\n\n[codetabs src=\"digits-negative-delay\"]\n\nJavaScript does it with an extra line:\n\n```js\nstripe.onclick = function() {\n  let sec = new Date().getSeconds() % 10;\n*!*\n  // for instance, -3s here starts the animation from the 3rd second\n  stripe.style.transitionDelay = '-' + sec + 's';\n*/!*\n  stripe.classList.add('animate');\n};\n```\n\n## transition-timing-function\n\nThe timing function describes how the animation process is distributed along its timeline. Will it start slowly and then go fast, or vice versa.\n\nIt appears to be the most complicated property at first. But it becomes very simple if we devote a bit time to it.\n\nThat property accepts two kinds of values: a Bezier curve or steps. Let's start with the curve, as it's used more often.\n\n### Bezier curve\n\nThe timing function can be set as a [Bezier curve](/bezier-curve) with 4 control points that satisfy the conditions:\n\n1. First control point: `(0,0)`.\n2. Last control point: `(1,1)`.\n3. For intermediate points, the values of `x` must be in the interval `0..1`, `y` can be anything.\n\nThe syntax for a Bezier curve in CSS: `cubic-bezier(x2, y2, x3, y3)`. Here we need to specify only 2nd and 3rd control points, because the 1st one is fixed to `(0,0)` and the 4th one is `(1,1)`.\n\nThe timing function describes how fast the animation process goes.\n\n- The `x` axis is the time: `0` -- the start, `1` -- the end of `transition-duration`.\n- The `y` axis specifies the completion of the process: `0` -- the starting value of the property, `1` -- the final value.\n\nThe simplest variant is when the animation goes uniformly, with the same linear speed. That can be specified by the curve `cubic-bezier(0, 0, 1, 1)`.\n\nHere's how that curve looks:\n\n![](bezier-linear.svg)\n\n...As we can see, it's just a straight line. As the time (`x`) passes, the completion (`y`) of the animation steadily goes from `0` to `1`.\n\nThe train in the example below goes from left to right with the permanent speed (click it):\n\n[codetabs src=\"train-linear\"]\n\nThe CSS `transition` is based on that curve:\n\n```css\n.train {\n  left: 0;\n  transition: left 5s cubic-bezier(0, 0, 1, 1);\n  /* click on a train sets left to 450px, thus triggering the animation */\n}\n```\n\n...And how can we show a train slowing down?\n\nWe can use another Bezier curve: `cubic-bezier(0.0, 0.5, 0.5 ,1.0)`.\n\nThe graph:\n\n![](train-curve.svg)\n\nAs we can see, the process starts fast: the curve soars up high, and then slower and slower.\n\nHere's the timing function in action (click the train):\n\n[codetabs src=\"train\"]\n\nCSS:\n```css\n.train {\n  left: 0;\n  transition: left 5s cubic-bezier(0, .5, .5, 1);\n  /* click on a train sets left to 450px, thus triggering the animation */\n}\n```\n\nThere are several built-in curves: `linear`, `ease`, `ease-in`, `ease-out` and `ease-in-out`.\n\nThe `linear` is a shorthand for `cubic-bezier(0, 0, 1, 1)` -- a straight line, which we described above.\n\nOther names are shorthands for the following `cubic-bezier`:\n\n| <code>ease</code><sup>*</sup> | <code>ease-in</code> | <code>ease-out</code> | <code>ease-in-out</code> |\n|-------------------------------|----------------------|-----------------------|--------------------------|\n| <code>(0.25, 0.1, 0.25, 1.0)</code> | <code>(0.42, 0, 1.0, 1.0)</code> | <code>(0, 0, 0.58, 1.0)</code> | <code>(0.42, 0, 0.58, 1.0)</code> |\n| ![ease, figure](ease.svg) | ![ease-in, figure](ease-in.svg) | ![ease-out, figure](ease-out.svg) | ![ease-in-out, figure](ease-in-out.svg) |\n\n`*` -- by default, if there's no timing function, `ease` is used.\n\nSo we could use `ease-out` for our slowing down train:\n\n```css\n.train {\n  left: 0;\n  transition: left 5s ease-out;\n  /* same as transition: left 5s cubic-bezier(0, .5, .5, 1); */\n}\n```\n\nBut it looks a bit differently.\n\n**A Bezier curve can make the animation exceed its range.**\n\nThe control points on the curve can have any `y` coordinates: even negative or huge ones. Then the Bezier curve would also extend very low or high, making the animation go beyond its normal range.\n\nIn the example below the animation code is:\n\n```css\n.train {\n  left: 100px;\n  transition: left 5s cubic-bezier(.5, -1, .5, 2);\n  /* click on a train sets left to 450px */\n}\n```\n\nThe property `left` should animate from `100px` to `400px`.\n\nBut if you click the train, you'll see that:\n\n- First, the train goes *back*: `left` becomes less than `100px`.\n- Then it goes forward, a little bit farther than `400px`.\n- And then back again -- to `400px`.\n\n[codetabs src=\"train-over\"]\n\nWhy it happens is pretty obvious if we look at the graph of the given Bezier curve:\n\n![](bezier-train-over.svg)\n\nWe moved the `y` coordinate of the 2nd point below zero, and for the 3rd point we made it over `1`, so the curve goes out of the \"regular\" quadrant. The `y` is out of the \"standard\" range `0..1`.\n\nAs we know, `y` measures \"the completion of the animation process\". The value `y = 0` corresponds to the starting property value and `y = 1` -- the ending value. So values `y<0` move the property beyond the starting `left` and `y>1` -- past the final `left`.\n\nThat's a \"soft\" variant for sure. If we put `y` values like `-99` and `99` then the train would jump out of the range much more.\n\nBut how do we make a Bezier curve for a specific task? There are many tools.\n\n- For instance, we can do it on the site <https://cubic-bezier.com>.\n- Browser developer tools also have special support for Bezier curves in CSS:\n    1. Open the developer tools with `key:F12` (Mac: `key:Cmd+Opt+I`).\n    2. Select the `Elements` tab, then pay attention to the `Styles` sub-panel at the right side.\n    3. CSS properties with a word `cubic-bezier` will have an icon before this word.\n    4. Click this icon to edit the curve.\n\n\n### Steps\n\nThe timing function `steps(number of steps[, start/end])` allows splitting an transition into multiple steps.\n\nLet's see that in an example with digits.\n\nHere's a list of digits, without any animations, just as a source:\n\n[codetabs src=\"step-list\"]\n\nIn the HTML, a stripe of digits is enclosed into a fixed-length `<div id=\"digits\">`:\n\n```html\n<div id=\"digit\">\n  <div id=\"stripe\">0123456789</div>\n</div>\n```\n\nThe `#digit` div has a fixed width and a border, so it looks like a red window.\n\nWe'll make a timer: the digits will appear one by one, in a discrete way.\n\nTo achieve that, we'll hide the `#stripe` outside of `#digit` using `overflow: hidden`, and then shift the `#stripe` to the left step-by-step.\n\nThere will be 9 steps, a step-move for each digit:\n\n```css\n#stripe.animate  {\n  transform: translate(-90%);\n  transition: transform 9s *!*steps(9, start)*/!*;\n}\n```\n\nThe first argument of `steps(9, start)` is the number of steps. The transform will be split into 9 parts (10% each). The time interval is automatically divided into 9 parts as well, so `transition: 9s` gives us 9 seconds for the whole animation – 1 second per digit.\n\nThe second argument is one of two words: `start` or `end`.\n\nThe `start` means that in the beginning of animation we need to make the first step immediately.\n\nIn action:\n\n[codetabs src=\"step\"]\n\nA click on the digit changes it to `1` (the first step) immediately, and then changes in the beginning of the next second.\n\nThe process is progressing like this:\n\n- `0s` -- `-10%` (first change in the beginning of the 1st second, immediately)\n- `1s` -- `-20%`\n- ...\n- `8s` -- `-90%`\n- (the last second shows the final value).\n\nHere, the first change was immediate because of `start` in the `steps`.\n\nThe alternative value `end` would mean that the change should be applied not in the beginning, but at the end of each second.\n\nSo the process for `steps(9, end)` would go like this:\n\n- `0s` -- `0` (during the first second nothing changes)\n- `1s` -- `-10%` (first change at the end of the 1st second)\n- `2s` -- `-20%`\n- ...\n- `9s` -- `-90%`\n\nHere's `steps(9, end)` in action (note the pause before the first digit change):\n\n[codetabs src=\"step-end\"]\n\nThere are also some pre-defined shorthands for `steps(...)`:\n\n- `step-start` -- is the same as `steps(1, start)`. That is, the animation starts immediately and takes 1 step. So it starts and finishes immediately, as if there were no animation.\n- `step-end` -- the same as `steps(1, end)`: make the animation in a single step at the end of `transition-duration`.\n\nThese values are rarely used, as they represent not a real animation, but rather a single-step change. We mention them here for completeness.\n\n## Event: \"transitionend\"\n\nWhen the CSS animation finishes, the `transitionend` event triggers.\n\nIt is widely used to do an action after the animation is done. Also we can join animations.\n\nFor instance, the ship in the example below starts to sail there and back when clicked, each time farther and farther to the right:\n\n[iframe src=\"boat\" height=300 edit link]\n\nThe animation is initiated by the function `go` that re-runs each time the transition finishes, and flips the direction:\n\n```js\nboat.onclick = function() {\n  //...\n  let times = 1;\n\n  function go() {\n    if (times % 2) {\n      // sail to the right\n      boat.classList.remove('back');\n      boat.style.marginLeft = 100 * times + 200 + 'px';\n    } else {\n      // sail to the left\n      boat.classList.add('back');\n      boat.style.marginLeft = 100 * times - 200 + 'px';\n    }\n\n  }\n\n  go();\n\n  boat.addEventListener('transitionend', function() {\n    times++;\n    go();\n  });\n};\n```\n\nThe event object for `transitionend` has a few specific properties:\n\n`event.propertyName`\n: The property that has finished animating. Can be good if we animate multiple properties simultaneously.\n\n`event.elapsedTime`\n: The time (in seconds) that the animation took, without `transition-delay`.\n\n## Keyframes\n\nWe can join multiple simple animations together using the `@keyframes` CSS rule.\n\nIt specifies the \"name\" of the animation and rules - what, when and where to animate. Then using the `animation` property, we can attach the animation to the element and specify additional parameters for it.\n\nHere's an example with explanations:\n\n```html run height=60 autorun=\"no-epub\" no-beautify\n<div class=\"progress\"></div>\n\n<style>\n*!*\n  @keyframes go-left-right {        /* give it a name: \"go-left-right\" */\n    from { left: 0px; }             /* animate from left: 0px */\n    to { left: calc(100% - 50px); } /* animate to left: 100%-50px */\n  }\n*/!*\n\n  .progress {\n*!*\n    animation: go-left-right 3s infinite alternate;\n    /* apply the animation \"go-left-right\" to the element\n       duration 3 seconds\n       number of times: infinite\n       alternate direction every time\n    */\n*/!*\n\n    position: relative;\n    border: 2px solid green;\n    width: 50px;\n    height: 20px;\n    background: lime;\n  }\n</style>\n```\n\nThere are many articles about `@keyframes` and a [detailed specification](https://drafts.csswg.org/css-animations/).\n\nYou probably won't need `@keyframes` often, unless everything is in constant motion on your sites.\n\n## Performance\n\nMost CSS properties can be animated, because most of them are numeric values. For instance, `width`, `color`, `font-size` are all numbers. When you animate them, the browser gradually changes these numbers frame by frame, creating a smooth effect.\n\nHowever, not all animations will look as smooth as you'd like, because different CSS properties cost differently to change.\n\nIn more technical details, when there's a style change, the browser goes through 3 steps to render the new look:\n\n1. **Layout**: re-compute the geometry and position of each element, then\n2. **Paint**: re-compute how everything should look like at their places, including background, colors,\n3. **Composite**: render the final results into pixels on screen, apply CSS transforms if they exist.\n\nDuring a CSS animation, this process repeats every frame. However, CSS properties that never affect geometry or position, such as `color`, may skip the Layout step. If a `color` changes, the browser  doesn't calculate any new geometry, it goes to Paint -> Composite. And there are few properties that directly go to Composite. You can find a longer list of CSS properties and which stages they trigger at <https://csstriggers.com>.\n\nThe calculations may take time, especially on pages with many elements and a complex layout. And the delays are actually visible on most devices, leading to \"jittery\", less fluid animations.\n\nAnimations of properties that skip the Layout step are faster. It's even better if Paint is skipped too.\n\nThe `transform` property is a great choice, because:\n- CSS transforms affect the target element box as a whole (rotate, flip, stretch, shift it).\n- CSS transforms never affect neighbour elements.\n\n...So browsers apply `transform` \"on top\" of existing Layout and Paint calculations, in the Composite stage.\n\nIn other words, the browser calculates the Layout (sizes, positions), paints it with colors, backgrounds, etc at the Paint stage, and then applies `transform` to element boxes that need it.\n\nChanges (animations) of the `transform` property never trigger Layout and Paint steps. More than that, the browser  leverages the graphics accelerator (a special chip on the CPU or graphics card) for CSS transforms, thus making them very efficient.\n\nLuckily, the `transform` property is very powerful. By using `transform` on an element, you could rotate and flip it, stretch and shrink it, move it around, and [much more](https://developer.mozilla.org/docs/Web/CSS/transform#syntax). So instead of `left/margin-left` properties we can use `transform: translateX(…)`, use `transform: scale` for increasing element size, etc.\n\nThe `opacity` property also never triggers Layout (also skips Paint in Mozilla Gecko). We can use it for show/hide or fade-in/fade-out effects.\n\nParing `transform` with `opacity` can usually solve most of our needs, providing fluid, good-looking animations.\n\nFor example, here clicking on the `#boat` element adds the class with `transform: translateX(300)` and `opacity: 0`, thus making it move `300px` to the right and disappear:\n\n```html run height=260 autorun no-beautify\n<img src=\"https://js.cx/clipart/boat.png\" id=\"boat\">\n\n<style>\n#boat {\n  cursor: pointer;\n  transition: transform 2s ease-in-out, opacity 2s ease-in-out;\n}\n\n.move {\n  transform: translateX(300px);\n  opacity: 0;\n}\n</style>\n<script>\n  boat.onclick = () => boat.classList.add('move');\n</script>\n```\n\nHere's a more complex example, with `@keyframes`:\n\n```html run height=80 autorun no-beautify\n<h2 onclick=\"this.classList.toggle('animated')\">click me to start / stop</h2>\n<style>\n  .animated {\n    animation: hello-goodbye 1.8s infinite;\n    width: fit-content;\n  }\n  @keyframes hello-goodbye {\n    0% {\n      transform: translateY(-60px) rotateX(0.7turn);\n      opacity: 0;\n    }\n    50% {\n      transform: none;\n      opacity: 1;\n    }\n    100% {\n      transform: translateX(230px) rotateZ(90deg) scale(0.5);\n      opacity: 0;\n    }\n  }\n</style>\n```\n\n## Summary\n\nCSS animations allow smoothly (or step-by-step) animated changes of one or multiple CSS properties.\n\nThey are good for most animation tasks. We're also able to use JavaScript for animations, the next chapter is devoted to that.\n\nLimitations of CSS animations compared to JavaScript animations:\n\n```compare plus=\"CSS animations\" minus=\"JavaScript animations\"\n+ Simple things done simply.\n+ Fast and lightweight for CPU.\n- JavaScript animations are flexible. They can implement any animation logic, like an \"explosion\" of an element.\n- Not just property changes. We can create new elements in JavaScript as part of the animation.\n```\n\nIn early examples in this chapter, we animate `font-size`, `left`, `width`, `height`, etc. In real life projects, we should use `transform: scale()` and `transform: translate()` for better performance.\n\nThe majority of animations can be implemented using CSS as described in this chapter. And the `transitionend` event allows JavaScript to be run after the animation, so it integrates fine with the code.\n\nBut in the next chapter we'll do some JavaScript animations to cover more complex cases.\n"
  },
  {
    "path": "7-animation/2-css-animations/boat.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <img src=\"https://js.cx/clipart/boat.png\" id=\"boat\">\n\n  <script>\n    boat.onclick = function() {\n\n      this.onclick = null; // only the first click should start the animation\n\n      let times = 1;\n\n      function go() {\n        if (times % 2) {\n          boat.classList.remove('back');\n          boat.style.marginLeft = 100 * times + 200 + 'px';\n        } else {\n          boat.classList.add('back');\n          boat.style.marginLeft = 100 * times - 200 + 'px';\n        }\n\n      }\n\n      go();\n\n      boat.addEventListener('transitionend', function() {\n        times++;\n        go();\n      });\n    }\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/boat.view/style.css",
    "content": "#boat {\n  margin-left: 0;\n  cursor: pointer;\n  transition: margin-left 3s ease-in-out;\n}\n\n/* flipping the picture with CSS */\n.back {\n  transform: scaleX(-1);\n  filter: fliph;\n}\n"
  },
  {
    "path": "7-animation/2-css-animations/digits-negative-delay.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  Click below to animate:\n  <div id=\"digit\"><div id=\"stripe\">0123456789</div></div>\n\n  <script src=\"script.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/digits-negative-delay.view/script.js",
    "content": "stripe.onclick = function() {\n  let sec = new Date().getSeconds() % 10;\n  stripe.style.transitionDelay = '-' + sec + 's';\n  stripe.classList.add('animate');\n};"
  },
  {
    "path": "7-animation/2-css-animations/digits-negative-delay.view/style.css",
    "content": "#digit {\n  width: .5em;\n  overflow: hidden;\n  font: 32px monospace;\n  cursor: pointer;\n}\n\n#stripe {\n  display: inline-block\n}\n\n#stripe.animate {\n  transform: translate(-90%);\n  transition-property: transform;\n  transition-duration: 9s;\n  transition-timing-function: linear;\n}\n"
  },
  {
    "path": "7-animation/2-css-animations/digits.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  Click below to animate:\n\n  <div id=\"digit\"><div id=\"stripe\">0123456789</div></div>\n\n  <script src=\"script.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/digits.view/script.js",
    "content": "stripe.onclick = function() {\n  stripe.classList.add('animate');\n};"
  },
  {
    "path": "7-animation/2-css-animations/digits.view/style.css",
    "content": "#digit {\n  width: .5em;\n  overflow: hidden;\n  font: 32px monospace;\n  cursor: pointer;\n}\n\n#stripe {\n  display: inline-block\n}\n\n#stripe.animate {\n  transform: translate(-90%);\n  transition-property: transform;\n  transition-duration: 9s;\n  transition-timing-function: linear;\n}\n"
  },
  {
    "path": "7-animation/2-css-animations/step-end.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  Click below to animate:\n\n  <div id=\"digit\"><div id=\"stripe\">0123456789</div></div>\n\n  <script>\n    digit.onclick = function() {\n      stripe.classList.add('animate');\n    }\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/step-end.view/style.css",
    "content": "#digit {\n  width: .5em;\n  overflow: hidden;\n  font: 32px monospace;\n  cursor: pointer;\n}\n\n#stripe {\n  display: inline-block\n}\n\n#stripe.animate {\n  transform: translate(-90%);\n  transition-property: transform;\n  transition-duration: 9s;\n  transition-timing-function: steps(9, end);\n}\n"
  },
  {
    "path": "7-animation/2-css-animations/step-list.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"digit\"><div id=\"stripe\">0123456789</div></div>\n\n</body>\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/step-list.view/style.css",
    "content": "#digit {\n  border: 1px solid red;\n  width: 1.2em;\n}\n\n#stripe {\n  display: inline-block;\n  font: 32px monospace;\n}\n"
  },
  {
    "path": "7-animation/2-css-animations/step.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  Click below to animate:\n\n  <div id=\"digit\"><div id=\"stripe\">0123456789</div></div>\n\n  <script>\n    digit.onclick = function() {\n      stripe.classList.add('animate');\n    }\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/step.view/style.css",
    "content": "#digit {\n  width: .5em;\n  overflow: hidden;\n  font: 32px monospace;\n  cursor: pointer;\n}\n\n#stripe {\n  display: inline-block\n}\n\n#stripe.animate {\n  transform: translate(-90%);\n  transition-property: transform;\n  transition-duration: 9s;\n  transition-timing-function: steps(9, start);\n}\n"
  },
  {
    "path": "7-animation/2-css-animations/train-linear.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <img class=\"train\" src=\"https://js.cx/clipart/train.gif\" onclick=\"this.style.left='450px'\">\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/2-css-animations/train-linear.view/style.css",
    "content": ".train {\n  position: relative;\n  cursor: pointer;\n  width: 177px;\n  height: 160px;\n  left: 0;\n  transition: left 5s cubic-bezier(0, 0, 1, 1);\n}"
  },
  {
    "path": "7-animation/2-css-animations/train-over.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <img class=\"train\" src=\"https://js.cx/clipart/train.gif\" onclick=\"this.style.left='400px'\">\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/2-css-animations/train-over.view/style.css",
    "content": ".train {\n  position: relative;\n  cursor: pointer;\n  width: 177px;\n  height: 160px;\n  left: 100px;\n  transition: left 5s cubic-bezier(.5, -1, .5, 2);\n}"
  },
  {
    "path": "7-animation/2-css-animations/train.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <img class=\"train\" src=\"https://js.cx/clipart/train.gif\" onclick=\"this.style.left='450px'\">\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/2-css-animations/train.view/style.css",
    "content": ".train {\n  position: relative;\n  cursor: pointer;\n  width: 177px;\n  height: 160px;\n  left: 0px;\n  transition: left 5s cubic-bezier(0.0, 0.5, 0.5, 1.0);\n}"
  },
  {
    "path": "7-animation/3-js-animation/1-animate-ball/solution.md",
    "content": "Pour rebondir, nous pouvons utiliser les propriétés CSS `top` et `position:absolute` pour la balle à l'intérieur du champ avec `position:relative`.\n\nLa coordonnée du bas du champ est `field.clientHeight`. La propriété CSS `top` fait référence au bord supérieur de la balle. Elle doit donc aller de `0` à `field.clientHeight - ball.clientHeight`, c'est-à-dire la position finale la plus basse du bord supérieur de la balle.\n\nPour obtenir l'effet de \"rebond\", nous pouvons utiliser la fonction de timing `bounce` en mode `easeOut`.\n\nVoici le code final de l'animation :\n\n```js\nlet to = field.clientHeight - ball.clientHeight;\n\nanimate({\n  duration: 2000,\n  timing: makeEaseOut(bounce),\n  draw(progress) {\n    ball.style.top = to * progress + 'px'\n  }\n});\n```\n"
  },
  {
    "path": "7-animation/3-js-animation/1-animate-ball/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n\n  <div id=\"field\">\n    <img src=\"https://js.cx/clipart/ball.svg\" width=\"40\" height=\"40\" id=\"ball\">\n  </div>\n\n  <script>\n    function makeEaseOut(timing) {\n      return function(timeFraction) {\n        return 1 - timing(1 - timeFraction);\n      }\n    }\n\n    function bounce(timeFraction) {\n      for (let a = 0, b = 1; 1; a += b, b /= 2) {\n        if (timeFraction >= (7 - 4 * a) / 11) {\n          return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)\n        }\n      }\n    }\n\n    ball.onclick = function() {\n\n      let to = field.clientHeight - ball.clientHeight;\n\n      animate({\n        duration: 2000,\n        timing: makeEaseOut(bounce),\n        draw(progress) {\n          ball.style.top = to * progress + 'px'\n        }\n      });\n\n\n    };\n  </script>\n\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/3-js-animation/1-animate-ball/solution.view/style.css",
    "content": "#field {\n  height: 200px;\n  border-bottom: 3px black groove;\n  position: relative;\n}\n\n#ball {\n  position: absolute;\n  cursor: pointer;\n}"
  },
  {
    "path": "7-animation/3-js-animation/1-animate-ball/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <script src=\"https://en.js.cx/libs/animate.js\"></script>\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n\n  <div id=\"field\">\n    <img src=\"https://en.js.cx/clipart/ball.svg\" width=\"40\" height=\"40\" id=\"ball\">\n  </div>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/3-js-animation/1-animate-ball/source.view/style.css",
    "content": "#field {\n  height: 200px;\n  border-bottom: 3px black groove;\n  position: relative;\n}\n\n#ball {\n  position: absolute;\n  cursor: pointer;\n}"
  },
  {
    "path": "7-animation/3-js-animation/1-animate-ball/task.md",
    "content": "importance: 5\n\n---\n\n# Animer la balle rebondissante\n\nCréez une balle rebondissante. Cliquez pour voir à quoi elle doit ressembler :\n\n[iframe height=250 src=\"solution\"]\n"
  },
  {
    "path": "7-animation/3-js-animation/2-animate-ball-hops/solution.md",
    "content": "Dans la tâche <info:task/animate-ball>, nous n'avions qu'une seule propriété à animer. Maintenant, nous avons besoin d'une supplémentaire : `elem.style.left`.\n\nLa coordonnée horizontale change selon une autre loi : elle ne \"rebondit\" pas, mais augmente progressivement en déplaçant la balle vers la droite.\n\nNous pouvons écrire un autre `animate` pour elle.\n\nComme fonction de temporisation, nous pourrions utiliser `linear`, mais quelque chose comme `makeEaseOut(quad)` semble bien mieux.\n\nLe code :\n\n```js\nlet height = field.clientHeight - ball.clientHeight;\nlet width = 100;\n\n// animer top (rebondissement)\nanimate({\n  duration: 2000,\n  timing: makeEaseOut(bounce),\n  draw: function(progress) {\n    ball.style.top = height * progress + 'px'\n  }\n});\n\n// animer left (déplacement vers la droite)\nanimate({\n  duration: 2000,\n  timing: makeEaseOut(quad),\n  draw: function(progress) {\n    ball.style.left = width * progress + \"px\"\n  }\n});\n```\n"
  },
  {
    "path": "7-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n\n  <div id=\"field\">\n    <img src=\"https://js.cx/clipart/ball.svg\" width=\"40\" height=\"40\" id=\"ball\">\n  </div>\n\n  <script>\n    function makeEaseOut(timing) {\n      return function(timeFraction) {\n        return 1 - timing(1 - timeFraction);\n      }\n    }\n\n    function bounce(timeFraction) {\n      for (let a = 0, b = 1; 1; a += b, b /= 2) {\n        if (timeFraction >= (7 - 4 * a) / 11) {\n          return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)\n        }\n      }\n    }\n\n    function quad(timeFraction) {\n      return Math.pow(timeFraction, 2);\n    }\n\n    ball.onclick = function() {\n\n      let height = field.clientHeight - ball.clientHeight;\n      let width = 100;\n\n      animate({\n        duration: 2000,\n        timing: makeEaseOut(bounce),\n        draw: function(progress) {\n          ball.style.top = height * progress + 'px'\n        }\n      });\n\n      animate({\n        duration: 2000,\n        timing: makeEaseOut(quad),\n        draw: function(progress) {\n          ball.style.left = width * progress + \"px\"\n        }\n      });\n    }\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/2-animate-ball-hops/solution.view/style.css",
    "content": "#field {\n  height: 200px;\n  border-bottom: 3px black groove;\n  position: relative;\n}\n\n#ball {\n  position: absolute;\n  cursor: pointer;\n}"
  },
  {
    "path": "7-animation/3-js-animation/2-animate-ball-hops/task.md",
    "content": "importance: 5\n\n---\n\n# Animer la balle pour qu'elle rebondisse vers la droite.\n\nFaites rebondir la balle vers la droite. Comme ceci :\n\n[iframe height=250 src=\"solution\"]\n\nÉcrivez le code d'animation. La distance à gauche est de `100px`.\n\nPrenez la solution de la tâche précédente <info:task/animate-ball> comme source.\n"
  },
  {
    "path": "7-animation/3-js-animation/article.md",
    "content": "# Animations JavaScript\n\nLes animations JavaScript peuvent gérer des choses que CSS ne peut pas gérer.\n\nPar exemple, le déplacement le long d'un chemin complexe, avec une fonction de temporisation différente des courbes de Bézier, ou une animation sur un élément canvas.\n\n## Utilisation de setInterval\n\nUne animation peut être implémentée sous la forme d'une séquence d'images -- généralement de petites modifications des propriétés HTML/CSS.\n\nPar exemple, en changeant `style.left` de `0px` à `100px`, on déplace l'élément. Et si nous l'augmentons dans `setInterval`, en changeant de `2px` avec un minuscule retard, comme 50 fois par seconde, alors cela semble fluide. C'est le même principe qu'au cinéma : 24 images par seconde suffisent pour que l'image soit fluide.\n\nLe pseudo-code peut ressembler à ceci :\n\n```js\nlet timer = setInterval(function() {\n  if (animation complete) clearInterval(timer);\n  else increase style.left by 2px\n}, 20); // changement de 2px toutes les 20ms, environ 50 images par seconde\n```\n\nExemple plus complet de l'animation :\n\n```js\nlet start = Date.now(); // mémoriser l'heure de début\n\nlet timer = setInterval(function() {\n  // combien de temps s'est écoulé depuis le début ?\n  let timePassed = Date.now() - start;\n\n  if (timePassed >= 2000) {\n    clearInterval(timer); // terminer l'animation après 2 secondes\n    return;\n  }\n\n  // dessiner l'animation à l'instant timePassed\n  draw(timePassed);\n\n}, 20);\n\n// à mesure que timePassed passe de 0 à 2000\n// left obtient des valeurs de 0px à 400px\nfunction draw(timePassed) {\n  train.style.left = timePassed / 5 + 'px';\n}\n```\n\nCliquez pour la démo :\n\n[codetabs height=200 src=\"move\"]\n\n## Utilisation de requestAnimationFrame\n\nImaginons que nous ayons plusieurs animations fonctionnant simultanément.\n\nSi nous les exécutons séparément, alors même si chacune d'entre elles possède `setInterval(..., 20)`, le navigateur devra repeindre bien plus souvent que toutes les `20ms`.\n\nC'est parce qu'elles ont un temps de départ différent, donc \"toutes les 20 ms\" diffère entre les différentes animations. Les intervalles ne sont pas alignés. Nous aurons donc plusieurs animations indépendantes dans un intervalle de `20ms`.\n\nEn d'autres termes, ceci :\n\n```js\nsetInterval(function() {\n  animate1();\n  animate2();\n  animate3();\n}, 20)\n```\n\n...Est plus léger que trois appels indépendants :\n\n```js\nsetInterval(animate1, 20); // animations indépendantes\nsetInterval(animate2, 20); // à différents endroits du script\nsetInterval(animate3, 20);\n```\n\nCes redessinages indépendants doivent être regroupés, afin de faciliter le redessinage pour le navigateur et donc de réduire la charge du processeur et d'obtenir un aspect plus fluide.\n\nIl y a une autre chose à garder en tête. Parfois, le CPU est surchargé, ou il y a d'autres raisons de redessiner moins souvent (comme lorsque l'onglet du navigateur est caché), donc nous ne devrions vraiment pas le lancer tous les `20ms`.\n\nMais comment le savoir en JavaScript ? Il existe une spécification [Animation timing](https://www.w3.org/TR/animation-timing/) qui fournit la fonction `requestAnimationFrame`. Elle répond à toutes ces questions et même plus.\n\nLa syntaxe :\n```js\nlet requestId = requestAnimationFrame(callback)\n```\n\nCela programme la fonction `callback` pour qu'elle s'exécute au moment le plus proche où le navigateur veut faire une animation.\n\nSi nous modifions des éléments dans `callback`, ils seront regroupés avec d'autres callbacks `requestAnimationFrame` et avec les animations CSS. Il y aura donc un seul recalcul de la géométrie et un seul repeint au lieu de plusieurs.\n\nLa valeur retournée `requestId` peut être utilisée pour annuler l'appel :\n```js\n// annuler l'exécution programmée du callback\ncancelAnimationFrame(requestId);\n```\n\nLe `callback` reçoit un argument -- le temps écoulé depuis le début du chargement de la page en microsecondes. Ce temps peut aussi être obtenu en appelant [performance.now()](https://developer.mozilla.org/fr/docs/Web/API/Performance/now).\n\nHabituellement, `callback` s'exécute très rapidement, à moins que le CPU soit surchargé ou que la batterie de l'ordinateur portable soit presque déchargée, ou qu'il y ait une autre raison.\n\nLe code ci-dessous montre le temps entre les 10 premières exécutions de `requestAnimationFrame`. Habituellement, c'est 10-20ms :\n\n```html run height=40 refresh\n<script>\n  let prev = performance.now();\n  let times = 0;\n\n  requestAnimationFrame(function measure(time) {\n    document.body.insertAdjacentHTML(\"beforeEnd\", Math.floor(time - prev) + \" \");\n    prev = time;\n\n    if (times++ < 10) requestAnimationFrame(measure);\n  })\n</script>\n```\n\n## Animation structurée\n\nMaintenant nous pouvons faire une fonction d'animation plus universelle basée sur `requestAnimationFrame` :\n\n```js\nfunction animate({timing, draw, duration}) {\n\n  let start = performance.now();\n\n  requestAnimationFrame(function animate(time) {\n    // timeFraction passe de 0 à 1\n    let timeFraction = (time - start) / duration;\n    if (timeFraction > 1) timeFraction = 1;\n\n    // calculer l'état courant de l'animation\n    let progress = timing(timeFraction)\n\n    draw(progress); // dessinez-le\n\n    if (timeFraction < 1) {\n      requestAnimationFrame(animate);\n    }\n\n  });\n}\n```\n\nLa fonction `animate` accepte 3 paramètres qui décrivent essentiellement l'animation :\n\n`duration``\n: Durée totale de l'animation. Par exemple, `1000`.\n\n`timing(timeFraction)`\n: Fonction de chronométrage, comme la propriété CSS `transition-timing-function` qui obtient la fraction de temps qui s'est écoulée (`0` au début, `1` à la fin) et renvoie la fin de l'animation (comme `y` sur la courbe de Bézier).\n\n    Par exemple, une fonction linéaire signifie que l'animation se déroule uniformément avec la même vitesse :\n\n    ```js\n    function linear(timeFraction) {\n      return timeFraction;\n    }\n    ```\n\n    Son graph :\n    ![](linear.svg)\n\n    C'est comme `transition-timing-function : linear`. Il existe d'autres variantes intéressantes présentées ci-dessous.\n\n`draw(progress)`\n: La fonction qui prend l'état final de l'animation et le dessine. La valeur `progress=0` indique l'état de début d'animation, et `progress=1` -- l'état de fin.\n\n    Il s'agit de la fonction qui dessine réellement l'animation.\n\n    Elle peut déplacer l'élément :\n    ```js\n    function draw(progress) {\n      train.style.left = progress + 'px';\n    }\n    ```\n\n    ...Ou faire n'importe quoi d'autre, nous pouvons animer toute chose, de n'importe quelle manière.\n\n\nAnimons l'élément `width` de `0` à `100%` en utilisant notre fonction.\n\nCliquez sur l'élément pour la démonstration :\n\n[codetabs height=60 src=\"width\"]\n\nLe code pour cela :\n\n```js\nanimate({\n  duration: 1000,\n  timing(timeFraction) {\n    return timeFraction;\n  },\n  draw(progress) {\n    elem.style.width = progress * 100 + '%';\n  }\n});\n```\n\nContrairement à l'animation CSS, nous pouvons créer ici n'importe quelle fonction de temporisation et n'importe quelle fonction draw (de dessinnage). La fonction de timing n'est pas limitée par les courbes de Bézier. Et `draw` peut aller au-delà des propriétés, créer de nouveaux éléments pour une animation de feu d'artifice ou autre.\n\n## Fonctions de temporisation\n\nNous avons vu la fonction de temporisation la plus simple, linéaire, ci-dessus.\n\nNous allons en voir d'autres. Nous allons essayer des animations de mouvements avec différentes fonctions de temporisation pour voir comment elles fonctionnent.\n\n### Puissance de n\n\nSi nous voulons accélérer l'animation, nous pouvons utiliser `progress` à la puissance `n`.\n\nPar exemple, une courbe parabolique :\n\n```js\nfunction quad(timeFraction) {\n  return Math.pow(timeFraction, 2)\n}\n```\n\nLe graph :\n\n![](quad.svg)\n\nVoir en action (cliquer pour activer) :\n\n[iframe height=40 src=\"quad\" link]\n\n...Ou la courbe cubique ou encore un `n` plus grand. En augmentant la puissance, on accélère la vitesse.\n\nVoici le graphique de `progress` à la puissance `5` :\n\n![](quint.svg)\n\nEn action :\n\n[iframe height=40 src=\"quint\" link]\n\n### L'arc\n\nFonction :\n\n```js\nfunction circ(timeFraction) {\n  return 1 - Math.sin(Math.acos(timeFraction));\n}\n```\n\nLe graph:\n\n![](circ.svg)\n\n[iframe height=40 src=\"circ\" link]\n\n### Back : tir à l'arc\n\nCette fonction effectue le \"tir à l'arc\" (bow shooting). On commence par \"tirer la corde de l'arc\", puis on \"tire\".\n\nContrairement aux fonctions précédentes, elle dépend d'un paramètre supplémentaire `x`, le \"coefficient d'élasticité\". La distance de \"traction de la corde de l'arc\" est définie par celui-ci.\n\nLe code :\n\n```js\nfunction back(x, timeFraction) {\n  return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x)\n}\n```\n\n**Le graph pour `x = 1.5`:**\n\n![](back.svg)\n\nPour l'animation, nous l'utilisons avec une valeur spécifique de `x`. Exemple pour `x = 1.5` :\n\n[iframe height=40 src=\"back\" link]\n\n### Bounce\n\nImaginez que nous lâchons une balle. Elle tombe, puis rebondit plusieurs fois et s'arrête.\n\nLa fonction `bounce` fait la même chose, mais dans l'ordre inverse : Le \"rebond\" commence immédiatement. Elle utilise quelques coefficients spéciaux pour cela :\n\n```js\nfunction bounce(timeFraction) {\n  for (let a = 0, b = 1; 1; a += b, b /= 2) {\n    if (timeFraction >= (7 - 4 * a) / 11) {\n      return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)\n    }\n  }\n}\n```\n\nEn action :\n\n[iframe height=40 src=\"bounce\" link]\n\n### Animation élastique\n\nUne fonction \"élastique\" de plus qui accepte un paramètre supplémentaire `x` pour la \"portée initiale\".\n\n```js\nfunction elastic(x, timeFraction) {\n  return Math.pow(2, 10 * (timeFraction - 1)) * Math.cos(20 * Math.PI * x / 3 * timeFraction)\n}\n```\n\n**Le graph pour `x=1.5`:**\n![](elastic.svg)\n\nEn action pour `x=1.5` :\n\n[iframe height=40 src=\"elastic\" link]\n\n## Reversal : ease*\n\nNous avons donc une collection de fonctions de temporisation. Leur application directe est appelée \"easeIn\".\n\nParfois, nous avons besoin de montrer l'animation dans l'ordre inverse. C'est possible avec la transformation \"easeOut\".\n\n### easeOut\n\nDans le mode \"easeOut\", la fonction `timing` est placée dans un wrapper `timingEaseOut` :\n\n```js\ntimingEaseOut(timeFraction) = 1 - timing(1 - timeFraction)\n```\n\nEn d'autres termes, nous avons une fonction de \"transformation\" `makeEaseOut` qui prend une fonction de temporisation \"régulière\" et renvoie le wrapper qui l'entoure :\n\n```js\n// accepte une fonction de temporisation, renvoie la variante transformée\nfunction makeEaseOut(timing) {\n  return function(timeFraction) {\n    return 1 - timing(1 - timeFraction);\n  }\n}\n```\n\nPar exemple, nous pouvons prendre la fonction `bounce` décrite ci-dessus et l'appliquer :\n\n```js\nlet bounceEaseOut = makeEaseOut(bounce);\n```\n\nAinsi, le rebond ne sera pas au début, mais à la fin de l'animation. C'est encore mieux :\n\n[codetabs src=\"bounce-easeout\"]\n\nIci, nous pouvons voir comment la transformation change le comportement de la fonction :\n\n![](bounce-inout.svg)\n\nS'il y a un effet d'animation au début, comme le rebondissement -- il sera affiché à la fin.\n\nDans le graphique ci-dessus, le <span style=\"color:#EE6B47\">regular bounce</span> a la couleur rouge, et le <span style=\"color:#62C0DC\">easeOut bounce</span> est bleu.\n\n- Regular bounce -- l'objet rebondit en bas, puis à la fin saute brusquement en haut.\n- Après `easeOut` -- il saute d'abord vers le haut, puis rebondit là.\n\n### easeInOut\n\nNous pouvons également montrer l'effet à la fois au début et à la fin de l'animation. La transformation est appelée \"easeInOut\".\n\nÉtant donné la fonction de temporisation, nous calculons l'état de l'animation comme suit :\n\n```js\nif (timeFraction <= 0.5) { // première moitié de l'animation\n  return timing(2 * timeFraction) / 2;\n} else { // deuxième moitié de l'animation\n  return (2 - timing(2 * (1 - timeFraction))) / 2;\n}\n```\n\nLe code du wrapper :\n\n```js\nfunction makeEaseInOut(timing) {\n  return function(timeFraction) {\n    if (timeFraction < .5)\n      return timing(2 * timeFraction) / 2;\n    else\n      return (2 - timing(2 * (1 - timeFraction))) / 2;\n  }\n}\n\nbounceEaseInOut = makeEaseInOut(bounce);\n```\n\nEn action, `bounceEaseInOut` :\n\n[codetabs src=\"bounce-easeinout\"]\n\nLa transformation \"easeInOut\" joint deux graphiques en un seul : `easeIn` (régulier) pour la première moitié de l'animation et `easeOut` (inversé) -- pour la deuxième moitié.\n\nL'effet est clairement visible si l'on compare les graphiques de `easeIn`, `easeOut` et `easeInOut` de la fonction de temporisation `circ` :\n\n![](circ-ease.svg)\n\n- <span style=\"color:#EE6B47\">Red</span> est la variante régulière de `circ` (`easeIn`).\n- <span style=\"color:#8DB173\">Green</span> -- `easeOut`.\n- <span style=\"color:#62C0DC\">Blue</span> -- `easeInOut`.\n\nComme nous pouvons le voir, le graphique de la première moitié de l'animation est le `easeIn` atténué, et la seconde moitié est le `easeOut` atténué. Par conséquent, l'animation commence et se termine avec le même effet.\n\n## Un \" draw \" plus intéressant\n\nAu lieu de déplacer l'élément, nous pouvons faire autre chose. Il suffit d'écrire le bon `draw`.\n\nVoici la saisie animée du texte \"rebondissant\" :\n\n[codetabs src=\"text\"]\n\n## Résumé\n\nPour les animations que CSS ne peut pas bien gérer, ou celles qui nécessitent un contrôle précis, JavaScript peut aider. Les animations JavaScript doivent être implémentées via `requestAnimationFrame`. Cette méthode intégrée permet de configurer une fonction callback à exécuter lorsque le navigateur prépare un repeint. En général, c'est très bientôt, mais le moment exact dépend du navigateur.\n\nLorsqu'une page est en arrière-plan, il n'y a pas de repeint du tout, donc la fonction de rappel ne sera pas exécutée : l'animation sera suspendue et ne consommera pas de ressources. C'est très bien.\n\nVoici la fonction d'aide `animate` pour configurer la plupart des animations :\n\n```js\nfunction animate({timing, draw, duration}) {\n\n  let start = performance.now();\n\n  requestAnimationFrame(function animate(time) {\n    // timeFraction passe de 0 à 1\n    let timeFraction = (time - start) / duration;\n    if (timeFraction > 1) timeFraction = 1;\n\n    // calculer l'état courant de l'animation\n    let progress = timing(timeFraction);\n\n    draw(progress); // dessinez-le\n\n    if (timeFraction < 1) {\n      requestAnimationFrame(animate);\n    }\n\n  });\n}\n```\n\nOptions :\n\n- `duration` -- la durée totale de l'animation en ms.\n- `timing` -- la fonction pour calculer la progression de l'animation. Donne une fraction de temps de 0 à 1, retourne la progression de l'animation, généralement de 0 à 1.\n- `draw` -- la fonction pour dessiner l'animation.\n\nNous pourrions certainement l'améliorer, mais les animations JavaScript ne sont pas utilisées quotidiennement. Elles sont utilisées pour faire quelque chose d'intéressant et de non standard. Vous voudriez donc ajouter les fonctionnalitées dont vous avez besoin quand vous en avez besoin.\n\nLes animations JavaScript peuvent utiliser n'importe quelle fonction de temporisation. Nous avons couvert beaucoup d'exemples et de transformations pour les rendre encore plus polyvalentes. Contrairement à CSS, nous ne sommes pas limités ici aux courbes de Bézier.\n\nIl en va de même pour `draw` : nous pouvons animer n'importe quoi, pas seulement des propriétés CSS.\n"
  },
  {
    "path": "7-animation/3-js-animation/back.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    brick.onclick = function() {\n      animate({\n        duration: 1000,\n        timing: function back(x, timeFraction) {\n          return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x)\n        }.bind(null, 1.5),\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/back.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/bounce-easeinout.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    function makeEaseInOut(timing) {\n      return function(timeFraction) {\n        if (timeFraction < .5)\n          return timing(2 * timeFraction) / 2;\n        else\n          return (2 - timing(2 * (1 - timeFraction))) / 2;\n      }\n    }\n\n\n    function bounce(timeFraction) {\n      for (let a = 0, b = 1; 1; a += b, b /= 2) {\n        if (timeFraction >= (7 - 4 * a) / 11) {\n          return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)\n        }\n      }\n    }\n\n    let bounceEaseInOut = makeEaseInOut(bounce);\n\n    brick.onclick = function() {\n      animate({\n        duration: 3000,\n        timing: bounceEaseInOut,\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/bounce-easeinout.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/bounce-easeout.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    function makeEaseOut(timing) {\n      return function(timeFraction) {\n        return 1 - timing(1 - timeFraction);\n      }\n    }\n\n    function bounce(timeFraction) {\n      for (let a = 0, b = 1; 1; a += b, b /= 2) {\n        if (timeFraction >= (7 - 4 * a) / 11) {\n          return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)\n        }\n      }\n    }\n\n    let bounceEaseOut = makeEaseOut(bounce);\n\n    brick.onclick = function() {\n      animate({\n        duration: 3000,\n        timing: bounceEaseOut,\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/bounce-easeout.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/bounce.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    brick.onclick = function() {\n      animate({\n        duration: 3000,\n        timing: function bounce(timeFraction) {\n          for (let a = 0, b = 1; 1; a += b, b /= 2) {\n            if (timeFraction >= (7 - 4 * a) / 11) {\n              return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)\n            }\n          }\n        },\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/bounce.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/circ.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    brick.onclick = function() {\n      animate({\n        duration: 1000,\n        timing: function circ(timeFraction) {\n          return 1 - Math.sin(Math.acos(timeFraction))\n        },\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/circ.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/elastic.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    brick.onclick = function() {\n      animate({\n        duration: 3000,\n        timing: function elastic(x, timeFraction) {\n          return Math.pow(2, 10 * (timeFraction - 1)) * Math.cos(20 * Math.PI * x / 3 * timeFraction)\n        }.bind(null, 1.5),\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/elastic.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/move-raf.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    #train {\n      position: relative;\n      cursor: pointer;\n    }\n  </style>\n</head>\n\n<body>\n\n  <img id=\"train\" src=\"https://en.js.cx/clipart/train.gif\">\n\n  <script>\n    train.onclick = function() {\n      animate(function(progress) {\n        train.style.left = progress * 400 + 'px';\n      }, 2000);\n    };\n\n\n    function animate(draw, duration) {\n      let start = performance.now();\n\n      requestAnimationFrame(function animate(time) {\n        // how much time passed from the start?\n        let timePassed = time - start;\n\n        if (timePassed > duration) timePassed = duration;\n\n        // progress is from 0 to 1, the fraction of time that passed\n        let progress = duration / timePassed;\n\n        // draw the animation progress\n        draw(progress);\n\n        // if time is not up - schedule one more run\n        if (timePassed < duration) {\n          requestAnimationFrame(animate);\n        }\n\n      });\n    }\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/3-js-animation/move.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    #train {\n      position: relative;\n      cursor: pointer;\n    }\n  </style>\n</head>\n\n<body>\n\n  <img id=\"train\" src=\"https://js.cx/clipart/train.gif\">\n\n\n  <script>\n    train.onclick = function() {\n      let start = Date.now();\n\n      let timer = setInterval(function() {\n        let timePassed = Date.now() - start;\n\n        train.style.left = timePassed / 5 + 'px';\n\n        if (timePassed > 2000) clearInterval(timer);\n\n      }, 20);\n    }\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/3-js-animation/quad.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    brick.onclick = function() {\n      animate({\n        duration: 1000,\n        timing: function(timeFraction) {\n          return Math.pow(timeFraction, 2);\n        },\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/quad.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/quint.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    brick.onclick = function() {\n      animate({\n        duration: 1000,\n        timing: function(timeFraction) {\n          return Math.pow(timeFraction, 5);\n        },\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/quint.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/text.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <textarea id=\"textExample\" rows=\"5\" cols=\"60\">He took his vorpal sword in hand:\nLong time the manxome foe he sought—\nSo rested he by the Tumtum tree,\nAnd stood awhile in thought.\n  </textarea>\n\n  <button onclick=\"animateText(textExample)\">Run the animated typing!</button>\n\n  <script>\n    function animateText(textArea) {\n      let text = textArea.value;\n      let to = text.length,\n        from = 0;\n\n      animate({\n        duration: 5000,\n        timing: bounce,\n        draw: function(progress) {\n          let result = (to - from) * progress + from;\n          textArea.value = text.slice(0, Math.ceil(result))\n        }\n      });\n    }\n\n\n    function bounce(timeFraction) {\n      for (let a = 0, b = 1; 1; a += b, b /= 2) {\n        if (timeFraction >= (7 - 4 * a) / 11) {\n          return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)\n        }\n      }\n    }\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/3-js-animation/text.view/style.css",
    "content": "textarea {\n  display: block;\n  border: 1px solid #BBB;\n  color: #444;\n  font-size: 110%;\n}\n\nbutton {\n  margin-top: 10px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/width.view/animate.js",
    "content": "function animate({duration, draw, timing}) {\n\n  let start = performance.now();\n\n  requestAnimationFrame(function animate(time) {\n    let timeFraction = (time - start) / duration;\n    if (timeFraction > 1) timeFraction = 1;\n\n    let progress = timing(timeFraction)\n\n    draw(progress);\n\n    if (timeFraction < 1) {\n      requestAnimationFrame(animate);\n    }\n\n  });\n}\n"
  },
  {
    "path": "7-animation/3-js-animation/width.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    progress {\n      width: 5%;\n    }\n  </style>\n  <script src=\"animate.js\"></script>\n</head>\n\n<body>\n\n\n  <progress id=\"elem\"></progress>\n\n  <script>\n    elem.onclick = function() {\n      animate({\n        duration: 1000,\n        timing: function(timeFraction) {\n          return timeFraction;\n        },\n        draw: function(progress) {\n          elem.style.width = progress * 100 + '%';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/index.md",
    "content": "# Animation\n\nAnimations CSS et JavaScript."
  },
  {
    "path": "8-web-components/1-webcomponents-intro/.vs/VSWorkspaceState.json",
    "content": "{\n  \"ExpandedNodes\": [\n    \"\"\n  ],\n  \"PreviewInSolutionExplorer\": false\n}"
  },
  {
    "path": "8-web-components/1-webcomponents-intro/article.md",
    "content": "# Prenons un peu de recul\n\nCette section décrit un ensemble des standards actuels pour les \"web components\".\n\nEncore aujourd'hui, ces standards sont toujours en développement. Quelques fonctionnalités sont bien supportées/intégrées dans les conventions HTML/DOM moderne, tandis que d'autres sont toujours au stade d'ébauches. Vous pouvez essayer les différents exemples dans n'importe quel navigateur, tout en gardant à l'esprit que Google Chrome est probablement le plus avancé en ce qui concerne l'intégration de ces fonctionnalités... Probablement car ce sont les développeurs de chez Google qui ont travaillé sur beaucoup de ces dites fonctionnalités.\n\n## Rien de nouveau à l'horizon...\n\nL'idée même de composant n'a rien de nouveau : ils sont déjà utilisés, entre autre, dans beaucoups de frameworks.\n\nAvant de parler des détails techniques, prenons un instant pour contempler l'une des plus grande réussite de l'humanité :\n\n![](satellite.jpg)\n\nVoici la Station Spatiale Internationale (ISS pour les intimes).\n\nEt voilà comment c'est fait à l'intérieur (plus ou moins):\n\n![](satellite-expanded.jpg)\n\nLa Station Spatiale Internationale :\n- Repose sur beaucoup de composants.\n- Chaque composant, à son tour, repose sur encore plus de petits éléments.\n- Les composants sont donc très complexe, bien plus complexe que la plupart des sites web.\n- Ces composants sont développés internationalement, par des équipes venant de pays différents, parlant différents langages...\n\n...Et ce machin vole, mieux encore : il garde des humains en vie dans l'espace !\n\nComment sont fabriqués des engins aussi complexes ?\n\nQuels principes pourrions-nous emprunter pour rendre notre développement de même niveau fiable et évolutif ? Ou, du moins, à s’en rapprocher ?\n\n## L'architecture des composants web\n\nLa règle d'or pour développer des programmes compliqués est : ne pas faire de programmes compliqués.\n\nSi quelque chose devient trop complexe -- divisez le problème en parties plus abordables et assemblez-les de la manière la plus naturelle possible.\n\n**Le bon architecte est celui qui arrive à simplifier ce qui est compliqué.**\n\nOn peut diviser les interfaces utilisateur en plusieurs composants visuels : chacun d'eux a ainsi sa propre place sur la page, peut exécuter une tâche bien définie et diffère des autres.\n\nRegardons par exemple Twitter.\n\nNous pouvons facilement isoler plusieurs composants :\n\n![](web-components-twitter.svg)\n\n1. Navigation.\n2. Information utilisateur.\n3. Suggestions.\n4. Formulaire d'envoi.\n5. (et enfin 6, 7) -- messages.\n\nLes composants peuvent avoir des sous-composants, par exemple les messages : ils peuvent faire partie du composant plus général \"liste des messages\". Une photo de profil cliquable peut elle-même être un composant et ainsi de suite.\n\nComment définir ou décider de ce qui doit être un composant ? Tout est question d'intuition, d'expérience et de sens commun. Généralement, c'est un élément visuel distinct, que l'on peut décrire en fonction de ce qu'il fait et de comment il interagit avec la page. Pour l'exemple ci-dessus, la page a des blocs, et chacun d'entre eux jouent leur propre rôle : il devient ainsi logique de les définir en tant que composant.\n\nUn composant contient :\n- Sa propre classe JavaScript.\n- Sa structure du DOM, gérée seulement par sa classe : le code exterieur n'y aura pas accès (c'est le principe \"d'encapsulation\").\n- Son style CSS, qui sera appliqué à ce composant seulement.\n- Son API : les évenements, les méthodes de classe, ect, pour lui permettre d'interagir avec les autres composants. \n\nEncore une fois, un composant n'a rien de spécial.\n\nIl existe déjà beaucoup de frameworks et de méthode de développement pour en construire, chacun d'eux ayant leur propres avantages. Généralement, ce sont des classes et des conventions spéciales qui sont utilisées en CSS pour reproduire l'impression d'utiliser des composants -- le 'CSS scoping' et l'encapsulation du DOM.\n\nLes composants web proposent maintenant des outils intégrés au navigateur pour cela, nous n'avons plus besoin de les simuler :\n\n- [Custom elements](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements) -- pour définir les customs elements.\n- [Shadow DOM](https://dom.spec.whatwg.org/#shadow-trees) -- pour créer un DOM interne au composant, inaccessible aux autres.\n- [CSS Scoping](https://drafts.csswg.org/css-scoping/) -- pour définir des styles qui s'appliquent seulement à l'interieur du Shadow DOM du composant.\n- [Event retargeting](https://dom.spec.whatwg.org/#retarget) -- et d'autres petites choses pour rendre nos composants encore plus facile à développer.\n\nDans le prochain chapitre nous allons voir les détails des \"Custom Elements\" -- les fondamentaux et les fonctionnalitées déjà bien intégrées des composants web, qui se suffisent à eux-mêmes.\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/solution.md",
    "content": "\nPlease note:\n1. We clear `setInterval` timer when the element is removed from the document. That's important, otherwise it continues ticking even if not needed any more. And the browser can't clear the memory from this element and referenced by it.\n2. We can access current date as `elem.date` property. All class methods and properties are naturally element methods and properties.\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/solution.view/index.html",
    "content": "<!doctype html>\n<script src=\"time-formatted.js\"></script>\n<script src=\"live-timer.js\"></script>\n\n<live-timer id=\"elem\"></live-timer>\n\n<script>\n  elem.addEventListener('tick', event => console.log(event.detail));\n</script>\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/solution.view/live-timer.js",
    "content": "class LiveTimer extends HTMLElement {\n\n  render() {\n    this.innerHTML = `\n    <time-formatted hour=\"numeric\" minute=\"numeric\" second=\"numeric\">\n    </time-formatted>\n    `;\n\n    this.timerElem = this.firstElementChild;\n  }\n\n  connectedCallback() { // (2)\n    if (!this.rendered) {\n      this.render();\n      this.rendered = true;\n    }\n    this.timer = setInterval(() => this.update(), 1000);\n  }\n\n  update() {\n    this.date = new Date();\n    this.timerElem.setAttribute('datetime', this.date);\n    this.dispatchEvent(new CustomEvent('tick', { detail: this.date }));\n  }\n\n  disconnectedCallback() {\n    clearInterval(this.timer); // important to let the element be garbage-collected\n  }\n\n}\n\ncustomElements.define(\"live-timer\", LiveTimer);\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/solution.view/time-formatted.js",
    "content": "class TimeFormatted extends HTMLElement {\n\n  render() {\n    let date = new Date(this.getAttribute('datetime') || Date.now());\n\n    this.innerHTML = new Intl.DateTimeFormat(\"default\", {\n      year: this.getAttribute('year') || undefined,\n      month: this.getAttribute('month') || undefined,\n      day: this.getAttribute('day') || undefined,\n      hour: this.getAttribute('hour') || undefined,\n      minute: this.getAttribute('minute') || undefined,\n      second: this.getAttribute('second') || undefined,\n      timeZoneName: this.getAttribute('time-zone-name') || undefined,\n    }).format(date);\n  }\n\n  connectedCallback() {\n    if (!this.rendered) {\n      this.render();\n      this.rendered = true;\n    }\n  }\n\n  static get observedAttributes() {\n    return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name'];\n  }\n\n  attributeChangedCallback(name, oldValue, newValue) { \n    this.render();\n  }\n\n}\n\ncustomElements.define(\"time-formatted\", TimeFormatted);\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/source.view/index.html",
    "content": "<!doctype html>\n<!-- don't modify this -->\n<script src=\"time-formatted.js\"></script>\n\n<!-- your code here: -->\n<script src=\"live-timer.js\"></script>\n\n<live-timer id=\"elem\"></live-timer>\n\n<script>\n  elem.addEventListener('tick', event => console.log(event.detail));\n</script>\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/source.view/live-timer.js",
    "content": "class LiveTimer extends HTMLElement {\n\n  /* your code here */\n\n}\n\ncustomElements.define(\"live-timer\", LiveTimer);\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/source.view/time-formatted.js",
    "content": "class TimeFormatted extends HTMLElement {\n\n  render() {\n    let date = new Date(this.getAttribute('datetime') || Date.now());\n\n    this.innerHTML = new Intl.DateTimeFormat(\"default\", {\n      year: this.getAttribute('year') || undefined,\n      month: this.getAttribute('month') || undefined,\n      day: this.getAttribute('day') || undefined,\n      hour: this.getAttribute('hour') || undefined,\n      minute: this.getAttribute('minute') || undefined,\n      second: this.getAttribute('second') || undefined,\n      timeZoneName: this.getAttribute('time-zone-name') || undefined,\n    }).format(date);\n  }\n\n  connectedCallback() {\n    if (!this.rendered) {\n      this.render();\n      this.rendered = true;\n    }\n  }\n\n  static get observedAttributes() {\n    return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name'];\n  }\n\n  attributeChangedCallback(name, oldValue, newValue) { \n    this.render();\n  }\n\n}\n\ncustomElements.define(\"time-formatted\", TimeFormatted);\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/task.md",
    "content": "\n# Live timer element\n\nWe already have `<time-formatted>` element to show a nicely formatted time.\n\nCreate `<live-timer>` element to show the current time:\n1. It should use `<time-formatted>` internally, not duplicate its functionality.\n2. Ticks (updates) every second.\n3. For every tick, a custom event named `tick` should be generated, with the current date in `event.detail` (see chapter <info:dispatch-events>).\n\nUsage:\n\n```html\n<live-timer id=\"elem\"></live-timer>\n\n<script>\n  elem.addEventListener('tick', event => console.log(event.detail));\n</script>\n```\n\nDemo:\n\n[iframe src=\"solution\" height=40]\n"
  },
  {
    "path": "8-web-components/2-custom-elements/article.md",
    "content": "\n# Custom elements\n\nNous pouvons créer des éléments HTML personnalisés, définis par nos classes, avec leur propres méthodes et propriétés, gestionnaires d'événement, etc. \n\nUne fois qu'un élément personnalisé est définit, nous pouvons l'utiliser au même titre qu'un élément HTML classique.\n\nC'est parfait, sachant que le dictionnaire HTML est riche, mais pas infini. Il n'y a pas de `<easy-tabs>`, `<sliding-carousel>`, `<beautiful-upload>`... Pensez un instant à toute les balises dont nous pourrions avoir besoin.\n\nNous pouvons les définir avec une classe spéciale, et les utiliser comme des balises HTML classique.\n\nIl y a deux sortes d'éléments personnalisés :\n\n2. **Éléments personnalisés autonome** -- les nouveaux éléments, qui étendent la classe abstraite `HTMLElement`.\n<!-- 2. **Customized built-in elements** -- extending built-in elements, like a customized button, based on `HTMLButtonElement` etc. -->\n**Éléments personnalisés intégrés** -- ils étendent les éléments déjà intégrés au navigateur, comme un bouton personnalisé, basé sur `HTMLButtonElement`.\n\n<!-- First we'll cover autonomous elements, and then move to customized built-in ones. -->\nNous allons voir les éléments personnalisés autonome dans un premier temps, puis nous passerons aux éléments personnalisés déjà intégrés au navigateur.\n\n<!-- To create a custom element, we need to tell the browser several details about it: how to show it, what to do when the element is added or removed to page, etc. -->\nPour créer un élément personnalisé, nous allons devoir donner quelques détails au navigateur : Comment le montrer, que faire lorsque cet élément est chargé dans le DOM, ect...\n\n<!-- That's done by making a class with special methods. That's easy, as there are only few methods, and all of them are optional. -->\nC'est  possible en créant une classe avec des méthodes spéciales. C'est facile, sachant qu'il n'y seulement que quelques méthodes, et elles sont toutes optionnelles.\n\n<!-- Here's a sketch with the full list: -->\nVoici la classe et toute ses méthodes :\n\n```js\nclass MyElement extends HTMLElement {\n  constructor() {\n    super();\n    // créer l'élément\n  }\n\n  connectedCallback() {\n    // le navigateur appelle cette méthode lorsque l'élément est ajouté au document\n    // elle peut-être appelé autant de fois que lélément est ajouté ou supprimé)\n  }\n\n  disconnectedCallback() {\n    // le navigateur appelle cette méthode lorsque l'élément est supprimé du document\n    // elle peut-être appelé autant de fois que lélément est ajouté ou supprimé)\n  }\n\n  static get observedAttributes() {\n    return [/* tableau listant les attributs dont les changements sont à surveiller */];\n  }\n\n  attributeChangedCallback(name, oldValue, newValue) {\n    // appelé lorsque l'un des attributs listé par la méthode ci-dessus est modifié\n  }\n\n  adoptedCallback() {\n    // méthode appelé lorsque l'élément est envoyé vers un nouveau document\n    // (utilisé très rarement avec document.adoptNode)\n  }\n\n  // vous pouvez ajouter d'autres méthodes ou propriétées\n}\n```\n\n<!-- After that, we need to register the element: -->\nAprès ça, nous devons enregistrer cet élément :\n\n```js\n// let the browser know that <my-element> is served by our new class\ncustomElements.define(\"my-element\", MyElement);\n```\n\nNow for any HTML elements with tag `<my-element>`, an instance of `MyElement` is created, and the aforementioned methods are called. We also can `document.createElement('my-element')` in JavaScript.\n\n```smart header=\"Custom element name must contain a hyphen `-`\"\nCustom element name must have a hyphen `-`, e.g. `my-element` and `super-button` are valid names, but `myelement` is not.\n\nThat's to ensure that there are no name conflicts between built-in and custom HTML elements.\n```\n\n## Example: \"time-formatted\"\n\nFor example, there already exists `<time>` element in HTML, for date/time. But it doesn't do any formatting by itself.\n\nLet's create `<time-formatted>` element that displays the time in a nice, language-aware format:\n\n\n```html run height=50 autorun=\"no-epub\"\n<script>\n*!*\nclass TimeFormatted extends HTMLElement { // (1)\n*/!*\n\n  connectedCallback() {\n    let date = new Date(this.getAttribute('datetime') || Date.now());\n\n    this.innerHTML = new Intl.DateTimeFormat(\"default\", {\n      year: this.getAttribute('year') || undefined,\n      month: this.getAttribute('month') || undefined,\n      day: this.getAttribute('day') || undefined,\n      hour: this.getAttribute('hour') || undefined,\n      minute: this.getAttribute('minute') || undefined,\n      second: this.getAttribute('second') || undefined,\n      timeZoneName: this.getAttribute('time-zone-name') || undefined,\n    }).format(date);\n  }\n\n}\n\n*!*\ncustomElements.define(\"time-formatted\", TimeFormatted); // (2)\n*/!*\n</script>\n\n<!-- (3) -->\n*!*\n<time-formatted datetime=\"2019-12-01\"\n*/!*\n  year=\"numeric\" month=\"long\" day=\"numeric\"\n  hour=\"numeric\" minute=\"numeric\" second=\"numeric\"\n  time-zone-name=\"short\"\n></time-formatted>\n```\n\n1. The class has only one method `connectedCallback()` -- the browser calls it when `<time-formatted>` element is added to page (or when HTML parser detects it), and it uses the built-in [Intl.DateTimeFormat](mdn:/JavaScript/Reference/Global_Objects/DateTimeFormat) data formatter, well-supported across the browsers, to show a nicely formatted time.\n2. We need to register our new element by `customElements.define(tag, class)`.\n3. And then we can use it everywhere.\n\n\n```smart header=\"Custom elements upgrade\"\nIf the browser encounters any `<time-formatted>` elements before `customElements.define`, that's not an error. But the element is yet unknown, just like any non-standard tag.\n\nSuch \"undefined\" elements can be styled with CSS selector `:not(:defined)`.\n\nWhen `customElement.define` is called, they are \"upgraded\": a new instance of `TimeFormatted`\nis created for each, and `connectedCallback` is called. They become `:defined`.\n\nTo get the information about custom elements, there are methods:\n- `customElements.get(name)` -- returns the class for a custom element with the given `name`,\n- `customElements.whenDefined(name)` -- returns a promise that resolves (without value) when a custom element with the given `name` becomes defined.\n```\n\n```smart header=\"Rendering in `connectedCallback`, not in `constructor`\"\nIn the example above, element content is rendered (created) in `connectedCallback`.\n\nWhy not in the `constructor`?\n\nThe reason is simple: when `constructor` is called, it's yet too early. The element is created, but the browser did not yet process/assign attributes at this stage: calls to `getAttribute` would return `null`. So we can't really render there.\n\nBesides, if you think about it, that's better performance-wise -- to delay the work until it's really needed.\n\nThe `connectedCallback` triggers when the element is added to the document. Not just appended to another element as a child, but actually becomes a part of the page. So we can build detached DOM, create elements and prepare them for later use. They will only be actually rendered when they make it into the page.\n```\n\n## Observing attributes\n\nIn the current implementation of `<time-formatted>`, after the element is rendered, further attribute changes don't have any effect. That's strange for an HTML element. Usually, when we change an attribute, like `a.href`, we expect the change to be immediately visible. So let's fix this.\n\nWe can observe attributes by providing their list in `observedAttributes()` static getter. For such attributes, `attributeChangedCallback` is called when they are modified. It doesn't trigger for other, unlisted attributes (that's for performance reasons).\n\nHere's a new `<time-formatted>`, that auto-updates when attributes change:\n\n```html run autorun=\"no-epub\" height=50\n<script>\nclass TimeFormatted extends HTMLElement {\n\n*!*\n  render() { // (1)\n*/!*\n    let date = new Date(this.getAttribute('datetime') || Date.now());\n\n    this.innerHTML = new Intl.DateTimeFormat(\"default\", {\n      year: this.getAttribute('year') || undefined,\n      month: this.getAttribute('month') || undefined,\n      day: this.getAttribute('day') || undefined,\n      hour: this.getAttribute('hour') || undefined,\n      minute: this.getAttribute('minute') || undefined,\n      second: this.getAttribute('second') || undefined,\n      timeZoneName: this.getAttribute('time-zone-name') || undefined,\n    }).format(date);\n  }\n\n*!*\n  connectedCallback() { // (2)\n*/!*\n    if (!this.rendered) {\n      this.render();\n      this.rendered = true;\n    }\n  }\n\n*!*\n  static get observedAttributes() { // (3)\n*/!*\n    return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name'];\n  }\n\n*!*\n  attributeChangedCallback(name, oldValue, newValue) { // (4)\n*/!*\n    this.render();\n  }\n\n}\n\ncustomElements.define(\"time-formatted\", TimeFormatted);\n</script>\n\n<time-formatted id=\"elem\" hour=\"numeric\" minute=\"numeric\" second=\"numeric\"></time-formatted>\n\n<script>\n*!*\nsetInterval(() => elem.setAttribute('datetime', new Date()), 1000); // (5)\n*/!*\n</script>\n```\n\n1. The rendering logic is moved to `render()` helper method.\n2. We call it once when the element is inserted into page.\n3. For a change of an attribute, listed in `observedAttributes()`, `attributeChangedCallback` triggers.\n4. ...and re-renders the element.\n5. At the end, we can easily make a live timer.\n\n## Rendering order\n\nWhen HTML parser builds the DOM, elements are processed one after another, parents before children. E.g. if we have `<outer><inner></inner></outer>`, then `<outer>` element is created and connected to DOM first, and then `<inner>`.\n\nThat leads to important consequences for custom elements.\n\nFor example, if a custom element tries to access `innerHTML` in `connectedCallback`, it gets nothing:\n\n```html run height=40\n<script>\ncustomElements.define('user-info', class extends HTMLElement {\n\n  connectedCallback() {\n*!*\n    alert(this.innerHTML); // empty (*)\n*/!*\n  }\n\n});\n</script>\n\n*!*\n<user-info>John</user-info>\n*/!*\n```\n\nIf you run it, the `alert` is empty.\n\nThat's exactly because there are no children on that stage, the DOM is unfinished. HTML parser connected the custom element `<user-info>`, and is going to proceed to its children, but just didn't yet.\n\nIf we'd like to pass information to custom element, we can use attributes. They are available immediately.\n\nOr, if we really need the children, we can defer access to them with zero-delay `setTimeout`.\n\nThis works:\n\n```html run height=40\n<script>\ncustomElements.define('user-info', class extends HTMLElement {\n\n  connectedCallback() {\n*!*\n    setTimeout(() => alert(this.innerHTML)); // John (*)\n*/!*\n  }\n\n});\n</script>\n\n*!*\n<user-info>John</user-info>\n*/!*\n```\n\nNow the `alert` in line `(*)` shows \"John\", as we run it asynchronously, after the HTML parsing is complete. We can process children if needed and finish the initialization.\n\nOn the other hand, this solution is also not perfect. If nested custom elements also use `setTimeout` to initialize themselves, then they queue up: the outer `setTimeout` triggers first, and then the inner one.\n\nSo the outer element finishes the initialization before the inner one.\n\nLet's demonstrate that on example:\n\n```html run height=0\n<script>\ncustomElements.define('user-info', class extends HTMLElement {\n  connectedCallback() {\n    alert(`${this.id} connected.`);\n    setTimeout(() => alert(`${this.id} initialized.`));\n  }\n});\n</script>\n\n*!*\n<user-info id=\"outer\">\n  <user-info id=\"inner\"></user-info>\n</user-info>\n*/!*\n```\n\nOutput order:\n\n1. outer connected.\n2. inner connected.\n3. outer initialized.\n4. inner initialized.\n\nWe can clearly see that the outer element finishes initialization `(3)` before the inner one `(4)`.\n\nThere's no built-in callback that triggers after nested elements are ready. If needed, we can implement such thing on our own. For instance, inner elements can dispatch events like `initialized`, and outer ones can listen and react on them.\n\n## Customized built-in elements\n\nNew elements that we create, such as `<time-formatted>`, don't have any associated semantics. They are unknown to search engines, and accessibility devices can't handle them.\n\nBut such things can be important. E.g, a search engine would be interested to know that we actually show a time. And if we're making a special kind of button, why not reuse the existing `<button>` functionality?\n\nWe can extend and customize built-in HTML elements by inheriting from their classes.\n\nFor example, buttons are instances of `HTMLButtonElement`, let's build upon it.\n\n1. Extend `HTMLButtonElement` with our class:\n\n    ```js\n    class HelloButton extends HTMLButtonElement { /* custom element methods */ }\n    ```\n\n2. Provide the third argument to `customElements.define`, that specifies the tag:\n    ```js\n    customElements.define('hello-button', HelloButton, *!*{extends: 'button'}*/!*);\n    ```    \n\n    There may be different tags that share the same DOM-class, that's why specifying `extends` is needed.\n\n3. At the end, to use our custom element, insert a regular `<button>` tag, but add `is=\"hello-button\"` to it:\n    ```html\n    <button is=\"hello-button\">...</button>\n    ```\n\nHere's a full example:\n\n```html run autorun=\"no-epub\"\n<script>\n// The button that says \"hello\" on click\nclass HelloButton extends HTMLButtonElement {\n*!*\n  constructor() {\n*/!*\n    super();\n    this.addEventListener('click', () => alert(\"Hello!\"));\n  }\n}\n\n*!*\ncustomElements.define('hello-button', HelloButton, {extends: 'button'});\n*/!*\n</script>\n\n*!*\n<button is=\"hello-button\">Click me</button>\n*/!*\n\n*!*\n<button is=\"hello-button\" disabled>Disabled</button>\n*/!*\n```\n\nOur new button extends the built-in one. So it keeps the same styles and standard features like `disabled` attribute.\n\n## References\n\n- HTML Living Standard: <https://html.spec.whatwg.org/#custom-elements>.\n- Compatiblity: <https://caniuse.com/#feat=custom-elementsv1>.\n\n## Summary\n\nCustom elements can be of two types:\n\n1. \"Autonomous\" -- new tags, extending `HTMLElement`.\n\n    Definition scheme:\n\n    ```js\n    class MyElement extends HTMLElement {\n      constructor() { super(); /* ... */ }\n      connectedCallback() { /* ... */ }\n      disconnectedCallback() { /* ... */  }\n      static get observedAttributes() { return [/* ... */]; }\n      attributeChangedCallback(name, oldValue, newValue) { /* ... */ }\n      adoptedCallback() { /* ... */ }\n     }\n    customElements.define('my-element', MyElement);\n    /* <my-element> */\n    ```\n\n2. \"Customized built-in elements\" -- extensions of existing elements.\n\n    Requires one more `.define` argument, and `is=\"...\"` in HTML:\n    ```js\n    class MyButton extends HTMLButtonElement { /*...*/ }\n    customElements.define('my-button', MyElement, {extends: 'button'});\n    /* <button is=\"my-button\"> */\n    ```\n\nCustom elements are well-supported among browsers. There's a polyfill <https://github.com/webcomponents/polyfills/tree/master/packages/webcomponentsjs>.\n"
  },
  {
    "path": "8-web-components/2-custom-elements/head.html",
    "content": "<script>\n  /*\nclass TimeFormatted extends HTMLElement {\n\n  render() {\n    let date = new Date(this.getAttribute('datetime') || Date.now());\n\n    this.innerHTML = new Intl.DateTimeFormat(\"default\", {\n      year: this.getAttribute('year') || undefined,\n      month: this.getAttribute('month') || undefined,\n      day: this.getAttribute('day') || undefined,\n      hour: this.getAttribute('hour') || undefined,\n      minute: this.getAttribute('minute') || undefined,\n      second: this.getAttribute('second') || undefined,\n      timeZoneName: this.getAttribute('time-zone-name') || undefined,\n    }).format(date);\n  }\n\n  connectedCallback() { // (2)\n    if (!this.rendered) {\n      this.render();\n      this.rendered = true;\n    }\n  }\n\n  static get observedAttributes() { // (3)\n    return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name'];\n  }\n\n  attributeChangedCallback(name, oldValue, newValue) { // (4)\n    this.render();\n  }\n\n}\n\nwindow.customElements && customElements.define(\"time-formatted\", TimeFormatted);\n*/\n</script>\n"
  },
  {
    "path": "8-web-components/3-shadow-dom/article.md",
    "content": "# DOM fantôme\n\nLe DOM fantôme (Shadow DOM) sert à l'encapsulation. Il permet à un composant d'avoir son propre arbre DOM \"fantôme\", qui ne peut pas être accidentellement accédé à partir du document principal, peut avoir des règles de style locales, et plus encore.\n\n## DOM fantôme intégré\n\nAvez-vous déjà pensé à la façon dont les contrôles complexes des navigateurs sont créés et stylisés ?\n\nComme par exemple `<input type=\"range\">` :\n\n<p>\n<input type=\"range\">\n</p>\n\nLe navigateur utilise DOM/CSS en interne pour les dessiner. Cette structure DOM nous est normalement cachée, mais nous pouvons la voir dans les outils de développement. Par exemple, dans Chrome, nous devons activer dans Dev Tools l'option \"Show user agent shadow DOM\".\n\nEnsuite, `<input type=\"range\">` ressemble à ceci :\n\n![](shadow-dom-range.png)\n\nCe que vous voyez sous `#shadow-root` est appelé \"shadow DOM\" (le DOM fantôme).\n\nNous ne pouvons pas obtenir d'éléments DOM fantôme intégrés par des appels JavaScript réguliers ou des sélecteurs. Ce ne sont pas des enfants réguliers, mais une technique d'encapsulation puissante.\n\nDans l'exemple ci-dessus, nous pouvons voir un attribut utile `pseudo`. Il n'est pas standard et existe pour des raisons historiques. Nous pouvons l'utiliser pour styliser les sous-éléments avec CSS, comme ceci :\n\n```html run autorun\n<style>\n/* rendre la piste du curseur rouge */\ninput::-webkit-slider-runnable-track {\n  background: red;\n}\n</style>\n\n<input type=\"range\">\n```\n\nEncore une fois, `pseudo` est un attribut non standard. Chronologiquement, les navigateurs ont d'abord commencé à expérimenter avec des structures DOM internes pour implémenter des contrôles, puis, après un certain temps, le DOM fantôme a été standardisé pour nous permettre à nous, développeurs, de faire la même chose.\n\nPlus loin, nous utiliserons la norme moderne du DOM fantôme, couverte par [DOM spec](https://dom.spec.whatwg.org/#shadow-trees) et d'autres spécifications connexes.\n\n## Arbre fantôme\n\nUn élément DOM peut avoir deux types de sous-arbres DOM :\n\n1. Arbre standard - un sous-arbre DOM normal, composé d'enfants HTML. Tous les sous-arbres que nous avons vus dans les chapitres précédents étaient \"standard\".\n2. Arbre fantôme - un sous-arbre DOM caché, qui ne se reflète pas dans le HTML, caché des regards indiscrets.\n\nSi un élément possède les deux, le navigateur ne rendra que l'arbre fantôme. Mais nous pouvons également établir une sorte de composition entre les deux arbres. Nous verrons les détails plus tard dans le chapitre <info:slots-composition>.\n\nL'arbre fantôme peut être utilisé dans les éléments personnalisés pour cacher les internes des composants et appliquer des styles locaux aux composants.\n\nPar exemple, cet élément `<show-hello>` cache son DOM interne dans l'arbre fantôme :\n\n```html run autorun height=60\n<script>\ncustomElements.define('show-hello', class extends HTMLElement {\n  connectedCallback() {\n    const shadow = this.attachShadow({mode: 'open'});\n    shadow.innerHTML = `<p>\n      Hello, ${this.getAttribute('name')}\n    </p>`;\n  }  \n});\n</script>\n\n<show-hello name=\"John\"></show-hello>\n```\n\nVoici à quoi ressemble le DOM résultant dans Chrome dev tools, tout le contenu est sous \"#shadow-root\" :\n\n![](shadow-dom-say-hello.png)\n\nPremièrement, l'appel à `elem.attachShadow({mode : ...})` crée un arbre fantôme.\n\nIl y a deux limitations :\n1. On ne peut créer qu'une seule racine fantôme par élément.\n2. Le `elem` doit être soit un élément personnalisé, soit un élément parmi : \"article\", \"aside\", \"blockquote\", \"body\", \"div\", \"footer\", \"h1..h6\", \"header\", \"main\" \"nav\", \"p\", \"section\", ou \"span\". D'autres éléments, comme `<img>`, ne peuvent pas héberger d'arbre fantôme.\n\nL'option `mode` définit le niveau d'encapsulation. Elle doit avoir l'une des deux valeurs suivantes :\n- `\"open\"` -- la racine fantôme est disponible comme `elem.shadowRoot`.\n\n    N'importe quel code est capable d'accéder à l'arbre fantôme de `elem`.   \n- `\"closed\"` -- `elem.shadowRoot` est toujours `null`.\n\n    On ne peut accéder le DOM fantôme que par la référence retournée par `attachShadow` (et probablement cachée dans une classe). Les arbres fantômes natifs des navigateurs, tels que `<input type=\"range\">`, sont fermés. Il n'y a aucun moyen d'y accéder.\n\nLa [racine fantôme](https://dom.spec.whatwg.org/#shadowroot), renvoyée par `attachShadow`, est comme un élément : on peut utiliser `innerHTML` ou des méthodes DOM, comme `append`, pour la remplir.\n\nL'élément avec une racine fantôme est appelé \"hôte fantôme\", et est disponible comme la propriété `host` de la racine fantôme :\n\n```js\n// en supposant que {mode : \"open\"}, sinon elem.shadowRoot est null\nalert(elem.shadowRoot.host === elem); // true\n```\n\n## Encapsulation\n\nLe DOM fantôme est fortement délimité du document principal :\n\n1. Les éléments du DOM fantôme ne sont pas visibles par `querySelector` depuis le DOM standard. En particulier, les éléments du DOM fantôme peuvent avoir des identifiants qui entrent en conflit avec ceux du DOM standard. Ils doivent être uniques seulement dans l'arbre d'ombre.\n2. Le DOM fantôme a ses propres feuilles de style. Les règles de style du DOM externe ne sont pas appliquées.\n\nPar exemple :\n\n```html run untrusted height=40\n<style>\n*!*\n  /* le style du document ne s'appliquera pas à l'arbre fantôme à l'intérieur de #elem (1) */\n*/!*\n  p { color: red; }\n</style>\n\n<div id=\"elem\"></div>\n\n<script>\n  elem.attachShadow({mode: 'open'});\n*!*\n    // l'arbre fantôme a son propre style (2)\n*/!*\n  elem.shadowRoot.innerHTML = `\n    <style> p { font-weight: bold; } </style>\n    <p>Hello, John!</p>\n  `;\n\n*!*\n  // <p> est seulement visible depuis les requêtes à l'intérieur de l'arbre fantôme. (3)\n*/!*\n  alert(document.querySelectorAll('p').length); // 0\n  alert(elem.shadowRoot.querySelectorAll('p').length); // 1\n</script>  \n```\n\n1. Le style provenant du document n'affecte pas l'arbre fantôme.\n2. ...Mais le style provenant de l'intérieur fonctionne.\n3. Pour obtenir des éléments dans l'arbre fantôme, nous devons faire une requête depuis l'intérieur de l'arbre.\n\n## Références\n\n- DOM: <https://dom.spec.whatwg.org/#shadow-trees>\n- Compatibilité: <https://caniuse.com/#feat=shadowdomv1>\n- Le DOM fantôme est mentionné dans de nombreuses autres spécifications, par exemple [DOM Parsing](https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin) spécifie que la racine fantôme a `innerHTML`.\n\n\n## Résumé\n\nLe DOM fantôme est un moyen de créer un DOM local pour les composants.\n\n1. `shadowRoot = elem.attachShadow({mode : open|closed})` -- crée un DOM fantôme pour `elem`. Si `mode=\"open\"`, alors il est accessible par la propriété `elem.shadowRoot`.\n2. Nous pouvons remplir `shadowRoot` en utilisant `innerHTML` ou d'autres méthodes DOM.\n\nLes éléments du DOM fantôme :\n- Ont leur propre id,\n- Invisible aux sélecteurs JavaScript du document principal, comme `querySelector`,\n- N'utilisent les styles que de l'arbre fantôme, pas du document principal.\n\nLe DOM fantôme, s'il existe, est rendu par le navigateur à la place du DOM standard (enfants réguliers). Dans le chapitre <info:slots-composition>, nous verrons comment les composer.\n"
  },
  {
    "path": "8-web-components/4-template-element/article.md",
    "content": "\n# L'élément Template\n\nL'élément intégré `<template>` sert de stockage pour les modèles de balisage HTML. Le navigateur ignore son contenu et vérifie uniquement la validité de la syntaxe, mais nous pouvons y accéder et l'utiliser dans JavaScript, pour créer d'autres éléments.\n\nEn théorie, nous pourrions créer n'importe quel élément invisible quelque part dans le code HTML à des fins de stockage de balises HTML. Quelle est la particularité de `<template>` ?\n\nTout d'abord, son contenu peut être n'importe quel HTML valide, même s'il nécessite normalement une balise d'entourage appropriée.\n\nPar exemple, nous pouvons y placer une ligne de tableau `<tr>` :\n\n```html\n<template>\n  <tr>\n    <td>Contents</td>\n  </tr>\n</template>\n```\n\nHabituellement, si nous essayons de mettre `<tr>` à l'intérieur, disons, d'un `<div>`, le navigateur détecte la structure DOM invalide et la \"corrige\", en ajoutant `<table>` autour. Ce n'est pas ce que nous voulons. D'un autre côté, `<template>` conserve exactement ce que nous y plaçons.\n\nNous pouvons également placer des styles et des scripts dans `<template>` :\n\n```html\n<template>\n  <style>\n    p { font-weight: bold; }\n  </style>\n  <script>\n    alert(\"Hello\");\n  </script>\n</template>\n```\n\nLe navigateur considère que le contenu `<template>` est \"hors du document\" : les styles ne sont pas appliqués, les scripts ne sont pas exécutés, `<video autoplay>` n'est pas lancée, etc.\n\nLe contenu devient actif (les styles s'appliquent, les scripts s'exécutent, etc.) lorsque nous l'insérons dans le document.\n\n## Insertion d'un modèle\n\nLe contenu du modèle est disponible dans sa propriété `content` comme un [DocumentFragment](info:modifying-document#document-fragment) -- un type spécial de noeud DOM.\n\nNous pouvons le traiter comme n'importe quel autre noeud DOM, à l'exception d'une propriété spéciale : lorsque nous l'insérons quelque part, ses enfants sont insérés à la place.\n\nPar exemple :\n\n```html run\n<template id=\"tmpl\">\n  <script>\n    alert(\"Hello\");\n  </script>\n  <div class=\"message\">Hello, world!</div>\n</template>\n\n<script>\n  let elem = document.createElement('div');\n\n*!*\n  // Cloner le contenu du modèle pour le réutiliser plusieurs fois\n  elem.append(tmpl.content.cloneNode(true));\n*/!*\n\n  document.body.append(elem);\n  // Maintenant le script de <template> s'exécute\n</script>\n```\n\nRéécrivons un exemple de Shadow DOM du chapitre précédent en utilisant `<template>` :\n\n```html run untrusted autorun=\"no-epub\" height=60\n<template id=\"tmpl\">\n  <style> p { font-weight: bold; } </style>\n  <p id=\"message\"></p>\n</template>\n\n<div id=\"elem\">Click me</div>\n\n<script>\n  elem.onclick = function() {\n    elem.attachShadow({mode: 'open'});\n\n*!*\n    elem.shadowRoot.append(tmpl.content.cloneNode(true)); // (*)\n*/!*\n\n    elem.shadowRoot.getElementById('message').innerHTML = \"Hello from the shadows!\";\n  };\n</script>\n```\n\nDans la ligne `(*)`, lorsque nous clonons et insérons `tmpl.content`, comme son `DocumentFragment`, ses enfants (`<style>`, `<p>`) sont insérés à la place.\n\nIls forment le shadow DOM :\n\n```html\n<div id=\"elem\">\n  #shadow-root\n    <style> p { font-weight: bold; } </style>\n    <p id=\"message\"></p>\n</div>\n```\n\n## Résumé\n\nPour résumer :\n\n- Le contenu de `<template>` peut être n'importe quel HTML syntaxiquement correct.\n- Le contenu de `<template>` est considéré comme \"hors du document\", donc il n'affecte rien.\n- Nous pouvons accéder à `template.content` depuis JavaScript, le cloner pour le réutiliser dans un nouveau composant.\n\nLa balise `<template>` est assez unique, car :\n\n- Le navigateur vérifie la syntaxe HTML à l'intérieur de celle-ci (par opposition à l'utilisation d'une chaîne de modèle à l'intérieur d'un script).\n- ...Mais il permet toujours l'utilisation de n'importe quelle balise HTML de niveau supérieur, même celles qui n'ont pas de sens sans les wrappers appropriées (par exemple, `<tr>`).\n- Le contenu devient interactif : les scripts s'exécutent, la vidéo se joue en autoplay, etc.\n\nL'élément `<template>` ne comporte aucun mécanisme d'itération, de liaison de données ou de substitution de variables, mais nous pouvons les mettre en œuvre par-dessus.\n"
  },
  {
    "path": "8-web-components/5-slots-composition/article.md",
    "content": "# Shadow DOM slots, composition\n\nMany types of components, such as tabs, menus, image galleries, and so on, need the content to render.\n\nJust like built-in browser `<select>` expects `<option>` items, our `<custom-tabs>` may expect the actual tab content to be passed. And a `<custom-menu>` may expect menu items.\n\nThe code that makes use of `<custom-menu>` can look like this:\n\n```html\n<custom-menu>\n  <title>Candy menu</title>\n  <item>Lollipop</item>\n  <item>Fruit Toast</item>\n  <item>Cup Cake</item>\n</custom-menu>\n```\n\n...Then our component should render it properly, as a nice menu with given title and items, handle menu events, etc.\n\nHow to implement it?\n\nWe could try to analyze the element content and dynamically copy-rearrange DOM nodes. That's possible, but if we're moving elements to shadow DOM, then CSS styles from the document do not apply in there, so the visual styling may be lost. Also that requires some coding.\n\nLuckily, we don't have to. Shadow DOM supports `<slot>` elements, that are automatically filled by the content from light DOM.\n\n## Named slots\n\nLet's see how slots work on a simple example.\n\nHere, `<user-card>` shadow DOM provides two slots, filled from light DOM:\n\n```html run autorun=\"no-epub\" untrusted height=80\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `\n      <div>Name:\n*!*\n        <slot name=\"username\"></slot>\n*/!*\n      </div>\n      <div>Birthday:\n*!*\n        <slot name=\"birthday\"></slot>\n*/!*\n      </div>\n    `;\n  }\n});\n</script>\n\n<user-card>\n  <span *!*slot=\"username\"*/!*>John Smith</span>\n  <span *!*slot=\"birthday\"*/!*>01.01.2001</span>\n</user-card>\n```\n\nIn the shadow DOM, `<slot name=\"X\">` defines an \"insertion point\", a place where elements with `slot=\"X\"` are rendered.\n\nThen the browser performs \"composition\": it takes elements from the light DOM and renders them in corresponding slots of the shadow DOM. At the end, we have exactly what we want -- a component that can be filled with data.\n\nHere's the DOM structure after the script, not taking composition into account:\n\n```html\n<user-card>\n  #shadow-root\n    <div>Name:\n      <slot name=\"username\"></slot>\n    </div>\n    <div>Birthday:\n      <slot name=\"birthday\"></slot>\n    </div>\n  <span slot=\"username\">John Smith</span>\n  <span slot=\"birthday\">01.01.2001</span>\n</user-card>\n```\n\nWe created the shadow DOM, so here it is, under `#shadow-root`. Now the element has both light and shadow DOM.\n\nFor rendering purposes, for each `<slot name=\"...\">` in shadow DOM, the browser looks for `slot=\"...\"` with the same name in the light DOM. These elements are rendered inside the slots:\n\n![](shadow-dom-user-card.svg)\n\nThe result is called \"flattened\" DOM:\n\n```html\n<user-card>\n  #shadow-root\n    <div>Name:\n      <slot name=\"username\">\n        <!-- slotted element is inserted into the slot -->\n        <span slot=\"username\">John Smith</span>\n      </slot>\n    </div>\n    <div>Birthday:\n      <slot name=\"birthday\">\n        <span slot=\"birthday\">01.01.2001</span>\n      </slot>\n    </div>\n</user-card>\n```\n\n...But the flattened DOM exists only for rendering and event-handling purposes. It's kind of \"virtual\". That's how things are shown. But the nodes in the document are actually not moved around!\n\nThat can be easily checked if we run `querySelectorAll`: nodes are still at their places.\n\n```js\n// light DOM <span> nodes are still at the same place, under `<user-card>`\nalert( document.querySelectorAll('user-card span').length ); // 2\n```\n\nSo, the flattened DOM is derived from shadow DOM by inserting slots. The browser renders it and uses for style inheritance, event propagation (more about that later). But JavaScript still sees the document \"as is\", before flattening.\n\n````warn header=\"Only top-level children may have slot=\\\"...\\\" attribute\"\nThe `slot=\"...\"` attribute is only valid for direct children of the shadow host (in our example, `<user-card>` element). For nested elements it's ignored.\n\nFor example, the second `<span>` here is ignored (as it's not a top-level child of `<user-card>`):\n```html\n<user-card>\n  <span slot=\"username\">John Smith</span>\n  <div>\n    <!-- invalid slot, must be direct child of user-card -->\n    <span slot=\"birthday\">01.01.2001</span>\n  </div>\n</user-card>\n```\n````\n\nIf there are multiple elements in light DOM with the same slot name, they are appended into the slot, one after another.\n\nFor example, this:\n\n```html\n<user-card>\n  <span slot=\"username\">John</span>\n  <span slot=\"username\">Smith</span>\n</user-card>\n```\n\nGives this flattened DOM with two elements in `<slot name=\"username\">`:\n\n```html\n<user-card>\n  #shadow-root\n    <div>Name:\n      <slot name=\"username\">\n        <span slot=\"username\">John</span>\n        <span slot=\"username\">Smith</span>\n      </slot>\n    </div>\n    <div>Birthday:\n      <slot name=\"birthday\"></slot>\n    </div>\n</user-card>\n```\n\n## Slot fallback content\n\nIf we put something inside a `<slot>`, it becomes the fallback, \"default\" content. The browser shows it if there's no corresponding filler in light DOM.\n\nFor example, in this piece of shadow DOM, `Anonymous` renders if there's no `slot=\"username\"` in light DOM.\n\n```html\n<div>Name:\n  <slot name=\"username\">Anonymous</slot>\n</div>\n```\n\n## Default slot: first unnamed\n\nThe first `<slot>` in shadow DOM that doesn't have a name is a \"default\" slot. It gets all nodes from the light DOM that aren't slotted elsewhere.\n\nFor example, let's add the default slot to our `<user-card>` that shows all unslotted information about the user:\n\n```html run autorun=\"no-epub\" untrusted height=140\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `\n    <div>Name:\n      <slot name=\"username\"></slot>\n    </div>\n    <div>Birthday:\n      <slot name=\"birthday\"></slot>\n    </div>\n    <fieldset>\n      <legend>Other information</legend>\n*!*\n      <slot></slot>\n*/!*\n    </fieldset>\n    `;\n  }\n});\n</script>\n\n<user-card>\n*!*\n  <div>I like to swim.</div>\n*/!*\n  <span slot=\"username\">John Smith</span>\n  <span slot=\"birthday\">01.01.2001</span>\n*!*\n  <div>...And play volleyball too!</div>\n*/!*\n</user-card>\n```\n\nAll the unslotted light DOM content gets into the \"Other information\" fieldset.\n\nElements are appended to a slot one after another, so both unslotted pieces of information are in the default slot together.\n\nThe flattened DOM looks like this:\n\n```html\n<user-card>\n  #shadow-root\n    <div>Name:\n      <slot name=\"username\">\n        <span slot=\"username\">John Smith</span>\n      </slot>\n    </div>\n    <div>Birthday:\n      <slot name=\"birthday\">\n        <span slot=\"birthday\">01.01.2001</span>\n      </slot>\n    </div>\n    <fieldset>\n      <legend>Other information</legend>\n*!*\n      <slot>\n        <div>I like to swim.</div>\n        <div>...And play volleyball too!</div>\n      </slot>\n*/!*\n    </fieldset>\n</user-card>\n```\n\n## Menu example\n\nNow let's back to `<custom-menu>`, mentioned at the beginning of the chapter.\n\nWe can use slots to distribute elements.\n\nHere's the markup for `<custom-menu>`:\n\n```html\n<custom-menu>\n  <span slot=\"title\">Candy menu</span>\n  <li slot=\"item\">Lollipop</li>\n  <li slot=\"item\">Fruit Toast</li>\n  <li slot=\"item\">Cup Cake</li>\n</custom-menu>\n```\n\nThe shadow DOM template with proper slots:\n\n```html\n<template id=\"tmpl\">\n  <style> /* menu styles */ </style>\n  <div class=\"menu\">\n    <slot name=\"title\"></slot>\n    <ul><slot name=\"item\"></slot></ul>\n  </div>\n</template>\n```\n\n1. `<span slot=\"title\">` goes into `<slot name=\"title\">`.\n2. There are many `<li slot=\"item\">` in the `<custom-menu>`, but only one `<slot name=\"item\">` in the template. So all such `<li slot=\"item\">` are appended to `<slot name=\"item\">` one after another, thus forming the list.\n\nThe flattened DOM becomes:\n\n```html\n<custom-menu>\n  #shadow-root\n    <style> /* menu styles */ </style>\n    <div class=\"menu\">\n      <slot name=\"title\">\n        <span slot=\"title\">Candy menu</span>\n      </slot>\n      <ul>\n        <slot name=\"item\">\n          <li slot=\"item\">Lollipop</li>\n          <li slot=\"item\">Fruit Toast</li>\n          <li slot=\"item\">Cup Cake</li>\n        </slot>\n      </ul>\n    </div>\n</custom-menu>\n```\n\nOne might notice that, in a valid DOM, `<li>` must be a direct child of `<ul>`. But that's flattened DOM, it describes how the component is rendered, such thing happens naturally here.\n\nWe just need to add a `click` handler to open/close the list, and the `<custom-menu>` is ready:\n\n```js\ncustomElements.define('custom-menu', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n\n    // tmpl is the shadow DOM template (above)\n    this.shadowRoot.append( tmpl.content.cloneNode(true) );\n\n    // we can't select light DOM nodes, so let's handle clicks on the slot\n    this.shadowRoot.querySelector('slot[name=\"title\"]').onclick = () => {\n      // open/close the menu\n      this.shadowRoot.querySelector('.menu').classList.toggle('closed');\n    };\n  }\n});\n```\n\nHere's the full demo:\n\n[iframe src=\"menu\" height=140 edit]\n\nOf course, we can add more functionality to it: events, methods and so on.\n\n## Updating slots\n\nWhat if the outer code wants to add/remove menu items dynamically?\n\n**The browser monitors slots and updates the rendering if slotted elements are added/removed.**\n\nAlso, as light DOM nodes are not copied, but just rendered in slots, the changes inside them immediately become visible.\n\nSo we don't have to do anything to update rendering. But if the component code wants to know about slot changes, then `slotchange` event is available.\n\nFor example, here the menu item is inserted dynamically after 1 second, and the title changes after 2 seconds:\n\n```html run untrusted height=80\n<custom-menu id=\"menu\">\n  <span slot=\"title\">Candy menu</span>\n</custom-menu>\n\n<script>\ncustomElements.define('custom-menu', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `<div class=\"menu\">\n      <slot name=\"title\"></slot>\n      <ul><slot name=\"item\"></slot></ul>\n    </div>`;\n\n    // shadowRoot can't have event handlers, so using the first child\n    this.shadowRoot.firstElementChild.addEventListener('slotchange',\n      e => alert(\"slotchange: \" + e.target.name)\n    );\n  }\n});\n\nsetTimeout(() => {\n  menu.insertAdjacentHTML('beforeEnd', '<li slot=\"item\">Lollipop</li>')\n}, 1000);\n\nsetTimeout(() => {\n  menu.querySelector('[slot=\"title\"]').innerHTML = \"New menu\";\n}, 2000);\n</script>\n```\n\nThe menu rendering updates each time without our intervention.\n\nThere are two `slotchange` events here:\n\n1. At initialization:\n\n    `slotchange: title` triggers immediately, as the `slot=\"title\"` from the light DOM gets into the corresponding slot.\n2. After 1 second:\n\n    `slotchange: item` triggers, when a new `<li slot=\"item\">` is added.\n\nPlease note: there's no `slotchange` event after 2 seconds, when the content of `slot=\"title\"` is modified. That's because there's no slot change. We modify the content inside the slotted element, that's another thing.\n\nIf we'd like to track internal modifications of light DOM from JavaScript, that's also possible using a more generic mechanism: [MutationObserver](info:mutation-observer).\n\n## Slot API\n\nFinally, let's mention the slot-related JavaScript methods.\n\nAs we've seen before, JavaScript looks at the \"real\" DOM, without flattening. But, if the shadow tree has `{mode: 'open'}`, then we can figure out which elements assigned to a slot and, vice-versa, the slot by the element inside it:\n\n- `node.assignedSlot` -- returns the `<slot>` element that the `node` is assigned to.\n- `slot.assignedNodes({flatten: true/false})` -- DOM nodes, assigned to the slot. The `flatten` option is `false` by default. If explicitly set to `true`, then it looks more deeply into the flattened DOM, returning nested slots in case of nested components and the fallback content if no node assigned.\n- `slot.assignedElements({flatten: true/false})` -- DOM elements, assigned to the slot (same as above, but only element nodes).\n\nThese methods are useful when we need not just show the slotted content, but also track it in JavaScript.\n\nFor example, if `<custom-menu>` component wants to know, what it shows, then it could track `slotchange` and get the items from `slot.assignedElements`:\n\n```html run untrusted height=120\n<custom-menu id=\"menu\">\n  <span slot=\"title\">Candy menu</span>\n  <li slot=\"item\">Lollipop</li>\n  <li slot=\"item\">Fruit Toast</li>\n</custom-menu>\n\n<script>\ncustomElements.define('custom-menu', class extends HTMLElement {\n  items = []\n\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `<div class=\"menu\">\n      <slot name=\"title\"></slot>\n      <ul><slot name=\"item\"></slot></ul>\n    </div>`;\n\n    // triggers when slot content changes\n*!*\n    this.shadowRoot.firstElementChild.addEventListener('slotchange', e => {\n      let slot = e.target;\n      if (slot.name == 'item') {\n        this.items = slot.assignedElements().map(elem => elem.textContent);\n        alert(\"Items: \" + this.items);\n      }\n    });\n*/!*\n  }\n});\n\n// items update after 1 second\nsetTimeout(() => {\n  menu.insertAdjacentHTML('beforeEnd', '<li slot=\"item\">Cup Cake</li>')\n}, 1000);\n</script>\n```\n\n\n## Summary\n\nUsually, if an element has shadow DOM, then its light DOM is not displayed. Slots allow to show elements from light DOM in specified places of shadow DOM.\n\nThere are two kinds of slots:\n\n- Named slots: `<slot name=\"X\">...</slot>` -- gets light children with `slot=\"X\"`.\n- Default slot: the first `<slot>` without a name (subsequent unnamed slots are ignored) -- gets unslotted light children.\n- If there are many elements for the same slot -- they are appended one after another.\n- The content of `<slot>` element is used as a fallback. It's shown if there are no light children for the slot.\n\nThe process of rendering slotted elements inside their slots is called \"composition\". The result is called a \"flattened DOM\".\n\nComposition does not really move nodes, from JavaScript point of view the DOM is still same.\n\nJavaScript can access slots using methods:\n- `slot.assignedNodes/Elements()` -- returns nodes/elements inside the `slot`.\n- `node.assignedSlot` -- the reverse property, returns slot by a node.\n\nIf we'd like to know what we're showing, we can track slot contents using:\n- `slotchange` event -- triggers the first time a slot is filled, and on any add/remove/replace operation of the slotted element, but not its children. The slot is `event.target`.\n- [MutationObserver](info:mutation-observer) to go deeper into slot content, watch changes inside it.\n\nNow, as we know how to show elements from light DOM in shadow DOM, let's see how to style them properly. The basic rule is that shadow elements are styled inside, and light elements -- outside, but there are notable exceptions.\n\nWe'll see the details in the next chapter.\n"
  },
  {
    "path": "8-web-components/5-slots-composition/menu.view/index.html",
    "content": "<!doctype html>\n<template id=\"tmpl\">\n  <style>\n  ul {\n    margin: 0;\n    list-style: none;\n    padding-left: 20px;\n  }\n\n  ::slotted([slot=\"title\"]) {\n    font-size: 18px;\n    font-weight: bold;\n    cursor: pointer;\n  }\n\n  ::slotted([slot=\"title\"])::before {\n    content: '📂';\n    font-size: 14px;\n  }\n\n  .closed ::slotted([slot=\"title\"])::before {\n    content: '📁';\n  }\n\n  .closed ul {\n    display: none;\n  }\n  </style>\n\n  <div class=\"menu\">\n    <slot name=\"title\"></slot>\n    <ul><slot name=\"item\"></slot></ul>\n  </div>\n</template>\n\n<script>\ncustomElements.define('custom-menu', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.append( tmpl.content.cloneNode(true) );\n\n    this.shadowRoot.querySelector('slot[name=\"title\"]').onclick = () => {\n      this.shadowRoot.querySelector('.menu').classList.toggle('closed');\n    };\n  }\n});\n</script>\n\n<custom-menu>\n  <span slot=\"title\">Candy menu</span>\n  <li slot=\"item\">Lollipop</li>\n  <li slot=\"item\">Fruit Toast</li>\n  <li slot=\"item\">Cup Cake</li>\n</custom-menu>\n"
  },
  {
    "path": "8-web-components/6-shadow-dom-style/article.md",
    "content": "# Application de style depuis le Shadow DOM\n\nLe Shadow DOM peut inclure des balises `<style>` and `<link rel=\"stylesheet\" href=\"…\">`. Dans le dernier cas, les feuilles de style sont mises en cache (HTTP-Cached), donc elles ne sont pas retéléchargées pour plusieurs composants qui utilisent le même template.\n\nEn règle générale, les styles locaux fonctionnent au sein de l'arborescence Shadow, et les styles du document fonctionnent en dehors. Mais il y a quelques exceptions.\n\n## :host\n\nLe sélecteur `:host` permet de sélectionner l'hôte shadow (l'élément contenant l'arborescence shadow).\n\nPar exemple, nous créeons un élément `<custom-dialog>` qui doit être centré. Pour cela nous avons besoin d'appliquer un style depuis l'élément `<custom-dialog>` lui-même.\n\nC'est exactement ce que fait `:host` :\n\n```html run autorun=\"no-epub\" untrusted height=80\n<template id=\"tmpl\">\n  <style>\n    /* La règle de style sera appliquée depuis\n    l'intérieur de l'élément custom-dialog */\n    :host {\n      position: fixed;\n      left: 50%;\n      top: 50%;\n      transform: translate(-50%, -50%);\n      display: inline-block;\n      border: 1px solid red;\n      padding: 10px;\n    }\n  </style>\n  <slot></slot>\n</template>\n\n<script>\ncustomElements.define('custom-dialog', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'}).append(tmpl.content.cloneNode(true));\n  }\n});\n</script>\n\n<custom-dialog>\n  Hello!\n</custom-dialog>\n```\n\n## Cascading\n\nL'hôte shadow (`<custom-dialog>` lui-même) réside dans le light DOM, donc il est affecté par les règles CSS du document.\n\nS'il y a une propriété de style locale dans `:host`, et dans le document, alors le style du document prendra le pas.\n\nPar exemple, si nous avons dans le document :\n```html\n<style>\ncustom-dialog {\n  padding: 0;\n}\n</style>\n```\n...Alors le `<custom-dialog>` n'aura pas de marge interne.\n\nC'est vraiment pratique, comme nous pouvons définir des styles par défaut dans les règles d'`:host`, et les outrepasser dans le document.\n\nL'exception est quand une propriété locale est marquée `!important`, pour de telles propriétés, les styles locaux prennent le pas.\n\n## :host(selector)\n\nTout comme `:host`, mais appliqué si l'hôte shadow correspond au `sélecteur`.\n\nPar exemple, nous aimerions centrer le `<custom-dialog>` seulement s'il a l'attribut `centered` :\n\n```html run autorun=\"no-epub\" untrusted height=80\n<template id=\"tmpl\">\n  <style>\n*!*\n    :host([centered]) {\n*/!*\n      position: fixed;\n      left: 50%;\n      top: 50%;\n      transform: translate(-50%, -50%);\n      border-color: blue;\n    }\n\n    :host {\n      display: inline-block;\n      border: 1px solid red;\n      padding: 10px;\n    }\n  </style>\n  <slot></slot>\n</template>\n\n<script>\ncustomElements.define('custom-dialog', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'}).append(tmpl.content.cloneNode(true));\n  }\n});\n</script>\n\n\n<custom-dialog centered>\n  Centered!\n</custom-dialog>\n\n<custom-dialog>\n  Not centered.\n</custom-dialog>\n```\n\nMaintenant les styles additionnels concernant le centrage sont uniquement appliqués au premier dialogue : `<custom-dialog centered>`.\n\nPour résumé, nous pouvons utiliser une famille de sélecteur `:host` pour appliquer des styles à l'élément principal du composant. Ces styles (excepté `!important`) peuvent être outrepasser par le document.\n\n## Application de style au contenu \"slotted\"\n\nMaintenant considèrons la situation avec des slots.\n\nLes éléments \"slotted\" proviennent du light DOM, donc ils utilisent les styles du document. Les styles locaux n'affectent pas les contenus \"slotted\".\n\nDans l'exemple ci-dessous, la `<span>` \"slotted\" est en gras, de par le style du document, mais `background` du style local n'est pas pris en compte :\n\n```html run autorun=\"no-epub\" untrusted height=80\n<style>\n*!*\n  span { font-weight: bold }\n*/!*\n</style>\n\n<user-card>\n  <div slot=\"username\">*!*<span>John Smith</span>*/!*</div>\n</user-card>\n\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `\n      <style>\n*!*\n      span { background: red; }\n*/!*\n      </style>\n      Name: <slot name=\"username\"></slot>\n    `;\n  }\n});\n</script>\n```\n\nLe résultat est gras, mais pas rouge.\n\nSi nous voulions appliquer du style sur les éléments \"slotted\" dans notre composant, il y a deux possibilités.\n\nLa première, on pourrait appliquer du style à `<slot>` elle même et compter sur l'héritage du CSS :\n\n```html run autorun=\"no-epub\" untrusted height=80\n<user-card>\n  <div slot=\"username\">*!*<span>John Smith</span>*/!*</div>\n</user-card>\n\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `\n      <style>\n*!*\n      slot[name=\"username\"] { font-weight: bold; }\n*/!*\n      </style>\n      Name: <slot name=\"username\"></slot>\n    `;\n  }\n});\n</script>\n```\n\nIci `<p>John Smith</p>` devient gras, grâce à l'héritage du CSS entre `<slot>` et son contenu. Mais dans le CSS lui-même, toutes les propriétés ne sont pas héritée.\n\nUne autre option est d'utiliser la pseudo classe `::slotted(selector)`. Elle fait correspondre deux éléments selon deux conditions :\n\n1. Il s'agit d'un élément \"slotted\", ça vient du light DOM. Le nom du slot n'a pas d'importance. Ça fonctionne pour tout élément \"slotted\", mais seulement pour l'élément lui-même, pas pour ses enfants.\n2. L'élément correspond au `sélecteur`.\n\nDans notre exemple, `::slotted(div)` selectionne uniquement `<div slot=\"username\">`, mais pas ses enfants :\n\n```html run autorun=\"no-epub\" untrusted height=80\n<user-card>\n  <div slot=\"username\">\n    <div>John Smith</div>\n  </div>\n</user-card>\n\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `\n      <style>\n*!*\n      ::slotted(div) { border: 1px solid red; }\n*/!*\n      </style>\n      Name: <slot name=\"username\"></slot>\n    `;\n  }\n});\n</script>\n```\n\nVeuillez noter, le sélecteur `::slotted` ne peut pas descendre plus bas dans le slot. Ces sélecteurs sont invalides :\n\n```css\n::slotted(div span) {\n  /* Notre <div> \"slotted\" ne correspond pas */\n}\n\n::slotted(div) p {\n  /* Ne peut pas aller dans le light DOM */\n}\n```\n\nAussi, `::slotted` peut être utilisé uniquement en CSS. On ne peut pas l'utiliser dans `querySelector`.\n\n## Les Hooks en CSS avec des propriétés personnalisées\n\nComment appliquons-nous du style aux éléments internes à un composant depuis le document principal ?\n\nLes sélecteurs comme `:host` appliquent des règles aux éléments `<custom-dialog>` ou `<user-card>`, mais comment appliquons-nous du style shadow DOM qui leurs sont internes ?\n\nIl n'y a pas de sélecteurs qui puisse directement affecté les styles du shadow DOM depuis le document. Mais comme nous venons d'exposer des méthodes pour interagir avec notre composant, nous pouvons exposer des variables CSS (propriétés CSS personnalisées) pour lui appliquer du style.\n\n**Les propriétés CSS personnalisées existent à tous les niveaux, dans le light et le shadow.**\n\nPar exemple, dans le shadow DOM nous pouvons utiliser la variable CSS `--user-card-field-color` pour appliquer du style aux champs, et pouvoir définir la valeur dans le document extérieur :\n\n```html\n<style>\n  .field {\n    color: var(--user-card-field-color, black);\n    /* Si --user-card-field-color n'est pas définie, utiliser la couleur noir*/\n  }\n</style>\n<div class=\"field\">Name: <slot name=\"username\"></slot></div>\n<div class=\"field\">Birthday: <slot name=\"birthday\"></slot></div>\n```\n\nAlors, on peut déclarer cette propriété dans le document extérieur pour `<user-card>` :\n\n```css\nuser-card {\n  --user-card-field-color: green;\n}\n```\n\nLes propriétés CSS personnalisées passent au travers du shadow DOM, elles sont visibles depuis n'importe où, donc la règle intérieure `.field` s'en servira.\n\nVoici l'exemple complet :\n\n```html run autorun=\"no-epub\" untrusted height=80\n<style>\n*!*\n  user-card {\n    --user-card-field-color: green;\n  }\n*/!*\n</style>\n\n<template id=\"tmpl\">\n  <style>\n*!*\n    .field {\n      color: var(--user-card-field-color, black);\n    }\n*/!*\n  </style>\n  <div class=\"field\">Name: <slot name=\"username\"></slot></div>\n  <div class=\"field\">Birthday: <slot name=\"birthday\"></slot></div>\n</template>\n\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.append(document.getElementById('tmpl').content.cloneNode(true));\n  }\n});\n</script>\n\n<user-card>\n  <span slot=\"username\">John Smith</span>\n  <span slot=\"birthday\">01.01.2001</span>\n</user-card>\n```\n\n## Résumé\n\nLe Shadow DOM peut inclure des styles, tels que `<style>` ou `<link rel=\"stylesheet\">`.\n\nLes styles locaux peuvent affectés :\n\n- l'arborescence shadow,\n- l'hôte shadow avec `:host` et les pseudo classes `:host()`,\n- les éléments \"slotted\" (qui proviennet du light DOM), `::slotted(selector)` permet de selectionner les éléments \"slotted\" en eux-même, mais pas leurs enfants.\n\nLes styles du document peuvent affectés :\n\n- l'hôte shadow (puisqu'il existe dans le document extérieur)\n- les éléments \"slotted\" et leurs contenus (puisqu'ils existent aussi dans le document extérieur)\n\nQuand les propriétés CSS entrent en conflit, les styles du document prennent le pas, sauf lorsque la propriété est marquée `!important`. Dans ce cas les styles locaux prennent le pas.\n\nLes propriétés CSS personnalisées passent au travers le shadow DOM. Elles sont utilisées comme des \"hooks\" pour appliquer du style au composant :\n\n1. Le composant utilise une propriété CSS personnalisée pour appliquer du style aux éléments clés, tels que `var(--component-name-title, <default value>)`.\n2. L'autheur d'un composant publie ces propriétés pour les développeurs, elles sont aussi importantes que les autres méthodes du composant.\n3. Quand un développeur veut appliquer du style à un titre, ils assignent une propriété CSS `--component-name-title` pour l'hôte shadow ou au dessus.\n4. Profitez !"
  },
  {
    "path": "8-web-components/7-shadow-dom-events/article.md",
    "content": "# DOM fantôme et événements\n\nL'idée derrière l'arbre fantôme est d'encapsuler les détails d'implémentation internes d'un composant.\n\nDisons qu'un événement de clic se produit à l'intérieur du DOM fantôme du composant `<user-card>`. Mais les scripts dans le document principal n'ont aucune idée des internes du DOM fantôme, surtout si le composant provient d'une bibliothèque tierce.  \n\nDonc, pour garder les détails encapsulés, le navigateur *recible* l'événement.\n\n**Les événements qui se produisent dans le DOM fantôme ont pour cible l'élément hôte, lorsqu'ils sont capturés en dehors du composant.**\n\nVoici un exemple simple :\n\n```html run autorun=\"no-epub\" untrusted height=60\n<user-card></user-card>\n\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `<p>\n      <button>Click me</button>\n    </p>`;\n    this.shadowRoot.firstElementChild.onclick =\n      e => alert(\"Inner target: \" + e.target.tagName);\n  }\n});\n\ndocument.onclick =\n  e => alert(\"Outer target: \" + e.target.tagName);\n</script>\n```\n\nSi vous cliquez sur le bouton, les messages sont :\n\n1. Inner target : `BUTTON` -- le gestionnaire d'événement interne obtient la cible correcte, l'élément dans le DOM fantôme.\n2. Outer target : `USER-CARD` -- le gestionnaire d'événement du document obtient l'hôte fantôme comme cible.\n\nLe reciblage d'événement est une bonne chose à avoir, parce que le document externe n'a pas à connaître les internes du composant. De son point de vue, l'événement s'est produit sur `<user-card>`.\n\n**Le reciblage ne se produit pas si l'événement se produit sur un élément placé à l'intérieur d'un emplacement, qui vit physiquement dans le DOM standard.**\n\nPar exemple, si un utilisateur clique sur `<span slot=\"username\">` dans l'exemple ci-dessous, la cible de l'événement est exactement cet élément `span`, pour les gestionnaires fantôme et standard :\n\n```html run autorun=\"no-epub\" untrusted height=60\n<user-card id=\"userCard\">\n*!*\n  <span slot=\"username\">John Smith</span>\n*/!*\n</user-card>\n\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `<div>\n      <b>Name:</b> <slot name=\"username\"></slot>\n    </div>`;\n\n    this.shadowRoot.firstElementChild.onclick =\n      e => alert(\"Inner target: \" + e.target.tagName);\n  }\n});\n\nuserCard.onclick = e => alert(`Outer target: ${e.target.tagName}`);\n</script>\n```\n\nSi un clic se produit sur `\"John Smith\"`, pour les gestionnaires interne et externe, la cible est `<span slot=\"username\">`. C'est un élément du DOM standard, donc pas de reciblage.\n\nEn revanche, si le clic se produit sur un élément provenant du DOM fantôme, par exemple sur `<b>Nom</b>`, alors, comme il sort du DOM fantôme, son `event.target` est réinitialisé à `<user-card>`.\n\n## Bouillonnement, event.composedPath()\n\nPour les besoins du bouillonnement d'événements, le DOM aplati est utilisé.\n\nDonc, si nous avons un élément dans un emplacement, et qu'un événement se produit quelque part à l'intérieur de celui-ci, alors il est propagé jusqu'à l'emplacement `<slot>` et vers le haut.\n\nLe chemin complet vers la cible originale de l'événement, avec tous les éléments fantômes, peut être obtenu en utilisant `event.composedPath()`. Comme on peut le voir d'après le nom de la méthode, ce chemin est pris après la composition.\n\nDans l'exemple ci-dessus, le DOM aplati est :\n\n```html\n<user-card id=\"userCard\">\n  #shadow-root\n    <div>\n      <b>Name:</b>\n      <slot name=\"username\">\n        <span slot=\"username\">John Smith</span>\n      </slot>\n    </div>\n</user-card>\n```\n\n\nAinsi, pour un clic sur `<span slot=\"username\">`, un appel à `event.composedPath()` renvoie un tableau : [`span`, `slot`, `div`, `shadow-root`, `user-card`, `body`, `html`, `document`, `window`]. C'est exactement la chaîne parentale de l'élément cible dans le DOM aplati, après la composition.\n\n```warn header=\"Les détails de l'arbre fantôme ne sont fournis que pour les arbres `{mode : 'open'}`\"\nSi l'arbre fantôme a été créé avec `{mode : 'closed'}`, alors le chemin composé commence à partir de l'hôte : `user-card` et plus haut.\n\nC'est le même principe que pour les autres méthodes qui fonctionnent avec les DOM fantômes. Les internes des arbres fermés sont complètement cachés.\n```\n\n\n## event.composed\n\nLa plupart des événements réussissent à traverser une frontière DOM fantôme. Il y a quelques événements qui ne le font pas.\n\nCeci est régi par la propriété `composed` de l'objet événement. Si elle est `true`, alors l'événement traverse la frontière. Sinon, il ne peut être attrapé qu'à l'intérieur du DOM fantôme.\n\nSi vous jetez un coup d'oeil à la spécification [UI Events] (https://www.w3.org/TR/uievents), la plupart des événements ont `composed : true` :\n\n- `blur`, `focus`, `focusin`, `focusout`,\n- `click`, `dblclick`,\n- `mousedown`, `mouseup`, `mousemove`, `mouseout`, `mouseover`,\n- `wheel`,\n- `beforeinput`, `input`, `keydown`, `keyup`.\n\nTous les événements liés au toucher et au pointeur ont également la propriété `composed : true`.\n\nCertains événements ont cependant `composed : false` :\n\n- `mouseenter`, `mouseleave` (ils ne bouillonnent pas du tout),\n- `load`, `unload`, `abort`, `error`,\n- `select`,\n- `slotchange`.\n\nCes événements ne peuvent être capturés que sur les éléments du même DOM, où se trouve la cible de l'événement.\n\n## Événements personnalisés\n\nLorsque nous envoyons des événements personnalisés, nous devons définir les propriétés `bubbles` et `composed` à `true` pour qu'il y ait bouillonnement vers le haut et hors du composant.\n\nPar exemple, ici, nous créons `div#inner` dans le DOM fantôme de `div#outer` et nous déclenchons deux événements sur lui. Seul celui dont la valeur est `composed : true` se retrouve à l'extérieur du document :\n\n```html run untrusted height=0\n<div id=\"outer\"></div>\n\n<script>\nouter.attachShadow({mode: 'open'});\n\nlet inner = document.createElement('div');\nouter.shadowRoot.append(inner);\n\n/*\ndiv(id=outer)\n  #shadow-dom\n    div(id=inner)\n*/\n\ndocument.addEventListener('test', event => alert(event.detail));\n\ninner.dispatchEvent(new CustomEvent('test', {\n  bubbles: true,\n*!*\n  composed: true,\n*/!*\n  detail: \"composed\"\n}));\n\ninner.dispatchEvent(new CustomEvent('test', {\n  bubbles: true,\n*!*\n  composed: false,\n*/!*\n  detail: \"not composed\"\n}));\n</script>\n```\n\n## Résumé\n\nLes événements ne traversent les frontières du DOM que si leur drapeau `composed` est mis à `true`.\n\nLes événements intégrés ont pour la plupart `composed : true`, comme décrit dans les spécifications correspondantes :\n\n- Evènements UI <https://www.w3.org/TR/uievents>.\n- Événements tactiles <https://w3c.github.io/touch-events>.\n- Événements pointeur <https://www.w3.org/TR/pointerevents>.\n- ...Et ainsi de suite.\n\nQuelques événements intégrés qui ont `composed : false` :\n- `mouseenter`, `mouseleave` (ne bouillonnent pas non plus),\n- `load`, `unload`, `abort`, `error`,\n- `select`,\n- `slotchange`.\n\nCes événements ne peuvent être capturés que sur des éléments du même DOM.\n\nSi nous envoyons un `CustomEvent`, alors nous devons explicitement définir `composed : true`.\n\nVeuillez noter que dans le cas de composants imbriqués, un DOM fantôme peut être imbriqué dans un autre. Dans ce cas, les événements composés traversent toutes les frontières du DOM fantôme. Ainsi, si un événement n'est destiné qu'au composant qui l'entoure immédiatement, nous pouvons également le dispatcher sur l'hôte fantôme et mettre `composed : false`. Ainsi, il sortira du DOM caché du composant, mais n'atteindra pas le DOM de niveau supérieur.\n"
  },
  {
    "path": "8-web-components/index.md",
    "content": "# Composants Web\n\nLes composants Web sont un ensemble de standards pour faire des composants auto-contenu: des éléments HTML personnalisés avec leurs propres propriétés et méhodes, encapsulé dans le DOM et les styles."
  },
  {
    "path": "9-regular-expressions/01-regexp-introduction/article.md",
    "content": "# Modèles et marqueurs\n\nLes expressions régulières sont un moyen puissant de rechercher et de remplacer du texte.\n\nEn JavaScript, ils sont disponibles en tant que object [RegExp](mdn:js/RegExp) et également intégrés dans les méthodes de chaînes de caractères.\n\n## Expressions régulières\n\nUne expression régulière (également \"regexp\" ou simplement \"reg\") est constituée d'un *pattern* et de *flags* optionnels.\n\nIl existe deux syntaxes pour créer un objet expression régulière.\n\nLa syntaxe \"longue\" :\n\n```js\nregexp = new RegExp(\"pattern\", \"flags\");\n```\n\nEt la syntaxe courte, en utilisant des slash `\"/\"` :\n\n```js\nregexp = /pattern/; // aucun marqueur\nregexp = /pattern/gmi; // avec marqueurs g, m, et i (bientôt abordés)\n```\n\nLes slash `pattern:/.../` indique à JavaScript que l'on crée une expression régulière. Il joue le même rôle que les guillemets pour les chaînes de caractères (les \"string\").\n\nDans les deux cas `regexp` devient un objet de la classe intégrée `RegExp`.\n\nLa différence principale entre ces deux syntaxes réside dans le fait que les pattern utilisants des slashes `/.../` ne permettent pas d'insérer des expressions (comme les modèles littéraux de chaîne de caractères `$ {...}`). Ils sont complètement statiques.\n\nLes slashes sont utilisés lorsque nous connaissons l'expression régulière au moment de l'écriture du code -- et c'est la situation la plus courante. Alors que `new RegExp` est plus utilisé lorsque nous devons créer une expression régulière \"à la volée\" à partir d'une chaîne de caractères générée dynamiquement, par exemple :\n\n```js\nlet tag = prompt(\"What tag do you want to find?\", \"h2\");\n\nlet regexp = new RegExp(`<${tag}>`); // same as /<h2>/ if answered \"h2\" in the prompt above\n```\n\n## Flags\n\nLes expressions régulières peuvent avoir des flags qui affectent la recherche.\n\nIl n'y en a que 6 en JavaScript :\n\n`pattern:i`\n: Avec cet indicateur, la recherche est insensible à la casse: pas de différence entre `A` et `a` (voir l'exemple ci-dessous).\n\n`pattern:g`\n: Avec cet indicateur, la recherche liste toutes les correspondances, sans lui - seulement la première.\n\n`pattern:m`\n: Mode multiligne (couvert dans le chapitre <info:regexp-multiline-mode>).\n\n`pattern:s`\n: Active le mode \"dotall\", qui permet à un `pattern : .` de correspondre au caractère de nouvelle ligne `\\n` (traité dans le chapitre <info:regexp-character-classes>).\n\n`pattern:u`\n: Active le support complet Unicode. Le flag permet le traitement correct des paires de substitution. Plus à ce sujet dans le chapitre <info:regexp-unicode>.\n\n`pattern:y`\n: mode \"Sticky\" : chercher à la position exacte dans le texte (couvert dans le chapitre <info:regexp-sticky>)\n\n```smart header=\"Couleurs\"\nÀ partir de maintenant le code couleur sera :\n\n- regexp -- `pattern:red`\n- chaîne de caractère (là où l'on recherchera) -- `subject:blue`\n- résultat -- `match:green`\n```\n\n## Rechercher : str.match\n\nComme cela a été dit précédemment, les expressions régulières sont intégrées aux méthodes de chaîne de caractères.\n\nLa méthode `str.match(regexp)` trouve tous les résultats de `regexp` dans la chaîne de caractères `str`.\n\nIl dispose de 3 modes de travail :\n\n1. If the regular expression has flag `pattern:g`, it returns an array of all matches:\n    ```js run\n    let str = \"We will, we will rock you\";\n\n    alert( str.match(/we/gi) ); // We,we (un tableau de 2 sous-chaînes de caractères correspondant)\n    ```\n    Veuillez noter que les deux `match:We` et `match:we` sont trouvés, parce que le flag `pattern:i` rend l'expression régulière insensible à la casse.\n\n2. Si aucun indicateur de ce type n'existe, il retourne uniquement la première correspondance sous la forme d'un tableau, avec la correspondance complète à l'index `0` et quelques détails supplémentaires dans les propriétés :\n    ```js run\n    let str = \"We will, we will rock you\";\n\n    let result = str.match(/we/i); // without flag g\n\n    alert( result[0] );     // We (1st match)\n    alert( result.length ); // 1\n\n    // Details:\n    alert( result.index );  // 0 (position of the match)\n    alert( result.input );  // We will, we will rock you (source string)\n    ```\n    Le tableau peut avoir d’autres index, en plus de `0` si une partie de l’expression régulière est entre parenthèses. Nous couvrirons cela dans le chapitre <info:regexp-groups>.\n\n3. Et, enfin, s'il n'y a pas de correspondance, `null` est renvoyé (peu importe qu'il y ait un flag `pattern:g` ou pas).\n\n    C'est une nuance très importante. S'il n'y a pas de correspondance, nous n'obtenons pas un tableau vide, mais `null`. Oublier cela peut entraîner des erreurs, par exemple :\n\n    ```js run\n    let matches = \"JavaScript\".match(/HTML/); // = null\n\n    if (!matches.length) { // Error: Impossible de lire la propriété 'length' de null\n      alert(\"Error in the line above\");\n    }\n    ```\n\n    Si nous souhaitons que le résultat soit toujours un tableau, nous pouvons l'écrire comme ceci :\n\n    ```js run\n    let matches = \"JavaScript\".match(/HTML/)*!* || []*/!*;\n\n    if (!matches.length) {\n      alert(\"No matches\"); // maintenant ça fonctionne\n    }\n    ```\n\n## Remplacer : str.replace\n\nLa méthode `str.replace(regexp, replacement)` remplace les correspondances en utilisant `regexp` dans la chaîne de caractères `str` avec `replacement` (tous les résultats s'il y a un flag `pattern:g`, sinon seulement le premier).\n\nPar exemple :\n\n```js run\n// no flag g\nalert( \"We will, we will\".replace(/we/i, \"I\") ); // I will, we will\n\n// with flag g\nalert( \"We will, we will\".replace(/we/ig, \"I\") ); // I will, I will\n```\n\nLe deuxième argument est la chaîne de caractères `replacement`. Nous pouvons utiliser des combinaisons de caractères spéciaux pour insérer des fragments de la correspondance :\n\n| Symboles             | Action dans la chaîne de caractères de remplacement string                                                                                           |\n|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `$&`                 | insère toute la correspondance match                                                                                                                 |\n| <code>$&#096;</code> | insère une partie de la chaîne de caractères avant la correspondance                                                                                 |\n| `$'`                 | insère une partie de la chaîne de caractères après la correspondance                                                                                 |\n| `$n`                 | si `n` est un nombre à un ou deux chiffres, alors il insère le contenu des nièmes parenthèses, plus à ce sujet dans le chapitre <info:regexp-groups> |\n| `$<name>`            | insère le contenu des parenthèses avec le `name` donné, plus à ce sujet dans le chapitre <info:regexp-groups>                                        |\n| `$$`                 | insère le caractère `$`                                                                                                                              |\n\nUn exemple avec `pattern:$&` :\n\n```js run\nalert( \"I love HTML\".replace(/HTML/, \"$& and JavaScript\") ); // I love HTML and JavaScript\n```\n\n## Tester : regexp.test\n\nLa méthode `regexp.test(str)` recherche au moins une correspondance ; si elle est trouvée, retourne `true`, sinon `false`.\n\n```js run\nlet str = \"I love JavaScript\";\nlet regexp = /LOVE/i;\n\nalert( regexp.test(str) ); // true\n```\n\nPlus loin dans ce chapitre, nous étudierons davantage d’expressions régulières, parcourerons de nombreux autres exemples et rencontrerons d’autres méthodes.\n\nFull information about the methods is given in the article <info:regexp-methods>.\n\n## Résumé\n\n- Une expression régulière consiste en un modèle et des indicateurs facultatifs : `pattern:g`, `pattern:i`, `pattern:m`, `pattern:u`, `pattern:s`, `pattern:y`.\n- Sans les flags et symboles spéciaux que nous étudierons plus tard, la recherche par une expression régulière est identique à une recherche par sous-chaîne de caractères.\n- La méthode `str.match(regexp)` cherche des correspondances : toutes si il y a un flag `pattern:g`, sinon seulement le premier.\n- La méthode `str.replace(regexp, replacement)` remplace les correspondance en utilisant `regexp` avec `replacement` : toutes s'il y a  un flag `pattern:g`, sinon seulement la première.\n- La méthode `regexp.test(str)` retourne `true` s'il y a au moins une correspondance, sinon `false`.\n"
  },
  {
    "path": "9-regular-expressions/02-regexp-character-classes/article.md",
    "content": "# Classes de caractères\n\nConsidérons un exemple pratique -- nous avons un numero de téléphone tel que `\"+7(903)-123-45-67\"`, et nous souhaitons le convertir en nombres purs : `79031234567`.\n\nPour ce faire, nous pouvons rechercher et supprimer tout ce qui n'est pas un nombre. Les classes de caractères peuvent nous aider.\n\nUne *classe de caractères* est une notation spéciale qui correspond à n'importe quel symbole d'un certain ensemble.\n\nPour commencer, explorons la classe \"digit\". Elle s'écrit comme `pattern:\\d` et correspond à \"n'importe quel chiffre\".\n\nPar exemple, recherchons le premier chiffre dans le numéro de téléphone :\n\n```js run\nlet str = \"+7(903)-123-45-67\";\n\nlet regexp = /\\d/;\n\nalert( str.match(regexp) ); // 7\n```\n\nSans l'indicateur `pattern:g`, l'expression régulière ne recherche que la première correspondance, c'est-à-dire le premier chiffre `pattern:\\d`.\n\nAjoutons l'indicateur `pattern:g` pour trouver tous les chiffres :\n\n```js run\nlet str = \"+7(903)-123-45-67\";\n\nlet regexp = /\\d/g;\n\nalert( str.match(regexp) ); // liste de correspondances: 7,9,0,3,1,2,3,4,5,6,7\n\n// Obtenons un numéro de télephone composé uniquement de ces chiffres:\nalert( str.match(regexp).join('') ); // 79031234567\n```\n\nC'était une classe de caractères pour les chiffres. Il existe également d'autres classes de caractères.\n\nLes plus utilisés sont :\n\n`pattern:\\d` (\"d\" vient de \"digit\" (\"chiffre\"))\n: Un chiffre: un caractère de `0` à `9`.\n\n`pattern:\\s` (\"s\" vient de \"space\" (\"espace\"))\n: Un symbole d'espace: inclut les espaces, les tabulations `\\t`, les sauts de ligne `\\n` et quelques autres caractères rares, tels que `\\v`, `\\f` et `\\r`.\n\n`pattern:\\w` (\"w\" vient de \"word\" (\"mot\"))\n: Un caractère \"verbeux\": soit une lettre de l'alphabet latin, soit un chiffre ou un trait de soulignement `_`. Les lettres non latines (comme le cyrillique ou l'hindi) n'appartiennent pas au `pattern:\\w`.\n\nPar exemple, `pattern:\\d\\s\\w` signifie un \"chiffre\" suivi d'un \"caractère espace\" suivi d'un \"caractère verbeux\", tel que `match:1 a`.\n\n**Une expression régulière peut contenir à la fois des symboles normaux et des classes de caractères.**\n\nPar exemple, `pattern:CSS\\d` correspond à une chaîne `match:CSS` suivi d'un chiffre :\n\n```js run\nlet str = \"Is there CSS4?\";\nlet regexp = /CSS\\d/\n\nalert( str.match(regexp) ); // CSS4\n```\n\nOn peut également utiliser les classes de caractères :\n\n```js run\nalert( \"I love HTML5!\".match(/\\s\\w\\w\\w\\w\\d/) ); // ' HTML5'\n```\n\nLa correspondance (chaque classe de caractères d'expression régulière a le caractère de résultat correspondant) :\n\n![](love-html5-classes.svg)\n\n## Classes inverses\n\nPour chaque classe de caractères, il existe une \"classe inverse\", notée avec la même lettre, mais en majuscule.\n\nL'\"inverse\" signifie qu'il correspond à tous les autres caractères, par exemple :\n\n`pattern:\\D`\n: Non-chiffre: tout caractère sauf `pattern:\\d`, par exemple une lettre.\n\n`pattern:\\S`\n: Non-espace: tout caractère sauf `pattern:\\s`, par exemple une lettre.\n\n`pattern:\\W`\n: Caractère non verbal : tout sauf `pattern:\\w`, par exemple une lettre non latine ou un espace.\n\nAu début du chapitre, nous avons vu comment créer un numéro de téléphone uniquement à partir d'une chaîne telle que `subject:+7(903)-123-45-67`: trouver tous les chiffres et les concaténer.\n\n```js run\nlet str = \"+7(903)-123-45-67\";\n\nalert( str.match(/\\d/g).join('') ); // 79031234567\n```\n\nUne autre manière, plus courte, consiste à rechercher un motif non numérique `pattern:\\D` et à le supprimer de la chaîne:\n\n```js run\nlet str = \"+7(903)-123-45-67\";\n\nalert( str.replace(/\\D/g, \"\") ); // 79031234567\n```\n\n## Un point est \"n'importe quel caractère\"\n\nUn point `pattern:.` est une classe de caractères spéciale qui correspond à \"n'importe quel caractère sauf une nouvelle ligne\".\n\nPar exemple:\n\n```js run\nalert( \"Z\".match(/./) ); // Z\n```\n\nOu au milieu d'une expression régulière:\n\n```js run\nlet regexp = /CS.4/;\n\nalert( \"CSS4\".match(regexp) ); // CSS4\nalert( \"CS-4\".match(regexp) ); // CS-4\nalert( \"CS 4\".match(regexp) ); // CS 4 (l'espace est aussi un caractère)\n```\n\nVeuillez noter qu'un point signifie \"n'importe quel caractère\", mais pas \"l'absence de caractère\". Il doit y avoir un caractère avec lequel le faire correspondre :\n\n```js run\nalert( \"CS4\".match(/CS.4/) ); // null, pas de correspondance car il n'y a pas de caractère pour le point\n```\n\n### Point tel que n'importe quel caractère avec l'indicateur \"s\"\n\nPar défaut, un point ne correspond pas au caractère de saut de ligne `\\n`.\n\nPar exemple, l'expression rationnelle `pattern:A.B` correspond à `match:A`, puis `match:B` avec n'importe quel caractère entre eux, sauf un saut de ligne `\\n`:\n\n```js run\nalert( \"A\\nB\".match(/A.B/) ); // null (pas de correspondance)\n```\n\nIl existe de nombreuses situations où nous aimerions qu'un point signifie littéralement \"n'importe quel caractère\", y compris le saut de ligne.\n\nC'est ce que fait l'indicateur `pattern:s`. Si une expression rationnelle l'a, alors un point `pattern:.` correspond littéralement à n'importe quel caractère :\n\n```js run\nalert( \"A\\nB\".match(/A.B/s) ); // A\\nB (correspondance!)\n```\n\n````warn header=\"Non pris en charge par IE\"\nLe flag `pattern: s` n'est pas pris en charge dans IE.\n\nHeureusement, il existe une alternative qui fonctionne partout. Nous pouvons utiliser une expression rationnelle comme `pattern:[\\s\\S]` pour faire correspondre \"n'importe quel caractère\" (ce modèle sera traité dans l'article <info:regex-character-sets-and-ranges>).\n\n```js run\nalert( \"A\\nB\".match(/A[\\s\\S]B/) ); // A\\nB (correspondance!)\n```\n\nLe motif `pattern:[\\s\\S]` dit littéralement: \"un caractère espace OU pas un caractère espace\". En d'autres termes, \"n'importe quoi\". Nous pourrions utiliser une autre paire de classes complémentaires, telles que `pattern:[\\d\\D]`, cela n'a pas d'importance. Ou même le `pattern:[^]` -- car cela signifie correspondre à n'importe quel caractère sauf rien.\n\nNous pouvons également utiliser cette astuce si nous voulons les deux types de \"points\" dans le même motif: le point réel `pattern:.` se comportant de manière habituelle (\"ne pas inclure de saut de ligne\") est également une facon de correspondre à \"n'importe quel caractère\" avec `pattern:[\\s\\S]` ou un motif semblable.\n````\n\n````warn header=\"Faites attention aux espaces\"\nHabituellement, nous prêtons peu d'attention aux espaces. Pour nous, les chaînes `subject:1-5` et `subject:1 - 5` sont presque identiques.\n\nMais si une expression régulière ne prend pas en compte les espaces, elle peut ne pas fonctionner.\n\nEssayons de trouver des chiffres séparés par un tiret :\n\n```js run\nalert( \"1 - 5\".match(/\\d-\\d/) ); // null, pas de correspondance!\n```\n\nCorrigeons-le en ajoutant des espaces dans l'expression régulière `pattern:\\d - \\d` :\n\n```js run\nalert( \"1 - 5\".match(/\\d - \\d/) ); // 1 - 5, désormais, cela fonctionne\n// ou on peut utiliser la classe \\s:\nalert( \"1 - 5\".match(/\\d\\s-\\s\\d/) ); // 1 - 5, fonctionne aussi\n```\n\n**Un espace est un caractère. Aussi important que n'importe quel autre caractère.**\n\nNous ne pouvons pas ajouter ou supprimer des espaces dans une expression régulière et nous attendre à ce que cela fonctionne de la même manière.\n\nEn d'autres termes, dans une expression régulière, tous les caractères comptent, les espaces aussi.\n````\n\n## Résumé\n\nIl existe les classes de caractères suivantes :\n\n- `pattern:\\d` -- chiffres.\n- `pattern:\\D` -- non-chiffres.\n- `pattern:\\s` -- symboles d'espace, tabulations, sauts de ligne.\n- `pattern:\\S` -- tout sauf `pattern:\\s`.\n- `pattern:\\w` -- Lettres latines, chiffres, soulignement `'_'`.\n- `pattern:\\W` -- tout sauf `pattern:\\w`.\n- `pattern:.` -- n'importe quel caractère avec l'indicateur d'expression régulière `'s'`, sinon tout sauf un saut de ligne `\\n`.\n\n...Mais ce n'est pas tout!\n\nLe codage Unicode, utilisé par JavaScript pour les chaînes de caractères, fournit de nombreuses propriétés aux caractères, tels que : à quelle langue la lettre appartient (si c'est une lettre), si c'est un signe de ponctuation, etc.\n\nNous pouvons également faire une recherche selon leurs propriétés. Cela nécessite l'indicateur `pattern:u`, couvert dans le prochain article.\n"
  },
  {
    "path": "9-regular-expressions/03-regexp-unicode/article.md",
    "content": "# Unicode: indicateur \"u\" et classe \\p{...}\n\nJavaScript utilise [l'encodage Unicode](https://fr.wikipedia.org/wiki/Unicode) pour les chaînes de cractères. La plupart des caractères sont codés sur 2 octets, mais cela permet de représenter au plus 65536 caractères.\n\nCette plage n'est pas assez grande pour encoder tous les caractères possibles, c'est pourquoi certains caractères rares sont encodés sur 4 octets, par exemple comme `𝒳` (X mathématique) ou `😄` (un sourire), certains hiéroglyphes et ainsi de suite.\n\nVoici les valeurs unicode de certains caractères :\n\n| Caractère | Unicode | Nombre d'octets en unicode |\n|-----------|---------|----------|\n| a | `0x0061` | 2 |\n| ≈ | `0x2248` | 2 |\n| 𝒳 | `0x1d4b3` | 4 |\n| 𝒴 | `0x1d4b4` | 4 |\n| 😄 | `0x1f604` | 4 |\n\nAinsi, les caractères comme `a` et `≈` occupent 2 octets, tandis que les codes pour `𝒳`, `𝒴` et `😄` sont plus longs, ils ont 4 octets.\n\nIl y a longtemps, lorsque le langage JavaScript a été créé, l'encodage Unicode était plus simple : il n'y avait pas de caractères à 4 octets. Ainsi, certaines fonctionnalités du langage les gèrent toujours de manière incorrecte.\n\nPar exemple, la propriété `length` pense qu'il y a deux caractères :\n\n```js run\nalert('😄'.length); // 2\nalert('𝒳'.length); // 2\n```\n\n... Mais nous pouvons voir qu'il n'y en a qu'un, n'est-ce pas? Le fait est que la propriété `length` traite 4 octets comme deux caractères de 2 octets. C'est incorrect, car ils doivent être considérés uniquement ensemble (aussi appelé \"paire de substitution\", vous pouvez en lire plus dans l'article <info:string>).\n\nPar défaut, les expressions régulières traitent également les \"caractères longs\" de 4 octets comme une paire de caractères de 2 octets. Et, comme cela arrive avec les chaînes, cela peut conduire à des résultats étranges. Nous verrons cela un peu plus tard, dans l'article <info:regexp-character-sets-and-ranges>.\n\nContrairement aux chaînes de caractères, les expressions régulières ont l'indicateur `pattern:u` qui résout ces problèmes. Avec un tel indicateur, une expression rationnelle gère correctement les caractères de 4 octets. Et ainsi la recherche de propriétés Unicode devient également disponible, nous y reviendrons ensuite.\n\n## Propriétés Unicode \\p{...}\n\nChaque caractère dans Unicode a beaucoup de propriétés. Ils décrivent à quelle \"catégorie\" le caractère appartient, et contiennent diverses informations à son sujet.\n\nPar exemple, si un caractère a la propriété `Letter` (Lettre), cela signifie que le caractère appartient à un alphabet (de n'importe quelle langue). Et la propriété `Number` (Nombre) signifie que c'est un chiffre : peut-être l'arabe ou le chinois, et ainsi de suite.\n\nNous pouvons rechercher des caractères avec une propriété, écrite sous la forme `pattern:\\p{…}`. Pour utiliser `pattern:\\p{…}`, une expression régulière doit avoir l'indicateur `pattern:u`.\n\nPar exemple, `\\p{Letter}` désigne une lettre dans n'importe quelle langue. Nous pouvons également utiliser `\\p{L}`, car `L` est un alias de `Letter` (Lettre). Il existe des alias plus courts pour presque toutes les propriétés.\n\nDans l'exemple ci-dessous, on trouvera trois types de lettres : Anglais, Géorgien et Coréen.\n\n```js run\nlet str = \"A ბ ㄱ\";\n\nalert( str.match(/\\p{L}/gu) ); // A,ბ,ㄱ\nalert( str.match(/\\p{L}/g) ); // null (aucune correspondance, \\p ne fonctionne pas sans le flag \"u\")\n```\n\nVoici les principales catégories de caractères et leurs sous-catégories :\n\n- Lettre `L` :\n  - minuscules `Ll`,\n  - modificateur `Lm`,\n  - titre `Lt`,\n  - majuscule `Lu`,\n  - autres `Lo`.\n- Nombre `N` :\n  - chiffre décimal `Nd`,\n  - numéro de lettre `Nl`,\n  - autre `No`.\n- Ponctuation `P` :\n  - connecteur `Pc`,\n  - tiret `Pd`,\n  - citation initiale `Pi`,\n  - citation finale `Pf`,\n  - ponctuation ouvrante `Ps`,\n  - ponctuation fermante `Pe`,\n  - autre `Po`.\n- Marqueur `M` (accents, etc.) :\n  - espacement combinant `Mc`,\n  - contenant `Me`,\n  - sans espacement `Mn`.\n- Symbole `S` :\n  - devise `Sc`,\n  - modificateur `Sk`,\n  - mathématique `Sm`,\n  - autre `So`.\n- Séparateur `Z` :\n  - ligne `Zl`,\n  - paragraphe `Zp`,\n  - espace `Zs`.\n- Autre `C` :\n  - contrôle `Cc`,\n  - format `Cf`,\n  - non affecté `Cn`,\n  - usage privé `Co`,\n  - substitut `Cs`.\n\nAinsi, par exemple si nous avons besoin de lettres en minuscules, nous pouvons écrire `pattern:\\p{Ll}`, de signes de ponctuation : `pattern:\\p{P}` et ainsi de suite.\n\nIl existe également d'autres catégories dérivées, comme :\n\n- `Alphabetic` (Alphabétique, `Alpha`), qui comprend les lettres `L`, plus les numéros de lettre `Nl` (par exemple Ⅻ - un caractère pour le chiffre romain 12), plus quelques autres symboles `Other_Alphabetic` (Autre alphabétiques, `OAlpha`).\n- `Hex_Digit` comprend des chiffres hexadécimaux : `0-9`, `a-f`.\n- ...Et ainsi de suite.\n\nUnicode prend en charge de nombreuses propriétés différentes, leur liste complète nécessiterait beaucoup d'espace, voici donc les références :\n\n- Liste de toutes les propriétés par caractère : <https://unicode.org/cldr/utility/character.jsp>.\n- Liste de tous les caractères par propriété : <https://unicode.org/cldr/utility/list-unicodeset.jsp>.\n- Alias ​​courts pour les propriétés : <https://www.unicode.org/Public/UCD/latest/ucd/PropertyValueAliases.txt>.\n- Une base complète de caractères Unicode au format texte, avec toutes les propriétés, se trouve ici: <https://www.unicode.org/Public/UCD/latest/ucd/>.\n\n### Exemple : nombres hexadécimaux\n\nPar exemple, recherchons des nombres hexadécimaux, écrits sous la forme `xFF`, où `F` est un chiffre hexadécimal (0..9 ou A..F).\n\nUn chiffre hexadécimal peut être désigné par `pattern:\\p{Hex_Digit}` :\n\n```js run\nlet regexp = /x\\p{Hex_Digit}\\p{Hex_Digit}/u;\n\nalert(\"number: xAF\".match(regexp)); // xAF\n```\n\n### Exemple : Hiéroglyphes Chinois\n\nCherchons des hiéroglyphes Chinois.\n\nIl y a une propriété unicode `Script` (un système d'écriture), qui peut avoir une valeur : `Cyrillic` (Cyrillique), `Greek` (Grec), `Arabic` (Arabe), `Han` (Chinois) et ainsi de suite, [voici la liste complète](\"https://en.wikipedia.org/wiki/Script_(Unicode)\").\n\nPour rechercher des caractères dans un système d'écriture donné, nous devons utiliser `pattern:Script=<value>`, par exemple pour les lettres cyrilliques : `pattern:\\p{sc=Cyrillic}`, pour les hiéroglyphes chinois : `pattern:\\p{sc=Han}`, et ainsi de suite :\n\n```js run\nlet regexp = /\\p{sc=Han}/gu; // renvoie des hiéroglyphes Chinois\n\nlet str = `Hello Привет 你好 123_456`;\n\nalert( str.match(regexp) ); // 你,好\n```\n\n### Exemple: devise\n\nLes caractères qui désignent une devise, tels que `$`, `€`, `¥`, ont la propriété unicode `pattern:\\p{Currency_Symbol}`, l'alias court : `pattern:\\p{Sc}`.\n\nUtilisons-le pour rechercher des prix au format \"devise, suivi d'un chiffre\" :\n\n```js run\nlet regexp = /\\p{Sc}\\d/gu;\n\nlet str = `Prices: $2, €1, ¥9`;\n\nalert( str.match(regexp) ); // $2,€1,¥9\n```\n\nPlus loin, dans l'article <info:regexp-quantifiers>, nous verrons comment rechercher des nombres contenant de nombreux chiffres.\n\n## Résumé\n\nL'indicateur `pattern:u` permet la prise en charge d'Unicode dans les expressions régulières.\n\nCela signifie deux choses :\n\n1. Les caractères de 4 octets sont traités correctement : comme un seul caractère, pas comme deux caractères de 2 octets.\n2. Les propriétés Unicode peuvent être utilisées dans la recherche : `\\p{…}`.\n\nAvec les propriétés Unicode, nous pouvons rechercher des mots dans des langues données, des caractères spéciaux (guillemets, devises) et ainsi de suite.\n"
  },
  {
    "path": "9-regular-expressions/04-regexp-anchors/1-start-end/solution.md",
    "content": "Une chaîne de caractères vide est la seule correspondance : elle commence et se termine aussitôt.\n\nCette tâche montre à nouveau que les ancres ne sont pas des caractères, mais des tests.\n\nLa chaîne de caractères est vide `\"\"`. Le moteur regarde en premier `pattern:^` (début de l'entrée), ça correspond, et immédiatement après la fin `pattern:$`, ça correspond également. Donc il y a une correspondance.\n"
  },
  {
    "path": "9-regular-expressions/04-regexp-anchors/1-start-end/task.md",
    "content": "# Regexp ^$\n\nQuelle chaîne de caractères correspond à `pattern:^$` ?\n"
  },
  {
    "path": "9-regular-expressions/04-regexp-anchors/article.md",
    "content": "# Ancres : début ^ et fin $ d'une chaîne de caractères\n\nL'accent circonflexe `pattern:^` et le signe dollar `pattern:$` ont une signification particulière dans une regexp. Ils sont appelés \"ancres\".\n\nL'accent circonflexe `pattern:^` correspond au début du texte, et le signe dollar `pattern:$` -- à la fin.\n\nPar exemple, testons si le texte commence par `Mary`:\n\n```js run\nlet str1 = \"Mary had a little lamb\";\nalert( /^Mary/.test(str1) ); // true\n```\n\nLe paterne `pattern:^Mary` signifie : \"le texte commence puis Mary\".\n\nSimilairement, nous pouvons vérifier si le texte termine par `snow` en utilisant `pattern:snow$`:\n\n```js run\nlet str1 = \"its fleece was white as snow\";\nalert( /snow$/.test(str1) ); // true\n```\n\nIn these particular cases we could use string methods `startsWith/endsWith` instead. Regular expressions should be used for more complex tests.\n\n## Test pour une correspondance complète\n\nLes deux ancres mises ensemble `pattern:^...$` pour vérifier si une chaîne de caractères correspond entièrement à un paterne. Par exemple, pour vérifier si l'entrée de l'utilisateur est dans le bon format.\n\nVérifions si une chaîne de caractères est une heure au format `12:34`. En résumé : deux nombres, puis deux points, et enfin deux autres nombres.\n\nDans le langage des expressions régulières, c'est `pattern:\\d\\d:\\d\\d`:\n\n```js run\nlet goodInput = \"12:34\";\nlet badInput = \"12:345\";\n\nlet regexp = /^\\d\\d:\\d\\d$/;\nalert( regexp.test(goodInput) ); // true\nalert( regexp.test(badInput) ); // false\n```\n\nIci, la correspondance pour `pattern:\\d\\d:\\d\\d` doit commencer juste après le début du texte `pattern:^`, et la fin `pattern:$` doit immédiatement suivre.\n\nLa chaîne entière doit être dans ce format. S'il y a la moindre déviation ou le moindre caractère de trop, le résultat sera `false`.\n\nLes ancres agissent différemment si le marqueur `pattern:m` est présent. Nous verrons cela dans le prochain article.\n\n```smart header=\"Les ancres n'ont \\\"aucune longueur\\\"\"\nLes ancres `pattern:^` et `pattern:$` sont des tests. Elles n'ont aucune longueur.\n\nEn d'autres termes, elles ne vérifient pas un caractère, mais forcent plutôt le moteur à vérifier une condition (le texte commence/termine).\n```\n"
  },
  {
    "path": "9-regular-expressions/05-regexp-multiline-mode/article.md",
    "content": "# Multiline mode of anchors ^ $, flag \"m\"\n\nThe multiline mode is enabled by the flag `pattern:m`.\n\nIt only affects the behavior of `pattern:^` and `pattern:$`.\n\nIn the multiline mode they match not only at the beginning and the end of the string, but also at start/end of line.\n\n## Searching at line start ^\n\nIn the example below the text has multiple lines. The pattern `pattern:/^\\d/gm` takes a digit from the beginning of each line:\n\n```js run\nlet str = `1st place: Winnie\n2nd place: Piglet\n3rd place: Eeyore`;\n\n*!*\nconsole.log( str.match(/^\\d/gm) ); // 1, 2, 3\n*/!*\n```\n\nWithout the flag `pattern:m` only the first digit is matched:\n\n```js run\nlet str = `1st place: Winnie\n2nd place: Piglet\n3rd place: Eeyore`;\n\n*!*\nconsole.log( str.match(/^\\d/g) ); // 1\n*/!*\n```\n\nThat's because by default a caret `pattern:^` only matches at the beginning of the text, and in the multiline mode -- at the start of any line.\n\n```smart\n\"Start of a line\" formally means \"immediately after a line break\": the test  `pattern:^` in multiline mode matches at all positions preceded by a newline character `\\n`.\n\nAnd at the text start.\n```\n\n## Searching at line end $\n\nThe dollar sign `pattern:$` behaves similarly.\n\nThe regular expression `pattern:\\d$` finds the last digit in every line\n\n```js run\nlet str = `Winnie: 1\nPiglet: 2\nEeyore: 3`;\n\nconsole.log( str.match(/\\d$/gm) ); // 1,2,3\n```\n\nWithout the flag `pattern:m`, the dollar `pattern:$` would only match the end of the whole text, so only the very last digit would be found.\n\n```smart\n\"End of a line\" formally means \"immediately before a line break\": the test  `pattern:$` in multiline mode matches at all positions succeeded by a newline character `\\n`.\n\nAnd at the text end.\n```\n\n## Searching for \\n instead of ^ $\n\nTo find a newline, we can use not only anchors `pattern:^` and `pattern:$`, but also the newline character `\\n`.\n\nWhat's the difference? Let's see an example.\n\nHere we search for `pattern:\\d\\n` instead of `pattern:\\d$`:\n\n```js run\nlet str = `Winnie: 1\nPiglet: 2\nEeyore: 3`;\n\nconsole.log( str.match(/\\d\\n/g) ); // 1\\n,2\\n\n```\n\nAs we can see, there are 2 matches instead of 3.\n\nThat's because there's no newline after `subject:3` (there's text end though, so it matches `pattern:$`).\n\nAnother difference: now every match includes a newline character `match:\\n`. Unlike the anchors `pattern:^` `pattern:$`, that only test the condition (start/end of a line), `\\n` is a character, so it becomes a part of the result.\n\nSo, a `\\n` in the pattern is used when we need newline characters in the result, while anchors are used to find something at the beginning/end of a line.\n"
  },
  {
    "path": "9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/solution.md",
    "content": "\nRéponse : `pattern:\\b\\d\\d:\\d\\d\\b`.\n\n```js run\nalert( \"Breakfast at 09:00 in the room 123:456.\".match( /\\b\\d\\d:\\d\\d\\b/ ) ); // 09:00\n```\n"
  },
  {
    "path": "9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/task.md",
    "content": "# Trouvez l'heure\n\nL'heure à un format : `hours:minutes`. Les heures et les minutes ont deux chiffres, comme `09:00`.\n\nÉcrire une expression régulière pour trouver l'heure dans la chaîne de caractère : `subject:Breakfast at 09:00 in the room 123:456.`\n\nP.S. Dans cet exercice il n'y a pas besoin de vérifier la validité de l'heure, donc ici `25:99` peut être une correspondance valable.\n\nP.P.S. L'expression régulière ne doit pas valider `123:456`.\n"
  },
  {
    "path": "9-regular-expressions/06-regexp-boundary/article.md",
    "content": "# Limite de mot : \\b\n\nUne limite de mot `pattern:\\b` teste une position, de la même manière que les ancres `pattern:^` et `pattern:$`.\n\nQuand le moteur d'expression régulière (module qui implémente la recherche d'expressions régulières) trouve le motif `pattern:\\b`, il vérifie si la position dans la chaine de caractères est une limite de mot.\n\nIl y a trois positions possibles pour une limite de mot :\n\n- Au début de la chaîne de caractères, si le premier caractère est alphanumérique (ou un trait de soulignement), c'est-à-dire qu'il correspond au motif `pattern:\\w`.\n- Entre deux caractères d'une chaîne, si seulement l'un des caractères correspond au motif `pattern:\\w`, (alphanumérique ou trait de soulignement).\n- À la fin de la chaîne de caractères, si le dernier caractère correspond au motif `pattern:\\w`.\n\nPar exemple l'expression régulière `pattern:\\bJava\\b` sera trouvé dans `subject:Hello, Java!`, où `subject:Java` est un mot isolé, mais pas dans `subject:Hello, JavaScript!`.\n\n```js run\nalert( \"Hello, Java!\".match(/\\bJava\\b/) ); // Java\nalert( \"Hello, JavaScript!\".match(/\\bJava\\b/) ); // null\n```\n\nDans la chaîne `subject:Hello, Java!` les positions suivantes correspondent au motif `pattern:\\b`:\n\n![](hello-java-boundaries.svg)\n\nCette chaîne passe le test du motif `pattern:\\bHello\\b`, car :\n\n1. Le début de la chaîne passe le premier test `pattern:\\b`.\n2. Puis trouve le mot `pattern:Hello`.\n3. Enfin le test `pattern:\\b` est encore valide, comme nous sommes entre `subject:o` et une virgule.\n\nDonc le motif `pattern:\\bHello\\b` sera trouvé, mais pas `pattern:\\bHell\\b` (car il n'y a pas de limite de mot après `l`) ni `Java!\\b` (car le point d'exclamation ne correspond pas au motif `pattern:\\w`, il n'est donc pas suivi par une limite de mot).\n\n```js run\nalert( \"Hello, Java!\".match(/\\bHello\\b/) ); // Hello\nalert( \"Hello, Java!\".match(/\\bJava\\b/) );  // Java\nalert( \"Hello, Java!\".match(/\\bHell\\b/) );  // null (aucune correspondance)\nalert( \"Hello, Java!\".match(/\\bJava!\\b/) ); // null (aucune correspondance)\n```\n\nLa limite de mot `pattern:\\b` ne s'utilise pas uniquement pour des mots, mais aussi pour les nombres.\n\nPar exemple, le motif `pattern:\\b\\d\\d\\b` recherche un nombre isolé à deux chiffres. C'est-à-dire, qu'il cherche un nombre à deux chiffres entouré par des caractères qui ne correspondent pas au motif `pattern:\\w`, comme des espaces, une ponctuation, un début ou une fin de chaîne.\n\n```js run\nalert( \"1 23 456 78\".match(/\\b\\d\\d\\b/g) ); // 23,78\nalert( \"12,34,56\".match(/\\b\\d\\d\\b/g) ); // 12,34,56\n```\n\n```warn header=\"La limite de mot `pattern:\\b` ne fonctionne pas pour des alphabets non latin\"\nLe test de limite de mot `pattern:\\b` vérifie qu'il doit y avoir `pattern:\\w` d'un côté de la position et \"not `pattern:\\w`\" - de l'autre côté.\n\nComme `pattern:\\w` signifie `a-z`(en minuscule ou majuscule), un chiffre ou un trait de soulignement, le test ne fonctionne pas pour d'autres caractères, p. ex. lettre cyrillique ou idéogramme.\n```\n"
  },
  {
    "path": "9-regular-expressions/07-regexp-escaping/article.md",
    "content": "\n# Échappement, caractères spéciaux\n\nComme nous l'avons vu, la barre oblique inversée (ou backslash) `pattern:\\` est utilisée pour désigner une classe de caractères, p. ex. `pattern:\\d`. C'est donc un caractère spécial dans les expressions régulières (comme dans les chaînes de caractères classiques).\n\nIl existe également d'autres caractères spéciaux qui ont une signification particulière dans une expression régulières, tels que `pattern:[ ] { } ( ) \\ ^ $ . | ? * +`. Ils sont utilisés pour faire des recherches plus puissantes.\n\nInutile de mémoriser maintenant cette liste -- nous verrons chacun d'entre eux en détail, et vous les connaîtrez bientôt tous par cœur automatiquement.\n\n## Échappement\n\nAdmettons que nous voulons chercher un point. Pas n'importe quel caractère, mais juste un point.\n\nPour utiliser un caractère spécial en tant que caractère normal, on le précède d'un backslash : `pattern:\\.`.\n\nOn appelle aussi cela \"échapper un caractère\".\n\nPar exemple :\n```js run\nalert( \"Chapter 5.1\".match(/\\d\\.\\d/) ); // 5.1 (trouvé!)\nalert( \"Chapter 511\".match(/\\d\\.\\d/) ); // null (cherche un vrai point \\.)\n```\n\nLes parenthèses sont aussi des caractères spéciaux, donc pour en rechercher une, nous devons utiliser `pattern:\\(`. L'exemple ci-dessous recherche une chaîne de caractères `\"g()\"`:\n\n```js run\nalert( \"function g()\".match(/g\\(\\)/) ); // \"g()\"\n```\n\nSi nous recherchons un backslash `\\`, comme c'est un caractère spécial aussi bien pour une expression régulière que pour une chaîne de caractère classique, nous devons donc le doubler.\n\n```js run\nalert( \"1\\\\2\".match(/\\\\/) ); // '\\'\n```\n\n## La barre oblique ou slash\n\nUn slash `'/'` n'est pas un caractère spécial, mais en javascript, il est utilisé pour ouvrir et fermer l'expression régulière : `pattern:/...pattern.../`, nous devons donc aussi l'échapper.\n\nVoici à quoi ressemble une recherche d'un slash `'/'` :\n\n```js run\nalert( \"/\".match(/\\//) ); // '/'\n```\n\nPar contre si nous n'utilisons pas l'écriture `pattern:/.../`, mais créons l'expression régulière avec `new RegExp`, alors nous n'avons plus besoin de l'échapper :\n\n```js run\nalert( \"/\".match(new RegExp(\"/\")) ); // trouve /\n```\n\n## new RegExp\n\nSi nous construisons une expression régulière avec `new RegExp`, nous n'avons pas besoin d'échapper les `/`, mais nous aurons besoin d'autres échappements.\n\nPar exemple, considérons :\n\n```js run\nlet regexp = new RegExp(\"\\d\\.\\d\");\n\nalert( \"Chapter 5.1\".match(regexp) ); // null\n```\n\nC'est une recherche pourtant similaire à un exemple précédent, qui fonctionnait avec `pattern:/\\d\\.\\d/`, mais pas ici avec `new RegExp(\"\\d\\.\\d\")`. Pourquoi ?\n\nLes backslashes sont en fait \"consommés\" par la chaîne de caractères. On peut se souvenir, que les chaîne de caractères ont leurs propres caractères spéciaux, comme `\\n`, et le backslash est aussi utilisé pour l'échappement.\n\nVoici comment \"\\d\\.\\d\" est perçu :\n\n```js run\nalert(\"\\d\\.\\d\"); // d.d\n```\n\nLes guillemets \"consomment\" les backslashes et les interprètent pour la chaîne de caractère, par exemple :\n\n- `\\n` -- devient le caractère de nouvelle ligne,\n- `\\u1234` -- devient le caractère unicode de ce code,\n- ... Et lorsqu'il n'y a pas de sens particulier : comme `pattern:\\d` ou `\\z`, alors le backslash est simplement retiré.\n\nDonc `new RegExp` reçoit une chaîne de caractères sans backslash. C'est pour ça que la recherche ne fonctionnait pas !\n\nPour résoudre ça, nous devons doubler les backslashes, parce que la chaine de caractères transforme les `\\\\` en `\\`:\n\n```js run\n*!*\nlet regStr = \"\\\\d\\\\.\\\\d\";\n*/!*\nalert(regStr); // \\d\\.\\d (correct maitenant)\n\nlet regexp = new RegExp(regStr);\n\nalert( \"Chapter 5.1\".match(regexp) ); // 5.1\n```\n\n## Résumé\n\n- Pour rechercher exactement un caractère spécial `pattern:[ \\ ^ $ . | ? * + ( )`, nous devons le précéder d'un backslash `\\` (\"nous l'échappons\").\n- Nous devons aussi échapper un `/` si nous sommes dans une expression régulière `pattern:/.../` (mais pas en utilisant `new RegExp`).\n- Lorsque l'on passe une chaîne de caractères à `new RegExp`, nous devons doubler les backslashes `\\\\`, car la chaîne en consomme un.\n"
  },
  {
    "path": "9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/solution.md",
    "content": "Réponses : **non, oui**.\n\n- Dans la chaîne de caractères `subject:Java`, elle ne trouve aucune correspondance, parce que `pattern:[^script]` signifie \"n'importe quel caractère sauf ceux cités\". L'expression rationnelle cherche donc `\"Java\"` suivi d'un autre symbole, mais arrivant en fin de chaîne, elle n'en trouve aucun.\n\n    ```js run\n    alert( \"Java\".match(/Java[^script]/) ); // null\n    ```\n- Oui, car la partie `pattern:[^script]` correspond au caractère `\"S\"`. Qui n'est pas l'un des caractères de  `pattern:script`. Comme l'expression rationnelle est sensible à la casse (pas de marqueur `pattern:i`), elle considère bien `\"S\"` différemment de `\"s\"`.\n\n    ```js run\n    alert( \"JavaScript\".match(/Java[^script]/) ); // \"JavaS\"\n    ```\n"
  },
  {
    "path": "9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/task.md",
    "content": "# Java[^script]\n\nConsidérons l'expression rationnelle `pattern:/Java[^script]/`.\n\nTrouve-t-elle quelque chose dans la chaîne de caractères `subject:Java`? Dans la chaîne `subject:JavaScript`?\n"
  },
  {
    "path": "9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/solution.md",
    "content": "Réponse : `pattern:\\d\\d[-:]\\d\\d`.\n\n```js run\nlet regexp = /\\d\\d[-:]\\d\\d/g;\nalert( \"Breakfast at 09:00. Dinner at 21-30\".match(regexp) ); // 09:00, 21-30\n```\n\nA noter que `pattern:'-'` à un sens particulier entre crochet, mais seulement entre deux autres caractères, et pas lorsqu'il débute ou termine l'ensemble, nous n'avons donc pas besoin de l'échapper ici.\n"
  },
  {
    "path": "9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/task.md",
    "content": "# Trouvez l'heure sous forme hh:mm ou hh-mm\n\nL'heure peut être au format `hours:minutes` ou `hours-minutes`. Les nombres \"hours\" et \"minutes\" sont composées de deux chiffres :  `09:00` ou `21-30`.\n\nÉcrire une expression rationnelle pour trouver l'heure quelle que soit sa forme :\n\n```js\nlet regexp = /your regexp/g;\nalert( \"Breakfast at 09:00. Dinner at 21-30\".match(regexp) ); // 09:00, 21-30\n```\n\nP.S. Dans cet exercice, on considère n'importe quelle heure comme valide, il n'y a pas besoin d'exclure une heure comme \"45:67\" par exemple. Nous nous occuperons de cela plus tard.\n"
  },
  {
    "path": "9-regular-expressions/08-regexp-character-sets-and-ranges/article.md",
    "content": "# Ensembles et intervalles [...]\n\nPlusieurs caractères ou classes de caractères, entourés de crochets `[…]` signifient \"chercher un caractère parmi ceux-là\".\n\n## Ensembles\n\nPar exemple, `pattern:[eao]` signifie un caractère qui est soit `'a'`, `'e'`, ou `'o'`.\n\nOn appelle cela un *ensemble*. Les ensembles peuvent être combinés avec d'autres caractères dans une même expression régulière :\n\n```js run\n// trouve [t ou m], puis \"op\"\nalert( \"Mop top\".match(/[tm]op/gi) ); // \"Mop\", \"top\"\n```\n\nBien qu'il y ait plusieurs caractères dans un ensemble, vous remarquez que l'on ne cherche la correspondance que d'un seul de ces caractères.\n\nL'exemple suivant ne donne donc aucun résultat :\n\n```js run\n// trouve \"V\", puis [o ou i], puis \"la\"\nalert( \"Voila\".match(/V[oi]la/) ); // null, pas de correspondance\n```\n\nL'expression régulière recherche :\n\n- `pattern:V`,\n- puis *une* des lettres `pattern:[oi]`,\n- enfin `pattern:la`.\n\nCe qui correspondrait à `match:Vola` ou `match:Vila`.\n\n## Intervalles\n\nLes crochets peuvent aussi contenir des *intervalles de caractères*.\n\nPar exemple, `pattern:[a-z]` est un caractère pouvant aller de `a` à `z`, et `pattern:[0-5]` est un chiffre allant de `0` à `5`.\n\nDans l'exemple ci-dessous nous recherchons un `\"x\"` suivi par deux chiffres ou lettres de `A` à `F`:\n\n```js run\nalert( \"Exception 0xAF\".match(/x[0-9A-F][0-9A-F]/g) ); // xAF\n```\n\nIci `pattern:[0-9A-F]` comporte deux intervalles : il recherche un caractère qui est soit chiffre entre `0` et `9` compris ou bien une lettre entre `A` et `F` comprise.\n\nSi nous voulons y inclure les lettres minuscules, nous pouvons ajouter l'intervalle `a-f`: `pattern:[0-9A-Fa-f]`. Ou bien ajouter le marqueur `pattern:i`.\n\nNous pouvons aussi utiliser les classes de caractères entre `[…]`.\n\nPar exemple, si nous voulons chercher un caractère alphanumérique, un trait de soulignement `pattern:\\w` ou un tiret `pattern:-`, alors l'ensemble s'écrit `pattern:[\\w-]`.\n\nIl est aussi possible de combiner plusieurs classes, p. ex. `pattern:[\\s\\d]` signifie \"un caractère d'espacement ou un chiffre\".\n\n```smart header=\"Les classes de caractères sont en fait des racourcis pour des intervalles de caractères particuliers\"\nPar exemple:\n\n- **\\d** -- équivaut à `pattern:[0-9]`,\n- **\\w** -- équivaut à `pattern:[a-zA-Z0-9_]`,\n- **\\s** -- équivaut à `pattern:[\\t\\n\\v\\f\\r ]`, plus quelques autres rares caractères unicodes d'espacement.\n```\n\n### Exemple : \\w multi-langue\n\nComme la classe de caractères `pattern:\\w` est un raccourci pour `pattern:[a-zA-Z0-9_]`, il ne peut pas trouver les idéogrammes chinois, ni les lettres cyrilliques, etc.\n\nNous pouvons écrire un motif plus universel, pour rechercher le caractère d'un mot quelle que soit la langue. Grâce aux propriétés Unicode, on obtient facilement : `pattern:[\\p{Alpha}\\p{M}\\p{Nd}\\p{Pc}\\p{Join_C}]`.\n\nDéchiffrons cela. Tout comme `pattern:\\w`, nous construisons notre propre ensemble qui contient les caractères qui portent les propriétés Unicode :\n\n- `Alphabetic` (`Alpha`) - pour les lettres,\n- `Mark` (`M`) - pour les accents,\n- `Decimal_Number` (`Nd`) - pour les nombres,\n- `Connector_Punctuation` (`Pc`) - pour le trait de soulignement `'_'` et autres caractères similaires,\n- `Join_Control` (`Join_C`) - deux codes spéciaux `200c` et `200d`, utilisés comme liaisons, p. ex. en arabe.\n\nExemple d'usage :\n\n```js run\nlet regexp = /[\\p{Alpha}\\p{M}\\p{Nd}\\p{Pc}\\p{Join_C}]/gu;\n\nlet str = `Hi 你好 12`;\n\n// trouve toutes les lettres et chiffres:\nalert( str.match(regexp) ); // H,i,你,好,1,2\n```\n\nCet ensemble est bien sûr encore modifiable : on peut y ajouter ou retirer des propriétés Unicode. Plus de détail sur ces propriétés Unicode dans l'article <info:regexp-unicode>.\n\n```warn header=\"Les propriétés Unicode ne sont pas supportées par IE\"\nLes propriétés Unicode `pattern:p{…}` ne sont pas implémentées dans IE. Si nous en avons vraiment besoin, nous pouvons utiliser la librairie [XRegExp](https://xregexp.com/).\n\nOu simplement utiliser des intervalles de caractères dans la langue qui nous intéresse, p. ex. `pattern:[а-я]` pour les lettres cyrilliques.\n```\n\n## Intervalles d'exclusion\n\nEn plus des intervalles classiques, il existe des intervalles d'exclusion de la forme `pattern:[^…]`.\n\nIls se distinguent par un premier accent circonflexe `^` et correspond à n'importe quel caractère *à l'exception de ceux contenus dans ces crochets*.\n\nPar exemple :\n\n- `pattern:[^aeyo]` -- n'importe quel caractère sauf  `'a'`, `'e'`, `'y'` ou `'o'`.\n- `pattern:[^0-9]` -- n'importe quel caractère à l'exception des chiffres, équivalent à `pattern:\\D`.\n- `pattern:[^\\s]` -- tout caractère qui n'est pas un espacement, équivalent à `\\S`.\n\nL'exemple ci-dessous cherche n'importe quel caractère n'étant pas une lettre, un chiffre ou un espace :\n\n```js run\nalert( \"alice15@gmail.com\".match(/[^\\d\\sA-Z]/gi) ); // @ et .\n```\n\n## L'échappement entre […]\n\nHabituellement, lorsque nous cherchons précisément un caractère spécial, nous devons l'échapper `pattern:\\.`. Et si nous cherchons un backslash, nous utilisons `pattern:\\\\`, etc.\n\nÀ l'intérieur de crochets nous pouvons utiliser une grande majorité des caractères spéciaux sans échappement :\n\n- Les symbols `pattern:. + ( )` ne sont jamais échappés.\n- Un tiret `pattern:-` n'est pas échappé en début ou fin d'ensemble (là où il ne peut pas définir d'intervalle).\n- Un accent circonflexe `pattern:^` est échappé uniquement s'il débute l'ensemble (sinon il signifie l'exclusion).\n- Le crochet fermant `pattern:]` est toujours échappé (si nous le cherchons précisément).\n\nEn d'autres termes, tous les caractères spéciaux ne sont pas échappés, sauf s'ils ont un sens particulier pour un ensemble.\n\nUn point `.` à l'intérieur de crochets signifie juste un point. Le motif `pattern:[.,]` recherche un caractère : soit un point soit une virgule.\n\nDans l'exemple ci-dessous l'expression régulière `pattern:[-().^+]` cherche un des caractères `-().^+`:\n\n```js run\n// Pas besoin d'échapper\nlet regexp = /[-().^+]/g;\n\nalert( \"1 + 2 - 3\".match(regexp) ); // trouve +, -\n```\n\n... Si vous décidez de les échapper, \"au cas où\", il n'y aura de toute façon aucun d'impact :\n\n```js run\n// Tout échappé\nlet regexp = /[\\-\\(\\)\\.\\^\\+]/g;\n\nalert( \"1 + 2 - 3\".match(regexp) ); // fonctionne aussi: +, -\n```\n\n## Intervalles et marqueur \"u\"\n\nS'il y a une paire de seizets d'indirection([surrogate pair](https://fr.wikipedia.org/wiki/Table_des_caract%C3%A8res_Unicode_(D000-DFFF))) dans l'ensemble, le marqueur `pattern:u` est requis pour qu'elle soit interprétée correctement.\n\nPar exemple, cherchons `pattern:[𝒳𝒴]` dans la chaîne `subject:𝒳`:\n\n```js run\nalert( '𝒳'.match(/[𝒳𝒴]/) ); // affiche un caractère étrange qui ressemble à [?]\n// (la recherche n'a pas fonctionné correctement, seule une moitié du caractère est retournée)\n```\n\nLe résultat n'est pas celui attendu, car par défaut une expression régulière ne reconnait pas une telle paire.\n\nLe moteur d'expression régulière pense que `[𝒳𝒴]` -- ne sont pas deux mais quatre caractères :\n1. la moitié gauche de `𝒳` `(1)`,\n2. la moitié droite de `𝒳` `(2)`,\n3. la moitié gauche de `𝒴` `(3)`,\n4. la moitié droite de `𝒴` `(4)`.\n\nOn peut voir le code de ces caractères ainsi :\n\n```js run\nfor(let i=0; i<'𝒳𝒴'.length; i++) {\n  alert('𝒳𝒴'.charCodeAt(i)); // 55349, 56499, 55349, 56500\n};\n```\n\nDonc, le premier exemple trouve et affiche la première moitié de `𝒳`.\n\nMais si nous ajoutons le marqueur `pattern:u`, on aura alors le comportement attendu :\n\n```js run\nalert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳\n```\n\nOn retrouve un mécanisme similaire dans les intervalles, comme `[𝒳-𝒴]`.\n\nSi nous oublions le marqueur `pattern:u`, il y aura une erreur :\n\n```js run\n'𝒳'.match(/[𝒳-𝒴]/); // Error: Invalid regular expression\n```\n\nEn effet sans le marqueur `pattern:u` une paire de seizets est perçue comme deux caractères distincts, donc `[𝒳-𝒴]` est interprété en `[<55349><56499>-<55349><56500>]` (chacune des paires est remplacée par ses codes). Il est maintenant évident que l'intervalle `56499-55349` n'est pas valide : le premier code `56499` est plus grand que le dernier `55349`. Ce qui explique l'erreur précédente.\n\nAvec le marqueur `pattern:u` le motif est interprété correctement :\n\n```js run\n// Cherche un caractère entre 𝒳 et 𝒵 compris\nalert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴\n```\n"
  },
  {
    "path": "9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/solution.md",
    "content": "\nSolution:\n\n```js run\nlet regexp = /\\.{3,}/g;\nalert( \"Hello!... How goes?.....\".match(regexp) ); // ..., .....\n```\n\nNotez que le point est un caractère spécial, nous devons donc l'échapper et l'insérer comme ceci `\\.`.\n"
  },
  {
    "path": "9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/task.md",
    "content": "importance: 5\n\n---\n\n#  Comment trouver une ellipse \"...\" ?\n\nCréer une regexp pour trouver une ellipse: 3 (ou plus?) points à la suite.\n\nVérifiez:\n\n```js\nlet regexp = /votre regexp/g;\nalert( \"Hello!... How goes?.....\".match(regexp) ); // ..., .....\n```\n"
  },
  {
    "path": "9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/solution.md",
    "content": "Nous devons chercher `#` suivi de 6 caractères hexadécimaux.\n\nUn caractère hexadécimal est défini comme `pattern:[0-9a-fA-F]`. Ou si nous utilisons le flag `pattern:i`, juste  `pattern:[0-9a-f]`.\n\nNous pouvons ensuite rechercher 6 d'entre eux en utilisant le quantificateur `pattern:{6}`.\n\nRésultat, nous avons la regexp: `pattern:/#[a-f0-9]{6}/gi`.\n\n```js run\nlet regexp = /#[a-f0-9]{6}/gi;\n\nlet str = \"color:#121212; background-color:#AA00ef bad-colors:f#fddee #fd2\"\n\nalert( str.match(regexp) );  // #121212,#AA00ef\n```\n\nLe problème est qu'elle trouve la couleur dans des séquences plus longues:\n\n```js run\nalert( \"#12345678\".match( /#[a-f0-9]{6}/gi ) ) // #123456\n```\n\nPour règler ceci nous pouvons ajouter `pattern:\\b` à la fin:\n\n```js run\n// color\nalert( \"#123456\".match( /#[a-f0-9]{6}\\b/gi ) ); // #123456\n\n// not a color\nalert( \"#12345678\".match( /#[a-f0-9]{6}\\b/gi ) ); // null\n```\n"
  },
  {
    "path": "9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/task.md",
    "content": "# Regexp pour couleurs HTML\n\nCréez une regexp pour trouver les couleurs HTML écrites comme `#ABCDEF`: d'abord `#` puis 6 caractères hexadécimaux.\n\nExemple d'utilisation:\n\n```js\nlet regexp = /...votre regexp.../\n\nlet str = \"color:#121212; background-color:#AA00ef bad-colors:f#fddee #fd2 #12345678\";\n\nalert( str.match(regexp) )  // #121212,#AA00ef\n```\n\nP.S. Dans cette tâche nous n'avons pas besoin des autres formats de couleur comme `#123` ou `rgb(1,2,3)` etc.\n"
  },
  {
    "path": "9-regular-expressions/09-regexp-quantifiers/article.md",
    "content": "# Quantificateurs +, *, ? et {n}\n\nConsidérons que nous avons une chaîne de caractères `+7(903)-123-45-67` et que nous voulons trouver tous les nombres dedans. Mais contrairement à avant nous ne voulons pas seulement trouver les chiffres mais les nombres en entier: `7, 903, 123, 45, 67`.\n\nUn nombre est une séquence de 1 ou plus chiffres `pattern:\\d`. Pour marquer la quantité dont nous avons besoin, nous pouvons ajouter un *quantificateur*.\n\n## Quantité {n}\n\nLe quantificateur le plus simple est un nombre entre accolades: `pattern:{n}`.\n\nUn quantificateur est attaché à un caractère (ou une classe de caractère, ou un jeu `[...]`, etc) et spécifie la quantité dont nous avons besoin.\n\nIl a quelques formes avancées, comme par exemple:\n\nLe nombre exact: `pattern:{5}`\n: `pattern:\\d{5}` indique exactement 5 chiffres, identique à `pattern:\\d\\d\\d\\d\\d`.\n\n    L'exemple ci-dessous recherche un nombre à 5 chiffres:\n\n    ```js run\n    alert( \"I'm 12345 years old\".match(/\\d{5}/) ); //  \"12345\"\n    ```\n\n    Nous pouvons ajouter `\\b` pour exclure les nombres plus longs: `pattern:\\b\\d{5}\\b`.\n\nLa portée: `pattern:{3,5}`, correspond de 3 à 5 fois\n: Pour trouver les nombres avec de 3 à 5 chiffres nous pouvons mettre les limites entre accolades: `pattern:\\d{3,5}`\n\n    ```js run\n    alert( \"I'm not 12, but 1234 years old\".match(/\\d{3,5}/) ); // \"1234\"\n    ```\n\n    Nous pouvons retirer la limite haute.\n\n    Une regexp `pattern:\\d{3,}` cherche donc une séquence de chiffres d'une longueur de 3 ou plus:\n\n    ```js run\n    alert( \"I'm not 12, but 345678 years old\".match(/\\d{3,}/) ); // \"345678\"\n    ```\n\nRetournons à la chaîne de caractères `+7(903)-123-45-67`.\n\nUn nombre est une séquence de un ou plus chiffres à la suite. Donc la regexp est `pattern:\\d{1,}`:\n\n```js run\nlet str = \"+7(903)-123-45-67\";\n\nlet numbers = str.match(/\\d{1,}/g);\n\nalert(numbers); // 7,903,123,45,67\n```\n\n## Abréviations\n\nIl y a des abréviations pour les quantificateur les plus utilisés:\n\n`pattern:+`\n: Signifie \"un ou plus\", identique à `pattern:{1,}`.\n\n    Par exemple, `pattern:\\d+` cherche les nombres:\n\n    ```js run\n    let str = \"+7(903)-123-45-67\";\n\n    alert( str.match(/\\d+/g) ); // 7,903,123,45,67\n    ```\n\n`pattern:?`\n: Signifie \"zéro ou un\", identique à `pattern:{0,1}`. En d'autres termes, il rend le symbole optionnel.\n\n    Par exemple, le pattern `pattern:ou?r` cherche `match:o` suivi de zéro ou un `match:u`, puis `match:r`.\n\n    Donc, `pattern:colou?r` trouve `match:color` et `match:colour`:\n\n    ```js run\n    let str = \"Should I write color or colour?\";\n\n    alert( str.match(/colou?r/g) ); // color, colour\n    ```\n\n`pattern:*`\n: Signifie \"zéro ou plus\", identique à `pattern:{0,}`. C'est-à-dire que le caractère peut être répété n'importe quel nombre de fois ou bien être absent.\n\n    Par exemple, `pattern:\\d0*` cherche un chiffre suivi de n'importe quel nombre de zéros (plusieurs ou aucun):\n\n    ```js run\n    alert( \"100 10 1\".match(/\\d0*/g) ); // 100, 10, 1\n    ```\n\n    Comparé à `pattern:+` (un ou plus):\n\n    ```js run\n    alert( \"100 10 1\".match(/\\d0+/g) ); // 100, 10\n    // 1 n'est pas trouvé, puisque 0+ nécessite au moins un zéro\n    ```\n\n## Plus d'exemples\n\nLes quantificateurs sont utilisés très souvent. Ils servent de \"bloc de construction\" principal pour les expressions régulières complexes, regardons d'autres exemples.\n\n**Regexp pour fractions décimales (un nombre à virgule flotante): `pattern:\\d+\\.\\d+`**\n\nEn action:\n```js run\nalert( \"0 1 12.345 7890\".match(/\\d+\\.\\d+/g) ); // 12.345\n```\n\n**Regexp pour une \"balise HTML d'ouverture sans attributs\", comme `<span>` ou `<p>`.**\n\n1. La plus simple: `pattern:/<[a-z]+>/i`\n\n    ```js run\n    alert( \"<body> ... </body>\".match(/<[a-z]+>/gi) ); // <body>\n    ```\n\n    Cette regexp cherche le caractère `pattern:'<'` suivi par une ou plusieurs lettres Latin, puis  `pattern:'>'`.\n\n2. Amélioré: `pattern:/<[a-z][a-z0-9]*>/i`\n\n    Conformément au standard, Le nom d'une balise HTML peut avoir un chiffre à n'importe quel endroit à l'exception de la première position, comme `<h1>`.\n\n    ```js run\n    alert( \"<h1>Hi!</h1>\".match(/<[a-z][a-z0-9]*>/gi) ); // <h1>\n    ```\n\n**Regexp \"balise HTML d'ouverture ou de fermeture sans attributs\": `pattern:/<\\/?[a-z][a-z0-9]*>/i`**\n\nNous avons ajouté un slash optionnel `pattern:/?` près du début du pattern. Nous avons dû l'échapper avec un backslash, sinon JavaScript aurait pensé que c'était la fin du pattern.\n\n```js run\nalert( \"<h1>Hi!</h1>\".match(/<\\/?[a-z][a-z0-9]*>/gi) ); // <h1>, </h1>\n```\n\n```smart header=\"Pour rendre une regexp plus précise, nous devons souvent la rendre plus complexe\"\nVous pouvez voir une règle commune dans tous ces exemples: plus une expression régulière est précise -- plus elle est longue et complexe.\n\nPar exemple, pour des balises HTML nous pourrions utiliser une regexp plus simple: `pattern:<\\w+>`. Mais comme HTML a des restrictions plus strictes pour les noms de balise, `pattern:<[a-z][a-z0-9]*>` est plus fiable.\n\nPouvons nous utiliser `pattern:<\\w+>` ou avons nous besoin de `pattern:<[a-z][a-z0-9]*>`?\n\nDans la vrai vie les deux variantes sont acceptables. En fonction de la tolérance que nous avons vis-à-vis des sélections \"en trop\" et la difficulté que l'on a de les retirer des résultats par d'autres moyens.\n```\n"
  },
  {
    "path": "9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/solution.md",
    "content": "\nLa réponse est : `match:123 4`.\n\nPour commencer, le motif paresseux `pattern:\\d+?` essaye de prendre aussi peu de chiffres que possible, mais il doit atteindre ensuite un espace, donc il prend  `match:123`.\n\nEnsuite le second `\\d+?` prend seulement un chiffre, parce que ça suffit.\n"
  },
  {
    "path": "9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/task.md",
    "content": "# Correspondance pour /d+? d+?/\n\nQuel est la correspondance trouvée ici ?\n\n```js\nalert( \"123 456\".match(/\\d+? \\d+?/g) ); // ?\n```\n"
  },
  {
    "path": "9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/solution.md",
    "content": "Nous devons trouver le début d'un commentaire `match:<!--`, puis tout jusqu'à la fin de `match:-->`.\n\nUne expression régulière possible est `pattern:<!--.*?-->` -- le quantificateur paresseux arrête le point juste avant `match:-->`. Nous avons aussi besoin du marqueur `pattern:s` pour que le point inclue les nouvelles lignes.\n\nLes commentaires multi-lignes ne seraient pas trouvés sans ce marqueur :\n\n```js run\nlet regexp = /<!--.*?-->/gs;\n\nlet str = `... <!-- My -- comment\n test --> ..  <!----> ..\n`;\n\nalert( str.match(regexp) ); // '<!-- My -- comment \\n test -->', '<!---->'\n```\n"
  },
  {
    "path": "9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/task.md",
    "content": "# Trouvez des commentaires HTML\n\nTrouvez tous les commentaires HTML dans le texte :\n\n```js\nlet regexp = /your regexp/g;\n\nlet str = `... <!-- My -- comment\n test --> ..  <!----> .. \n`;\n\nalert( str.match(regexp) ); // '<!-- My -- comment \\n test -->', '<!---->'\n```\n"
  },
  {
    "path": "9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/solution.md",
    "content": "\nUne solution `pattern:<[^<>]+>`.\n\n```js run\nlet regexp = /<[^<>]+>/g;\n\nlet str = '<> <a href=\"/\"> <input type=\"radio\" checked> <b>';\n\nalert( str.match(regexp) ); // '<a href=\"/\">', '<input type=\"radio\" checked>', '<b>'\n```\n"
  },
  {
    "path": "9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md",
    "content": "# Trouver des balises HTML\n\nÉcrire une expression régulière pour trouver toutes les balises HTML (ouvrantes et fermantes) avec leurs attributs.\n\nExemple d'usage :\n\n```js run\nlet regexp = /your regexp/g;\n\nlet str = '<> <a href=\"/\"> <input type=\"radio\" checked> <b>';\n\nalert( str.match(regexp) ); // '<a href=\"/\">', '<input type=\"radio\" checked>', '<b>'\n```\n\nPour simplifier un peu, nous considérons ici qu'une balise ne peut pas contenir de `<` ou `>` (même à l'intérieur de guillemets).\n"
  },
  {
    "path": "9-regular-expressions/10-regexp-greedy-and-lazy/article.md",
    "content": "# Quantificateurs gloutons ou paresseux\n\nLes quantificateurs, à première vue très simples, peuvent parfois être retors.\n\nPour réussir à trouver des motifs plus complexes que `pattern:/\\d+/`, nous devons voir plus en détail comment se déroule une recherche.\n\nPrenons comme exemple la tâche suivante.\n\nNous avons un texte dans lequel nous devons remplacer tous les guillemets droits (doubles) `\"...\"` par des guillemets français : `«...»`, souvent préférés comme typographie dans de nombreux pays.\n\nPar exemple : `\"Hello, world\"` devrait se transformer en `«Hello, world»`. Il existe aussi d'autres guillemets, comme `„Witaj, świecie!”` (polonais) ou `「你好，世界」` (chinois), mais pour notre exemple choisissons `«...»`.\n\nLa première chose à faire est de trouver ces guillemets droits, et nous pourrons ensuite les remplacer.\n\nUne expression régulière comme `pattern:/\".+\"/g` (des guillemets, puis quelque chose, puis d'autres guillemets) semble être une bonne approche...\n\nPas exactement, voyons cela :\n\n```js run\nlet regexp = /\".+\"/g;\n\nlet str = 'a \"witch\" and her \"broom\" is one';\n\nalert( str.match(regexp) ); // \"witch\" and her \"broom\"\n```\n\n... Nous pouvons voir que cela ne marche pas vraiment comme prévu !\n\nAu lieu de trouver deux correspondances `match:\"witch\"` et `match:\"broom\"`, il n'en trouve qu'une : `match:\"witch\" and her \"broom\"`.\n\nVoyons cela comme \"La gourmandise est un défaut qui cause beaucoup de torts à ceux qui s’y livrent\".\n\n## Recherche gloutonne\n\nPour trouver une correspondance, le moteur d'expression régulière utilise l'algorithme suivant :\n\n- Pour chacune des positions de la chaîne de caractère\n    - Essaye de trouver le motif à cette position.\n    - Si aucune correspondance, va à la prochaine position.\n\nCette description succincte ne suffit peut-être pas à mettre en évidence l'échec précédent, alors voyons plus en détail comment se déroule la recherche du motif `pattern:\".+\"`.\n\n1. Le premier caractère du motif sont des guillemets droits `pattern:\"`.\n\n    Le moteur d'expression régulière essaye de les trouver à la position zéro de la chaîne source `subject:a \"witch\" and her \"broom\" is one`, mais comme il y a un `subject:a` à cette place, il n'y a pas de correspondance possible.\n\n    Puis il avance : va aux positions suivantes dans la chaîne source et essaye d'y trouver le premier caractère du motif, il échoue une nouvelle fois, avant de trouver les guillemets en troisième position :\n\n    ![](witch_greedy1.svg)\n\n2. Les premiers guillemets trouvés, le moteur essaye de trouver la correspondance pour la suite du motif. Il essaye de voir si le reste de la chaîne suit le motif `pattern:.+\"`.\n\n    Dans notre cas le caractère suivant dans le motif est `pattern:.` (un point). Il signifie \"tout caractère, nouvelle ligne exceptée\", la lettre suivante de la chaîne `match:'w'` correspond bien :\n\n    ![](witch_greedy2.svg)\n\n3. Le point est alors répété avec le quantificateur `pattern:.+`. le moteur d'expression régulière ajoute à la correspondance, les caractères les uns à la suite des autres.\n\n    ... Jusqu'à quand ? Comme tout caractère correspond au point, il ne s'arrête qu'une fois arrivé à la fin de chaîne :\n\n    ![](witch_greedy3.svg)\n\n4. La répétition du motif `pattern:.+` maintenant finie, il essaye de trouver le caractère suivant. Ce sont des guillemets droits `pattern:\"`. Mais problème : arrivé en bout de chaîne, il n'y a plus de caractère !\n\n    Le moteur d'expression régulière comprend qu'il a pris trop de `pattern:.+` et commence à revenir sur ses pas.\n\n    En d'autres termes, il réduit la correspondance avec le quantificateur d'un caractère :\n\n    ![](witch_greedy4.svg)\n\n    Il considère maintenant que `pattern:.+` se termine un caractère avant la fin de la chaîne et essaye de la trouver la fin du motif à partir de cette position.\n\n    Et s'il y avait des guillemets ici, alors la recherche s'arrêterait, mais le dernier caractère est `subject:'e'`, il n'y a donc toujours pas de correspondance.\n\n5. ... Le moteur d'expression régulière diminue encore le nombre de répétitions de `pattern:.+` d'un autre caractère :\n\n    ![](witch_greedy5.svg)\n\n    les guillemets `pattern:'\"'` ne correspondent pas à `subject:'n'`.\n\n6. Le moteur continue sa marche arrière : il réduit le nombre de répétitions de `pattern:'.'` jusqu'à trouver une correspondance pour la suite du motif (dans notre cas `pattern:'\"'`) :\n\n    ![](witch_greedy6.svg)\n\n7. La correspondance au motif est complète.\n\n8. La première correspondance est donc `match:\"witch\" and her \"broom\"`. Si l'expression régulière porte le marqueur `pattern:g`, alors la recherche continuera à partir de la fin de cette première correspondance. Comme il n'y a plus de guillemets dans la suite de la chaîne `subject:is one`, il n'y pas d'autres correspondances.\n\nCe n'est peut-être pas ce que nous attendions, mais c'est bien ainsi que cela fonctionne.\n\n**En mode glouton (mode par défaut) un caractère suivi d'un quantificateur est répété autant de fois que possible.**\n\nLe moteur d'expression régulière ajoute autant de caractères que possible pour le motif `pattern:.+`, puis réduit la correspondance caractère par caractère, si la suite du motif ne trouve pas de correspondance.\n\nNous avons besoin d'autre chose pour mener à bien notre tâche. Et le mode paresseux va pouvoir nous aider.\n\n## Mode paresseux\n\nLe mode paresseux des quantificateurs est l'opposé du mode glouton. Il signifie : \"répète le motif le moins de fois possible\".\n\nNous pouvons l'activer en ajoutant un `pattern:'?'` après le quantificateur, il devient alors  `pattern:*?` ou `pattern:+?` ou encore `pattern:??` pour le `pattern:'?'`.\n\nPour clarifier les choses : le point d'interrogation `pattern:?` est en général un quantificateur (zero ou un), mais si nous l'ajoutons *à la suite d'un autre quantificateur (ou bien de lui-même)* il prend une autre signification -- il change le mode de correspondance de glouton à paresseux.\n\nLa regexp `pattern:/\".+?\"/g` fonctionne alors comme prévu : elle trouve `match:\"witch\"` et `match:\"broom\"`:\n\n```js run\nlet regexp = /\".+?\"/g;\n\nlet str = 'a \"witch\" and her \"broom\" is one';\n\nalert( str.match(regexp) ); // \"witch\", \"broom\"\n```\n\nPour bien comprendre la différence, suivons cette recherche pas à pas.\n\n1. La première étape est la même : elle trouve le premier motif `pattern:'\"'` en 3&#x1D49; position :\n\n    ![](witch_greedy1.svg)\n\n2. L'étape suivante est aussi semblable : le moteur trouve une correspondance pour le point `pattern:'.'`:\n\n    ![](witch_greedy2.svg)\n\n3. La recherche prend ensuite un chemin different. Comme nous somme en mode paresseux pour `pattern:+?`, le moteur n'essaye pas de faire correspondre à nouveau un point, mais s'arrête et essaye immédiatement de trouver une correspondance à la suite du motif `pattern:'\"'` :\n\n    ![](witch_lazy3.svg)\n\n    S'il y avait des guillemets ici, alors la recherche s'arrêterait, mais il y a un `'i'`, donc pas de correspondance.\n4. Le moteur d'expression régulière augmente alors le nombre de répétitions pour le point et essaye à nouveau :\n\n    ![](witch_lazy4.svg)\n\n    Nouvel échec. Le nombre de répétitions augmente alors encore et encore ...\n5. ... Jusqu'à trouver une correspondance à la suite du motif :\n\n    ![](witch_lazy5.svg)\n\n6. La recherche suivante commence alors depuis la fin de la correspondance et ressort une autre résultat :\n\n    ![](witch_lazy6.svg)\n\nDans cet exemple nous avons vu comment le mode paresseux fonctionne pour `pattern:+?`. Les quantificateurs `pattern:*?` et `pattern:??` fonctionne de la même manière -- le moteur de regexp augmente le nombre de répétitions seulement si le reste du motif ne trouve pas de correspondance à cette position.\n\n**La paresse n'est active que pour le quantificateur suivi de `?`.**\n\nLes autres quantificateurs restent gloutons.\n\nPar exemple :\n\n```js run\nalert( \"123 456\".match(/\\d+ \\d+?/) ); // 123 4\n```\n\n1. Le motif `pattern:\\d+` essaye de trouver autant de chiffres que possible (mode glouton), il trouve donc  `match:123` et s'arrête, car le caractère suivant est un `pattern:' '`.\n2. Il y a ensuite un espace dans le motif, il y bien correspondance.\n3. Enfin il y a `pattern:\\d+?`. Le quantificateur est en mode paresseux, il trouve donc un chiffre `match:4` et vérifie si la suite du motif trouve une correspondance à partir d'ici.\n\n    ... Mais il n'y a plus rien dans le motif après `pattern:\\d+?`.\n\n    Le mode paresseux ne répète rien sans en avoir besoin. Le motif est terminé, donc la recherche aussi. Nous avons une correspondance `match:123 4`.\n\n```smart header=\"Optimisations\"\nLes moteurs d'expression régulière récents arrive à optimiser leurs algorithmes internes. Ils fonctionnent donc un peu différemment.\n\nMais pour comprendre comment fonctionnent les expression régulières et pour en construire, nous n'avons pas besoin d'en savoir plus. Ils sont seulement utilisés en interne pour optimiser les choses.\n\nComme les expressions régulières complexes sont difficiles à optimiser, la recherche peut se dérouler exactement comme nous l'avons décri.\n```\n\n## Approche alternative\n\nAvec les expressions régulières, Il y a souvent plusieurs façons pour arriver au même résultat.\n\nDans notre cas, nous pouvons trouver des chaines de caractères entre guillemets sans mode paresseux en utilisant la regexp `pattern:\"[^\"]+\"`:\n\n```js run\nlet regexp = /\"[^\"]+\"/g;\n\nlet str = 'a \"witch\" and her \"broom\" is one';\n\nalert( str.match(regexp) ); // \"witch\", \"broom\"\n```\n\nLa regexp `pattern:\"[^\"]+\"` donne le bon résultat, parce qu'il cherche des guillemets `pattern:'\"'` suivis par un ou plusieurs \"non-guillemets\"  `pattern:[^\"]`, et ensuite les guillemets de fin.\n\nQuand le moteur de regexp cherche le motif `pattern:[^\"]+` il arrête ses répétitions en rencontrant les guillemets suivant, et renvoie la correspondance.\n\nMais notez que, que cette logique ne remplace pas les quantificateurs paresseux !\n\nC'est juste différent. Suivant les cas, nous pourrons avoir besoin de l'un comme de l'autre.\n\n**Examinons un exemple où les quantificateurs paresseux échouent mais cette variante fonctionne.**\n\nPar exemple, nous souhaitons trouver des liens de la forme `<a href=\"...\" class=\"doc\">`, quel que soit le `href`.\n\nQuelle expression régulière utiliser ?\n\nUne première idée pourrait donner : `pattern:/<a href=\".*\" class=\"doc\">/g`.\n\nVoyons cela :\n```js run\nlet str = '...<a href=\"link\" class=\"doc\">...';\nlet regexp = /<a href=\".*\" class=\"doc\">/g;\n\n// Ça fonctionne !\nalert( str.match(regexp) ); // <a href=\"link\" class=\"doc\">\n```\n\nCela a fonctionné. Mais voyons ce qu'il se passe s'il y a plusieurs liens dans le texte ?\n\n```js run\nlet str = '...<a href=\"link1\" class=\"doc\">... <a href=\"link2\" class=\"doc\">...';\nlet regexp = /<a href=\".*\" class=\"doc\">/g;\n\n// Oups! Les deux liens dans la même correspondance!\nalert( str.match(regexp) ); // <a href=\"link1\" class=\"doc\">... <a href=\"link2\" class=\"doc\">\n```\n\nCette fois le résultat n'est pas le bon, pour la même raison que l'exemple avec \"witches\". Le quantificateur `pattern:.*` a pris trop de caractères.\n\nLa correspondance ressemble à cela :\n\n```html\n<a href=\".....................................\" class=\"doc\">\n<a href=\"link1\" class=\"doc\">... <a href=\"link2\" class=\"doc\">\n```\n\nModifions le motif en rendant le quantificateur `pattern:.*?` paresseux :\n\n```js run\nlet str = '...<a href=\"link1\" class=\"doc\">... <a href=\"link2\" class=\"doc\">...';\nlet regexp = /<a href=\".*?\" class=\"doc\">/g;\n\n// Ça fonctionne !\nalert( str.match(regexp) ); // <a href=\"link1\" class=\"doc\">, <a href=\"link2\" class=\"doc\">\n```\n\nL'expression régulière semble fonctionner à présent, il y a bien deux correspondances :\n\n```html\n<a href=\".....\" class=\"doc\">    <a href=\".....\" class=\"doc\">\n<a href=\"link1\" class=\"doc\">... <a href=\"link2\" class=\"doc\">\n```\n\n... Mais testons-la sur un autre texte :\n\n```js run\nlet str = '...<a href=\"link1\" class=\"wrong\">... <p style=\"\" class=\"doc\">...';\nlet regexp = /<a href=\".*?\" class=\"doc\">/g;\n\n// Mauvaise correspondance !\nalert( str.match(regexp) ); // <a href=\"link1\" class=\"wrong\">... <p style=\"\" class=\"doc\">\n```\n\nEt maintenant ça échoue. La correspondance inclue non seulement le lien, mais aussi beaucoup du texte suivant, incluant `<p...>`.\n\nPourquoi ?\n\nVoici ce qu'il se passe:\n\n1. Pour commencer la regexp trouve un début de lien `match:<a href=\"`.\n2. Il cherche ensuite le motif `pattern:.*?`: prend un caractère (mode paresseux !), vérifie s'il a une correspondance pour `pattern:\" class=\"doc\">` (aucune).\n3. Puis prend un caractère supplémentaire dans `pattern:.*?`, et ainsi de suite... jusqu'à atteindre finalement `match:\" class=\"doc\">`.\n\nMais voilà le problème : c'est déjà au-delà du lien `<a...>`, dans un autre balise `<p>`. Pas vraiment ce que nous souhaitons.\n\nVoici le schéma de la correspondance en alignant les caractères :\n\n```html\n<a href=\"...................................\" class=\"doc\">\n<a href=\"link1\" class=\"wrong\">... <p style=\"\" class=\"doc\">\n```\n\nNous avons donc besoin que le motif recherche `<a href=\"...something...\" class=\"doc\">`, mais les modes, glouton ou paresseux, rencontrent des problèmes.\n\nUne alternative fonctionnelle peut être : `pattern:href=\"[^\"]*\"`. Cela prendra tous les caractères à l'intérieur de l'attribut `href` jusqu'aux prochains guillemets, juste ce qu'il faut.\n\nUn exemple fonctionnel :\n\n```js run\nlet str1 = '...<a href=\"link1\" class=\"wrong\">... <p style=\"\" class=\"doc\">...';\nlet str2 = '...<a href=\"link1\" class=\"doc\">... <a href=\"link2\" class=\"doc\">...';\nlet regexp = /<a href=\"[^\"]*\" class=\"doc\">/g;\n\n// Ça marche !\nalert( str1.match(regexp) ); // null, aucune corespondance, c'est bien le résultat attendu\nalert( str2.match(regexp) ); // <a href=\"link1\" class=\"doc\">, <a href=\"link2\" class=\"doc\">\n```\n\n## Résumé\n\nLes quantificateurs ont deux modes de travail :\n\nGlouton\n: Par défaut le moteur d'expression régulière essaye de répéter le caractère quantifié autant de fois que possible. Par exemple, `pattern:\\d+` consomme tous les chiffres possibles. Quand il devient impossible d'en consommer d'autre (plus aucun chiffre ou fin de chaîne), il continue alors pour trouver la fin du motif. S'il ne trouve pas de correspondance, il réduit alors le nombre de répétitions effectuées (il revient sur ses pas) et essaye à nouveau.\n\nParesseux\n: Activé par le point d'interrogation `pattern:?` après le quantificateur. Le moteur de regexp essaye de trouver une correspondance pour le reste du motif avant chaque répétition du caractère quantifié.\n\nComme nous l'avons vu, Le mode paresseux n'est pas la \"panacée\" de la recherche gloutonne. Une alternative est une recherche gloutonne bien dosé, avec des exclusions, comme dans le motif `pattern:\"[^\"]+\"`.\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/01-test-mac/solution.md",
    "content": "Un nombre hexadécimal à deux chiffres correspond à `pattern:[0-9a-f]{2}` (avec le marqueur `pattern:i`).\n\nNous avons besoin de ce nombre `NN`, et ensuite `:NN` répété 5 fois (pour les autres nombres) ;\n\nL'expression régulière est : `pattern:[0-9a-f]{2}(:[0-9a-f]{2}){5}`\n\nMontrons maintenant que la correspondance se fait bien sur l'ensemble du texte : commence dès le début de la chaîne testée et termine à la fin. Cela se fait en entourant le motif de `pattern:^...$`.\n\nFinalement :\n\n```js run\nlet regexp = /^[0-9a-f]{2}(:[0-9a-f]{2}){5}$/i;\n\nalert( regexp.test('01:32:54:67:89:AB') ); // true\n\nalert( regexp.test('0132546789AB') ); // false (pas de double point)\n\nalert( regexp.test('01:32:54:67:89') ); // false (5 nombres, au lieu de 6)\n\nalert( regexp.test('01:32:54:67:89:ZZ') ) // false (ZZ à la fin)\n```\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/01-test-mac/task.md",
    "content": "# Vérification d'adresse MAC\n\nL'[addresse MAC](https://fr.wikipedia.org/wiki/Adresse_MAC) d'une interface réseau est constitué de 6 paires de nombres hexadécimaux séparées par un double point.\n\nPar exemple : `subject:'01:32:54:67:89:AB'`.\n\nÉcrire une regexp qui vérifie qu'une chaîne de caractères soit bien une adresse MAC.\n\nUtilisation:\n```js\nlet regexp = /your regexp/;\n\nalert( regexp.test('01:32:54:67:89:AB') ); // true\n\nalert( regexp.test('0132546789AB') ); // false (double point manquant)\n\nalert( regexp.test('01:32:54:67:89') ); // false (5 paires, mais 6 attendues)\n\nalert( regexp.test('01:32:54:67:89:ZZ') ) // false (ZZ à la fin)\n```\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/solution.md",
    "content": "Une regexp pour chercher une couleur à trois chiffres `#abc`: `pattern:/#[a-f0-9]{3}/i`.\n\nNous pouvons y ajouter les 3 autres chiffres optionnels. Nous n'avons pas besoin de plus ou moins. La couleur a soit 3 ou 6 chiffres.\n\nUtilisons le quantificateur `pattern:{1,2}` pour obtenir `pattern:/#([a-f0-9]{3}){1,2}/i`.\n\nIci le schéma `pattern:[a-f0-9]{3}` est entouré de parenthèses pour lui appliquer le quantificateur `pattern:{1,2}`.\n\nEn pratique :\n\n```js run\nlet regexp = /#([a-f0-9]{3}){1,2}/gi;\n\nlet str = \"color: #3f3; background-color: #AA00ef; and: #abcd\";\n\nalert( str.match(regexp) ); // #3f3 #AA00ef #abc\n```\n\nIl reste un petit problème ici : car ce schéma trouve `match:#abc` dans `subject:#abcd`. Pour éviter cela nous pouvons ajouter à la fin `pattern:\\b` :\n\n```js run\nlet regexp = /#([a-f0-9]{3}){1,2}\\b/gi;\n\nlet str = \"color: #3f3; background-color: #AA00ef; and: #abcd\";\n\nalert( str.match(regexp) ); // #3f3 #AA00ef\n```\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/task.md",
    "content": "# Trouver des couleurs au format #abc ou #abcdef\n\nÉcrire une RegExp qui correspond à des couleurs au format `#abc` ou `#abcdef`. C'est à dire : `#` suivi par 3 ou 6 chiffres hexadécimaux.\n\nExemple d'utilisation :\n```js\nlet regexp = /your regexp/g;\n\nlet str = \"color: #3f3; background-color: #AA00ef; and: #abcd\";\n\nalert( str.match(regexp) ); // #3f3 #AA00ef\n```\n\nP.S. Cela doit être exactement 3 ou 6 chiffres. Des valeurs avec 4 chiffres, comme `#abcd`, ne doivent pas ressortir.\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/solution.md",
    "content": "Un nombre positif avec une éventuelle partie décimale correspond à : `pattern:\\d+(\\.\\d+)?`.\n\nAjoutons l'option `pattern:-` au début :\n\n```js run\nlet regexp = /-?\\d+(\\.\\d+)?/g;\n\nlet str = \"-1.5 0 2 -123.4.\";\n\nalert( str.match(regexp) );   // -1.5, 0, 2, -123.4\n```\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/task.md",
    "content": "# Trouvez tous les nombres\n\nÉcrire un regexp qui cherche tous les nombres décimaux, comprenant les entiers, les nombres décimaux avec le point comme séparateur et les nombres négatifs.\n\nUn exemple d'utilisation :\n\n```js\nlet regexp = /your regexp/g;\n\nlet str = \"-1.5 0 2 -123.4.\";\n\nalert( str.match(regexp) ); // -1.5, 0, 2, -123.4\n```\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/04-parse-expression/solution.md",
    "content": "Une regexp pour un nombre : `pattern:-?\\d+(\\.\\d+)?`. Nous l'avons vu dans l'exercice précédent.\n\nPour l'opérateur `pattern:[-+*/]`. Le tiret `pattern:-` est en premier, car il pourrait signifier un intervalle de caractère, alors que nous souhaitons juste le caractère `-`.\n\nLe slash `/` doit être échappé en javascript dans une regexp `pattern:/.../`, et nous le ferons plus tard.\n\nNous cherchons un nombre, un opérateur puis un autre nombre. Et d'éventuels espaces entre eux.\n\nCela done l'expression régulière : `pattern:-?\\d+(\\.\\d+)?\\s*[-+*/]\\s*-?\\d+(\\.\\d+)?`.\n\nIl y a trois parties, avec `pattern:\\s*` entre elles :\n1. `pattern:-?\\d+(\\.\\d+)?` - le premier nombre,\n2. `pattern:[-+*/]` - l'opérateur,\n3. `pattern:-?\\d+(\\.\\d+)?` - le deuxième nombre.\n\nPour faire de chacune de ces parties un élément distinct du tableau de correspondance, entourons-les de parenthèses : `pattern:(-?\\d+(\\.\\d+)?)\\s*([-+*/])\\s*(-?\\d+(\\.\\d+)?)`.\n\nCela donne :\n\n```js run\nlet regexp = /(-?\\d+(\\.\\d+)?)\\s*([-+*\\/])\\s*(-?\\d+(\\.\\d+)?)/;\n\nalert( \"1.2 + 12\".match(regexp) );\n```\n\nLe résultat inclus :\n\n- `result[0] == \"1.2 + 12\"` (la correspondance complète)\n- `result[1] == \"1.2\"` (premier groupe `(-?\\d+(\\.\\d+)?)` -- le premier nombre, avec la partie décimale)\n- `result[2] == \".2\"` (second groupe`(\\.\\d+)?` -- la première partie décimale)\n- `result[3] == \"+\"` (troisième groupe `([-+*\\/])` -- l'opérateur)\n- `result[4] == \"12\"` (quatrième groupe `(-?\\d+(\\.\\d+)?)` -- le second nombre)\n- `result[5] == undefined` (cinquième groupe `(\\.\\d+)?` -- la deuxième partie décimale est absente, c'est non défini)\n\nNous ne souhaitons que les nombres et l'opérateur, sans la correspondance entière, ni les parties décimales. Faisons alors un peu le ménage.\n\nLa correspondance complète(le premier élément du tableau) peut être enlevée par `result.shift()`.\n\nLes groupes contenant les parties décimales(groupes 2 et 4) `pattern:(.\\d+)` peuvent être exclus en ajoutant `pattern:?:` au début : `pattern:(?:\\.\\d+)?`.\n\nLa solution complète :\n\n```js run\nfunction parse(expr) {\n  let regexp = /(-?\\d+(?:\\.\\d+)?)\\s*([-+*\\/])\\s*(-?\\d+(?:\\.\\d+)?)/;\n\n  let result = expr.match(regexp);\n\n  if (!result) return [];\n  result.shift();\n\n  return result;\n}\n\nalert( parse(\"-1.23 * 3.45\") );  // -1.23, *, 3.45\n```\n\nAs an alternative to using the non-capturing `?:`, we could name the groups, like this:\n\n```js run\nfunction parse(expr) {\n\tlet regexp = /(?<a>-?\\d+(?:\\.\\d+)?)\\s*(?<operator>[-+*\\/])\\s*(?<b>-?\\d+(?:\\.\\d+)?)/;\n\n\tlet result = expr.match(regexp);\n\n\treturn [result.groups.a, result.groups.operator, result.groups.b];\n}\n\nalert( parse(\"-1.23 * 3.45\") );  // -1.23, *, 3.45;\n```\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/04-parse-expression/task.md",
    "content": "# Parsez une expression\n\nUne expression arithmétique consiste en 2 nombres et un opérateur entre les deux, par exemple :\n\n- `1 + 2`\n- `1.2 * 3.4`\n- `-3 / -6`\n- `-2 - 2`\n\nL'opérateur l'un des : `\"+\"`, `\"-\"`, `\"*\"` ou `\"/\"`.\n\nIl peut y avoir des espaces supplémentaires au début, à la fin ou entre chaque partie.\n\nCréez une fonction `parse(expr)` qui prend une expression et retourne un tableau de trois éléments :\n\n1. Le premier nombre.\n2. L'opérateur.\n3. Le second nombre.\n\nPar exemple :\n\n```js\nlet [a, op, b] = parse(\"1.2 * 3.4\");\n\nalert(a); // 1.2\nalert(op); // *\nalert(b); // 3.4\n```\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/article.md",
    "content": "# Groupes capturant\n\nUne partie de motif peut être entourée de parenthèses `pattern:(...)`. Cela s'appelle un \"groupe capturant\".\n\nCeci a deux effets :\n\n1. Cela permet d'obtenir cette partie de correspondance comme élément du tableau de résultat.\n2. Si nous mettons après les parenthèses un quantificateur, celui-ci s'applique à tout l'ensemble entre parenthèses.\n\n## Exemples\n\nVoyons comment fonctionne le parenthésage par des exemples.\n\n### Exemple : gogogo\n\nSans parenthèses, le motif `pattern:go+` signifie le caractère `subject:g`, suivi par `subject:o` répété une ou plusieurs fois. Par exemple, `match:goooo` ou `match:gooooooooo`.\n\nAvec des parenthèses regroupant les caractères, `pattern:(go)+` signifie alors  `match:go`, `match:gogo`, `match:gogogo` et ainsi de suite.\n\n```js run\nalert( 'Gogogo now!'.match(/(go)+/ig) ); // \"Gogogo\"\n```\n\n### Exemple : domaine\n\nComplexifions maintenant un peu les choses -- une expression régulière pour rechercher le domaine d'un site web.\n\nPar exemple :\n\n```\nmail.com\nusers.mail.com\nsmith.users.mail.com\n```\n\nComme nous pouvons le voir, un domaine est constitué d'une répétition de mots, un point après chaque mot excepté pour le dernier.\n\nEn expression régulière cela donne `pattern:(\\w+\\.)+\\w+`:\n\n```js run\nlet regexp = /(\\w+\\.)+\\w+/g;\n\nalert( \"site.com my.site.com\".match(regexp) ); // site.com,my.site.com\n```\n\nLa recherche fonctionne, mais ce motif ne correspondra pas à un domaine comportant un tiret, par ex. `my-site.com`, car le tiret n'appartient pas à la classe `pattern:\\w`.\n\nNous pouvons corriger ça en remplaçant `pattern:\\w` par `pattern:[\\w-]` pour tous les mots excepté le dernier : `pattern:([\\w-]+\\.)+\\w+`.\n\n### Exemple : email\n\nEn se basant sur l'exemple précédent, nous pouvons créer une expression régulière pour les emails.\n\nLe format d'un email est : `nom@domaine`. Le nom peut comporter n'importe quel mot, tirets et points sont permis. En expression régulière cela donne `pattern:[-.\\w]+`.\n\nLe motif :\n\n```js run\nlet regexp = /[-.\\w]+@([\\w-]+\\.)+[\\w-]+/g;\n\nalert(\"my@mail.com @ his@site.com.uk\".match(regexp)); // my@mail.com, his@site.com.uk\n```\n\nCette regexp loin d'être parfaite, fonctionne dans une majorité de cas et aide à corriger d'éventuelles fautes de frappes. La seule vérification fiable à 100% pour un email est effectuée par l'envoi d'un courrier.\n\n## Les contenus de parenthèses dans la correspondance\n\nLes parenthèses sont numérotées de gauche à droite. Le moteur de recherche mémorise le contenu correspondant à chacune d'entre elles et permet d'y accéder dans le résultat.\n\nLa méthode `str.match(regexp)`, si `regexp` n'a pas de marqueur `g`, cherche la première correspondance et la retourne dans un tableau :\n\n1. À l'index `0`: la correspondance complète.\n2. À l'index `1`: le contenu des premières parenthèses.\n3. À l'index `2`: le contenu des secondes parenthèses.\n4. ...etc...\n\nPar exemple, si nous voulons trouver des balises HTML `pattern:<.*?>`, et agir dessus. Cela peut être pratique d'en avoir le contenu (l'intérieur des chevrons), dans des variables distinctes.\n\nEntourons l'intérieur de la balise de parenthèses, comme ceci : `pattern:<(.*?)>`.\n\nEt nous aurons maintenant à la fois la balise entière `match:<h1>` et son contenu `match:h1` dans le tableau de correspondance :\n\n```js run\nlet str = '<h1>Hello, world!</h1>';\n\nlet tag = str.match(/<(.*?)>/);\n\nalert( tag[0] ); // <h1>\nalert( tag[1] ); // h1\n```\n\n### Groupes imbriqués\n\nLes parenthèses peuvent être imbriquées. Dans ce cas la numérotation se fait aussi de gauche à droite.\n\nPar exemple, en effectuant une recherche dans la balise `subject:<span class=\"my\">` nous pourrions être intéressé par :\n\n1. Son contenu complet : `match:span class=\"my\"`.\n2. Son nom : `match:span`.\n3. Ses attributs : `match:class=\"my\"`.\n\nEntourons-les de parenthèses : `pattern:<(([a-z]+)\\s*([^>]*))>`.\n\nVoici comment les groupes sont numérotés(de gauche à droite, par ordre d'ouverture des parenthèses) :\n\n![](regexp-nested-groups-pattern.svg)\n\nCe qui donne :\n\n```js run\nlet str = '<span class=\"my\">';\n\nlet regexp = /<(([a-z]+)\\s*([^>]*))>/;\n\nlet result = str.match(regexp);\nalert(result[0]); // <span class=\"my\">\nalert(result[1]); // span class=\"my\"\nalert(result[2]); // span\nalert(result[3]); // class=\"my\"\n```\n\nL'index zero de `result` contient toujours l'entière correspondance, puis les groupes, numérotés de gauche à droite par ordre d'ouverture des parenthèses.\n\nLe premier groupe est retourné par `result[1]`. Il contient ici tout l'intérieur de la balise.\n\nPuis dans `result[2]` se trouve le groupe de la deuxième parenthèse ouvrante `pattern:([a-z]+)` - le nom de balise, puis dans `result[3]` la suite de la balise : `pattern:([^>]*)`.\n\nLes contenus de chaque groupe dans la chaîne de caractères :\n\n![](regexp-nested-groups-matches.svg)\n\n### Groupes optionnels\n\nMême si un groupe est optionnel et n'existe pas dans la correspondance (par ex. s'il a le quantificateur `pattern:(...)?`), son élément correspondant dans le tableau `result` est présent and vaut `undefined`.\n\nPar exemple, considérons l'expression régulière `pattern:a(z)?(c)?`. Cela cherche un `\"a\"` suivi d'un éventuel `\"z\"` suivi d'un éventuel `\"c\"`.\n\nSi nous lançons une recherche sur la seule lettre `subject:a`, alors le résultat donne:\n\n```js run\nlet match = 'a'.match(/a(z)?(c)?/);\n\nalert( match.length ); // 3\nalert( match[0] ); // a (correspondance complète)\nalert( match[1] ); // undefined\nalert( match[2] ); // undefined\n```\n\nLe tableau a une longueur de `3`, mais tous les groupes sont vides.\n\nEt voici une correspondance plus complexe avec la chaîne `subject:ac`:\n\n```js run\nlet match = 'ac'.match(/a(z)?(c)?/)\n\nalert( match.length ); // 3\nalert( match[0] ); // ac (correspondance complète)\nalert( match[1] ); // undefined, car il n'y a rien pour (z)?\nalert( match[2] ); // c\n```\n\nLa longueur du tableau fixe : `3`. Mais il n'y a rien pour le groupe `pattern:(z)?`, donc le résultat est `[\"ac\", undefined, \"c\"]`.\n\n## Rechercher toutes les correspondances avec des groupes : matchAll\n\n```warn header=\"`matchAll` est une méthode récente, et peut nécessiter un polyfill\"\nLa méthode `matchAll` n'est pas supportée par d'anciens navigateurs.\n\nUn polyfill peut être requis, comme <https://github.com/ljharb/String.prototype.matchAll>.\n```\n\nLorsque nous recherchons toutes les correspondances (flag `pattern:g`), la méthode `match` ne retourne pas le contenu des groupes.\n\nPar exemple, trouvons toutes les balises dans une chaîne de caractères:\n\n```js run\nlet str = '<h1> <h2>';\n\nlet tags = str.match(/<(.*?)>/g);\n\nalert( tags ); // <h1>,<h2>\n```\n\nLe résultat est un tableau de correspondance, mais sans les détails de chacune d'entre elles. Mais en pratique nous avons souvent besoin des contenus des groupes capturant dans le résultat.\n\nPour les obtenir, nous devons rechercher avec la méthode `str.matchAll(regexp)`.\n\nElle a été ajoutée au langage JavaScript longtemps après `match`, comme étant sa \"version nouvelle et améliorée\".\n\nTout comme `match`, elle cherche des correspondances, mais avec 3 différences :\n\n1. Elle ne retourne pas de tableau, mais un itérateur.\n2. Si le marqueur `pattern:g` est present, elle retourne toutes les correspondances dans des tableaux avec les groupes.\n3. S'il n'y a pas de correspondance, elle ne retourne pas `null`, mais un itérateur vide.\n\nPar exemple :\n\n```js run\nlet results = '<h1> <h2>'.matchAll(/<(.*?)>/gi);\n\n// results - n'est pas un tableau, mais un itérateur\nalert(results); // [object RegExp String Iterator]\n\nalert(results[0]); // undefined (*)\n\nresults = Array.from(results); // convertissons-le en tableau\n\nalert(results[0]); // <h1>,h1 (1st tag)\nalert(results[1]); // <h2>,h2 (2nd tag)\n```\n\nComme nous pouvons le voir, la première différence est très importante, comme le montre la ligne `(*)`. Nous ne pouvons pas trouver la correspondance dans `results[0]`, car il ne se comporte pas comme un tableau. Nous pouvons le convertir en véritable `Array` avec `Array.from`. Il y a plus de détails sur les objets itérables dans l'article <info:iterable>.\n\nIl n'y a pas besoin de `Array.from` si nous bouclons sur le résultat :\n\n```js run\nlet results = '<h1> <h2>'.matchAll(/<(.*?)>/gi);\n\nfor(let result of results) {\n  alert(result);\n  // premier alert: <h1>,h1\n  // second: <h2>,h2\n}\n```\n\n...Ou bien en déstructurant :\n\n```js\nlet [tag1, tag2] = '<h1> <h2>'.matchAll(/<(.*?)>/gi);\n```\n\nChaque correspondance, retournée par `matchAll`, a le même format que celui d'un `match` sans marqueur `pattern:g`: c'est un tableau avec les propriétés additionnelles `index` (index de la correspondance dans la chaîne) et `input` (chaîne source) :\n\n```js run\nlet results = '<h1> <h2>'.matchAll(/<(.*?)>/gi);\n\nlet [tag1, tag2] = results;\n\nalert( tag1[0] ); // <h1>\nalert( tag1[1] ); // h1\nalert( tag1.index ); // 0\nalert( tag1.input ); // <h1> <h2>\n```\n\n```smart header=\"Pourquoi le résultat d'un `matchAll` est un itérateur et pas un tableau ?\"\nPourquoi la méthode est-elle conçue comme cela ? La raison est simple - pour l'optimisation.\n\nL'appel à `matchAll` n'effectue pas la recherche. À la place, il retourne un itérateur, sans résultats préalables. La recherche est lancée à chaque fois que nous l'itérons, par ex. dans une boucle.\n\nNe seront donc trouvés qu'autant de résultats que besoin, pas plus.\n\nPar ex. s'il y a 100 correspondances potentielles dans un texte, mais dans une boucle `for..of` nous en trouvons 5, et décidons alors que c'est suffisant en faisant un `break`. Le moteur de recherche ne perdra pas son temps à rechercher les 95 autres correspondances.\n```\n\n## Groupes nommés\n\nIl est difficile de se souvenir de groupes par leur numéro. Bien que faisable pour des motifs simples, cela devient ardu dans des motifs plus complexes. Il existe une meilleure option : nommer les parenthèses.\n\nCela se fait en mettant `pattern:?<name>` immédiatement après la parenthèse ouvrante.\n\nPar exemple, recherchons une date au format \"year-month-day\":\n\n```js run\n*!*\nlet dateRegexp = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;\n*/!*\nlet str = \"2019-04-30\";\n\nlet groups = str.match(dateRegexp).groups;\n\nalert(groups.year); // 2019\nalert(groups.month); // 04\nalert(groups.day); // 30\n```\n\nComme vous pouvez le voir, les groupes figurent dans la propriété `.groups` de la correspondance.\n\nPour chercher toutes les dates, nous pouvons ajouter le marqueur `pattern:g`.\n\nNous aurons aussi besoin de `matchAll` pour obtenir des correspondances complètes, avec les groupes :\n\n```js run\nlet dateRegexp = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/g;\n\nlet str = \"2019-10-30 2020-01-01\";\n\nlet results = str.matchAll(dateRegexp);\n\nfor(let result of results) {\n  let {year, month, day} = result.groups;\n\n  alert(`${day}.${month}.${year}`);\n  // premier alert: 30.10.2019\n  // second: 01.01.2020\n}\n```\n\n## Groupes capturant dans un remplacement\n\nLa méthode `str.replace(regexp, replacement)` qui remplace dans `str` toutes les correspondances de `regexp`, nous permet d'utiliser le contenu des parenthèses dans la chaîne de `replacement`. Nous utiliserons alors `pattern:$n`, où `pattern:n` correspond au numéro de groupe.\n\nPar exemple,\n\n```js run\nlet str = \"John Bull\";\nlet regexp = /(\\w+) (\\w+)/;\n\nalert( str.replace(regexp, '$2, $1') ); // Bull, John\n```\n\nPour les parenthèses nommées la référence au groupe se fera avec `pattern:$<name>`.\n\nPar exemple, reformatons les dates depuis le format \"year-month-day\" vers \"day.month.year\":\n\n```js run\nlet regexp = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/g;\n\nlet str = \"2019-10-30, 2020-01-01\";\n\nalert( str.replace(regexp, '$<day>.$<month>.$<year>') );\n// 30.10.2019, 01.01.2020\n```\n\n## Groupe non capturant avec ?:\n\nNous avons parfois besoin de parenthèses pour appliquer correctement un quantificateur, sans avoir besoin de leurs contenu dans les résultats.\n\nUn groupe peut être exclu des résultats en ajoutant `pattern:?:` au début.\n\nPar exemple, si nous voulons trouver `pattern:(go)+`, sans avoir les contenus des parenthèses (`go`) comme élément du tableau de correspondance, nous pouvons écrire : `pattern:(?:go)+`.\n\nDans l'exemple suivant nous obtenons seulement `match:John` comme élément supplémentaire de la correspondance.:\n\n```js run\nlet str = \"Gogogo John!\";\n\n*!*\n// ?: exclu 'go' d'une capture\nlet regexp = /(?:go)+ (\\w+)/i;\n*/!*\n\nlet result = str.match(regexp);\n\nalert( result[0] ); // Gogogo John (correspondance entière)\nalert( result[1] ); // John\nalert( result.length ); // 2 (pas d'autres éléments dans le tableau)\n```\n\n## Résumé\n\nLes parenthèses regroupent ensemble une partie de l'expression régulière, de telle sorte qu'un quantificateur s'applique à toute cette partie.\n\nLes groupes de parenthèses sont numérotés de gauche à droite et peuvent éventuellement être nommés avec  `(?<name>...)`.\n\nLe contenu correspondant à un groupe, peut être obtenu dans les résultats :\n\n- La méthode `str.match` retourne les groupes capturant uniquement en l'absence du marqueur `pattern:g`.\n- La méthode `str.matchAll` retourne toujours les groupes capturant.\n\nSi les parenthèses n'ont pas de nom, alors leur contenu est dans le tableau de correspondances indexé par leur ordre d'ouverture. Les parenthèses nommées sont disponibles aussi par la propriété `groups`.\n\nNous pouvons aussi utiliser les contenus des parenthèses dans la chaîne de remplacement de `str.replace`: par leur numéro `$n` ou leur nom `$<name>`.\n\nUn groupe peut être exclu de la numérotation en ajoutant `pattern:?:` à son début. C'est utile pour appliquer un quantificateur à groupe entier, sans avoir besoin de cet élément dans les résultats. Nous ne pourrons pas non plus y faire référence dans une chaîne de remplacement.\n"
  },
  {
    "path": "9-regular-expressions/12-regexp-backreferences/article.md",
    "content": "# Rétro référence dans le pattern : \\N et \\k<name>\n\nNous pouvons utiliser le contenu des groupes de capture `pattern:(...)` non seulement dans le résultat ou dans la chaîne de caractères de remplacement, mais également dans le pattern en lui-même.\n\n## Rétro référence par un nombre : \\N\n\nUn groupe peut être référencé dans le pattern par `pattern:\\N`, où `N` est le numéro du groupe.\n\nPour rendre son utilité claire, considérons la tâche ci-dessous.\n\nNous devons trouver des chaînes citées : soit par des apostrophes `subject:'...'`, soit par des guillemets `subject:\"...\"` -- les deux variantes devraient correspondre.\n\nComment les trouver ?\n\nNous pouvons mettre les deux types entre crochets : `pattern:['\"](.*?)['\"]`, mais ce pattern pourrait correspondre avec des mélanges comme `match:\"...'` ou `match:'...\"`. Cela mènerait à des correspondances incorrectes lorsqu'une citation apparaît dans une autre, comme dans le texte `subject:\"She's the one!\"`:\n\n```js run\nlet str = `He said: \"She's the one!\".`;\n\nlet regexp = /['\"](.*?)['\"]/g;\n\n// Le résultat n'est pas celui que nous aimerions avoir\nalert( str.match(regexp) ); // \"She'\n```\n\nComme nous pouvons le voir, le pattern trouve des guillemets ouvrant `match:\"`, puis le texte est récupéré jusqu'au `match:'`, ce qui termine la correspondance.\n\nPour faire en sorte que le pattern vérifie que le caractère terminant la citation est précisément le même que celui qui l'ouvre, nous pouvons l'envelopper dans un groupe de capture et le rétro référencier : `pattern:(['\"])(.*?)\\1`.\n\nVoilà le code correct :\n\n```js run\nlet str = `He said: \"She's the one!\".`;\n\n*!*\nlet regexp = /(['\"])(.*?)\\1/g;\n*/!*\n\nalert( str.match(regexp) ); // \"She's the one!\"\n```\n\nMaintenant, ça fonctionne ! Le moteur trouve le premier caractère de citation `pattern:(['\"])` et mémorise son contenu. C'est le premier groupe de capture.\n\nPlus loin dans le pattern, `pattern:\\1` signifie \"cherche le même texte que dans le premier groupe de capture\", le même caractère de citation dans notre cas.\n\nSimilairement, `pattern:\\2` voudrait référencier le 2nd groupe, `pattern:\\3` - le 3e groupe, et ainsi de suite.\n\n```smart\nSi nous utilisons `?:` dans le groupe, alors nous ne pouvons pas le référencer. Les groupes exclus de la capture `(?:...)` ne sont pas mémorisés par le moteur.\n```\n\n```warn header=\"Ne mélangez pas : dans le pattern, `pattern:\\1`, dans le replacement : `pattern:$1`\"\nDans la chaîne de remplacement, on utilise un signe dollar : `pattern:$1`, alors que dans un pattern - un antislash `pattern:\\1`.\n```\n\n## Rétro référence par le nom: `\\k<name>`\n\nSi une expression régulière a beaucoup de groupes, il est pratique de leur attribuer un nom.\n\nPour référencer un groupe nommé, on peut utiliser `pattern:\\k<name>`.\n\nDans l'exemple ci-dessous, le groupe du caractère de citation s'appelle `pattern:?<quote>`, donc la rétro référence est `pattern:\\k<quote>`:\n\n```js run\nlet str = `He said: \"She's the one!\".`;\n\n*!*\nlet regexp = /(?<quote>['\"])(.*?)\\k<quote>/g;\n*/!*\n\nalert( str.match(regexp) ); // \"She's the one!\"\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/01-find-programming-language/solution.md",
    "content": "\nLa première idée peut être de lister les langages avec des `|` entre deux.\n\nMais cela ne fonctionne pas correctement :\n\n```js run\nlet regexp = /Java|JavaScript|PHP|C|C\\+\\+/g;\n\nlet str = \"Java, JavaScript, PHP, C, C++\";\n\nalert( str.match(regexp) ); // Java,Java,PHP,C,C\n```\n\nLe moteur d'expression régulière regarde les alternances une par une. C'est-à-dire : il regarde d'abord si nous avons `match:Java`, sinon il recherche `match:JavaScript` et ainsi de suite.\n\nAinsi, `match:JavaScript` ne peut jamais être trouvé, puisque `match:Java` est vérifié en premier.\n\nPareil pour `match:C` et `match:C++`.\n\nIl y a deux solutions à ce problème :\n\n1. Changer l'ordre pour vérifier le mot le plus long en premier : `pattern:JavaScript|Java|C\\+\\+|C|PHP`.\n2. Fusionner les mots commençant de la même manière : `pattern:Java(Script)?|C(\\+\\+)?|PHP`.\n\nEn action :\n\n```js run\nlet regexp = /Java(Script)?|C(\\+\\+)?|PHP/g;\n\nlet str = \"Java, JavaScript, PHP, C, C++\";\n\nalert( str.match(regexp) ); // Java,JavaScript,PHP,C,C++\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/01-find-programming-language/task.md",
    "content": "# Trouver les langages de programmation\n\nIl y a beaucoup de langages de programmation, par exemple Java, JavaScript, PHP, C, C++.\n\nCréez une regexp qui les trouve dans une chaine de caractère `subject:Java JavaScript PHP C++ C` :\n\n```js\nlet regexp = /your regexp/g;\n\nalert(\"Java JavaScript PHP C++ C\".match(regexp)); // Java JavaScript PHP C++ C\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/solution.md",
    "content": "\nUn tag d'ouverture est `pattern:\\[(b|url|quote)\\]`.\n\nEnsuite pour trouver tout jusqu'au tag de fermeture, utilisons le modèle `pattern:.*?` avec le flag `pattern:s` pour trouver n'importe quel caractère en plus des sauts de ligne, puis ajoutons une référence au tag de fermeture.\n\nLe modèle complet : `pattern:\\[(b|url|quote)\\].*?\\[/\\1]`.\n\nEn action :\n\n```js run\nlet regexp = /\\[(b|url|quote)].*?\\[\\/\\1]/gs;\n\nlet str = `\n  [b]hello![/b]\n  [quote]\n    [url]http://google.com[/url]\n  [/quote]\n`;\n\nalert( str.match(regexp) ); // [b]hello![/b],[quote][url]http://google.com[/url][/quote]\n```\n\nVeuillez noter qu'en plus d'échapper `pattern:[`, nous avons dû échapper une barre oblique pour la balise de fermeture `pattern:[\\/\\1]`, car normalement la barre oblique ferme le modèle.\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/task.md",
    "content": "# Trouver les paires de bbtag\n\nUn \"bb-tag\" ressemble à `[tag]...[/tag]`, où `tag` peut être : `b`, `url` ou `quote`.\n\nPar exemple :\n```\n[b]text[/b]\n[url]http://google.com[/url]\n```\n\nLes BB-tags peuvent être imbriqués. Mais un tag ne peut pas être imbriqué dans lui même, par exemple :\n\n```\nNormal:\n[url] [b]http://google.com[/b] [/url]\n[quote] [b]text[/b] [/quote]\n\nNe peut pas arriver:\n[b][b]text[/b][/b]\n```\n\nLes tags peuvent contenir des sauts de ligne, c'est normal :\n\n```\n[quote]\n  [b]text[/b]\n[/quote]\n```\n\nCréez une regexp pour trouver tous les BB-tags avec leur contenu.\n\nPar exemple :\n\n```js\nlet regexp = /your regexp/flags;\n\nlet str = \"..[url]http://google.com[/url]..\";\nalert( str.match(regexp) ); // [url]http://google.com[/url]\n```\n\nSi les tags sont imbriqués, alors nous voulons le tag extérieur (si nous voulons nous pouvons continuer la recherche dans le contenu) :\n\n```js\nlet regexp = /your regexp/flags;\n\nlet str = \"..[url][b]http://google.com[/b][/url]..\";\nalert( str.match(regexp) ); // [url][b]http://google.com[/b][/url]\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/03-match-quoted-string/solution.md",
    "content": "La solution : `pattern:/\"(\\\\.|[^\"\\\\])*\"/g`.\n\nEtape par etape :\n\n\n- D'abord nous recherchons une guillemet ouvrante `pattern:\"`\n- Ensuite si nous avons un antislash `pattern:\\\\` (puisque c'est un caractère spécial nous devons le doubler, mais dans les faits c'est un unique antislash), alors n'importe quel caractère peut se trouver à sa suite (un point).\n- Sinon nous prenons n'importe quel caractère à part une guillemet (cela signifierait la fin de la chaine de caractère) et un antislash (pour empêcher les antislash solitaires, un antislash est seulement utilisé avec un autre symbole après lui): `pattern:[^\"\\\\]`\n- ...Et on continue jusqu'à atteindre la guillemet fermante.\n\n\nEn action :\n\n```js run\nlet regexp = /\"(\\\\.|[^\"\\\\])*\"/g;\nlet str = ' .. \"test me\" .. \"Say \\\\\"Hello\\\\\"!\" .. \"\\\\\\\\ \\\\\"\" .. ';\n\nalert( str.match(regexp) ); // \"test me\",\"Say \\\"Hello\\\"!\",\"\\\\ \\\"\"\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/03-match-quoted-string/task.md",
    "content": "# Trouver les chaines de caractère\n\nCréer une regexp pour trouver les chaines de caractère entre guillemets doubles `subject:\"...\"`.\n\nLa chaine de caractère devrait supporter l'échappement, comme les chaines de caractère JavaScript. Par exemple, des guillemets peuvent être insérés comme ceci `subject:\\\"` une nouvelle ligne comme `subject:\\n`, et un antislash comme `subject:\\\\`.\n\n```js\nlet str = \"Just like \\\"here\\\".\";\n```\n\nVeuillez noter qu'une guillemet échapée `subject:\\\"` ne termine pas une chaine de caractère.\n\nNous devrions donc chercher une guillemet puis la suivante en ignorant celles échapées.\n\nC'est la partie essentielle de la tâche, à part cela, cela devrait être simple.\n\nExemple de chaine de caractère valides :\n```js\n.. *!*\"test me\"*/!* ..\n.. *!*\"Say \\\"Hello\\\"!\"*/!* ... (guillemets échapées à l'intérieur)\n.. *!*\"\\\\\"*/!* ..  (double backslash à l'intérieur)\n.. *!*\"\\\\ \\\"\"*/!* ..  (double backslash et guillemets échapées à l'intérieur)\n```\n\nEn JavaScript nous devons doubler les slash pour les placer dans la chaine de caractère, comme ceci :\n\n```js run\nlet str = ' .. \"test me\" .. \"Say \\\\\"Hello\\\\\"!\" .. \"\\\\\\\\ \\\\\"\" .. ';\n\n// the in-memory string\nalert(str); //  .. \"test me\" .. \"Say \\\"Hello\\\"!\" .. \"\\\\ \\\"\" ..\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/04-match-exact-tag/solution.md",
    "content": "\nLe début du modèle est évident: `pattern:<style`.\n\n...Mais nous ne pouvons pas juste écrire `pattern:<style.*?>` puisque `match:<styler>` y correspondrait.\n\nNous avons besoin soit d'un espace après `match:<style` et après optionellement quelque chose d'autre, ou bien la fin `match:>`.\n\nDans le langage des regexp : `pattern:<style(>|\\s.*?>)`.\n\nEn action :\n\n```js run\nlet regexp = /<style(>|\\s.*?>)/g;\n\nalert( '<style> <styler> <style test=\"...\">'.match(regexp) ); // <style>, <style test=\"...\">\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/04-match-exact-tag/task.md",
    "content": "# Trouver la balise entière\n\nÉcrivez une regexp pour trouver la balise `<style...>`. Il devrait trouver la balise en entier: il pourrait ne pas avoir d'attributs `<style>` ou en avoir plusieurs `<style type=\"...\" id=\"...\">`.\n\n...Mais la regexp ne devrait pas trouver `<styler>`!\n\nPar exemple:\n\n```js\nlet regexp = /your regexp/g;\n\nalert( '<style> <styler> <style test=\"...\">'.match(regexp) ); // <style>, <style test=\"...\">\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/article.md",
    "content": "# Alternance (OU) |\n\nAlternance est le terme d'expression régulière qui représente un \"OU\".\n\nDans une expression régulière l'alternance est représentée par une barre verticale `pattern:|`.\n\nPar exemple, nous souhaitons trouver les langages de programmation suivants: HTML, PHP, Java ou JavaScript.\n\nLa regexp correspondante : `pattern:html|php|java(script)?`.\n\nExemple d'utilisation:\n\n```js run\nlet regexp = /html|php|css|java(script)?/gi;\n\nlet str = \"First HTML appeared, then CSS, then JavaScript\";\n\nalert( str.match(regexp) ); // 'HTML', 'CSS', 'JavaScript'\n```\n\nNous avons déjà vu une chose similaire, les crochets. Ils permettent de choisir entre plusieurs caractères, par exemple `pattern:gr[ae]y` correspond à `match:gray` ou `match:grey`.\n\nLes crochets n'autorisent que les caractères ou les classes de caractère. L'alternance permet n'importe quelle expression. Une regexp `pattern:A|B|C` signifie `A`, `B` ou `C`.\n\nPar exemple:\n\n- `pattern:gr(a|e)y` signifie la même chose que `pattern:gr[ae]y`.\n- `pattern:gra|ey` signifie `match:gra` ou `match:ey`.\n\nPour appliquer l'alternance à une partie du modèle nous pouvons l'encadrer entre parenthèses:\n- `pattern:I love HTML|CSS` correspond à `match:I love HTML` ou `match:CSS`.\n- `pattern:I love (HTML|CSS)` correspond à `match:I love HTML` ou `match:I love CSS`.\n\n## Exemple: regexp d'un temps\n\nDans les articles précédents il y avait une tâche qui consistait à construire une regexp pour trouver un temps de la forme `hh:mm`, par exemple `12:00`. Mais un simple modèle `pattern:\\d\\d:\\d\\d` est trop vague. Il accepte `25:99` comme temps (puisque \"99 minutes\" correspond au modèle, mais ce temps est invalide).\n\nComment pouvons-nous créer un meilleur modèle ?\n\nNous pouvons utiliser une correspondance plus appropriée. Premièrement, les heures :\n\n- Si le premier chiffre est `0` ou `1`, alors le prochain chiffre peut être: `pattern:[01]\\d`.\n- Sinon, si le premier chiffre est `2`, alors le prochain doit être `pattern:[0-3]`.\n- (aucun autre premier chiffre est autorisé)\n\nNous pouvons écrire les deux variantes dans une regexp en utilisant l'alternance: `pattern:[01]\\d|2[0-3]`.\n\nEnsuite, les minutes doivent être entre `00` et `59`. Dans le langage des expression régulières cela peut être écrit `pattern:[0-5]\\d`: le premier chiffre `0-5`, puis n'importe quel chiffre.\n\nSi nous rejoignons les heures et les minutes ensemble, nous obtenons le modèle: `pattern:[01]\\d|2[0-3]:[0-5]\\d`.\n\nNous y sommes presque, mais il y a un problème. L'alternance `pattern:|` se trouve désormais entre `pattern:[01]\\d` et `pattern:2[0-3]:[0-5]\\d`.\n\nCela signifie que les minutes sont incluses dans la seconde variante d'alternance, voici un affichage plus clair:\n\n```\n[01]\\d  |  2[0-3]:[0-5]\\d\n```\n\nCe modèle recherche `pattern:[01]\\d` ou `pattern:2[0-3]:[0-5]\\d`.\n\nMais c'est incorrect, l'alternance ne devrait être utilisé que pour la partie \"heures\" de l'expression régulière, pour permettre `pattern:[01]\\d` OU `pattern:2[0-3]`. Corrigeons cela en mettant les \"heures\" entre parenthèses : `pattern:([01]\\d|2[0-3]):[0-5]\\d`.\n\nLa solution finale :\n\n```js run\nlet regexp = /([01]\\d|2[0-3]):[0-5]\\d/g;\n\nalert(\"00:00 10:10 23:59 25:99 1:2\".match(regexp)); // 00:00,10:10,23:59\n```\n"
  },
  {
    "path": "9-regular-expressions/14-regexp-lookahead-lookbehind/1-find-non-negative-integers/solution.md",
    "content": "\nL'expression régulière pour un nombre entier est `pattern:\\d+`.\n\nNous pouvons exclure les négatifs en les faisant précéder du lookbehind négatif : `pattern:(?<!-)\\d+`.\n\nBien que, si nous l'essayons maintenant, nous remarquerons peut-être un autre résultat \"supplémentaire\":\n\n```js run\nlet regexp = /(?<!-)\\d+/g;\n\nlet str = \"0 12 -5 123 -18\";\n\nconsole.log( str.match(regexp) ); // 0, 12, 123, *!*8*/!*\n```\n\nComme vous pouvez le voir, il correspond à `match:8`, à partir de `subject:-18`. Pour l'exclure, nous devons nous assurer que l'expression régulière commence à correspondre à un nombre qui ne se trouve pas au milieu d'un autre nombre (non correspondant).\n\nNous pouvons le faire en spécifiant un autre lookbehind négatif : `pattern:(?<!-)(?<!\\d)\\d+`. Maintenant, `pattern:(?<!\\d)` garantit qu'une correspondance ne commence pas après un autre chiffre, juste ce dont nous avons besoin.\n\nNous pouvons également les joindre en un seul lookbehind ici:\n\n```js run\nlet regexp = /(?<![-\\d])\\d+/g;\n\nlet str = \"0 12 -5 123 -18\";\n\nalert( str.match(regexp) ); // 0, 12, 123\n```\n"
  },
  {
    "path": "9-regular-expressions/14-regexp-lookahead-lookbehind/1-find-non-negative-integers/task.md",
    "content": "# Trouver des nombres entiers non négatifs\n\nIl y a une chaîne de nombres entiers.\n\nCréez une expression régulière qui ne recherche que les expressions non négatives (zéro est autorisé).\n\nUn exemple d'utilisation :\n```js\nlet regexp = /your regexp/g;\n\nlet str = \"0 12 -5 123 -18\";\n\nalert( str.match(regexp) ); // 0, 12, 123\n```\n"
  },
  {
    "path": "9-regular-expressions/14-regexp-lookahead-lookbehind/2-insert-after-head/solution.md",
    "content": "Pour insérer après la balise `<body>`, nous devons d'abord la trouver. Nous pouvons utiliser le modèle d'expression régulière `pattern:<body.*?>` pour cela.\n\nDans cette tâche, nous n'avons pas besoin de modifier la balise `<body>`. Nous n'avons qu'à ajouter le texte après.\n\nVoici comment nous pouvons le faire :\n\n```js run\nlet str = '...<body style=\"...\">...';\nstr = str.replace(/<body.*?>/, '$&<h1>Hello</h1>');\n\nalert(str); // ...<body style=\"...\"><h1>Hello</h1>...\n```\n\nDans la chaîne de remplacement, `$&` signifie la correspondance elle-même, c'est-à-dire la partie du texte source qui correspond à `pattern:<body.*?>`. Il est remplacé par lui-même suivi de `<h1>Hello</h1>`.\n\nUne alternative consiste à utiliser lookbehind :\n\n```js run\nlet str = '...<body style=\"...\">...';\nstr = str.replace(/(?<=<body.*?>)/, `<h1>Hello</h1>`);\n\nalert(str); // ...<body style=\"...\"><h1>Hello</h1>...\n```\n\nComme vous pouvez le voir, il n'y a qu'une partie lookbehind dans cette expression régulière.\n\nCela fonctionne comme ceci :\n- À chaque position dans le texte.\n- Vérifiez s'il est précédé de `pattern:<body.*?>`.\n- Si c'est le cas, nous avons le match.\n\nLa balise `pattern:<body.*?>` ne sera pas renvoyée. Le résultat de cette expression régulière est littéralement une chaîne vide, mais elle ne correspond qu'aux positions précédées de `pattern:<body.*?>`.\n\nIl remplace donc la \"ligne vide\", précédée de `pattern:<body.*?>`, par `<h1>Hello</h1>`. C'est l'insertion après `<body>`.\n\nPS Les drapeaux d'expression régulière, tels que `pattern:s` et `pattern:i` peuvent également être utiles : `pattern:/<body.*?>/si`. Le drapeau `pattern:s` fait correspondre le point `pattern:.` à un caractère de retour à la ligne, et le drapeau `pattern:i` fait que `pattern:<body>` correspond également à `match:<BODY>` insensible à la casse.\n"
  },
  {
    "path": "9-regular-expressions/14-regexp-lookahead-lookbehind/2-insert-after-head/task.md",
    "content": "# Insérer après Body\n\nNous avons une chaîne avec un document HTML.\n\nÉcrivez une expression régulière qui insère `<h1>Hello</h1>` immédiatement après la balise `<body>`. La balise peut avoir des attributs.\n\nPar exemple:\n\n```js\nlet regexp = /your regular expression/;\n\nlet str = `\n<html>\n  <body style=\"height: 200px\">\n  ...\n  </body>\n</html>\n`;\n\nstr = str.replace(regexp, `<h1>Hello</h1>`);\n```\n\nAprès cela, la valeur de `str` devrait être :\n```html\n<html>\n  <body style=\"height: 200px\"><h1>Hello</h1>\n  ...\n  </body>\n</html>\n```\n"
  },
  {
    "path": "9-regular-expressions/14-regexp-lookahead-lookbehind/article.md",
    "content": "# Lookahead et Lookbehind\n\nParfois nous avons juste besoin de trouver les motifs précédents ou suivant un autre motif.\n\nIl existe pour cela des syntaxes spéciales, appelées  \"lookahead\" et \"lookbehind\", ensemble désignées par \"lookaround\".\n\nPour commencer, trouvons le prix  à partir d'une chaîne de caractères comme `sujet:1 dindes coûte 30€`.C'est un nombre suivi par le signe  `sujet:€`\n\n## Lookahead\n\nLa syntaxe est: `pattern:X(?=Y)`, cela veut dire \"recherche  `pattern:X`, mais renvoie une correspondance seulement si il est suivi de `pattern:Y`\". Tous les motifs peuvent être utilisés au lieu de `pattern:X` et `pattern:Y`.\n\nPour un nombre entier suivi de `sujet:€`, l'expression régulière sera `pattern:\\d+(?=€)`:\n\n```js run\nlet str = \"1 dinde coûte 30€\";\n\nalert( str.match(/\\d+(?=€)/) ); // 30, le nombre 1 est ignoré, vu qu'il n'est pas suivi de €\n```\n\nNB: Le lookahead est seulement un test, le contenu de la parenthèse `pattern:(?=...)` n'est pas include dans le resultat `match:30`.\n\nQuand nous recherchons `pattern:X(?=Y)`, le moteur d'expressions régulières trouve `pattern:X` et verifie s'il y a `pattern:Y` immediatemment après. Si ce n'est pas le cas, la correspondqnce possible est ignoré, et la recherche continue.\n\nDes tests plus complexes sont possibles, ex: `pattern:X(?=Y)(?=Z)` signifie:\n\n1. Trouve`pattern:X`.\n2. Verifier si `pattern:Y` est immédiatement après `pattern:X` (ignorer sinon).\n3. Verifier si  `pattern:Z` se situe aussi immédiatement après `pattern:X` (ignorer sinon)..\n4. Si les deux tests sont réussis, alors le motif `pattern:X` correspond, sinon continuer à chercher.\n\nEn d'autres mots, ce genre de motif signifie que nous recherchons `pattern:X` suivi de `pattern:Y` et `pattern:Z` en meme temps\n\nC'est possible seulement  si `pattern:Y` et `pattern:Z` ne s'excluent pas mututellement.\n\nPar exemple, `pattern:\\d+(?=\\s)(?=.*30)` recherche `pattern:\\d+` suivi du motif `pattern:(?=\\s)`, et il y a `30` quelque part apres lui `pattern:(?=.*30)`:\n\n```js run\nlet str = \"1 dinde coute 30€\";\n\nalert( str.match(/\\d+(?=\\s)(?=.*30)/) ); // 1\n```\n\nDans notre chaîne de caractères cela correspond exactement au nombre `1`.\n\n## Lookahead negatif\n\nSupposons que nous recherchons plutôt une quantité, non un prix, a partir de la même chaîne de caractères.\n\nPour cela, le lookahead negatif peut etre utilisé.\n\nLa syntaxe est: `pattern:X(?!Y)`, cela veut dire `pattern:X`, mais seulement si il n'est pas suivi de  `pattern:Y`\".\n\n```js run\nlet str = \"2 turkeys cost 60€\";\n\nalert( str.match(/\\d+\\b(?!€)/g) ); // 2 (le prix ne correspond pas au motif)\n```\n\n## Lookbehind\n\n```warn header=\"Compatibilité des navigateurs pour Lookbehind\"\nVeuillez noter : Lookbehind n'est pas pris en charge dans les navigateurs non-V8, tels que Safari, Internet Explorer.\n```\n\nLookahead permet d'ajouter une condition sur  \"ce qui suit\".\n\nLookbehind est similaire a lookahead, mais il regarde derrière.Ça veut dire qu'il établit une correspondance seulement si il y a quelquechose avant lui,\n\nLa syntaxe est:\n- Lookbehind positif: `pattern:(?<=Y)X`, correspond à `pattern:X`, mais seulement si il y a `pattern:Y` avant lui.\n- Lookbehind negatif: `pattern:(?<=Y)X`, correspond à `pattern:X`, mais seulement si il n'y a pas `pattern:Y` avant lui.\n\nPqr exemple,changeons le prix en dollars US. Le signe dollar est généralement placé avant le chiffre,donc pour recupérer `$30`  nous utiliserons`pattern:(?<=\\$)\\d+` -- Une quantité précédé de `subject:$`:\n\n```js run\nlet str = \"1 turkey costs $30\";\n\n// le signe dollar est echappé \\$\nalert( str.match(/(?<=\\$)\\d+/) ); // 30 (ignore le nombre sans dollar)\n```\n\nEt si nous avons besoin d'une quantité -- un nombre  non précédé de `subject:$`, alors nous pouvons utiliser un lookbehind négatif `pattern:(?<!\\$)\\d+`:\n\n```js run\nlet str = \"2 dndes coûte $60\";\n\nalert( str.match(/(?<!\\$)\\b\\d+/g) ); // 2 (le prix ne correspond pas )\n```\n\n## Groupes capturants\n\nGénéralement, le contenu d'une parenthese de lookaround ne fait partie des resultats.\n\nPar exmple dans le motif `pattern:\\d+(?=€)`, le signe `pattern:€` n'est pas capture comme une partie de la corredpondance. C'est naturel: nous recherchons un nombre `pattern:\\d+`, tandis que `pattern:(?=€)` iest juste un test qui doit etre suivi de `subject:€`.\n\nMais dans certains cas, nous voulons capturer l'expression du lookaround aussi, comme une partie de la correspondance.C'est possible.Il suffit juste de le l'entourer d'une parenthese supplementaire. \n\nDans l'exemple suivant, le signe de la monnaie est capture, en meme temps aue la quqntite.\n\n```js run\nlet str = \"1 turkey costs 30€\";\nlet regexp = /\\d+(?=(€|kr))/; // parentheses supplemetaires autour de  €|kr\n\nalert( str.match(regexp) ); // 30, €\n```\n\nEt voila le meme chose pour lookbehind:\n\n```js run\nlet str = \"1 turkey costs $30\";\nlet regexp = /(?<=(\\$|£))\\d+/;\n\nalert( str.match(regexp) ); // 30, $\n```\n\n## Summary\n\nLookahead et lookbehind (ensemble désignés sous le nom de lookaround) sont utiles quand nous voulons identifier quelquechose selon le contexte avant/après lui.\n\nPour les expressions regulières simple, nous pouvons une chose similaire manuellement: considerer tous les elemets, dans tous les contextes et alors filtrer par contexte en boucle \n\nVous vous souvenez que `str.match` (sans le drapeau `pattern:g`) et `str.matchAll` retournent (toujours) les correspondances comme des tableaux avec une propriete `index`, et donc nous connaissons où exactement il est et nous pouvons verifier le contexte\n\nMais generalement lookaround est plus adapté.\n\nTypes de lookaround:\n\n| motif           | type             | correspondances |\n|--------------------|------------------|---------|\n| `X(?=Y)`   | Lookahead positif | `pattern:X` si il est suivi de `pattern:Y` |\n| `X(?!Y)`   | Lookahead négatif | `pattern:X` si il n'est pas suivi de`pattern:Y` |\n| `(?<=Y)X` |  Lookbehind positif| `pattern:X` s'il suit `pattern:Y` |\n| `(?<!Y)X` | Lookbehind négatif| `pattern:X` s'il ne suit pas `pattern:Y` |\n"
  },
  {
    "path": "9-regular-expressions/15-regexp-catastrophic-backtracking/article.md",
    "content": "# La rétroaction catastrophique\n\nCertaines expressions régulières semblent simples, mais peuvent prendre beaucoup de temps à s'exécuter, et même \"bloquer\" le moteur JavaScript.\n\nTôt ou tard, la plupart des développeurs sont parfois confrontés à un tel comportement. Le symptôme typique - une expression régulière fonctionne bien parfois, mais pour certaines chaînes, elle \"se bloque\", consommant 100% du CPU.\n\nDans ce cas, un navigateur Web suggère de tuer le script et de recharger la page. Pas une bonne chose à coup sûr.\n\nPour JavaScript côté serveur, une telle expression régulière peut bloquer le processus serveur, c'est encore pire. Nous devrions donc absolument y jeter un œil.\n\n## Exemple\n\nSupposons que nous ayons une chaîne et que nous voudrions vérifier si elle se compose de mots `pattern:\\w+` avec un espace optionnel `pattern:\\s?` après chacun.\n\nUne façon évidente de construire une expression régulières serait de prendre un mot suivi d'un espace optionnel `pattern:\\w+\\s?` puis de le répéter avec `*`.\n\nCela nous amène à l'expression régulières `pattern:^(\\w+\\s?)*$`, elle spécifie zéro ou plusieurs mots de ce type, qui commencent au début `pattern:^` et se terminent à la fin `pattern:$` de la ligne.\n\nEn action :\n\n```js run\nlet regexp = /^(\\w+\\s?)*$/;\n\nalert( regexp.test(\"A good string\") ); // true\nalert( regexp.test(\"Bad characters: $@#\") ); // false\n```\n\nL'expression régulières semble fonctionner. Le résultat est correct. Bien que, sur certaines chaines, cela prenne beaucoup de temps. Tellement longtemps que le moteur JavaScript \"se bloque\" avec une consommation CPU de 100%.\n\nSi vous exécutez l'exemple ci-dessous, vous ne verrez probablement rien, car JavaScript \"se bloquera\". Un navigateur Web cessera de réagir aux événements, l'interface utilisateur cessera de fonctionner (la plupart des navigateurs ne permettent que le défilement). Après un certain temps, il vous proposera de recharger la page. Alors soyez prudent avec ceci:\n\n```js run\nlet regexp = /^(\\w+\\s?)*$/;\nlet str = \"An input string that takes a long time or even makes this regexp hang!\";\n\n// prendra très longtemps\nalert( regexp.test(str) );\n```\n\nPour être juste, notons que certains moteurs d'expressions régulières peuvent gérer efficacement une telle recherche, par exemple la version du moteur V8 à partir de 8.8 peut le faire (donc Google Chrome 88 ne se bloque pas ici), tandis que le navigateur Firefox se bloque.\n\n## Exemple simplifié\n\nQuel est le problème? Pourquoi l'expression régulière se bloque-t-elle ?\n\nPour comprendre cela, simplifions l'exemple : supprimez les espaces `pattern:\\s?`. Il devient alors `pattern:^(\\w+)*$`.\n\nEt, pour rendre les choses plus évidentes, remplaçons `pattern:\\w` par `pattern:\\d`. L'expression régulière résultante est toujours bloquée, par exemple :\n\n```js run\nlet regexp = /^(\\d+)*$/;\n\nlet str = \"012345678901234567890123456789z\";\n\n// prendra beaucoup de temps (attention!)\nalert( regexp.test(str) );\n```\n\nAlors, quel est le problème avec l'expression régulière ?\n\nTout d'abord, on peut remarquer que l'expression régulière `pattern:(\\d+)*` est un peu étrange. Le quantificateur `pattern:*` semble superflu. Si nous voulons un nombre, nous pouvons utiliser `pattern:\\d+`.\n\nEn effet, l'expression régulière est artificielle ; nous l'avons obtenu en simplifiant l'exemple précédent. Mais la raison de sa lenteur est la même. Alors comprenons-le, et alors l'exemple précédent deviendra évident.\n\nQue se passe-t-il lors de la recherche de `pattern:^(\\d+)*$` dans la ligne `subject:123456789z` (raccourci un peu pour plus de clarté, veuillez noter un caractère non numérique `subject:z` à la fin, c'est important ), pourquoi cela prend-il autant de temps ?\n\nVoici ce que fait le moteur d'expression régulière :\n\n1. Tout d'abord, le moteur d'expression régulière essaie de trouver le contenu des parenthèses : le nombre `pattern:\\d+`. Le plus `pattern:+` est gourmand par défaut, donc il consomme tous les chiffres :\n\n    ```\n    \\d+.......\n    (123456789)z\n    ```\n\n    Une fois tous les chiffres consommés, `pattern:\\d+` est considéré comme trouvé (comme `match:123456789`).\n\n    Ensuite, le quantificateur en étoile `pattern:(\\d+)*` s'applique. Mais il n'y a plus de chiffres dans le texte, donc l'étoile ne donne rien.\n\n    Le caractère suivant du modèle est la fin de chaîne `pattern:$`. Mais dans le texte, nous avons `subject:z` à la place, donc il n'y a pas de correspondance :\n\n    ```\n               X\n    \\d+........$\n    (123456789)z\n    ```\n\n2. Comme il n'y a pas de correspondance, le quantificateur gourmand `pattern:+` diminue le nombre de répétitions, recule d'un caractère.\n\n    Maintenant `pattern:\\d+` prend tous les chiffres sauf le dernier (`match:12345678`) :\n    ```\n    \\d+.......\n    (12345678)9z\n    ```\n3. Ensuite, le moteur essaie de continuer la recherche à partir de la position suivante (juste après `match:12345678`).\n\n    L'étoile `pattern:(\\d+)*` peut être appliquée -- elle donne une autre correspondance de `pattern:\\d+`, le nombre `match:9` :\n\n    ```\n\n    \\d+.......\\d+\n    (12345678)(9)z\n    ```\n\n    Le moteur essaie à nouveau de faire correspondre `pattern:$`, mais échoue, car il rencontre `subject:z` à la place :\n\n    ```\n                 X\n    \\d+.......\\d+\n    (12345678)(9)z\n    ```\n\n\n4. Il n'y a pas de correspondance, donc le moteur continuera à revenir en arrière, diminuant le nombre de répétitions. Le backtracking fonctionne généralement comme ceci : le dernier quantificateur gourmand diminue le nombre de répétitions jusqu'à ce qu'il atteigne le minimum. Ensuite, le quantificateur gourmand précédent diminue, et ainsi de suite.\n\n    Toutes les combinaisons possibles sont tentées. Voici leurs exemples.\n\n    Le premier nombre `pattern:\\d+` comporte 7 chiffres, puis un nombre de 2 chiffres :\n\n    ```\n                 X\n    \\d+......\\d+\n    (1234567)(89)z\n    ```\n\n    Le premier nombre a 7 chiffres, puis deux nombres de 1 chiffre chacun :\n\n    ```\n                   X\n    \\d+......\\d+\\d+\n    (1234567)(8)(9)z\n    ```\n\n    Le premier nombre comporte 6 chiffres, puis un nombre de 3 chiffres :\n\n    ```\n                 X\n    \\d+.......\\d+\n    (123456)(789)z\n    ```\n\n    Le premier numéro comporte 6 chiffres, puis 2 chiffres :\n\n    ```\n                   X\n    \\d+.....\\d+ \\d+\n    (123456)(78)(9)z\n    ```\n\n    ...Et ainsi de suite.\n\n\nIl existe de nombreuses façons de diviser une séquence de chiffres \"123456789\" en nombres. Pour être précis, il y a <code>2<sup>n</sup>-1</code>, où `n` est la longueur de la séquence.\n\n- Pour `123456789` nous avons `n=9`, cela donne 511 combinaisons.\n- Pour une séquence plus longue avec `n=20` il y a environ un million (1048575) combinaisons.\n- Pour `n=30` - mille fois plus (1073741823 combinaisons).\n\nEssayer chacun d'eux est exactement la raison pour laquelle la recherche prend si longtemps.\n\n## Retour aux mots et aux chaînes\n\nLa même chose se produit dans notre premier exemple, lorsque nous recherchons des mots par `pattern:^(\\w+\\s?)*$` dans la chaîne `subject:An input that hangs!`.\n\nLa raison est qu'un mot peut être représenté par un `pattern:\\w+` ou plusieurs :\n```\n(input)\n(inpu)(t)\n(inp)(u)(t)\n(in)(p)(ut)\n...\n```\n\nPour un humain, il est évident qu'il peut n'y avoir aucune correspondance, car la chaîne se termine par un signe d'exclamation `!`, mais l'expression régulière attend un caractère verbal `pattern:\\w` ou un espace `pattern:\\s` à la fin. Mais le moteur ne le sait pas.\n\nIl essaie toutes les combinaisons de la façon dont l'expression régulière `pattern:(\\w+\\s?)*` peut \"consommer\" la chaîne, y compris les variantes avec des espaces `pattern:(\\w+\\s)*` et sans eux `pattern:(\\ w+)*` (parce que les espaces `pattern:\\s?` sont facultatifs). Comme il existe de nombreuses combinaisons de ce type (on l'a vu avec des chiffres), la recherche prend beaucoup de temps.\n\nQue faire?\n\nDoit-on activer le mode paresseux ?\n\nMalheureusement, cela n'aidera pas : si nous remplaçons `pattern:\\w+` par `pattern:\\w+?`, l'expression régulière sera toujours bloquée. L'ordre des combinaisons changera, mais pas leur nombre total.\n\nCertains moteurs d'expressions régulières ont des tests délicats et des automatisations finies qui permettent d'éviter de passer par toutes les combinaisons ou de le rendre beaucoup plus rapide, mais la plupart des moteurs ne le font pas, et cela n'aide pas toujours.\n\n## Comment corriger ?\n\nIl existe deux approches principales pour résoudre le problème.\n\nLa première consiste à réduire le nombre de combinaisons possibles.\n\nRendons l'espace non facultatif en réécrivant l'expression régulière sous la forme `pattern:^(\\w+\\s)*\\w*$` - nous chercherons n'importe quel nombre de mots suivis d'un espace `pattern:(\\w+\\ s)*`, puis (optionnellement) un dernier mot `pattern:\\w*`.\n\nCette expression régulière est équivalente à la précédente (correspond à la même chose) et fonctionne bien :\n\n```js run\nlet regexp = /^(\\w+\\s)*\\w*$/;\nlet str = \"An input string that takes a long time or even makes this regex hang!\";\n\nalert( regexp.test(str) ); // false\n```\n\nPourquoi le problème a-t-il disparu ?\n\nC'est parce que maintenant l'espace est obligatoire.\n\nL'expression régulière précédente, si nous omettons l'espace, devient `pattern:(\\w+)*`, conduisant à de nombreuses combinaisons de `\\w+` dans un seul mot\n\nAinsi, `subject:input` pourrait correspondre à deux répétitions de `pattern:\\w+`, comme ceci :\n\n```\n\\w+  \\w+\n(inp)(ut)\n```\n\nLe nouveau modèle est différent : `pattern:(\\w+\\s)*` spécifie des répétitions de mots suivies d'un espace ! La chaîne `subject:input` ne peut pas correspondre à deux répétitions de `pattern:\\w+\\s`, car l'espace est obligatoire.\n\nLe temps nécessaire pour essayer beaucoup (en fait la plupart) de combinaisons est maintenant économisé.\n\n## Empêcher la rétroaction\n\nCependant, il n'est pas toujours pratique de réécrire une expression régulière. Dans l'exemple ci-dessus, c'était facile, mais ce n'est pas toujours évident de savoir comment le faire.\n\nDe plus, une expression régulière réécrite est généralement plus complexe, et ce n'est pas bon. Les expressions régulières sont suffisamment complexes sans efforts supplémentaires.\n\nHeureusement, il existe une approche alternative. On peut interdire la rétroaction pour le quantificateur.\n\nLa racine du problème est que le moteur d'expressions régulières essaie de nombreuses combinaisons qui sont manifestement fausses pour un humain.\n\nPar exemple. dans l'expression régulière `pattern:(\\d+)*$` il est évident pour un humain que `pattern:+` ne devrait pas revenir en arrière. Si nous remplaçons un `pattern:\\d+` par deux `pattern:\\d+\\d+` séparés, rien ne change :\n\n```\n\\d+........\n(123456789)!\n\n\\d+...\\d+....\n(1234)(56789)!\n```\n\nEt dans l'exemple original `pattern:^(\\w+\\s?)*$` nous voudrions peut-être interdire la rétroaction dans `pattern:\\w+`. C'est-à-dire : `pattern:\\w+` doit correspondre à un mot entier, avec la longueur maximale possible. Il n'est pas nécessaire de réduire le nombre de répétitions dans `pattern:\\w+` ou de le diviser en deux mots `pattern:\\w+\\w+` et ainsi de suite.\n\nLes moteurs d'expressions régulières modernes prennent en charge les quantificateurs possessifs pour cela. Les quantificateurs réguliers deviennent possessifs si nous ajoutons `pattern:+` après eux. Autrement dit, nous utilisons `pattern:\\d++` au lieu de `pattern:\\d+` pour empêcher la rétroaction de `pattern:+`.\n\nLes quantificateurs possessifs sont en fait plus simples que les quantificateurs \"réguliers\". Ils correspondent au plus grand nombre possible, sans aucune rétroaction. Le processus de recherche sans la rétroaction est plus simple.\n\nIl existe également des \"groupes de capture atomique\" - un moyen de désactiver le retour en arrière à l'intérieur des parenthèses.\n\n...Mais la mauvaise nouvelle est que, malheureusement, en JavaScript, ils ne sont pas pris en charge.\n\nNous pouvons les émuler en utilisant une les \"assertions avant\" (lookahead).\n\n### Lookahead à la rescousse !\n\nNous en sommes donc arrivés à de véritables sujets avancés. Nous voudrions qu'un quantificateur, tel que `pattern:+` ne fasse pas marche arrière, car parfois la rétroaction n'a aucun sens.\n\nLe modèle pour prendre autant de répétitions de `pattern:\\w` que possible sans rétroaction est : `pattern:(?=(\\w+))\\1`. Bien sûr, nous pourrions prendre un autre modèle au lieu de `pattern:\\w`.\n\nCela peut sembler étrange, mais c'est en fait une transformation très simple.\n\nDécryptons-le :\n\n- Lookahead `pattern:?=` recherche le plus long mot `pattern:\\w+` commençant à la position actuelle.\n- Le contenu des parenthèses avec `pattern:?=...` n'est pas mémorisé par le moteur, alors placez `pattern:\\w+` entre parenthèses. Ensuite, le moteur mémorisera leur contenu\n- ...Et permettez-nous de le référencer dans le modèle en tant que `pattern:\\1`.\n\nC'est-à-dire : nous regardons en avant - et s'il y a un mot `pattern:\\w+`, alors faites-le correspondre à `pattern:\\1`.\n\nPourquoi? C'est parce que le lookahead trouve un mot `pattern:\\w+` dans son ensemble et nous le capturons dans le modèle avec `pattern:\\1`. Nous avons donc essentiellement implémenté un quantificateur possessif plus `pattern:+`. Il ne capture que le mot entier `pattern:\\w+`, pas une partie de celui-ci.\n\nPar exemple, dans le mot `subject:JavaScript`, il peut non seulement correspondre à `match:Java`, mais omettre `match:Script` pour correspondre au reste du modèle.\n\nVoici la comparaison de deux modèles :\n\n```js run\nalert( \"JavaScript\".match(/\\w+Script/)); // JavaScript\nalert( \"JavaScript\".match(/(?=(\\w+))\\1Script/)); // null\n```\n\n1. Dans la première variante, `pattern:\\w+` capture d'abord le mot entier `subject:JavaScript` mais ensuite `pattern:+` revient en arrière caractère par caractère, pour essayer de faire correspondre le reste du modèle, jusqu'à ce qu'il réussisse finalement (lorsque `pattern:\\w+` correspond à `match:Java`).\n2. Dans la deuxième variante, `pattern:(?=(\\w+))` regarde devant et trouve le mot `subject:JavaScript`, qui est inclus dans le pattern dans son ensemble par `pattern:\\1`, il reste donc aucun moyen de trouver `subject:Script` après.\n\nNous pouvons mettre une expression régulière plus complexe dans `pattern:(?=(\\w+))\\1` au lieu de `pattern:\\w`, lorsque nous devons interdire la rétroaction pour `pattern:+` après.\n\n```smart\nIl y a plus d'informations sur la relation entre les quantificateurs possessifs et lookahead dans les articles [Regex: Emulate Atomic Grouping (and Possessive Quantifiers) with LookAhead](https://instanceof.me/post/52245507631/regex-emulate-atomic-grouping-with-lookahead) et [Mimicking Atomic Groups](https://blog.stevenlevithan.com/archives/mimic-atomic-groups).\n```\n\nRéécrivons le premier exemple en utilisant lookahead pour éviter la rétroaction :\n\n```js run\nlet regexp = /^((?=(\\w+))\\2\\s?)*$/;\n\nalert( regexp.test(\"A good string\") ); // true\n\nlet str = \"An input string that takes a long time or even makes this regex hang!\";\n\nalert( regexp.test(str) ); // false, fonctionne et rapidement!\n```\n\nIci, `pattern:\\2` est utilisé à la place de `pattern:\\1`, car il y a des parenthèses externes supplémentaires. Pour éviter de se tromper avec les chiffres, nous pouvons donner un nom aux parenthèses, par ex. `pattern:(?<word>\\w+)`.\n\n```js run\n// les parenthèses sont nommées ?<word>, référencées comme \\k<word>\nlet regexp = /^((?=(?<word>\\w+))\\k<word>\\s?)*$/;\n\nlet str = \"An input string that takes a long time or even makes this regex hang!\";\n\nalert( regexp.test(str) ); // false\n\nalert( regexp.test(\"A correct string\") ); // true\n```\n\nLe problème décrit dans cet article est appelé \"rétroaction catastrophique\".\n\nNous avons couvert deux façons de le résoudre:\n- Réécrivez l'expression régulière pour réduire le nombre de combinaisons possibles.\n- Empêcher la rétroaction.\n"
  },
  {
    "path": "9-regular-expressions/16-regexp-sticky/article.md",
    "content": "\n# Marqueur collant \"y\", recherche depuis une position\n\nLe marqueur `pattern:y` permet d'effectuer une recherche à partir d'une position donnée dans la chaîne de caractères source.\n\nPour appréhender le cas d'usage du marqueur `pattern:y` et mieux comprendre le fonctionnement des regexps, regardons un exemple pratique.\n\nParmi les usages courants des regexps, l'analyse lexicale : Avec un texte donné, p. ex. dans un langage de programmation, nous avons besoin de trouver ses éléments de structure. Par exemple, l'HTML a des balises et des attributs, le code JavaScript a des fonctions, variables, etc.\n\nL'écriture d'analyseurs lexicaux est un domaine spécifique, avec ses propres outils et algorithmes que nous n'explorerons pas ici, mais il y a une tâche courante : Lire quelque chose depuis une position donnée.\n\nP. ex. prenons la chaîne de caractères `subject:let varName = \"value\"`, dans laquelle nous devons lire le nom de la variable, qui commence à la position `4`.\n\nNous chercherons un nom de variable en utilisant la regexp `pattern:\\w+`. Les noms de variable en JavaScript nécessitent en fait pour un résultat exact, une regexp un peu plus complexe, mais c'est sans importance ici.\n\n- Un appel à `str.match(/\\w+/)` trouvera seulement le premier mot de la ligne (`let`). Ça n'est pas ça.\n- Nous pouvons ajouter le marqueur `pattern:g`. Mais alors l'appel à `str.match(/\\w+/g)` cherchera tous les mots du text, alors que nous avons besoin que d'un mot à partir de la position `4`. Ça n'est donc pas encore ça.\n\n**Alors comment rechercher un motif à partir d'une position donnée ?**\n\nEssayons en utilisant la méthode `regexp.exec(str)`.\n\nPour une `regexp` sans marqueur `pattern:g` ni `pattern:y`, cette méthode cherche seulement la première occurrence, cela fonctionne exactement comme `str.match(regexp)`.\n\n... Mais s'il y a le marqueur `pattern:g`, il effectue alors une recherche dans `str`, à partir de la position stockée dans la propriété `regexp.lastIndex`. Et s'il trouve une correspondance, il fixe `regexp.lastIndex` à l'index immédiatement après cette correspondance.\n\nEn d'autres termes, `regexp.lastIndex` sert de point de départ pour la recherche, puis chaque appel à `regexp.exec(str)` la change en une nouvelle valeur (\"après la dernière correspondance\"). Cela, bien entendu, uniquement avec le marquer `pattern:g`.\n\nDonc chaque appel successif à `regexp.exec(str)` retourne une correspondance après l'autre.\n\nVoici un exemple de tels appels :\n\n```js run\nlet str = 'let varName'; // Cherchons tous les mots dans cette chaîne de caractères\nlet regexp = /\\w+/g;\n\nalert(regexp.lastIndex); // 0 (initialement lastIndex=0)\n\nlet word1 = regexp.exec(str);\nalert(word1[0]); // let (1er mot)\nalert(regexp.lastIndex); // 3 (position après la première correspondance)\n\nlet word2 = regexp.exec(str);\nalert(word2[0]); // varName (2e mot)\nalert(regexp.lastIndex); // 11 (position après la seconde correspondance)\n\nlet word3 = regexp.exec(str);\nalert(word3); // null (plus aucune correspondance)\nalert(regexp.lastIndex); // 0 (réinitialisé à la fin de la recherche)\n```\n\nNous pouvons ainsi obtenir toutes les correspondances dans la boucle :\n\n```js run\nlet str = 'let varName';\nlet regexp = /\\w+/g;\n\nlet result;\n\nwhile (result = regexp.exec(str)) {\n  alert( `Found ${result[0]} at position ${result.index}` );\n  // Found let at position 0, puis\n  // Found varName at position 4\n}\n```\n\nUne telle utilisation de `regexp.exec` est une alternative à la méthode `str.matchAll`, avec un peu plus de contrôle sur le processus.\n\nRetournons à notre objectif.\n\nNous pouvons assigner à `lastIndex` la valeur `4`, pour commencer la recherche à partir de cette position !\n\nComme ceci :\n\n```js run\nlet str = 'let varName = \"value\"';\n\nlet regexp = /\\w+/g; // sans le marqueur \"g\", la propriété lastIndex est ignorée\n\n*!*\nregexp.lastIndex = 4;\n*/!*\n\nlet word = regexp.exec(str);\nalert(word); // varName\n```\n\nHoura ! Problème résolu ! \n\nNous avons recherché le motif `pattern:\\w+`, à partir de la position `regexp.lastIndex = 4`.\n\nLe résultat est valide.\n\n...Mais attendez, pas si vite.\n\nVous noterez : l'appel à `regexp.exec` commence la recherche à la position `lastIndex` et continue ensuite plus loin. S'il n'y a pas de mot à la position `lastIndex`, mais qu'il y en a un plus loin, c'est celui-ci qui sera trouvé :\n\n```js run\nlet str = 'let varName = \"value\"';\n\nlet regexp = /\\w+/g;\n\n*!*\n// comme la recherche à la position 3\nregexp.lastIndex = 3;\n*/!*\n\nlet word = regexp.exec(str); \n// trouve la correspondance à la position 4\nalert(word[0]); // varName\nalert(word.index); // 4\n```\n\nPour certaines tâches, et pour les analyses lexicales en particulier, c'est complètement faux. Nous avons besoin de trouver la correspondance du motif à la position exacte, et non quelque part plus loin. Et c'est justement ce que fait le marqueur `y`.\n\n**Le marqueur `pattern:y` fait que `regexp.exec` recherche exactement à la position `lastIndex`, et non à partir de cette position.**\n\nVoici la même recherche avec le marqueur `pattern:y`:\n\n```js run\nlet str = 'let varName = \"value\"';\n\nlet regexp = /\\w+/y;\n\nregexp.lastIndex = 3;\nalert( regexp.exec(str) ); // null (il n'y a pas de mot en position 3, mais un espace)\n\nregexp.lastIndex = 4;\nalert( regexp.exec(str) ); // varName (mot en position 4)\n```\n\nComme nous pouvons le voir, la regexp `pattern:/\\w+/y` ne trouve pas de correspondance en position `3` (contrairement au marqueur  `pattern:g`), mais trouve la correspondance en position `4`.\n\nEn plus d'obtenir ce que nous cherchions, il y a un gain significatif de performance avec le marqueur `pattern:y`.\n\nImaginez avec un long texte, sans aucune correspondance dedans. Une recherche avec le marqueur `pattern:g` ira alors jusqu'à la fin du texte pour ne rien trouver, et cela prendra bien plus de temps qu'avec le marqueur `pattern:y`, qui vérifie seulement à la position exacte.\n\nDans des tâches comme en analyse lexicale, il y a habituellement beaucoup de recherches sur des positions exactes, pour vérifier ce qu'il s'y trouve. L'utilisation du marqueur `pattern:y` est la clé pour des bonnes implémentations et de bonnes performances.\n"
  },
  {
    "path": "9-regular-expressions/17-regexp-methods/article.md",
    "content": "# Methodes des Expressions Rationnelles et des chaînes de caractères\n\nDans cet article, nous aborderons différentes méthodes qui fonctionnent en profondeur avec des expressions rationnelles (regexps).\n\n\n## str.match(regexp)\n\nLa méthode `str.match(regexp)` trouve les correspondances de l'expression rationnelle `regexp` dans la chaîne de texte `str`.\n\n\nElle dispose de 3 options :\n\n1. si l'expression rationnelle n'à pas de marqueur `pattern:g`, alors seul la première correspondance est renvoyée sous la forme d'un tableau avec le groupe capturé et ses propriétés : index (indice de la correspondance), et input (chaîne d'entrée équivalent à str):\n\n    ```js run\n    let str = \"I love JavaScript\";\n\n    let result = str.match(/Java(Script)/);\n\n    alert( result[0] );     // JavaScript (correspondance exacte)\n    alert( result[1] );     // Script (premier groupe capturant)\n    alert( result.length ); // 2\n\n    // Additional information:\n    alert( result.index );  // 7 (indice de la chaîne de caractère où à été trouvée la correspondance)\n    alert( result.input );  // I love JavaScript (chaîne sur laquelle a été effectuée la recherche)\n    ```\n\n2. Si la `regexp` dispose d'un marqueur `pattern:g`, alors elle retourne un tableau de toutes les correspondances de texte, sans capturer les groupes ou les autres propriétés.\n\n    ```js run\n    let str = \"I love JavaScript\";\n\n    let result = str.match(/Java(Script)/g);\n\n    alert( result[0] ); // JavaScript\n    alert( result.length ); // 1\n    ```\n\n3. S'il n'y a pas de correspondance, qu'il y ait un marqueur `pattern:g` ou non, `null` est renvoyé.\n\n    C'est une nuance importante. Si il n'y a pas de correspondance, nous ne récupérons pas de tableau vide, mais `null`. Il n'est pas rare de faire une erreur en oubliant ce détail, e.g.:\n\n    ```js run\n    let str = \"I love JavaScript\";\n\n    let result = str.match(/HTML/);\n\n    alert(result); // null\n    alert(result.length); // Error: Cannot read property 'length' of null\n    ```\n\n    Si nous voulons que le résultat soit un tableau, nous pouvons écrire de cette façon:\n\n    ```js\n    let result = str.match(regexp) || [];\n    ```\n\n## str.matchAll(regexp)\n\n[recent browser=\"new\"]\n\nLa méthode `str.matchAll(regexp)` est une variante \"améliorée\" de `str.match`.\n\nElle est principalement utilisée pour rechercher toutes les correspondances au sein de chaque groupe.\n\nIl y a 3 différences avec `match`:\n\n1. Elle retourne un objet iterable avec les correspondances au lieu d'un tableau. Nous pouvons le transformer en un tableau classique en utilisant la méthode `Array.from`.\n2. Toutes les correspondances sont retournées dans un tableau incluant les groupes capturants (sous le même format que `str.match` sans le marqueur `pattern:g`).\n3. S'il n'y a aucun résultat, il renvoie un objet itérable vide au lieu de `null`.\n\nExemple d'utilisation:\n\n```js run\nlet str = '<h1>Hello, world!</h1>';\nlet regexp = /<(.*?)>/g;\n\nlet matchAll = str.matchAll(regexp);\n\nalert(matchAll); // [object RegExp String Iterator], pas un tableau, mais un itérateur\n\nmatchAll = Array.from(matchAll); // maintenant un tableau\n\nlet firstMatch = matchAll[0];\nalert( firstMatch[0] );  // <h1>\nalert( firstMatch[1] );  // h1\nalert( firstMatch.index );  // 0\nalert( firstMatch.input );  // <h1>Hello, world!</h1>\n```\n\nSi nous utilisons `for..of` pour boucler sur les résultats de `matchAll`, alors il n'est pas nécessaire d'utiliser `Array.from`.\n\n## str.split(regexp|substr, limit)\n\nDivise la chaîne de caractères en utilisant la regexp (ou une sous-chaîne de caractères) comme délimiteur.\n\nNous pouvons utiliser `split` avec une chaîne de caractères comme ceci :\n\n```js run\nalert('12-34-56'.split('-')) // array of ['12', '34', '56']\n```\n\nMais nous pouvons aussi diviser une chaîne de texte en utilisant une expression rationnelle:\n\n```js run\nalert('12, 34, 56'.split(/,\\s*/)) // array of ['12', '34', '56']\n```\n\n## str.search(regexp)\n\nLa méthode `str.search(regexp)` renvoie l'indice du premier motif correspondant, ou `-1` si aucune correspondance n'est trouvée:\n\n```js run\nlet str = \"A drop of ink may make a million think\";\n\nalert( str.search( /ink/i ) ); // 10 (indice du premier motif correspondant)\n```\n\n**Limitation importante: `search` renvoie uniquement la première correspondance.**\n\nSi nous avons besoin de l'indice ou de plus de correspondances, nous devrions utiliser d'autres méthodes, comme les trouver tous avec `str.matchAll(regexp)`.\n\n## str.replace(str|regexp, str|func)\n\nIl s'agit d'une méthode générique pour chercher et remplacer une chaîne de caractères, l'une des plus utiles. Le couteau suisse pour chercher et remplacer.\n\nNous pouvons l'utiliser sans regexps, pour chercher et remplacer une sous-chaîne de caractères:\n\n```js run\n// replace a dash by a colon\nalert('12-34-56'.replace(\"-\", \":\")) // 12:34-56\n```\n\nToutefois, il y a un piège.\n\n**Quand le premier argument de `replace` est une chaîne de caractères, elle ne remplace que la première occurence.**\n\nVous pouvez constater dans l'exemple ci-dessous que seul le premier `\"-\"` est remplacé par `\":\"`.\n\nPour trouver tous les traits d'unions, nous devons utiliser non pas le caractère `\"-\"`, mais une expression rationnelle `pattern:/-/g`, avec obligatoirement le marqueur `pattern:g`;\n\n```js run\n// remplace tous les tirets par deux-points\nalert( '12-34-56'.replace( *!*/-/g*/!*, \":\" ) )  // 12:34:56\n```\n\n\nLe second argument est une chaîne de caractères de remplacement. Nous pouvons utiliser des caractères spéciaux dedans :\n\n\n| Symbols | Action in the replacement string |\n|--------|--------|\n|`$&`|insère la chaine de caractère en correspondance|\n|<code>$&#096;</code>|Insère la partie de la chaîne de caractère qui précède la sous-chaîne en correspondance|\n|`$'`|insère la partie de la chaîne de caractère qui suit la sous-chaîne en correspondance|\n|`$n`|si `n` est un nombre à 1 ou 2 chiffres, insère la n-ième chaîne de sous-correspondance entre parenthèses, pour plus de détails voir [](info:regexp-groups)|\n|`$<name>`|insère la chaîne de caractère du `name` correspondant à celui entre parenthèse, pour plus de détails voir [](info:regexp-groups)|\n|`$$`|insère un caractère `$` |\n\nPar exemple:\n\n```js run\nlet str = \"John Smith\";\n\n// inverser le prénom et nom de famille\nalert(str.replace(/(john) (smith)/i, '$2, $1')) // Smith, John\n```\n\n**Si le contexte nécessite un remplacement \"intelligent\", le second argument peut être une fonction.**\n\nElle sera appelée pour chaque correspondance, et la valeur de retour sera insérée comme remplacement.\n\nLa fonction est appelée avec des arguments `func(match, p1, p2, ..., pn, offset, input, groups)`:\n\n1. `match` -- La chaîne de caractère en correspondance,\n2. `p1, p2, ..., pn` -- contenu des groupes capturants (s'il y en a),\n3. `offset` -- indice de la sous-chaîne correspondante,\n4. `input` -- la chaîne de texte initiale,\n5. `groups` -- un objet contenant les groupes nommés.\n\nSi la regexp ne comporte pas de parenthèses, alors la fonction ne contient que 3 arguments: `func(str, offset, input)`.\n\nPar exemple, pour convertir les chaînes de caractères correspondantes en majuscule:\n\n```js run\nlet str = \"html and css\";\n\nlet result = str.replace(/html|css/gi, str => str.toUpperCase());\n\nalert(result); // HTML and CSS\n```\n\nRemplace chaque résultat en utilisant son indice dans la chaîne de caractères:\n\n```js run\nalert(\"Ho-Ho-ho\".replace(/ho/gi, (match, offset) => offset)); // 0-3-6\n```\n\nDans l'exemple ci-dessous, il y a 2 groupes entre parenthèses. La fonction de remplacement est alors appelée avec 5 arguments: le premier est la correspondance complète, puis chacun des groupes entre parenthèses et enfin (non présent dans l'exemple) l'indice de la correspondance et la chaîne de caractères initiale:\n\n```js run\nlet str = \"John Smith\";\n\nlet result = str.replace(/(\\w+) (\\w+)/, (match, name, surname) => `${surname}, ${name}`);\n\nalert(result); // Smith, John\n```\n\nSi il y a de nombreux groupes entre parenthèses, il peut être pratique d'utiliser les paramètres du reste pour y accéder:\n\n```js run\nlet str = \"John Smith\";\n\nlet result = str.replace(/(\\w+) (\\w+)/, (...match) => `${match[2]}, ${match[1]}`);\n\nalert(result); // Smith, John\n```\n\nOu, si nous utilisons des groupes nommés, alors l'objet `groups` est toujours placé en dernier, et nous pouvons l'obtenir de cette façon:\n\n```js run\nlet str = \"John Smith\";\n\nlet result = str.replace(/(?<name>\\w+) (?<surname>\\w+)/, (...match) => {\n  let groups = match.pop();\n\n  return `${groups.surname}, ${groups.name}`;\n});\n\nalert(result); // Smith, John\n```\n\nLes fonctions représentent le pouvoir ultime pour effectuer un remplacement. Elles recupèrent toutes les informations des correspondances, ont accès aux variables externes et sont capable de tout faire.\n\n## str.replaceAll(str|regexp, str|func)\n\nThis method is essentially the same as `str.replace`, with two major differences:\n\n1. If the first argument is a string, it replaces *all occurrences* of the string, while `replace` replaces only the *first occurrence*.\n2. If the first argument is a regular expression without the `g` flag, there'll be an error. With `g` flag, it works the same as `replace`.\n\nThe main use case for `replaceAll` is replacing all occurrences of a string.\n\nLike this:\n\n```js run\n// replace all dashes by a colon\nalert('12-34-56'.replaceAll(\"-\", \":\")) // 12:34:56\n```\n\n\n## regexp.exec(str)\n\nLa méthode `regexp.exec(str)` renvoie une correspondance for `regexp` dans la chaîne de caractères `str`. À l'inverse de la méthode précédente, elle est appelée sur une regexp et non une chaîne de caractères.\n\nElle se comporte différement selon que la regexp dispose d'un marqueur `pattern:g` ou non.\n\nSi `pattern:g` n'est pas présent, alors `regexp.exec(str)` renvoie la première correspondance tel que le ferait `str.match(regexp)`. Ce comportement n'apporte rien de nouveau.\n\nMais si `pattern:g` est utilisé, alors:\n- Un appel à `regexp.exec(str)` renvoie la première correspondance et sauvegarde l'indice situé juste après, accessible via la propriété `regexp.lastIndex`.\n- l'appel suivant à la fonction commence la recherche depuis l'indice contenu dans `regexp.lastIndex`. La correspondance suivante est renvoyé et l'indice positionné après est sauvegardé dans `regexp.lastIndex`.\n- ...Et ainsi de suite.\n- Si aucune correspondance n'est trouvée, `regexp.exec`renvoie `null` et `regexp.lastIndex` est réinitialisé à `0`.\n\nDonc, un appel répété à cette fonction renvoie toutes les correspondances l'une après l'autre, utilisant la propriété `regexp.lastIndex` pour se souvenir de l'indice courant à partir duquel la recherche est effectuée.\n\nAvant que la méthode `str.matchAll` ait été ajoutée à JavaScript, des appels à `regexp.exec` étaient utilisés dans une boucle afin d'obtenir toutes les correspondances:\n\n```js run\nlet str = 'More about JavaScript at https://javascript.info';\nlet regexp = /javascript/ig;\n\nlet result;\n\nwhile (result = regexp.exec(str)) {\n  alert( `Found ${result[0]} at position ${result.index}` );\n  // Found JavaScript at position 11, puis\n  // Found javascript at position 33\n}\n```\n\nCela fonctionne également très bien, bien que sur les navigateurs les plus récents `str.matchAll` est généralement plus pratique.\n\n**Nous pouvons utiliser `regexp.exec` pour rechercher à partir d'un indice donné en réglant manuellement la valeur de `lastIndex`.**\n\nPar exemple:\n\n```js run\nlet str = 'Hello, world!';\n\nlet regexp = /\\w+/g; // sans le marqueur \"g\", la propriété lastIndex est ignorée\nregexp.lastIndex = 5; // commence la recherche à partir de la 5ème position (à partir de la virgule)\n\nalert( regexp.exec(str) ); // world\n```\n\nSi la regexp utilise le marqueur `pattern:y`, alors la recherche s'effectuera à l'indice précis de `regexp.lastIndex`, pas plus loin.\n\nRemplaçons le marqueur `pattern:g` par `pattern:y` dans l'exemple précédent. Aucune correspondance n'est trouvée, car il n'y a aucun mot à l'indice `5`:\n\n```js run\nlet str = 'Hello, world!';\n\nlet regexp = /\\w+/y;\nregexp.lastIndex = 5; // cherche exactement à l'indice 5\n\nalert( regexp.exec(str) ); // null\n```\n\nC'est pratique dans une situation où nous cherchons uniquement à lire quelque chose au sein d'un texte avec une regexp à un indice spécifique, en occultant le reste.\n\n## regexp.test(str)\n\nLa méthode `regexp.test(str)` vérifie qu'une correspondance existe et renvoie `true/false` selon le cas.\n\nPar exemple:\n\n```js run\nlet str = \"I love JavaScript\";\n\n// Ces deux tests réalisent exactement la même chose\nalert( *!*/love/i*/!*.test(str) ); // true\nalert( str.search(*!*/love/i*/!*) != -1 ); // true\n```\n\nUn exemple avec un retour négatif:\n\n```js run\nlet str = \"Bla-bla-bla\";\n\nalert( *!*/love/i*/!*.test(str) ); // false\nalert( str.search(*!*/love/i*/!*) != -1 ); // false\n```\n\nSi la regexp à le marqueur `pattern:g`, alors `regexp.test` verifiera la propriété `regexp.lastIndex` et mettra à jours cette propriété, tout comme `regexp.exec`.\n\nOn peut donc l'utiliser pour effectuer une recherche à partir d'un indice donnée:\n\n```js run\nlet regexp = /love/gi;\n\nlet str = \"I love JavaScript\";\n\n// commence la recherche à partir de l'indice 10:\nregexp.lastIndex = 10;\nalert( regexp.test(str) ); // false (pas de correspondance)\n```\n\n````warn header=\"Une même expression rationnelle testée de manière répétée sur différentes sources peut échouer\"\nAppliquer la même expression rationnelle globale sur différentes entrées peut conduire à de mauvais résultats, car l'appel à `regexp.test` modifie la propriété `regexp.lastIndex`, par conséquent la recherche sur une autre chaîne de caractères risque d'être lancer à partir d'un autre indice que `0`.\n\nPar exemple, nous appelons ici `regexp.test` à deux reprises sur la même chaîne de texte, and le second appel échoue:\n\n```js run\nlet regexp = /javascript/g;  // (création d'une nouvelle regexp: regexp.lastIndex=0)\n\nalert( regexp.test(\"javascript\") ); // true (maintenant regexp.lastIndex=10)\nalert( regexp.test(\"javascript\") ); // false\n```\n\nC'est exactement parce que `regexp.lastIndex` n'est pas `0` lors du second test.\n\nAfin de contourner cela, nous pouvons réinitialiser `regexp.lastIndex = 0` avant chaque recherche. Ou, au lieu d'appeler la méthode sur une regexp, nous pouvons utiliser les méthodes de l'objet String `str.match/search/...`, qui n'utilisent pas `lastIndex`.\n````\n"
  },
  {
    "path": "9-regular-expressions/index.md",
    "content": "# Expressions régulières\n\nLes expression régulières sont un moyen puissant de rechercher et de remplacer dans les strings."
  },
  {
    "path": "AUTHORING.md",
    "content": "\n# Authoring\n\nThis describes important stuff about authoring new articles of the tutorial.\n\n## Internal links\n\nAll tutorial links should start from the root, not including the domain.\n\n✅ OK:\n\n```md\nWe'll cover that in the chapter [about functions](/function-basics)\n```\n\n❌ Not ok:\n\n```md\nWe'll cover that in the chapter [about functions](https://javascript.info/function-basics)\n```\n\nAlso, to reference a chapter, there's a special \"info:\" scheme, like this:\n\n```md\nWe'll cover that in the chapter <info:function-basics>.\n```\n\nBecomes:\n```html\nWe'll cover that in the chapter <a href=\"/function-basics\">Function basics</a>.\n```\n\nThe title is auto-inserted from the referenced article. That has the benefit of keeping the right title if the article gets renamed.\n\n## TODO\n\nAsk @iliakan to for more details.\n"
  },
  {
    "path": "LICENSE.md",
    "content": "\nThe tutorial is free to read.\n\nIf you'd like to do something else with it, please get a permission from Ilya Kantor, iliakan@javascript.info.\n\nAs of now, we license the tutorial to almost everyone for free under the terms of an open license. Just please be so kind to contact me.\n\n## License (Short)\n\nThe license is basically [CC-BY-NC](https://creativecommons.org/licenses/by-nc/4.0/legalcode), revocable and exclusive.\n\nIt gives the right to:\n- **Share** – copy and redistribute the tutorial in any medium or material.\n- **Adapt** – remix, transform, and build upon the material.\n\nUnder the following terms:\n\n- **Attribution** — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.\n- **NonCommercial** — You may not use the material for commercial purposes.\n\n## License (Legal)\n\nBy exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this license (\"Public License\"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.\n\n### Section 1 – Definitions.\n\na. __Adapted Material__ means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.\n\nb. __Adapter's License__ means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.\n\nc. __Copyright and Similar Rights__ means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.\n\nd. __Effective Technological Measures__ means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.\n\ne. __Exceptions and Limitations__ means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.\n\nf. __Licensed Material__ means the artistic or literary work, database, or other material to which the Licensor applied this Public License.\n\ng. __Licensed Rights__ means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.\n\nh. __Licensor__ means the individual(s) or entity(ies) granting rights under this Public License.\n\ni. __NonCommercial__ means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.\n\nj. __Share__ means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.\n\nk. __Sui Generis Database Rights__ means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.\n\nl. __You__ means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.\n\n### Section 2 – Scope.\n\na. ___License grant.___\n\n   1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:\n\n       A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and\n\n       B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only.\n\n   2. __Exceptions and Limitations.__ For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.\n\n   3. __Term.__ The term of this Public License is specified in Section 6(a).\n\n   4. __Media and formats; technical modifications allowed.__ The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.\n\n   5. __Downstream recipients.__\n\n        A. __Offer from the Licensor – Licensed Material.__ Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.\n\n        B. __No downstream restrictions.__ You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.\n\n   6. __No endorsement.__ Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).\n\nb. ___Other rights.___\n\n   1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.\n\n   2. Patent and trademark rights are not licensed under this Public License.\n\n   3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.\n\n### Section 3 – License Conditions.\n\nYour exercise of the Licensed Rights is expressly made subject to the following conditions.\n\na. ___Attribution.___\n\n   1. If You Share the Licensed Material (including in modified form), You must:\n\n       A. retain the following if it is supplied by the Licensor with the Licensed Material:\n\n         i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);\n\n         ii. a copyright notice;\n\n         iii. a notice that refers to this Public License;\n\n         iv. a notice that refers to the disclaimer of warranties;\n\n         v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;\n\n       B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and\n\n       C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.\n\n   2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.\n\n   3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.\n\n   4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License.\n\n### Section 4 – Sui Generis Database Rights.\n\nWhere the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:\n\na. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only;\n\nb. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and\n\nc. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.\n\nFor the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.\n\n### Section 5 – Disclaimer of Warranties and Limitation of Liability.\n\na. __Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.__\n\nb. __To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.__\n\nc. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.\n\n### Section 6 – Term and Termination.\n\na. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.\n\nb. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:\n\n   1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or\n\n   2. upon express reinstatement by the Licensor.\n\n   For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.\n\nc. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.\n\nd. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.\n\n### Section 7 – Other Terms and Conditions.\n\na. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.\n\nb. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.\n\n### Section 8 – Interpretation.\n\na. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.\n\nb. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.\n\nc. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.\n\nd. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.\n"
  },
  {
    "path": "README.md",
    "content": "# Le tutoriel JavaScript moderne\n\nCe dépôt héberge le contenu français du tutoriel JavaScript moderne, publié à l'adresse [https://fr.javascript.info](https://fr.javascript.info).\n\nAidez-nous à améliorer la traduction.\n\n- Consultez l'[issue](https://github.com/javascript-tutorial/fr.javascript.info/issues/3).\n- Choisissez un article non coché que vous souhaitez traduire.\n- Répondez à l'issue avec uniquement le titre sans aucun autre ajout pour informer le bot et le mainteneur que vous le traduisez.\n- Forkez le repository, traduisez et envoyez un PR (pull requests) lorsque vous avez terminé.\n\n🎉 Merci !\n\nVotre nom et la taille de la contribution apparaîtront dans la page \"À propos du projet\" lorsque la traduction sera publiée.\n\nP.S. La liste complète des langues est disponible sur <https://javascript.info/translate>.\n\n## Structure\n\nChaque chapitre, article ou exercice réside dans son propre dossier.\n\nLe dossier est nommé `N-url`, où `N` – est le numéro de tri (les articles sont ordonnés), et `url` est l'URL raccourcie (slug) de la partie en question.\n\nN'hésitez pas à consulter <https://javascript.info/translate> pour plus de détails.\n\n## Contributions\n\nNous aimerions également collaborer dans ce tutoriel avec d'autres personnes.\n\nQuelque chose ne va pas ? Un sujet est manquant ? Expliquez-le nous, en ajoutant un PR 👏\n\n**Vous pouvez éditer le texte dans n'importe quel éditeur** Le tutoriel utilise un format amélioré de \"markdown\", facile à comprendre. Et si vous voulez voir à quoi ça ressemble sur le site, il y a un serveur pour exécuter le tutoriel localement à <https://github.com/javascript-tutorial/server>.\n\nLa liste des contributeurs est disponible à <https://javascript.info/about#contributors>.\n\nLe dossier contient l'un des fichiers suivants :\n\n- `index.md` pour une section,\n- `article.md` pour un article,\n- `task.md` pour un exercice (+`solution.md` avec le texte de la solution).\n\nUn fichier commence par le `# Titre principal`, et ensuite le texte au format Markdown, éditable dans un simple éditeur de texte.\n\nDes ressources supplémentaires et des exemples pour l'article ou l'exercice se trouvent également dans le même dossier.\n\n## Conseils de traduction\n\nLa traduction ne doit pas nécessairement être ultra précise (mot par mot). Elle devrait être techniquement correcte et bien expliquée.\n\nSi vous voyez que la version anglaise peut être améliorée, merci d'envoyer un PR sur le repo correspondant.\n\n### Texte dans les blocs de code\n\n- Traduire uniquement les commentaires.\n- Ne traduisez rien d'autre -- strings, variables.\n\n\nExemple:\n\n```js\n// Example\nconst text = \"Hello, world\";\ndocument.querySelector('.hello').innerHTML = text;\n```\n\n✅ DO (translate comment):\n\n```js\n// Ejemplo\nconst text = 'Hello, world';\ndocument.querySelector('.hello').innerHTML = text;\n```\n\n❌ DON'T (translate string or class):\n\n```js\n// Ejemplo\nconst text = 'Hola mundo';\n// \".hello\" is a class\n// DO NOT TRANSLATE\ndocument.querySelector('.hola').innerHTML = text;\n```\n\n### Liens externes\n\nSi un lien externe est vers Wikipedia, par exemple `https://en.wikipedia.org/wiki/JavaScript`, et qu'une version de cet article existe dans votre langue et qu'elle est de bonne qualité, créez un lien vers cette version.\n\nExemple:\n\n```md\n[JavaScript](https://en.wikipedia.org/wiki/JavaScript) is a programming language.\n```\n\n✅ OK (en -> es):\n\n```md\n[JavaScript](https://es.wikipedia.org/wiki/JavaScript) es un lenguaje de programación.\n```\n\nPour les liens vers MDN, qui ne sont que partiellement traduits, utilisez également la version spécifique à la langue.\n\nSi un article lié n'a pas de version traduite, laissez le lien \"tel quel\".\n\n\n## Exécuter une version locale\n\n  - `index.md` représente un chapitre\n  - `article.md` représente un article\n  - `task.md` représente un exercice (la solution doit également être fournie dans le fichier `solution.md`)\n\nLe serveur est disponible à cette adresse : <https://github.com/javascript-tutorial/server>.\nChacun de ces fichiers commence à partir du `# titre principal`.\n\nIl est très facile d'ajouter quelque chose de nouveau.\n\n---\n♥\nIlya Kantor @iliakan\n"
  },
  {
    "path": "css.md",
    "content": "\n# CSS for JS developers\n\n- Outline\n"
  },
  {
    "path": "property-accessors.md",
    "content": "# Propriété opérateurs de lecture et d'écriture\r\n\r\nIl existe deux types de propriétés d'objet.\r\n\r\nLe premier désigne *les propriétés de données*. Nous savons déjà comment les utiliser. Toutes les propriétés que nous avons utilisées jusqu'à maintenant étaient des propriétés de données.\r\n\r\nLe second type de propriétés est quelque chose de nouveau. Ce sont les *propriétés des accesseurs*. Ce sont essentiellement des fonctions qui s'exécutent lors de la lecture d'une valeur ou son écriture, mais elles ressemblent à des propriétés classiques pour un code externe.\r\n\r\n## Les opérateurs de lecture et d'écriture\r\n\r\nLes propriétés des accesseurs sont représentées par les méthodes \"lecture\" et \"écriture\". Dans un objet littéral ils sont notés comme `get` et `set`:\r\n\r\n```js\r\nlet obj = {\r\n  *!*get propName()*/!* {\r\n    //  opérateur de lecture, code exécuté en accédant à obj.propName\r\n  },\r\n\r\n  *!*set propName(value)*/!* {\r\n    // opérateur d'écriture, code exécuté en assignant obj.propName = value\r\n  }\r\n};\r\n```\r\n\r\nL'opérateur de lecture fonctionne lors de la lecture de `obj.propName`, l'opérateur d'écriture -- lors de son affectation.\r\n\r\nPar exemple, nous avons un objet `user` avec `name` et `surname`:\r\n\r\n```js\r\nlet user = {\r\n  name: \"John\",\r\n  surname: \"Smith\"\r\n};\r\n```\r\n\r\nMaintenant nous voulons ajouter une propriété `fullName`, qui serait`\"John Smith\"`. Bien sûr, nous ne voulons pas copier-coller l'information existante, aussi nous pouvons la créer comme accesseur:\r\n\r\n```js run\r\nlet user = {\r\n  name: \"John\",\r\n  surname: \"Smith\",\r\n\r\n*!*\r\n  get fullName() {\r\n    return `${this.name} ${this.surname}`;\r\n  }\r\n*/!*\r\n};\r\n\r\n*!*\r\nalert(user.fullName); // John Smith\r\n*/!*\r\n```\r\n\r\nDe l'extérieur, une propriété d'accesseur ressemble à une propriété classique. C'est l'idée avec les propriétés des accesseurs. Nous n'utilisons pas *call* pour appeler `user.fullName` comme une fonction, nous la lisons *read* normalement: l'accesseur s'exécute en coulisses.\r\n\r\nA partir de maintenant, `fullName` a juste un accesseur. Si nous tentons d'affecter `user.fullName=`, cela provoquera une erreur:\r\n\r\n```js run\r\nlet user = {\r\n  get fullName() {\r\n    return `...`;\r\n  }\r\n};\r\n\r\n*!*\r\nuser.fullName = \"Test\"; // Error (property has only a getter)\r\n*/!*\r\n```\r\n\r\nCorrigeons en ajoutant un opérateur d'écriture pour `user.fullName`:\r\n\r\n```js run\r\nlet user = {\r\n  name: \"John\",\r\n  surname: \"Smith\",\r\n\r\n  get fullName() {\r\n    return `${this.name} ${this.surname}`;\r\n  },\r\n\r\n*!*\r\n  set fullName(value) {\r\n    [this.name, this.surname] = value.split(\" \");\r\n  }\r\n*/!*\r\n};\r\n\r\n// set fullName est exécuté avec la valeur donnée.\r\nuser.fullName = \"Alice Cooper\";\r\n\r\nalert(user.name); // Alice\r\nalert(user.surname); // Cooper\r\n```\r\n\r\nLe résultat obtenu est une propriété \"virtuelle\" `fullName`. Elle est lisible et inscriptible.\r\n\r\n## Descripteurs de l'accesseur\r\n\r\nLes descripteurs pour les propriétés de l'accesseur sont différents de ceux concernant les propriétés de données.\r\n\r\nPour les propriétés de l'accesseur, il n'y a pas de valeur `value` ou inscriptible `writable`, mais à la place il y a les fonctions `get` et `set`.\r\n\r\nAinsi, un descripteur d'accesseur peut avoir:\r\n\r\n- **`get`** -- une fonction sans arguments, qui s'exécute quand une propriété est lue,\r\n- **`set`** -- une fonction avec un argument, qui est appelée quand la propriété est écrite,\r\n- **`enumerable`** -- identique aux propriétés de données,\r\n- **`configurable`** -- identique aux propriétés de données.\r\n\r\nPar exemple, pour créer un accesseur `fullName` avec `defineProperty`, nous pouvons passer un descripteur avec `get` et `set`:\r\n\r\n```js run\r\nlet user = {\r\n  name: \"John\",\r\n  surname: \"Smith\"\r\n};\r\n\r\n*!*\r\nObject.defineProperty(user, 'fullName', {\r\n  get() {\r\n    return `${this.name} ${this.surname}`;\r\n  },\r\n\r\n  set(value) {\r\n    [this.name, this.surname] = value.split(\" \");\r\n  }\r\n*/!*\r\n});\r\n\r\nalert(user.fullName); // John Smith\r\n\r\nfor(let key in user) alert(key); // prénom, nom\r\n```\r\n\r\nVeuillez noter qu'une propriété peut être soit un accesseur (a les méthodes `get/set`) soit une propriété de donnée (a une valeur `value`), pas les deux ensemble.\r\n\r\nSi nous essayons d'utiliser à la fois `get` et `value` dans le même descripteur, cela produira une erreur:\r\n\r\n```js run\r\n*!*\r\n// Error: Invalid property descriptor.\r\n*/!*\r\nObject.defineProperty({}, 'prop', {\r\n  get() {\r\n    return 1\r\n  },\r\n\r\n  value: 2\r\n});\r\n```\r\n\r\n## Des opérateurs de lecture/écriture plus intelligents\r\n\r\nLes opérateurs de lecture/écriture peuvent être utilisés comme des fonctions wrapper (pour appeler une ou d'autres fonctions) à la place de valeurs \"réelles\" d'une propriété en vue d'augmenter le contrôle des opérations.\r\n\r\nPar exemple, si nous voulons interdire les noms trop courts pour `user`, nous pouvons avoir un opérateur d'écriture `name` et garder la valeur dans une propriété séparée `_name`:\r\n\r\n```js run\r\nlet user = {\r\n  get name() {\r\n    return this._name;\r\n  },\r\n\r\n  set name(value) {\r\n    if (value.length < 4) {\r\n      alert(\"Name is too short, need at least 4 characters\");\r\n      return;\r\n    }\r\n    this._name = value;\r\n  }\r\n};\r\n\r\nuser.name = \"Pete\";\r\nalert(user.name); // Pete\r\n\r\nuser.name = \"\"; // Le nom est trop court...\r\n```\r\n\r\nAinsi, le nom est stocké dans la propriété `_name`, et l'accès s'effectue via les opérateurs de lecture et d'écriture.\r\n\r\nTechniquement, le code externe est capable d'accéder au nom directement en utilisant `user._name`. Mais d'après une convention largement reconnue, les propriétés commençant avec un underscore `\"_\"` sont internes et ne devraient pas être accessibles hors de l'objet.\r\n\r\n\r\n## Utilisation pour la compatibilité\r\n\r\nUne des utilisations géniales des accesseurs est qu'ils permettent de prendre le contrôle d'une propriété de données \"classique\" à tout moment en la remplaçant par un opérateur de lecture et d'écriture, et de modifier son comportement.\r\n\r\nImaginez que nous commencions à mettre en oeuvre des objets utilisateurs utilisant les propriétés de données `name` et `age`:\r\n\r\n```js\r\nfunction User(name, age) {\r\n  this.name = name;\r\n  this.age = age;\r\n}\r\n\r\nlet john = new User(\"John\", 25);\r\n\r\nalert( john.age ); // 25\r\n```\r\n\r\n...Mais tôt ou tard, les choses peuvent changer. Au lieu de `age` nous pouvons décider de stocker `birthday`, parce que c'est plus précis et commode:\r\n\r\n```js\r\nfunction User(name, birthday) {\r\n  this.name = name;\r\n  this.birthday = birthday;\r\n}\r\n\r\nlet john = new User(\"John\", new Date(1992, 6, 1));\r\n```\r\n\r\nMaintenant, que faire avec l'ancien code qui utilise encore la propriété `age`?\r\n\r\nNous pouvons essayer de trouver toutes les occurrences et les corriger mais cela prend du temps et peut être compliqué si ce code est utilisé par beaucoup d'autres personnes. Et en plus, `age` est une bonne chose à avoir dans `user`, n'est-ce pas?\r\n\r\nGardons-le.\r\n\r\nL'ajout d'un opérateur de lecture pour `age` résout le problème:\r\n\r\n```js run no-beautify\r\nfunction User(name, birthday) {\r\n  this.name = name;\r\n  this.birthday = birthday;\r\n\r\n*!*\r\n  // l'âge est calculé à partir de la date actuelle et de l'anniversaire\r\n  Object.defineProperty(this, \"age\", {\r\n    get() {\r\n      let todayYear = new Date().getFullYear();\r\n      return todayYear - this.birthday.getFullYear();\r\n    }\r\n  });\r\n*/!*\r\n}\r\n\r\nlet john = new User(\"John\", new Date(1992, 6, 1));\r\n\r\nalert( john.birthday ); // anniversaire est disponible\r\nalert( john.age );      // ...ainsi que l'âge\r\n```\r\n\r\nMaintenant l'ancien code fonctionne aussi et nous avons une chouette propriété supplémentaire.\r\n\r\n"
  },
  {
    "path": "script/clean-unused-png.php",
    "content": "<?php\n$files = trim(`find . -name *.png`);\n$files = explode(\"\\n\", $files);\n\n$root = getcwd();\n\necho $root;\nforeach($files as $file) {\n  $file = trim(substr($file, 1));\n  if (strstr($file, '@2x')) continue;\n\n  $filename = basename($file);\n  $dir = $root . dirname($file);\n  chdir($dir);\n  $result = `grep $filename *`;\n\n  if (!$result) {\n    echo $dir, \"\\n\", $filename;\n\n    exit;\n  }\n}\n"
  },
  {
    "path": "todo.md",
    "content": "\n- Drag events\n- History api\n- Pointer events\n- Touch events\n- Canvas (2d graphics)\n- Security (xsrf xss csp etc)\n"
  }
]