├── .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 |
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 |
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 | }
--------------------------------------------------------------------------------