├── .gitignore ├── index.html ├── scripts ├── config.js ├── main.js ├── popup.js └── script.js └── style └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |

AzerType

22 |

L'application pour écrire plus vite !

23 |

AzerType est une application en ligne pour apprendre à taper plus vite au clavier :)

24 |
25 | 26 | 27 |
28 |
29 |

Choisissez votre option et tapez la proposition qui s'affiche dans le champ en-dessous.

30 |
31 | 32 | 33 | 34 | 35 |
36 |
37 | 38 |
39 | Azerty 40 |
41 | 42 |
43 | 44 | 45 |
46 | 47 |
48 | Votre score : 0 49 |
50 | 51 |
52 | 53 |
54 | 55 |
56 | 57 | 58 | 61 | 62 | 63 |
64 | 74 |
75 | 76 | -------------------------------------------------------------------------------- /scripts/config.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************* 2 | * 3 | * Ce fichier contient toutes les constantes nécessaires au fonctionnement du jeu. 4 | * En particulier les listes de mots et de phrases proposés à l'utilisateur 5 | * 6 | *********************************************************************************/ 7 | 8 | // Déclaration des tableaux contenant les listes des mots proposés à l'utilisateur 9 | const listeMots = ["Cachalot", "Pétunia", "Serviette"] 10 | const listePhrases = ["Pas de panique !", "La vie, l'univers et le reste", "Merci pour le poisson"] -------------------------------------------------------------------------------- /scripts/main.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************* 2 | * 3 | * Point d'entrée, c'est lui qui intialise le jeu et lance la boucle de jeu. 4 | * 5 | *********************************************************************************/ 6 | 7 | lancerJeu() -------------------------------------------------------------------------------- /scripts/popup.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************* 2 | * 3 | * Ce fichier contient toutes les fonctions nécessaires à l'affichage et à la 4 | * fermeture de la popup de partage. 5 | * 6 | *********************************************************************************/ 7 | 8 | 9 | /** 10 | * Cette fonction affiche la popup pour partager son score. 11 | */ 12 | function afficherPopup() { 13 | let popupBackground = document.querySelector(".popupBackground") 14 | // La popup est masquée par défaut (display:none), ajouter la classe "active" 15 | // va changer son display et la rendre visible. 16 | popupBackground.classList.add("active") 17 | } 18 | 19 | /** 20 | * Cette fonction cache la popup pour partager son score. 21 | */ 22 | function cacherPopup() { 23 | let popupBackground = document.querySelector(".popupBackground") 24 | // La popup est masquée par défaut (display:none), supprimer la classe "active" 25 | // va rétablir cet affichage par défaut. 26 | popupBackground.classList.remove("active") 27 | } 28 | 29 | 30 | /** 31 | * Cette fonction initialise les écouteurs d'événements qui concernent 32 | * l'affichage de la popup. 33 | */ 34 | function initAddEventListenerPopup() { 35 | // On écoute le click sur le bouton "partager" 36 | btnPartage = document.querySelector(".zonePartage button") 37 | let popupBackground = document.querySelector(".popupBackground") 38 | btnPartage.addEventListener("click", () => { 39 | // Quand on a cliqué sur le bouton partagé, on affiche la popup 40 | afficherPopup() 41 | }) 42 | 43 | // On écoute le click sur la div "popupBackground" 44 | popupBackground.addEventListener("click", (event) => { 45 | // Si on a cliqué précisément sur la popupBackground 46 | // (et pas un autre élément qui se trouve dedant) 47 | if (event.target === popupBackground) { 48 | // Alors on cache la popup 49 | cacherPopup() 50 | } 51 | }) 52 | } -------------------------------------------------------------------------------- /scripts/script.js: -------------------------------------------------------------------------------- 1 | /********************************************************************************* 2 | * 3 | * Ce fichier contient toutes les fonctions nécessaires au fonctionnement du jeu. 4 | * 5 | *********************************************************************************/ 6 | 7 | /** 8 | * Cette fonction affiche dans la console le score de l'utilisateur 9 | * @param {number} score : le score de l'utilisateur 10 | * @param {number} nbMotsProposes : le nombre de mots proposés à l'utilisateur 11 | */ 12 | function afficherResultat(score, nbMotsProposes) { 13 | // Récupération de la zone dans laquelle on va écrire le score 14 | let spanScore = document.querySelector(".zoneScore span") 15 | // Ecriture du texte 16 | let affichageScore = `${score} / ${nbMotsProposes}` 17 | // On place le texte à l'intérieur du span. 18 | spanScore.innerText = affichageScore 19 | } 20 | 21 | /** 22 | * Cette fonction affiche une proposition, que le joueur devra recopier, 23 | * dans la zone "zoneProposition" 24 | * @param {string} proposition : la proposition à afficher 25 | */ 26 | function afficherProposition(proposition) { 27 | let zoneProposition = document.querySelector(".zoneProposition") 28 | zoneProposition.innerText = proposition 29 | } 30 | 31 | /** 32 | * Cette fonction construit et affiche l'email. 33 | * @param {string} nom : le nom du joueur 34 | * @param {string} email : l'email de la personne avec qui il veut partager son score 35 | * @param {string} score : le score. 36 | */ 37 | function afficherEmail(nom, email, score) { 38 | let mailto = `mailto:${email}?subject=Partage du score Azertype&body=Salut, je suis ${nom} et je viens de réaliser le score ${score} sur le site d'Azertype !` 39 | location.href = mailto 40 | } 41 | 42 | /** 43 | * Cette fonction prend un nom en paramètre et valide qu'il est au bon format 44 | * ici : deux caractères au minimum 45 | * @param {string} nom 46 | * @throws {Error} 47 | */ 48 | function validerNom(nom) { 49 | if (nom.length < 2) { 50 | throw new Error("Le nom est trop court. ") 51 | } 52 | 53 | } 54 | 55 | /** 56 | * Cette fonction prend un email en paramètre et valide qu'il est au bon format. 57 | * @param {string} email 58 | * @throws {Error} 59 | */ 60 | function validerEmail(email) { 61 | let emailRegExp = new RegExp("[a-z0-9._-]+@[a-z0-9._-]+\\.[a-z0-9._-]+") 62 | if (!emailRegExp.test(email)) { 63 | throw new Error("L'email n'est pas valide.") 64 | } 65 | 66 | } 67 | 68 | /** 69 | * Cette fonction affiche le message d'erreur passé en paramètre. 70 | * Si le span existe déjà, alors il est réutilisé pour ne pas multiplier 71 | * les messages d'erreurs. 72 | * @param {string} message 73 | */ 74 | function afficherMessageErreur(message) { 75 | 76 | let spanErreurMessage = document.getElementById("erreurMessage") 77 | 78 | if (!spanErreurMessage) { 79 | let popup = document.querySelector(".popup") 80 | spanErreurMessage = document.createElement("span") 81 | spanErreurMessage.id = "erreurMessage" 82 | 83 | popup.append(spanErreurMessage) 84 | } 85 | 86 | spanErreurMessage.innerText = message 87 | } 88 | 89 | /** 90 | * Cette fonction permet de récupérer les informations dans le formulaire 91 | * de la popup de partage et d'appeler l'affichage de l'email avec les bons paramètres. 92 | * @param {string} scoreEmail 93 | */ 94 | function gererFormulaire(scoreEmail) { 95 | try { 96 | let baliseNom = document.getElementById("nom") 97 | let nom = baliseNom.value 98 | validerNom(nom) 99 | 100 | let baliseEmail = document.getElementById("email") 101 | let email = baliseEmail.value 102 | validerEmail(email) 103 | afficherMessageErreur("") 104 | afficherEmail(nom, email, scoreEmail) 105 | 106 | } catch(erreur) { 107 | afficherMessageErreur(erreur.message) 108 | } 109 | 110 | } 111 | 112 | /** 113 | * Cette fonction lance le jeu. 114 | * Elle demande à l'utilisateur de choisir entre "mots" et "phrases" et lance la boucle de jeu correspondante 115 | */ 116 | function lancerJeu() { 117 | // Initialisations 118 | initAddEventListenerPopup() 119 | let score = 0 120 | let i = 0 121 | let listeProposition = listeMots 122 | 123 | let btnValiderMot = document.getElementById("btnValiderMot") 124 | let listeBtnRadio = document.querySelectorAll(".optionSource input") 125 | let inputEcriture = document.getElementById("inputEcriture") 126 | 127 | 128 | afficherProposition(listeProposition[i]) 129 | 130 | // Gestion de l'événement click sur le bouton "valider" 131 | btnValiderMot.addEventListener("click", () => { 132 | if (inputEcriture.value === listeProposition[i]) { 133 | score++ 134 | } 135 | i++ 136 | afficherResultat(score, i) 137 | inputEcriture.value = '' 138 | if (listeProposition[i] === undefined) { 139 | afficherProposition("Le jeu est fini") 140 | // On désactive le bouton valider 141 | btnValiderMot.disabled = true 142 | // On désactive les boutons radios 143 | for (let indexBtnRadio = 0; indexBtnRadio < listeBtnRadio.length; indexBtnRadio++) { 144 | listeBtnRadio[indexBtnRadio].disabled = true 145 | } 146 | 147 | } else { 148 | afficherProposition(listeProposition[i]) 149 | } 150 | }) 151 | 152 | // Gestion de l'événement change sur les boutons radios. 153 | 154 | for (let index = 0; index < listeBtnRadio.length; index++) { 155 | listeBtnRadio[index].addEventListener("change", (event) => { 156 | // Si c'est le premier élément qui a été modifié, alors nous voulons 157 | // jouer avec la listeMots. 158 | if (event.target.value === "1") { 159 | listeProposition = listeMots 160 | } else { 161 | // Sinon nous voulons jouer avec la liste des phrases 162 | listeProposition = listePhrases 163 | } 164 | // Et on modifie l'affichage en direct. 165 | afficherProposition(listeProposition[i]) 166 | }) 167 | } 168 | 169 | // Gestion de l'événement submit sur le formulaire de partage. 170 | let form = document.querySelector("form") 171 | form.addEventListener("submit", (event) => { 172 | event.preventDefault() 173 | let scoreEmail = `${score} / ${i}` 174 | gererFormulaire(scoreEmail) 175 | }) 176 | 177 | afficherResultat(score, i) 178 | } -------------------------------------------------------------------------------- /style/style.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Reset 3 | */ 4 | * { 5 | margin: 0; 6 | padding: 0; 7 | box-sizing: border-box; 8 | } 9 | 10 | /** 11 | * Eléments génériques 12 | */ 13 | 14 | body { 15 | --color-primary: #f76c5e; 16 | --color-secondary: #f68e5f; 17 | --color-tertiary: #ffeee6; 18 | } 19 | 20 | /* Mise en forme des titres */ 21 | h1 { 22 | margin: 10px; 23 | font-size: 60px; 24 | font-weight: 900; 25 | color: var(--color-primary); 26 | } 27 | 28 | h2 { 29 | margin: 10px; 30 | font-size: 30px; 31 | font-weight: 600; 32 | color: var(--color-secondary); 33 | } 34 | 35 | h3 { 36 | margin: 10px; 37 | font-size: 17px; 38 | font-weight: 500; 39 | color: var(--color-secondary); 40 | } 41 | 42 | /* Mise en forme des formulaires */ 43 | input[type='radio'] { 44 | filter: grayscale(1); /* Grayscale passe le bouton en niveau de gris */ 45 | } 46 | 47 | 48 | input[type='text'], 49 | input[type='email'] { 50 | width: 100%; 51 | margin-right: 10px; 52 | padding: 5px; 53 | border: none; 54 | border-radius: 8px; 55 | font-size: 17px; 56 | font-weight: 600; 57 | font-size: 17px; 58 | color: var(--color-primary); 59 | } 60 | 61 | input[type='text']::placeholder, 62 | input[type='email']::placeholder { 63 | color: var(--color-secondary); 64 | opacity: 50%; 65 | } 66 | 67 | button { 68 | padding: 5px; 69 | border: none; 70 | border-radius: 8px; 71 | font-family: 'Roboto', sans-serif; 72 | font-weight: 600; 73 | font-size: 17px; 74 | color: var(--color-secondary); 75 | background-color: white; 76 | } 77 | 78 | button:hover { 79 | color: var(--color-primary); 80 | background-color: var(--color-tertiary); 81 | cursor: pointer; 82 | } 83 | 84 | button:focus { 85 | background-color: var(--color-tertiary); 86 | border: 3px solid var(--color-primary); 87 | padding: 2px; 88 | 89 | } 90 | 91 | /** 92 | * Eléments principaux 93 | */ 94 | body { 95 | display: flex; 96 | flex-direction: column; 97 | justify-content: space-between; 98 | align-items: center; 99 | min-height: 100vh; 100 | font-family: 'Roboto', sans-serif; 101 | } 102 | 103 | header { 104 | display: flex; 105 | flex-direction: column; 106 | align-items: center; 107 | } 108 | 109 | main { 110 | display: flex; 111 | flex-direction: column; 112 | justify-content: space-between; 113 | align-items: center; 114 | width: 50%; 115 | min-width: 200px; 116 | margin-bottom: 50px; 117 | padding: 50px; 118 | text-align: center; 119 | color: white; 120 | font-size: 17px; 121 | font-weight: 500; 122 | border-radius: 30px; 123 | background-color: var(--color-secondary); 124 | } 125 | 126 | footer { 127 | position: sticky; 128 | bottom: 0; 129 | display: flex; 130 | justify-content: center; 131 | width: 100%; 132 | padding: 30px; 133 | font-weight: 600; 134 | font-size: 17px; 135 | color: white; 136 | background-color: var(--color-secondary); 137 | } 138 | 139 | /** 140 | * Mise en forme des éléments de la zone principale 141 | */ 142 | 143 | /* Zone d'options */ 144 | .optionSource { 145 | margin: 20px; 146 | } 147 | 148 | .optionSource input[type='radio']:not(:first-child) { 149 | margin-left: 20px; 150 | } 151 | 152 | /* Zone ou sera affiché le texte (mot ou phrase) à afficher */ 153 | .zoneProposition { 154 | display: flex; 155 | align-items: center; 156 | justify-content: center; 157 | width: 100%; 158 | margin: 10px 0 10px 0; 159 | padding: 20px; 160 | border-radius: 20px; 161 | background-color: white; 162 | font-size: 30px; 163 | font-weight: 600; 164 | color: var(--color-primary); 165 | } 166 | 167 | /* Zone de saisie */ 168 | .zoneSaisie { 169 | display: flex; 170 | justify-content: space-between; 171 | width: 100%; 172 | margin-bottom: 30px; 173 | } 174 | 175 | /* Eléments du formulaire de saisie */ 176 | .zoneSaisie input[type='text'] { 177 | width: 80%; 178 | } 179 | 180 | .zoneSaisie button { 181 | width: 20%; 182 | } 183 | 184 | .zoneScore { 185 | font-size: 20px; 186 | } 187 | 188 | .zonePartage { 189 | padding-top: 20px; 190 | } 191 | 192 | /** 193 | * Gestion des popups 194 | */ 195 | 196 | .popupBackground { 197 | display: none; 198 | position: fixed; 199 | height: 100vh; 200 | width: 100vw; 201 | background-color: rgba(255, 255, 255, 0.5); 202 | } 203 | 204 | .popup { 205 | position: absolute; 206 | top: 50%; 207 | left: 50%; 208 | transform: translate(-50%, -50%); 209 | flex-wrap: wrap; 210 | justify-content: center; 211 | align-items: center; 212 | width: 300px; 213 | padding: 20px; 214 | text-align: center; 215 | color: white; 216 | font-size: 17px; 217 | font-weight: 500; 218 | background-color: #f76c5e; 219 | border-radius: 30px; 220 | z-index: 1; 221 | } 222 | 223 | 224 | 225 | .active { 226 | display: flex !important; 227 | } 228 | 229 | .popup p { 230 | width: 100%; 231 | margin-bottom: 20px; 232 | } 233 | 234 | .popup div { 235 | font-size: 25px; 236 | padding : 5px; 237 | padding-bottom: 15px; 238 | } 239 | 240 | /* Eléments de formulaire */ 241 | .popup input { 242 | margin-bottom: 10px; 243 | } 244 | 245 | .popup button { 246 | margin: 5px; 247 | } 248 | 249 | /** 250 | * Responsive 251 | */ 252 | 253 | 254 | /* En dessous de 1200 px */ 255 | @media screen and (max-width: 1200px) { 256 | main { 257 | width: 60%; 258 | padding: 30px; 259 | } 260 | } 261 | 262 | 263 | /* En dessous de 800 px */ 264 | @media screen and (max-width: 800px) { 265 | main { 266 | width: 90%; 267 | padding: 10px; 268 | } 269 | } --------------------------------------------------------------------------------