Compare commits

...

29 commits

Author SHA1 Message Date
JonathanMM 6de079bf0b Correction d'un problème avec le bouton partie de la veille 2023-06-13 22:13:37 +02:00
JonathanMM 0b7d37adcf Mise à jour du numéro de dernière version 2023-06-03 14:36:59 +02:00
JonathanMM 58db9c65ad Ajout des notes de version 2023-06-02 22:52:04 +02:00
JonathanMM 2fa99d2a2e Correction de la couleur de la barre max en mode thème accessible 2023-06-02 22:43:17 +02:00
JonathanMM 999d7ef326 Réglage de la hauteur de l'écran 2023-06-02 22:39:08 +02:00
JonathanMM ee29afd206 Affichage du bouton partie de la veille quand la partie précédente ne date pas de la veille 2023-06-02 22:34:07 +02:00
JonathanMM 7200280c17 Désactivation du user select sur le clavier 2023-06-02 22:32:09 +02:00
JonathanMM 4ec1bf5975 Correction d'une coquille dans les options 2023-06-02 22:15:07 +02:00
JonathanMM aa1bcacc77 Ajout des couleurs pour les OS sans emoji dans les stats 2023-06-02 22:13:47 +02:00
JonathanMM a4aef5903c Aucune mention des mots à 10 lettres 2023-05-19 23:04:21 +02:00
JonathanMM d65cd5f45d Correction d'un problème lors de l'export des stats 2023-05-19 22:56:37 +02:00
JonathanMM b9e4dc30aa Petits ajustements dans le design 2023-05-19 22:37:57 +02:00
JonathanMM 7273a61271 Ajout d'un écran pour afficher les nouveautés 2023-05-19 19:05:19 +02:00
JonathanMM e6c67a52f0 Ajout de la possibilité de jouer à la partie de la veille 2023-05-19 18:43:49 +02:00
JonathanMM b9b83c9b79 Légers ajustement d'affichages 2023-05-19 14:14:14 +02:00
JonathanMM 8b7e939490 Réparation des outils du passage aux mots à 10 lettres 2023-05-19 14:13:55 +02:00
JonathanMM cfa080c6ff Ajustement de l'affichage du jeu 2023-05-19 13:55:30 +02:00
JonathanMM 8d126b48a3 Ajustement de l'affichage des stats sur petits écrans 2023-05-19 13:53:03 +02:00
JonathanMM 92985f56d0 Ajout d'un résumé textuel pour partager ses stats 2023-05-19 13:48:44 +02:00
JonathanMM a168654292 Refonte de l'affichage des statistiques 2023-05-19 13:15:54 +02:00
JonathanMM 8bb4d7139d Correction dans les stats à l'initialisation 2023-05-19 12:17:23 +02:00
JonathanMM 965404a51c Ajout du retour haptique 2023-05-18 11:38:41 +02:00
JonathanMM d2f035b1ef Ajout option pour exporter les statistiques 2023-05-18 11:14:32 +02:00
JonathanMM 51e43bf38c Déplacement de la notif dans les panel 2023-05-17 23:20:58 +02:00
JonathanMM e4a6439acb Passage à des mots de 10 lettres 2023-05-17 22:59:32 +02:00
JonathanMM 5471bd935e Ajout d'une icône de copie à côté du bouton de partage 2023-05-17 22:47:35 +02:00
JonathanMM e452c26724 Premiers ajustements UI 2023-05-16 23:21:31 +02:00
JonathanMM 34e16f9799 Utilisation des icônes de material design 2023-05-10 23:17:20 +02:00
JonathanMM e15bbc53f5 Mise à jour des dépendances 2023-05-10 23:15:41 +02:00
31 changed files with 65450 additions and 424 deletions

View file

@ -13,6 +13,6 @@ Ce script va vérifier la liste des mots, ne garder que les mots acceptés dans
Liste des règles suivi par les mots :
- Le mot n'est pas un nom propre (qui commence par une majuscule dans le fichier mots.txt)
- Le mot est entre 6 et 9 lettres
- Le mot est entre 6 et 10 lettres
- Le mot ne commence pas par une lettre rare, à savoir : K, Q, W, X, Y, Z
- Le mot ne contient pas d'espace, d'apostrophe ou de trait d'union

1857
package-lock.json generated

File diff suppressed because it is too large Load diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -43,7 +43,7 @@
</a>
</div>
</header>
<div id="notification"> </div>
<div id="notification-area"><div id="notification-label"></div></div>
<div id="panel-area">
<div id="panel-fenetre">
<div id="panel-fenetre-header">
@ -56,7 +56,7 @@
</a>
</div>
</div>
<div id="panel-fenetre-notification"> </div>
<div id="panel-fenetre-notification-area"><div id="panel-fenetre-notification-label"></div></div>
<div id="panel-fenetre-contenu"></div>
</div>
</div>
@ -68,99 +68,42 @@
<audio preload src="sons/lettre-bien-place.wav" id="son-lettre-bien-place"></audio>
<svg>
<symbol id="icone-son-active" viewBox="0 0 32 32">
<symbol id="icone-son-active" height="24" viewBox="0 96 960 960" width="24">
<path
d="m11.152 6.0314-4.3386 3.19v13.555l1.4368 1.0571c0.78755 0.57894 2.74 2.0158 4.3386 3.1924 1.5962 1.1743 2.9111 2.1353 2.9228 2.1353 0.01172 0 0.0211-5.6043 0.0211-13.161 0-7.6739-0.0094-13.161-0.0211-13.161-0.01172 0-1.9736 1.4368-4.3597 3.1924z"
/>
<path
d="m25.524 4.3485c-0.32112 0.10548-0.50394 0.35862-0.50628 0.69379 0 0.24142 0.05626 0.35159 0.33518 0.64457 0.76411 0.80865 1.268 1.4345 1.7884 2.2267 1.2493 1.9009 2.0181 4.0128 2.2994 6.3051 0.07735 0.62348 0.0961 0.95866 0.0961 1.7814 0 1.5259-0.16642 2.7213-0.57426 4.137-0.66332 2.3017-1.8634 4.3503-3.6166 6.1809-0.27424 0.2883-0.32815 0.38909-0.32815 0.63988 0 0.21564 0.075 0.39612 0.22267 0.52738 0.15236 0.14064 0.28127 0.18751 0.49925 0.18517 0.26955-0.0024 0.3469-0.04688 0.70083-0.40315 2.0415-2.079 3.5229-4.8097 4.1628-7.6763 0.69614-3.1221 0.47112-6.3848-0.64457-9.3334-0.75474-1.99-1.8564-3.7502-3.3471-5.3488-0.34455-0.37034-0.46878-0.4805-0.6141-0.54378-0.10313-0.044534-0.35862-0.05391-0.47347-0.016407z"
/>
<path
d="m21.624 8.237c-0.31408 0.1172-0.49456 0.36565-0.49691 0.68442-0.0047 0.24611 0.06329 0.37034 0.38206 0.70786 1.3759 1.4509 2.2173 3.2416 2.4775 5.2691 0.06563 0.51097 0.0586 1.7626-0.01172 2.2759-0.27892 2.0251-1.0946 3.7409-2.4775 5.2081-0.30705 0.3258-0.37268 0.45237-0.37268 0.69614 0.0047 0.56957 0.63285 0.9024 1.1157 0.59301 0.12891-0.07969 0.60238-0.59066 0.92584-0.99616 1.1626-1.4556 1.9454-3.2416 2.2103-5.0581 0.09376-0.63754 0.10548-0.81568 0.10548-1.6173 0-0.80161-0.01172-0.97975-0.10548-1.6173-0.21798-1.4884-0.77114-2.9393-1.6103-4.2261-0.52504-0.80396-1.3477-1.765-1.6126-1.8822-0.14532-0.063286-0.40784-0.082037-0.52972-0.037502z"
/>
<path d="m1.0002 16v6.7856l2.0579-0.0047 2.0556-7e-3v-13.548l-2.0556-0.0070317-2.0579-0.0046878z" />
<path
d="m17.759 12.119c-0.42425 0.13126-0.64457 0.59769-0.46409 0.98912 0.03516 0.07969 0.13595 0.21564 0.25314 0.33987 0.64926 0.70317 0.98913 1.5821 0.98913 2.5525 0 1.0032-0.3469 1.8681-1.0407 2.6064-0.18751 0.19923-0.26252 0.36799-0.26252 0.58598 0.0023 0.38206 0.2883 0.67973 0.67739 0.70551 0.31877 0.01875 0.51566-0.11016 0.92115-0.60941 1.0454-1.2868 1.411-2.9978 0.98913-4.6269-0.21798-0.84615-0.68911-1.6759-1.3009-2.297-0.12657-0.12892-0.19454-0.17814-0.30002-0.21798-0.13829-0.04922-0.34455-0.06329-0.46175-0.02812z"
d="M560 925v-82q90-26 145-100t55-168q0-94-55-168T560 307v-82q124 28 202 125.5T840 575q0 127-78 224.5T560 925ZM120 696V456h160l200-200v640L280 696H120Zm440 40V414q47 22 73.5 66t26.5 96q0 51-26.5 94.5T560 736ZM400 450l-86 86H200v80h114l86 86V450ZM300 576Z"
/>
</symbol>
<symbol id="icone-son-desactive" viewBox="0 0 32 32">
<symbol id="icone-son-desactive" height="24" viewBox="0 96 960 960" width="24">
<path
d="m20.077 11.234a1.1719 1.1719 0 0 0-0.829 0.3438 1.1719 1.1719 0 0 0 0 1.6564l7.1895 7.1879a1.1719 1.1719 0 0 0 1.6564 0 1.1719 1.1719 0 0 0 0-1.6564l-7.1879-7.1879a1.1719 1.1719 0 0 0-0.829-0.3438z"
stroke-linecap="round"
stroke-width=".82258"
/>
<path
d="m27.265 11.234a1.1719 1.1719 0 0 0-0.8274 0.3438l-7.1895 7.1879a1.1719 1.1719 0 0 0 0 1.6564 1.1719 1.1719 0 0 0 1.658 0l7.1879-7.1879a1.1719 1.1719 0 0 0 0-1.6564 1.1719 1.1719 0 0 0-0.829-0.3438z"
stroke-linecap="round"
stroke-width=".82258"
/>
<path
d="m11.152 6.0312-4.3386 3.1901v13.555l1.4368 1.0571c0.78756 0.57895 2.7401 2.0158 4.3386 3.1924 1.5962 1.1743 2.9112 2.1353 2.9229 2.1353s0.0211-5.6043 0.0211-13.161c0-7.6741-0.0094-13.161-0.0211-13.161-0.01172 0-1.9736 1.4368-4.3597 3.1924z"
/>
<path d="m1 16v6.7857l2.058-0.0049 2.0556-0.0074v-13.548l-2.0556-0.0074-2.058-0.00494z" />
</symbol>
<symbol id="icone-croix" viewBox="0 0 8.4666665 8.4666669">
<path
style="
fill: none;
stroke-width: 0.529167;
stroke-linecap: round;
stroke-linejoin: miter;
stroke-miterlimit: 4;
stroke-dasharray: none;
stroke-opacity: 1;
"
d="M 1.6140919,1.6140919 6.8525747,6.8525747"
id="path1118"
/>
<path
style="
fill: none;
stroke-width: 0.529167;
stroke-linecap: round;
stroke-linejoin: miter;
stroke-miterlimit: 4;
stroke-dasharray: none;
stroke-opacity: 1;
"
d="M 6.8525747,1.6140919 1.6140919,6.8525747"
id="path1432"
d="M792 1000 671 879q-25 16-53 27.5T560 925v-82q14-5 27.5-10t25.5-12L480 688v208L280 696H120V456h128L56 264l56-56 736 736-56 56Zm-8-232-58-58q17-31 25.5-65t8.5-70q0-94-55-168T560 307v-82q124 28 202 125.5T840 575q0 53-14.5 102T784 768ZM650 634l-90-90V414q47 22 73.5 66t26.5 96q0 15-2.5 29.5T650 634ZM480 464 376 360l104-104v208Zm-80 238v-94l-72-72H200v80h114l86 86Zm-36-130Z"
/>
</symbol>
<symbol id="icone-stats" viewBox="0 0 32 32">
<symbol id="icone-croix" height="40" viewBox="0 96 960 960" width="40">
<path
d="m3.077 5.3688c-0.14233 0-0.25668 0.12077-0.25668 0.27108v23.379c0 0.14944 0.11435 0.27108 0.25668 0.27108h5.9999c0.14233 0 0.25668-0.12164 0.25668-0.27108v-23.379c0-0.15031-0.11435-0.27108-0.25668-0.27108z"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.7439"
fill="none"
d="m251.333 851.333-46.666-46.666L433.334 576 204.667 347.333l46.666-46.666L480 529.334l228.667-228.667 46.666 46.666L526.666 576l228.667 228.667-46.666 46.666L480 622.666 251.333 851.333Z"
/>
<path
d="m12.807 16.365c-0.14056 0-0.25349 0.12077-0.25349 0.27108v12.643c0 0.15031 0.11293 0.27108 0.25349 0.27108h5.9262c0.14056 0 0.25349-0.12077 0.25349-0.27108v-12.643c0-0.15031-0.11294-0.27108-0.25349-0.27108z"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.744"
fill="none"
/>
<path
d="m22.515 10.747c-0.14233 0-0.25668 0.12077-0.25668 0.27108v18.094c0 0.15031 0.11435 0.27108 0.25668 0.27108h6.0007c0.14232 0 0.25668-0.12077 0.25668-0.27108v-18.094c0-0.15031-0.11436-0.27108-0.25668-0.27108z"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.7439"
fill="none"
/>
<path d="m2.3991 2.3983v27.203h27.202" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.7903" />
</symbol>
<symbol id="icone-regles" viewBox="0 0 32 32">
<path
d="m13.787 23.247h2.9075v3.638h-2.9075zm2.8216-2.1055h-2.7357v-2.2057q0-1.4466 0.40104-2.3776 0.40104-0.93099 1.6901-2.1628l1.2891-1.2747q0.8164-0.75911 1.1745-1.4323 0.3724-0.67318 0.3724-1.375 0-1.2747-0.94531-2.0625-0.93099-0.78776-2.4779-0.78776-1.1315 0-2.4206 0.5013-1.2747 0.5013-2.6641 1.4609v-2.6927q1.3464-0.81641 2.7214-1.2174 1.3893-0.40104 2.8646-0.40104 2.6354 0 4.2253 1.3893 1.6042 1.3893 1.6042 3.6667 0 1.0885-0.51562 2.0768-0.51562 0.97396-1.8047 2.2057l-1.2604 1.2318q-0.67318 0.67318-0.95963 1.0599-0.27214 0.3724-0.38672 0.73047-0.08594 0.30078-0.12891 0.73047-0.04297 0.42969-0.04297 1.1745z"
/>
<circle cx="16" cy="16" r="14" fill="none" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" />
<symbol id="icone-stats" height="24" viewBox="0 96 960 960" width="24">
<path d="M160 896V456h160v440H160Zm240 0V256h160v640H400Zm240 0V616h160v280H640Z" />
</symbol>
<symbol id="icone-config" viewBox="0 0 32 32">
<symbol id="icone-regles" height="24" viewBox="0 96 960 960" width="24">
<path
d="m25.1 1.0009c-0.24859-4e-3 -0.49949 0.01-0.74873 0.0174-2.0188 0.0622-4.01 1.6525-4.1616 3.7439-0.50981 2.1512-0.87169 4.4107-1.974 6.354-3.7058 3.71-7.6856 7.1393-11.505 10.732-1.5478 1.48-3.1912 2.8734-4.6112 4.4788-1.1941 1.7248 0.0372 4.6853 2.237 4.6724 1.7332-0.0568 2.7416-1.7429 3.8664-2.8178 4.1892-4.5584 8.0333-9.4317 12.34-13.883 1.1377-0.93788 2.716-1.0269 4.0959-1.4049 0.92741-0.23075 1.9087-0.30646 2.8033-0.63139 1.6048-1.0192 2.7463-2.8492 2.9189-4.7437 0.057-0.45104-0.44752-0.30978-0.44752-0.30978l-2.78 1.2811-2.1238-1.7534-0.35649-1.9778s0.64592-0.63983 0.93495-0.69523l2.7286-1.018c-0.10848-1.1283-1.5342-1.6956-2.4817-1.9526-0.24051-0.0653-0.48684-0.0877-0.73543-0.0919zm-2.0729 7.6496 1.2424 2.9363-1.1244 0.45314-0.0242-0.0565 0.3074-0.12799-0.0588-0.1407 0.0644-0.0145 0.1478-0.0331-0.0662-0.2953-0.1475 0.0328-0.11675 0.026-0.14721-0.35234 0.063-0.0142 0.1478-0.0331-0.0659-0.2953-0.14779 0.0328-0.11558 0.0257-0.1472-0.35265 0.0621-0.0139 0.14779-0.0328-0.0662-0.29559-0.1475 0.0331-0.11439 0.0257-0.1472-0.35293 0.0606-0.0136 0.1478-0.0328-0.0659-0.29558-0.1478 0.0331-0.11321 0.0254-0.20514-0.49186c-0.21772 0.0877-0.22976 0.0949-0.3358 0.13893l-0.0254-0.0594c0.37973-0.15303 0.7595-0.30603 1.1392-0.45906zm-1.9817 0.79839 0.029 0.0674c-0.10243 0.0423-0.10517 0.0455-0.29885 0.12356l0.0987 0.23499-0.0511 0.0115-0.14779 0.0331 0.0662 0.29529 0.1475-0.0331 0.10434-0.0233 0.14839 0.35264-0.0517 0.0115-0.14779 0.0331 0.0659 0.2953 0.1478-0.0331 0.10493-0.0234 0.14809 0.35235-0.0523 0.0118-0.1478 0.0331 0.0662 0.2953 0.1475-0.0328 0.10582-0.0236 0.14809 0.35234-0.0529 0.0118-0.14779 0.0331 0.0659 0.2953 0.14779-0.0328 0.10641-0.0237 0.1676 0.39876 0.31894-0.13272 0.0201 0.0467-1.0538 0.42447-1.2418-2.9364c0.34615-0.13948 0.69225-0.27907 1.0384-0.41855zm-16.315 16.667a1.7456 1.7456 0 0 1 1.7454 1.7457 1.7456 1.7456 0 0 1-1.7454 1.7454 1.7456 1.7456 0 0 1-1.7454-1.7454 1.7456 1.7456 0 0 1 1.7454-1.7457z"
stroke-width=".15134"
d="M478 816q21 0 35.5-14.5T528 766q0-21-14.5-35.5T478 716q-21 0-35.5 14.5T428 766q0 21 14.5 35.5T478 816Zm-36-154h74q0-33 7.5-52t42.5-52q26-26 41-49.5t15-56.5q0-56-41-86t-97-30q-57 0-92.5 30T342 438l66 26q5-18 22.5-39t53.5-21q32 0 48 17.5t16 38.5q0 20-12 37.5T506 530q-44 39-54 59t-10 73Zm38 314q-83 0-156-31.5T197 859q-54-54-85.5-127T80 576q0-83 31.5-156T197 293q54-54 127-85.5T480 176q83 0 156 31.5T763 293q54 54 85.5 127T880 576q0 83-31.5 156T763 859q-54 54-127 85.5T480 976Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"
/>
</symbol>
<symbol id="icone-config" height="24" viewBox="0 96 960 960" width="24">
<path
d="m370 976-16-128q-13-5-24.5-12T307 821l-119 50L78 681l103-78q-1-7-1-13.5v-27q0-6.5 1-13.5L78 471l110-190 119 50q11-8 23-15t24-12l16-128h220l16 128q13 5 24.5 12t22.5 15l119-50 110 190-103 78q1 7 1 13.5v27q0 6.5-2 13.5l103 78-110 190-118-50q-11 8-23 15t-24 12l-16 128H370Zm112-260q58 0 99-41t41-99q0-58-41-99t-99-41q-59 0-99.5 41T342 576q0 58 40.5 99t99.5 41Zm0-80q-25 0-42.5-17.5T422 576q0-25 17.5-42.5T482 516q25 0 42.5 17.5T542 576q0 25-17.5 42.5T482 636Zm-2-60Zm-40 320h79l14-106q31-8 57.5-23.5T639 729l99 41 39-68-86-65q5-14 7-29.5t2-31.5q0-16-2-31.5t-7-29.5l86-65-39-68-99 42q-22-23-48.5-38.5T533 362l-13-106h-79l-14 106q-31 8-57.5 23.5T321 423l-99-41-39 68 86 64q-5 15-7 30t-2 32q0 16 2 31t7 30l-86 65 39 68 99-42q22 23 48.5 38.5T427 790l13 106Z"
/>
</symbol>
<symbol id="icone-copie" height="20" viewBox="0 96 960 960" width="20">
<path
d="M216 960q-29.7 0-50.85-21.15Q144 917.7 144 888V336h72v552h456v72H216Zm144-144q-29.7 0-50.85-21.15Q288 773.7 288 744V264q0-29.7 21.15-50.85Q330.3 192 360 192h384q29.7 0 50.85 21.15Q816 234.3 816 264v480q0 29.7-21.15 50.85Q773.7 816 744 816H360Zm0-72h384V264H360v480Zm0 0V264v480Z"
/>
</symbol>
<symbol id="icone-restaure" height="20" viewBox="0 96 960 960" width="20">
<path
d="M479.788 648Q450 648 429 626.788q-21-21.213-21-51Q408 546 429.212 525q21.213-21 51-21Q510 504 531 525.212q21 21.213 21 51Q552 606 530.788 627q-21.213 21-51 21ZM480 912q-140 0-238.5-98T144 576h72q2 110 78.5 187T480 840q110.314 0 187.157-76.778Q744 686.443 744 576.222 744 466 667.157 389 590.314 312 480 312q-59 0-111.5 25.5T277 408h107v72H144V240h72v130q47.909-62.09 116.955-96.045Q402 240 480 240q70 0 131.133 26.6 61.134 26.6 106.4 71.867 45.267 45.266 71.867 106.4Q816 506 816 576t-26.6 131.133q-26.6 61.134-71.867 106.4-45.266 45.267-106.4 71.867Q550 912 480 912Z"
/>
</symbol>
</svg>

View file

@ -2,30 +2,45 @@
--taille-cellule: 48px;
--epaisseur-bordure-cellule: 1px;
--epaisseur-padding-cellule: 2px;
--couleur-bien-place: #e7002a;
--couleur-mal-place: #ffbd00;
--couleur-fond-grille: #0077c7;
--couleur-bar-max: rgb(231, 0, 42);
--couleur-bien-place: rgb(231, 0, 42);
--couleur-mal-place: rgb(255, 189, 0);
--couleur-fond-grille: rgb(0, 119, 199);
--couleur-non-trouve: rgb(112, 112, 112);
--couleur-icone: rgb(200, 200, 200);
--couleur-fond-rgb: 43, 43, 43;
--couleur-fond: rgb(var(--couleur-fond-rgb));
--couleur-bordure: #ffffff;
--couleur-bordure: rgb(200, 200, 200);
--couleur-bordure-grille: #ffffff;
--couleur-police: #ffffff;
--couleur-police-grille: #ffffff;
--taille-icone: 32px;
--couleur-police-grille: rgb(255, 255, 255);
--couleur-police-grille-pas-curseur: rgb(255, 255, 255, 0.65);
--couleur-lettre-speciale: rgb(75, 75, 75);
--couleur-lettre-survole: rgba(75, 75, 75, 0.65);
--couleur-lettre-speciale-survole: rgba(75, 75, 75, 0.65);
--couleur-lettre-survole-bien-place: rgba(231, 0, 42, 0.65);
--couleur-lettre-survole-mal-place: rgba(255, 189, 0, 0.65);
--taille-icone: 24px;
--taille-icone-zone: 48px;
}
@font-face {
font-family: "Roboto Medium";
src: url("/fonts/Roboto-Medium.ttf");
font-family: "Roboto Regular";
src: url("/fonts/Roboto-Regular.ttf");
}
@font-face {
font-family: "RobotoMono Regular";
src: url("/fonts/RobotoMono-Regular.ttf");
}
body {
font-family: "Roboto Medium", Ubuntu, Arial, Helvetica, sans-serif;
font-family: "Roboto Regular", Ubuntu, Arial, Helvetica, sans-serif;
font-size: 32px;
background-color: var(--couleur-fond);
height: 100vh;
max-height: 95vh;
height: 95vh;
height: -webkit-fill-available; /* Seulement pour safari et sa flotting bar */
text-align: center;
color: var(--couleur-police);
margin: 0;
@ -61,19 +76,33 @@ h1 {
justify-content: space-around;
}
#configuration-regles-bouton,
#configuration-config-bouton,
#configuration-stats-bouton,
#configuration-audio-bouton {
height: var(--taille-icone-zone);
width: var(--taille-icone-zone);
display: flex;
justify-content: center;
align-items: center;
}
#configuration-regles-icone,
#configuration-config-icone,
#configuration-stats-icone,
#configuration-audio-icone {
height: var(--taille-icone);
width: var(--taille-icone);
fill: var(--couleur-icone);
}
#grille {
margin-left: auto;
margin-right: auto;
background-color: var(--couleur-fond-grille);
min-height: calc(6 * var(--taille-cellule) + 12 * var(--epaisseur-bordure-cellule));
min-height: calc(6 * var(--taille-cellule) + 10 * var(--epaisseur-bordure-cellule));
margin-bottom: auto;
font-size: 32px;
}
.grille table {
@ -88,7 +117,7 @@ h1 {
position: relative;
padding: var(--epaisseur-padding-cellule);
color: var(--couleur-police-grille);
border: 1px solid var(--couleur-bordure-grille);
border: var(--epaisseur-bordure-cellule) solid var(--couleur-bordure-grille);
z-index: 0;
}
@ -122,14 +151,15 @@ h1 {
#panel-area {
display: none;
font-size: 24px;
font-size: 20px;
}
#input-area {
margin: 0.5em auto 2em;
max-width: 100%;
width: calc(100% - 20px);
width: calc(100% - 5px);
max-width: 500px;
user-select: none;
}
.input-ligne {
@ -155,7 +185,7 @@ h1 {
display: flex;
align-items: center;
justify-content: center;
font-family: monospace;
font-family: "RobotoMono Regular", monospace;
}
.input-lettre.input-lettre-vide,
@ -184,18 +214,55 @@ h1 {
.input-lettre:hover,
.input-lettre:active {
cursor: pointer;
background-color: var(--couleur-lettre-survole);
}
.input-lettre.lettre-bien-place:hover,
.input-lettre.lettre-bien-place:active {
background-color: var(--couleur-lettre-survole-bien-place);
}
.input-lettre.lettre-mal-place:hover,
.input-lettre.lettre-mal-place:active {
background-color: var(--couleur-lettre-survole-mal-place);
}
.input-lettre.input-lettre-vide:hover,
.input-lettre.input-lettre-vide:active {
cursor: initial;
background-color: transparent;
}
.input-lettre.input-lettre-effacer {
font-size: 28px;
}
.input-lettre.input-lettre-effacer,
.input-lettre.input-lettre-entree,
.input-lettre[data-lettre="."] {
background-color: var(--couleur-lettre-speciale);
border-color: var(--couleur-lettre-speciale);
}
.input-lettre.input-lettre-effacer:active,
.input-lettre.input-lettre-effacer:hover,
.input-lettre.input-lettre-entree:active,
.input-lettre.input-lettre-entree:hover,
.input-lettre[data-lettre="."]:active,
.input-lettre[data-lettre="."]:hover {
background-color: var(--couleur-lettre-speciale-survole);
}
.grille td.cellule-lettre-pas-curseur {
color: var(--couleur-police-grille-pas-curseur);
}
.regles-panel table {
font-size: 32px;
}
.regles-panel #panel-fenetre {
.regles-panel #panel-fenetre,
.notes-panel #panel-fenetre {
font-size: 14px;
text-align: left;
}
@ -210,10 +277,28 @@ h1 {
color: var(--couleur-police);
}
#notification,
#panel-fenetre-notification {
#notification-area,
#panel-fenetre-notification-area {
opacity: 0;
transition: opacity linear 1s;
min-height: 50px;
}
#panel-fenetre-notification-area {
position: absolute;
}
#notification-label,
#panel-fenetre-notification-label {
font-size: 22px;
background-color: #bdbdbd;
width: calc(100% - 5px);
max-width: 500px;
margin: 0px auto;
border-radius: 0.5em;
padding: 5px 0px;
color: black;
font-weight: bold;
}
#panel-area {
@ -247,7 +332,7 @@ h1 {
align-content: center;
justify-content: space-between;
width: calc(100% - 1em);
height: calc(36px + 0.75em);
min-height: calc(36px + 0.75em);
margin-left: 0.5em;
margin-right: 0.5em;
margin-top: 0.25em;
@ -260,18 +345,31 @@ h1 {
}
#panel-fenetre-bouton-fermeture {
width: 48px;
height: 48px;
text-decoration: none;
display: flex;
justify-content: center;
align-items: center;
}
#panel-fenetre-bouton-fermeture-icone {
width: 32px;
height: 32px;
width: 40px;
height: 40px;
fill: var(--couleur-icone);
}
.config-panel #panel-fenetre-contenu {
display: flex;
flex-direction: column;
gap: 0.5em;
text-align: start;
}
#config-liste {
display: flex;
flex-direction: column;
gap: 0.5em;
}
.config-item {
@ -279,26 +377,104 @@ h1 {
justify-content: space-between;
}
.stats-area {
display: table;
padding-left: 0.5em;
.config-item label {
font-size: 20px;
}
.config-item select {
min-width: 20%;
text-align: end;
height: min-content;
}
.stats-parties {
display: flex;
padding: 0 0.5em;
width: calc(100% - 1em);
flex-direction: column;
}
.stats-ligne {
display: table-row;
display: flex;
align-items: center;
}
.stats-cellule {
display: table-cell;
.stats-label {
width: 25px;
margin-right: 5px;
flex-grow: 1;
}
.stats-cellule:first-child {
.stats-bar-area {
flex-grow: 48;
}
.stats-bar {
border-radius: 3px;
height: 18px;
min-width: 10px;
background-color: var(--couleur-fond-grille);
}
.stats-bar.bar-max {
background-color: var(--couleur-bar-max);
}
.stats-valeur {
padding-left: 0.5em;
flex-grow: 1;
}
.stats-numeriques-area {
display: grid;
gap: 10px;
margin-top: 1em;
grid-template-columns: repeat(auto-fit, minmax(85px, 1fr));
justify-items: center;
}
.stats-numerique-case {
display: flex;
flex-direction: column;
width: 75px;
height: 75px;
border-radius: 5px;
border: 1px solid var(--couleur-bordure);
padding: 5px;
}
.stats-numerique-case-valeur {
font-size: 24px;
}
.stats-numerique-case-secondaire {
font-size: 12px;
}
.stats-numerique-case-secondaire::before {
content: "/";
}
.stats-numerique-case-secondaire {
text-align: right;
}
.stats-cellule:not(:first-child) {
padding-left: 0.5em;
text-align: left;
.stats-numerique-case-label {
margin-top: auto;
font-size: 16px;
}
.bouton-partage {
text-decoration: none;
}
.bouton-partage svg {
width: 20px;
height: 20px;
}
.bouton-partage-texte {
text-decoration: underline;
}
.fin-de-partie-panel-phrase {
@ -306,6 +482,40 @@ h1 {
padding-left: 0.5em;
}
.fin-de-partie-panel h3 {
text-align: start;
}
#fin-de-partie-panel-partie-veille-area {
text-align: start;
}
#fin-de-partie-panel-resume-bouton,
#fin-de-partie-panel-stats-bouton,
#fin-de-partie-panel-reset-bouton {
font-size: 1rem;
position: relative;
}
#fin-de-partie-panel-resume-bouton,
#fin-de-partie-panel-stats-bouton {
margin-left: 1rem;
}
.bouton-partage-texte {
bottom: 4px;
position: absolute;
width: max-content;
}
#config-sauvegarde-area {
text-align: start;
}
#config-sauvegarde-area p {
margin: 0;
}
@media (max-width: 1024px) {
#contenu {
margin-left: 2px;
@ -319,23 +529,25 @@ h1 {
@media (max-width: 500px) {
:root {
--taille-cellule: 42px;
--taille-cellule: 38px;
--taille-icone: 28px;
}
body,
.regles-panel table {
.regles-panel table,
#grille {
font-size: 28px;
}
}
@media (max-height: 640px), (max-width: 400px) {
:root {
--taille-cellule: 36px;
--taille-cellule: 34px;
}
body,
.regles-panel table {
.regles-panel table,
#grille {
font-size: 24px;
}
}

View file

@ -7,6 +7,7 @@ import { ClavierDisposition } from "./entites/clavierDisposition";
import Input from "./input";
import ThemeManager from "./themeManager";
import { Theme } from "./entites/theme";
import CopieHelper from "./copieHelper";
export default class ConfigurationPanel {
private readonly _panelManager: PanelManager;
@ -35,6 +36,7 @@ export default class ConfigurationPanel {
public afficher(): void {
let titre = "Configuration";
let contenu = document.createElement("div");
contenu.id = "config-liste";
let config = Sauvegardeur.chargerConfig() ?? Configuration.Default;
contenu.appendChild(
this.genererConfigItem(
@ -127,6 +129,31 @@ export default class ConfigurationPanel {
)
);
contenu.appendChild(
this.genererConfigItem(
"Retour haptique (si votre navigateur est compatible)",
[
{ value: false.toString(), label: "Non" },
{ value: true.toString(), label: "Oui" },
],
(config.haptique ?? Configuration.Default.haptique).toString(),
(event: Event) => {
event.stopPropagation();
let haptique = (event.target as HTMLSelectElement).value === true.toString();
Sauvegardeur.sauvegarderConfig({
...(Sauvegardeur.chargerConfig() ?? Configuration.Default),
haptique,
});
// On redessine le clavier pour la prise en compte de l'option
if (this._input) this._input.dessinerClavier(config.disposition ?? Configuration.Default.disposition);
}
)
);
if (Sauvegardeur.chargerSauvegardeStats()) contenu.appendChild(this.genererZoneExportSauvegarde());
this._panelManager.setContenuHtmlElement(titre, contenu);
this._panelManager.setClasses(["config-panel"]);
this._panelManager.afficherPanel();
@ -159,6 +186,52 @@ export default class ConfigurationPanel {
return div;
}
private genererZoneExportSauvegarde(): HTMLElement {
let div = document.createElement("div");
div.id = "config-sauvegarde-area";
const titreSection = document.createElement("h3");
titreSection.innerText = "Exporter vos statistiques";
div.appendChild(titreSection);
const explication = document.createElement("p");
explication.innerText = "Pour transférer vos statistiques sur un autre navigateur, il est possible de suivre les étapes suivantes :";
div.appendChild(explication);
const listeEtape = document.createElement("ol");
const etape1 = document.createElement("li");
const etape1Texte = document.createElement("p");
etape1Texte.innerText = "Copiez ce lien à usage unique.";
etape1.appendChild(etape1Texte);
const etape1Input = document.createElement("input");
const contenuLien = Sauvegardeur.genererLien();
const lien = window.location.origin + window.location.pathname + "#" + btoa("s=" + contenuLien);
etape1Input.value = lien;
etape1Input.readOnly = true;
etape1.appendChild(etape1Input);
const etape1Bouton = CopieHelper.creerBoutonPartage("config-sauvegarde-bouton");
CopieHelper.attacheBoutonCopieLien(etape1Bouton, lien, "Lien copié dans le presse papier.");
etape1.appendChild(etape1Bouton);
listeEtape.appendChild(etape1);
const etape2 = document.createElement("li");
etape2.innerText = "Envoyez le lien vers votre autre appareil.";
listeEtape.appendChild(etape2);
const etape3 = document.createElement("li");
etape3.innerText = "Ouvrez ce lien dans votre autre navigateur.";
listeEtape.appendChild(etape3);
div.appendChild(listeEtape);
return div;
}
public setInput(input: Input): void {
this._input = input;
}

58
ts/copieHelper.ts Normal file
View file

@ -0,0 +1,58 @@
import NotificationMessage from "./notificationMessage";
export default class CopieHelper {
public static attacheBoutonCopieLien(bouton: HTMLElement, lien: string, messageSucces: string): void {
bouton.addEventListener("click", (event) => {
event.stopPropagation();
new Promise((resolve, reject) => {
if (window.navigator.clipboard !== undefined) {
return resolve(window.navigator.clipboard.writeText(lien));
}
return reject();
})
.catch(
() =>
new Promise((resolve, reject) => {
if (window.navigator.share !== undefined) return resolve(navigator.share({ text: lien }));
return reject();
})
)
.then(() => {
NotificationMessage.ajouterNotificationPanel(messageSucces, bouton);
})
.catch((raison) => {
NotificationMessage.ajouterNotificationPanel("Votre navigateur n'est pas compatible.", bouton);
});
});
}
public static creerBoutonPartage(idBouton: string, label?: string): HTMLElement {
return this.creerBoutonAvecIcone(idBouton, "#icone-copie", label);
}
public static creerBoutonAvecIcone(idBouton: string, icone: string, label?: string): HTMLElement {
const lien = document.createElement("a");
lien.id = idBouton;
lien.className = "bouton-partage";
lien.href = "#";
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
const useSvg = document.createElementNS("http://www.w3.org/2000/svg", "use") as SVGUseElement;
useSvg.setAttribute("href", icone);
useSvg.setAttribute("stroke", "var(--couleur-icone)");
useSvg.setAttribute("fill", "var(--couleur-icone)");
svg.appendChild(useSvg);
lien.appendChild(svg);
if (label) {
const texteBouton = document.createElement("span");
texteBouton.className = "bouton-partage-texte";
texteBouton.innerText = label;
lien.appendChild(texteBouton);
}
return lien;
}
}

View file

@ -35,7 +35,7 @@ export default class Dictionnaire {
public static async estMotValide(mot: string, premiereLettre: string, longueur: number): Promise<boolean> {
mot = this.nettoyerMot(mot);
let ListeMotsProposables = await import("./mots/listeMotsProposables." + longueur + "." + premiereLettre);
return mot.length >= 6 && mot.length <= 9 && ListeMotsProposables.default.Dictionnaire.includes(mot);
return mot.length >= 6 && mot.length <= 10 && ListeMotsProposables.default.Dictionnaire.includes(mot);
}
public static nettoyerMot(mot: string): string {

View file

@ -10,6 +10,8 @@ export default class Configuration {
volumeSon: VolumeSon.Normal,
disposition: ClavierDisposition.Azerty,
theme: Theme.Sombre,
haptique: false,
changelog: 0,
};
hasAudio: boolean = false;
@ -18,4 +20,6 @@ export default class Configuration {
volumeSon: VolumeSon = VolumeSon.Normal;
disposition: ClavierDisposition = ClavierDisposition.Azerty;
theme: Theme = Theme.Sombre;
haptique: boolean = false;
changelog: number = 0;
}

View file

@ -2,7 +2,7 @@ export default class SauvegardeStats {
public static Default: SauvegardeStats = {
partiesJouees: 0,
partiesGagnees: 0,
dernierePartie: new Date(),
dernierePartie: null,
repartition: {
1: 0,
2: 0,
@ -19,7 +19,7 @@ export default class SauvegardeStats {
},
};
dernierePartie: Date = new Date();
dernierePartie: Date | null = null;
partiesJouees: number = 0;
partiesGagnees: number = 0;
repartition: {

View file

@ -1,15 +1,19 @@
import CopieHelper from "./copieHelper";
import Configuration from "./entites/configuration";
import LettreResultat from "./entites/lettreResultat";
import { LettreStatut } from "./entites/lettreStatut";
import SauvegardeStats from "./entites/sauvegardeStats";
import Gestionnaire from "./gestionnaire";
import InstanceConfiguration from "./instanceConfiguration";
import NotificationMessage from "./notificationMessage";
import PanelManager from "./panelManager";
import Sauvegardeur from "./sauvegardeur";
import StatistiquesDisplayer from "./statistiquesDisplayer";
export default class FinDePartiePanel {
private readonly _datePartie: Date;
private readonly _panelManager: PanelManager;
private readonly _statsButton: HTMLElement;
private readonly _gestionnaire: Gestionnaire;
private _resumeTexte: string = "";
private _resumeTexteLegacy: string = "";
@ -17,11 +21,12 @@ export default class FinDePartiePanel {
private _estVictoire: boolean = false;
private _partieEstFinie: boolean = false;
public constructor(datePartie: Date, panelManager: PanelManager) {
public constructor(datePartie: Date, panelManager: PanelManager, gestionnaire: Gestionnaire) {
this._datePartie = new Date(datePartie);
this._datePartie.setHours(0, 0, 0);
this._panelManager = panelManager;
this._statsButton = document.getElementById("configuration-stats-bouton") as HTMLElement;
this._gestionnaire = gestionnaire;
this._statsButton.addEventListener(
"click",
@ -104,31 +109,8 @@ export default class FinDePartiePanel {
}
private attacherPartage(): void {
let resumeBouton = document.getElementById("fin-de-partie-panel-resume-bouton") as HTMLElement;
resumeBouton.addEventListener("click", (event) => {
event.stopPropagation();
new Promise((resolve, reject) => {
if (window.navigator.clipboard !== undefined) {
return resolve(window.navigator.clipboard.writeText(this._resumeTexte + "\n\nhttps://sutom.nocle.fr"));
}
return reject();
})
.catch(
() =>
new Promise((resolve, reject) => {
if (window.navigator.share !== undefined) return resolve(navigator.share({ text: this._resumeTexte + "\n\nhttps://sutom.nocle.fr" }));
return reject();
})
)
.then(() => {
NotificationMessage.ajouterNotificationPanel("Résumé copié dans le presse-papier.");
})
.catch((raison) => {
NotificationMessage.ajouterNotificationPanel("Votre navigateur n'est pas compatible.");
});
});
const resumeBouton = document.getElementById("fin-de-partie-panel-resume-bouton") as HTMLElement;
CopieHelper.attacheBoutonCopieLien(resumeBouton, this._resumeTexte + "\n\nhttps://sutom.nocle.fr", "Résumé copié dans le presse papier.");
}
public afficher(): void {
@ -152,47 +134,52 @@ export default class FinDePartiePanel {
Peut-être feras-tu mieux demain ? \
</p>";
}
contenu +=
'<p>Résumé de ta partie <a href="#" id="fin-de-partie-panel-resume-bouton">Partager</a></p> \
<pre id="fin-de-partie-panel-resume">' +
this._resumeTexteLegacy +
"</pre>";
contenu += StatistiquesDisplayer.genererResumeTexte(this._resumeTexteLegacy).outerHTML;
if (Sauvegardeur.hasPartieVeilleNonTerminee()) {
const partieVeilleArea = document.createElement("div");
partieVeilleArea.id = "fin-de-partie-panel-partie-veille-area";
const partieVeilleLabel = document.createElement("div");
partieVeilleLabel.innerText = "Il semblerait que vous n'avez pas terminé votre partie d'hier…";
partieVeilleArea.appendChild(partieVeilleLabel);
partieVeilleArea.appendChild(CopieHelper.creerBoutonAvecIcone("fin-de-partie-panel-reset-bouton", "#icone-restaure", "Terminer la partie"));
contenu += partieVeilleArea.outerHTML;
}
}
let stats = Sauvegardeur.chargerSauvegardeStats();
if (stats) {
contenu +=
'<p>Statistiques</p><div class="stats-area"><div class="stats-ligne"><div class="stats-cellule">Parties :</div>' +
`<div class="stats-cellule">${stats.partiesGagnees}/${stats.partiesJouees}</div>` +
"</div>" +
`<div class="stats-ligne"><div class="stats-cellule">1/6 :</div><div class="stats-cellule">${stats.repartition[1]}</div></div>` +
`<div class="stats-ligne"><div class="stats-cellule">2/6 :</div><div class="stats-cellule">${stats.repartition[2]}</div></div>` +
`<div class="stats-ligne"><div class="stats-cellule">3/6 :</div><div class="stats-cellule">${stats.repartition[3]}</div></div>` +
`<div class="stats-ligne"><div class="stats-cellule">4/6 :</div><div class="stats-cellule">${stats.repartition[4]}</div></div>` +
`<div class="stats-ligne"><div class="stats-cellule">5/6 :</div><div class="stats-cellule">${stats.repartition[5]}</div></div>` +
`<div class="stats-ligne"><div class="stats-cellule">6/6 :</div><div class="stats-cellule">${stats.repartition[6]}</div></div>` +
`<div class="stats-ligne"><div class="stats-cellule">-/6 :</div><div class="stats-cellule">${stats.repartition["-"]}</div></div>` +
`<div class="stats-ligne"><div class="stats-cellule">Moyenne :</div><div class="stats-cellule">${this.getMoyenne(stats.repartition)}</div></div>` +
'<div class="stats-ligne"><div class="stats-cellule">Lettres :</div>' +
'<div class="stats-cellule">' +
`${stats.lettresRepartitions.bienPlace} 🟥 ` +
`${stats.lettresRepartitions.malPlace} 🟡 ` +
`${stats.lettresRepartitions.nonTrouve} 🟦` +
"</div>" +
"</div>" +
"</div>";
contenu += StatistiquesDisplayer.genererHtmlStats(stats).outerHTML;
}
this._panelManager.setContenu(titre, contenu);
this._panelManager.setClasses(["fin-de-partie-panel"]);
if (this._partieEstFinie) this.attacherPartage();
if (stats) this.attacherPartageStats(stats);
const resetButton = document.getElementById("fin-de-partie-panel-reset-bouton") as HTMLElement;
if (resetButton) {
const veille = new Date();
veille.setDate(veille.getDate() - 1);
resetButton.addEventListener(
"click",
(() => {
this._gestionnaire.chargerPartieAncienne(veille, Sauvegardeur.chargerPartieVeille());
this._panelManager.cacherPanel();
}).bind(this)
);
}
this._panelManager.afficherPanel();
}
private getMoyenne(repartition: { 1: number; 2: number; 3: number; 4: number; 5: number; 6: number; "-": number }): string {
return (
(repartition[1] * 1 + repartition[2] * 2 + repartition[3] * 3 + repartition[4] * 4 + repartition[5] * 5 + repartition[6] * 6 + repartition["-"] * 6) /
(repartition[1] + repartition[2] + repartition[3] + repartition[4] + repartition[5] + repartition[6] + repartition["-"])
).toLocaleString("fr-FR", { maximumFractionDigits: 2 });
private attacherPartageStats(stats: SauvegardeStats): void {
const resumeBouton = document.getElementById("fin-de-partie-panel-stats-bouton") as HTMLElement;
let resumeTexte = StatistiquesDisplayer.genererResumeTexteStatistiques(stats);
CopieHelper.attacheBoutonCopieLien(resumeBouton, resumeTexte + "\n\nhttps://sutom.nocle.fr", "Résumé copié dans le presse papier.");
}
}

View file

@ -15,18 +15,21 @@ import ConfigurationPanel from "./configurationPanel";
import AudioPanel from "./audioPanel";
import ThemeManager from "./themeManager";
import InstanceConfiguration from "./instanceConfiguration";
import LienHelper from "./lienHelper";
import NotesMaJPanel from "./notesMaJPanel";
export default class Gestionnaire {
private _grille: Grille | null = null;
private _input: Input | null = null;
private readonly _reglesPanel: ReglesPanel;
private readonly _finDePartiePanel: FinDePartiePanel;
private _finDePartiePanel: FinDePartiePanel;
private readonly _configurationPanel: ConfigurationPanel;
private readonly _propositions: Array<string>;
private readonly _resultats: Array<Array<LettreResultat>>;
private readonly _panelManager: PanelManager;
private readonly _themeManager: ThemeManager;
private readonly _audioPanel: AudioPanel;
private readonly _notesMaJPanel: NotesMaJPanel;
private _motATrouver: string = "";
private _compositionMotATrouver: { [lettre: string]: number } = {};
@ -64,8 +67,9 @@ export default class Gestionnaire {
this._panelManager = new PanelManager();
this._themeManager = new ThemeManager(this._config);
this._reglesPanel = new ReglesPanel(this._panelManager);
this._finDePartiePanel = new FinDePartiePanel(this._datePartieEnCours, this._panelManager);
this._finDePartiePanel = new FinDePartiePanel(this._datePartieEnCours, this._panelManager, this);
this._configurationPanel = new ConfigurationPanel(this._panelManager, this._audioPanel, this._themeManager);
this._notesMaJPanel = new NotesMaJPanel(this._panelManager);
this.choisirMot(this._idPartieEnCours, this._datePartieEnCours)
.then(async (mot) => {
@ -83,19 +87,9 @@ export default class Gestionnaire {
}
private getIdPartie(partieEnCours: PartieEnCours) {
if (window.location.hash !== "" && window.location.hash !== "#") {
let hashPart = atob(window.location.hash.substring(1)).split("/");
for (let infoPos in hashPart) {
let info = hashPart[infoPos];
if (!info.includes("=")) continue;
let infoPart = info.split("=");
let infoKey = infoPart[0];
const infoDansLocation = LienHelper.extraireInformation("p");
if (infoKey !== "p") continue;
return infoPart[1];
}
}
if (infoDansLocation !== null) return infoDansLocation;
if (partieEnCours.idPartie !== undefined) return partieEnCours.idPartie;
@ -120,6 +114,16 @@ export default class Gestionnaire {
}
private enregistrerPartieDansStats(): void {
// On regarde si c'est le même jour que la dernière partie dans les stats.
// Si c'est identique, on ne sauvegarde pas
if (
this._stats.dernierePartie &&
this._stats.dernierePartie.getFullYear() === this._datePartieEnCours.getFullYear() &&
this._stats.dernierePartie.getMonth() === this._datePartieEnCours.getMonth() &&
this._stats.dernierePartie.getDate() === this._datePartieEnCours.getDate()
)
return;
this._stats.partiesJouees++;
let estVictoire = this._resultats.some((resultat) => resultat.every((item) => item.statut === LettreStatut.BienPlace));
if (estVictoire) {
@ -264,8 +268,59 @@ export default class Gestionnaire {
}
private afficherReglesSiNecessaire(): void {
if (this._config.afficherRegles !== undefined && !this._config.afficherRegles) return;
if (this._config.afficherRegles !== undefined && !this._config.afficherRegles) {
if (this._config.changelog === undefined || this._config.changelog < InstanceConfiguration.derniereMiseAJour) {
this._notesMaJPanel.afficher(this._config.changelog ?? 0);
}
return;
}
this._reglesPanel.afficher();
}
public chargerPartieAncienne(datePartie: Date, etatPartie: PartieEnCours): void {
let partieEnCours = etatPartie;
this._idPartieEnCours = this.getIdPartie(partieEnCours);
const veille = new Date();
veille.setDate(veille.getDate() - 1);
if (this._idPartieEnCours !== partieEnCours.idPartie && partieEnCours.idPartie !== undefined) {
partieEnCours = new PartieEnCours();
}
if (
partieEnCours.datePartie &&
!(
veille.getDate() === partieEnCours.datePartie.getDate() &&
veille.getMonth() === partieEnCours.datePartie.getMonth() &&
veille.getFullYear() === partieEnCours.datePartie.getFullYear()
)
) {
partieEnCours = new PartieEnCours();
}
if (partieEnCours.datePartie) {
this._datePartieEnCours = partieEnCours.datePartie;
} else {
this._datePartieEnCours = datePartie;
}
this._dateFinPartie = undefined;
this._propositions.splice(0);
this._resultats.splice(0);
this._finDePartiePanel = new FinDePartiePanel(this._datePartieEnCours, this._panelManager, this);
this.choisirMot(this._idPartieEnCours, this._datePartieEnCours)
.then(async (mot) => {
this._motATrouver = mot;
this._input = new Input(this, this._config, this._motATrouver.length, this._motATrouver[0]);
this._panelManager.setInput(this._input);
this._grille = new Grille(this._motATrouver.length, this._maxNbPropositions, this._motATrouver[0], this._audioPanel);
this._configurationPanel.setInput(this._input);
this._compositionMotATrouver = this.decompose(this._motATrouver);
await this.chargerPropositions(partieEnCours.propositions);
})
.catch((raison) => NotificationMessage.ajouterNotification("Aucun mot n'a été trouvé pour aujourd'hui"));
}
}

View file

@ -39,13 +39,20 @@ export default class Grille {
if (nbMot < this._motActuel || (nbMot === this._motActuel && mot.length !== 0)) {
if (mot.length <= nbLettre) {
contenuCellule = ".";
cellule.classList.add("cellule-lettre-pas-curseur");
} else {
contenuCellule = mot[nbLettre].toUpperCase();
cellule.classList.remove("cellule-lettre-pas-curseur");
}
} else if (nbMot === this._motActuel) {
let lettreIndice = this._indice[nbLettre];
if (lettreIndice !== undefined) contenuCellule = lettreIndice;
else contenuCellule = ".";
if (lettreIndice !== undefined) {
contenuCellule = lettreIndice;
cellule.classList.remove("cellule-lettre-pas-curseur");
} else {
contenuCellule = ".";
cellule.classList.add("cellule-lettre-pas-curseur");
}
}
if (this._resultats.length > nbMot && this._resultats[nbMot][nbLettre]) {
let resultat = this._resultats[nbMot][nbLettre];

View file

@ -4,6 +4,7 @@ import { LettreStatut } from "./entites/lettreStatut";
import { ClavierDisposition } from "./entites/clavierDisposition";
import Configuration from "./entites/configuration";
import Dictionnaire from "./dictionnaire";
import Sauvegardeur from "./sauvegardeur";
export enum ContexteBloquage {
ValidationMot,
@ -18,8 +19,9 @@ export default class Input {
private _longueurMot: number;
private _motSaisi: string;
private _estBloque: Array<ContexteBloquage>; // TODO : Faire un dictionnaire pour savoir qui bloque, pour que si c'est bloqué par finDePartie, et que la fermeture de panel essaye de débloquer, ça ne fasse rien
private _estBloque: Array<ContexteBloquage>;
private _resultats: Array<Array<LettreResultat>>;
private _haptiqueActive: boolean;
public constructor(gestionnaire: Gestionnaire, configuration: Configuration, longueurMot: number, premiereLettre: string) {
this._grille = document.getElementById("grille") as HTMLElement;
@ -30,6 +32,7 @@ export default class Input {
this._motSaisi = "";
this._estBloque = new Array<ContexteBloquage>();
this._resultats = new Array<Array<LettreResultat>>();
this._haptiqueActive = configuration.haptique ?? Configuration.Default.haptique;
this.ajouterEvenementClavierPhysique();
@ -51,6 +54,7 @@ export default class Input {
case "_effacer":
lettreDiv.dataset["lettre"] = lettre;
lettreDiv.innerText = "⌫";
lettreDiv.classList.add("input-lettre-effacer");
break;
case "_entree":
lettreDiv.innerText = "↲";
@ -72,6 +76,7 @@ export default class Input {
this._inputArea.appendChild(ligneDiv);
}
this._haptiqueActive = Sauvegardeur.chargerConfig()?.haptique ?? Configuration.Default.haptique;
this.ajouterEvenementClavierVirtuel();
this.remettrePropositions();
}
@ -111,6 +116,7 @@ export default class Input {
event.stopPropagation();
let div = event.currentTarget;
if (!div) return;
if (this._haptiqueActive && window.navigator.vibrate) window.navigator.vibrate(75);
let lettre = (div as HTMLElement).dataset["lettre"];
if (lettre === undefined) {
return;

View file

@ -1,4 +1,5 @@
export default class InstanceConfiguration {
public static readonly dateOrigine = new Date(2022, 0, 8); // Attention, c'est du js/ts, donc pour le mois, il faut faire -1, Janvier = 0 !
public static readonly idPartieParDefaut = "34ccc522-c264-4e51-b293-fd5bd60ef7aa";
public static readonly derniereMiseAJour = 512;
}

19
ts/lienHelper.ts Normal file
View file

@ -0,0 +1,19 @@
export default class LienHelper {
public static extraireInformation(cle: string): string | null {
if (window.location.hash === "" || window.location.hash === "#") return null;
let hashPart = atob(window.location.hash.substring(1)).split("/");
for (let infoPos in hashPart) {
let info = hashPart[infoPos];
if (!info.includes("=")) continue;
let infoPart = info.split("=");
let infoKey = infoPart[0];
if (infoKey !== cle) continue;
return infoPart[1];
}
return null;
}
}

File diff suppressed because it is too large Load diff

85
ts/notesMaJPanel.ts Normal file
View file

@ -0,0 +1,85 @@
import Configuration from "./entites/configuration";
import InstanceConfiguration from "./instanceConfiguration";
import PanelManager from "./panelManager";
import Sauvegardeur from "./sauvegardeur";
export default class NotesMaJPanel {
private readonly _panelManager: PanelManager;
private readonly _notes = [
{
version: 512,
notes: [
"Correction du non affichage du bouton Partie de la veille",
"Ajustement de la hauteur du jeu sur certains téléphones",
"Correction de la couleur de la longue barre des statistiques",
"Ajout des couleurs dans les stats pour les terminaux sans emoji",
"Correction d'une coquille dans les options",
"Résolution d'un problème de selection de texte sur le clavier",
],
},
{
version: 500,
notes: [
"Revue du design général de l'application",
"Refonte du mode clair",
"Refonte de l'affichage des statistiques",
"Ajout d'un bouton pour partager ses statistiques",
"Ajout d'un bouton pour terminer la partie de la veille",
"Ajout d'une option pour avoir le clavier haptique",
"Ajout d'une option pour transférer ses statistiques sur un autre navigateur",
],
},
];
public constructor(panelManager: PanelManager) {
this._panelManager = panelManager;
}
public afficher(versionOrigine: number): void {
let titre = "Notes de mises à jour";
// On affiche du plus récent au plus ancien
const notesAAfficher = this._notes
.filter((note) => note.version > versionOrigine)
.sort((a, b) => {
if (b.version > a.version) return 1;
if (b.version < a.version) return -1;
return 0;
});
if (notesAAfficher.length === 0) return;
const notesArea = document.createElement("div");
for (let note of notesAAfficher) {
const divNote = document.createElement("div");
const titre = document.createElement("h3");
titre.innerText = `Version ${note.version}`;
divNote.appendChild(titre);
const listeNotes = document.createElement("ul");
for (let item of note.notes) {
const itemLi = document.createElement("li");
itemLi.innerText = item;
listeNotes.appendChild(itemLi);
}
divNote.appendChild(listeNotes);
notesArea.appendChild(divNote);
}
this._panelManager.setContenuHtmlElement(titre, notesArea);
this._panelManager.setClasses(["notes-panel"]);
this._panelManager.setCallbackFermeture(() => {
Sauvegardeur.sauvegarderConfig({
...(Sauvegardeur.chargerConfig() ?? Configuration.Default),
changelog: InstanceConfiguration.derniereMiseAJour,
});
});
this._panelManager.afficherPanel();
}
}

View file

@ -1,28 +1,34 @@
export default class NotificationMessage {
private static _notificationArea: HTMLElement = document.getElementById("notification") as HTMLElement;
private static _notificationPanelArea: HTMLElement = document.getElementById("panel-fenetre-notification") as HTMLElement;
private static _notificationArea: HTMLElement = document.getElementById("notification-area") as HTMLElement;
private static _notificationLabel: HTMLElement = document.getElementById("notification-label") as HTMLElement;
private static _notificationPanelArea: HTMLElement = document.getElementById("panel-fenetre-notification-area") as HTMLElement;
private static _notificationPanelLabel: HTMLElement = document.getElementById("panel-fenetre-notification-label") as HTMLElement;
private static _currentTimeout: NodeJS.Timeout | undefined;
public static ajouterNotification(message: string): void {
this.ajouterNotificationDiv(this._notificationArea, message);
this.ajouterNotificationDiv(this._notificationArea, this._notificationLabel, message);
}
public static ajouterNotificationPanel(message: string): void {
this.ajouterNotificationDiv(this._notificationPanelArea, message);
public static ajouterNotificationPanel(message: string, origine: HTMLElement): void {
this.ajouterNotificationDiv(this._notificationPanelArea, this._notificationPanelLabel, message);
const { top: topParent, left: leftParent } = origine.getBoundingClientRect();
this._notificationPanelArea.style.top = `${topParent + 30}px`;
this._notificationPanelArea.style.left = `${leftParent - this._notificationPanelArea.getBoundingClientRect().width / 2}px`;
}
private static ajouterNotificationDiv(div: HTMLElement, message: string): void {
private static ajouterNotificationDiv(divArea: HTMLElement, divLabel: HTMLElement, message: string): void {
if (this._currentTimeout) {
clearTimeout(this._currentTimeout);
this._currentTimeout = undefined;
}
div.innerHTML = message;
div.style.opacity = "1";
divLabel.innerHTML = message;
divArea.style.opacity = "1";
this._currentTimeout = setTimeout(
(() => {
div.style.opacity = "0";
divArea.style.opacity = "0";
this._currentTimeout = setTimeout(
(() => {
div.innerHTML = " ";
divLabel.innerHTML = "";
this._currentTimeout = undefined;
}).bind(this),
1000

View file

@ -46,7 +46,8 @@ export default class ReglesPanel {
' ou <a target="_blank" href="https://mastodon.social/@JonathanMM">@JonathanMM@mastodon.social</a> sur mastodon.  ' +
'<a target="_blank" href="https://framagit.org/JonathanMM/sutom">Page du projet</a><br />' +
'Basé sur l\'excellent <a target="_blank" href="https://www.nytimes.com/games/wordle/index.html">Wordle</a> et le regretté Motus.<br />' +
"Merci à Emmanuel pour l'aide sur les mots à trouver, et à GaranceAmarante pour l'aide sur le dictionnaire." +
"Merci à Emmanuel pour l'aide sur les mots à trouver, et à GaranceAmarante pour l'aide sur le dictionnaire.<br />" +
'Les icônes proviennent de <a target="_blank" href="https://m3.material.io/styles/icons/overview">Material Design</a>' +
"</p>";
this._panelManager.setContenu(titre, contenu);

View file

@ -2,10 +2,13 @@ import Configuration from "./entites/configuration";
import PartieEnCours from "./entites/partieEnCours";
import SauvegardePartie from "./entites/sauvegardePartie";
import SauvegardeStats from "./entites/sauvegardeStats";
import LienHelper from "./lienHelper";
import NotificationMessage from "./notificationMessage";
export default class Sauvegardeur {
private static readonly _cleStats = "statistiques";
private static readonly _clePartieEnCours = "partieEnCours";
private static readonly _clePartieVeille = "partieVeille";
private static readonly _cleConfiguration = "configuration";
public static sauvegarderStats(stats: SauvegardeStats): void {
@ -13,10 +16,25 @@ export default class Sauvegardeur {
}
public static chargerSauvegardeStats(): SauvegardeStats | undefined {
let dataStats = localStorage.getItem(this._cleStats);
const contenuLocation = LienHelper.extraireInformation("s");
if (contenuLocation) {
const donneesDepuisLien = Sauvegardeur.chargerInformationDepuisLien(contenuLocation);
window.location.hash = "";
if (donneesDepuisLien) {
NotificationMessage.ajouterNotification("Statistiques chargés avec succès.");
Sauvegardeur.sauvegarderStats(donneesDepuisLien);
return donneesDepuisLien;
}
NotificationMessage.ajouterNotification("Impossible de charger les statistiques depuis le lien.");
}
const dataStats = localStorage.getItem(this._cleStats);
if (!dataStats) return;
let stats = JSON.parse(dataStats) as SauvegardeStats;
if (stats.dernierePartie !== null) stats.dernierePartie = new Date(stats.dernierePartie);
return stats;
}
@ -31,19 +49,54 @@ export default class Sauvegardeur {
}
public static chargerSauvegardePartieEnCours(): PartieEnCours | undefined {
let dataPartieEnCours = localStorage.getItem(this._clePartieEnCours);
if (!dataPartieEnCours) return;
let partieEnCours = JSON.parse(dataPartieEnCours) as SauvegardePartie;
let aujourdhui = new Date();
let datePartieEnCours = new Date(partieEnCours.datePartie);
if (
aujourdhui.getDate() !== datePartieEnCours.getDate() ||
aujourdhui.getMonth() !== datePartieEnCours.getMonth() ||
aujourdhui.getFullYear() !== datePartieEnCours.getFullYear()
) {
localStorage.removeItem(this._clePartieEnCours);
return;
let partieEnCours: SauvegardePartie;
let datePartieEnCours: Date;
let dataPartieEnCours = localStorage.getItem(this._clePartieEnCours);
if (!dataPartieEnCours) {
// On regarde si par hasard, on n'a pas la partie du jour dans les infos de la veille
const partieVeille = this.getInfoVeille();
if (
partieVeille &&
aujourdhui.getDate() === partieVeille.datePartie.getDate() &&
aujourdhui.getMonth() === partieVeille.datePartie.getMonth() &&
aujourdhui.getFullYear() === partieVeille.datePartie.getFullYear()
) {
partieEnCours = partieVeille;
datePartieEnCours = partieVeille.datePartie;
localStorage.removeItem(this._clePartieVeille);
} else {
return;
}
} else {
partieEnCours = JSON.parse(dataPartieEnCours) as SauvegardePartie;
datePartieEnCours = new Date(partieEnCours.datePartie);
if (
aujourdhui.getDate() !== datePartieEnCours.getDate() ||
aujourdhui.getMonth() !== datePartieEnCours.getMonth() ||
aujourdhui.getFullYear() !== datePartieEnCours.getFullYear()
) {
// On regarde si par hasard, on n'a pas la partie du jour dans les infos de la veille
const partieVeille = this.getInfoVeille();
if (
partieVeille &&
aujourdhui.getDate() === partieVeille.datePartie.getDate() &&
aujourdhui.getMonth() === partieVeille.datePartie.getMonth() &&
aujourdhui.getFullYear() === partieVeille.datePartie.getFullYear()
) {
partieEnCours = partieVeille;
datePartieEnCours = partieVeille.datePartie;
// Et on inverse les données
localStorage.setItem(this._clePartieVeille, dataPartieEnCours);
} else {
localStorage.setItem(this._clePartieVeille, dataPartieEnCours);
localStorage.removeItem(this._clePartieEnCours);
return;
}
}
}
let dateFinPartie = partieEnCours.dateFinPartie === undefined ? undefined : new Date(partieEnCours.dateFinPartie);
@ -55,6 +108,68 @@ export default class Sauvegardeur {
};
}
private static getInfoVeille(): SauvegardePartie | undefined {
const dataPartieVeille = localStorage.getItem(this._clePartieVeille);
if (!dataPartieVeille) return undefined;
const veille = new Date();
veille.setDate(veille.getDate() - 1);
let partieVeille = JSON.parse(dataPartieVeille) as SauvegardePartie;
if (partieVeille.datePartie) partieVeille.datePartie = new Date(partieVeille.datePartie);
if (partieVeille.dateFinPartie) partieVeille.dateFinPartie = new Date(partieVeille.dateFinPartie);
return partieVeille;
}
public static hasPartieVeilleNonTerminee(): boolean {
const partieVeille = this.getInfoVeille();
if (!partieVeille) return true;
const aujourdhui = new Date();
const veille = new Date();
veille.setDate(veille.getDate() - 1);
return (
(aujourdhui.getDate() !== partieVeille.datePartie.getDate() && veille.getDate() !== partieVeille.datePartie.getDate()) ||
(aujourdhui.getMonth() !== partieVeille.datePartie.getMonth() && veille.getMonth() !== partieVeille.datePartie.getMonth()) ||
(aujourdhui.getFullYear() !== partieVeille.datePartie.getFullYear() && veille.getFullYear() !== partieVeille.datePartie.getFullYear()) ||
!partieVeille.dateFinPartie
);
}
public static chargerPartieVeille(): PartieEnCours {
const veille = new Date();
veille.setDate(veille.getDate() - 1);
const partieVeille = this.getInfosPartieVeille(veille);
let dateFinPartie = partieVeille.dateFinPartie === undefined ? undefined : new Date(partieVeille.dateFinPartie);
// On va sauvegarder la partie en cours dans la veille pour ne pas la perde
const partieEnCours = localStorage.getItem(this._clePartieEnCours);
if (partieEnCours) {
localStorage.setItem(this._clePartieVeille, partieEnCours);
localStorage.removeItem(this._clePartieEnCours);
}
return {
datePartie: new Date(partieVeille.datePartie),
dateFinPartie: dateFinPartie,
propositions: partieVeille.propositions,
idPartie: partieVeille.idPartie,
};
}
private static getInfosPartieVeille(veille: Date): SauvegardePartie {
const dataPartieVeille = localStorage.getItem(this._clePartieVeille);
if (!dataPartieVeille) {
const dataPartie = new SauvegardePartie();
dataPartie.datePartie = veille;
return dataPartie;
}
return JSON.parse(dataPartieVeille) as SauvegardePartie;
}
public static sauvegarderConfig(config: Configuration): void {
localStorage.setItem(this._cleConfiguration, JSON.stringify(config));
}
@ -66,4 +181,68 @@ export default class Sauvegardeur {
let config = JSON.parse(dataConfig) as Configuration;
return config;
}
public static genererLien(): string {
const stats = Sauvegardeur.chargerSauvegardeStats() ?? SauvegardeStats.Default;
return [
stats.repartition[1],
stats.repartition[2],
stats.repartition[3],
stats.repartition[4],
stats.repartition[5],
stats.repartition[6],
stats.repartition["-"],
stats.lettresRepartitions.bienPlace,
stats.lettresRepartitions.malPlace,
stats.lettresRepartitions.nonTrouve,
stats.dernierePartie ? stats.dernierePartie.toISOString() : "null",
].join(",");
}
private static chargerInformationDepuisLien(contenu: string): SauvegardeStats | null {
const [
UnCoupString,
DeuxCoupsString,
TroisCoupsString,
QuatreCoupsString,
CinqCoupsString,
SixCoupsString,
PerduString,
LettresBienPlaceesString,
LettresMalPlaceesString,
LettresNonTrouveString,
dernierePartie,
] = contenu.split(",");
const UnCoup = parseInt(UnCoupString);
const DeuxCoups = parseInt(DeuxCoupsString);
const TroisCoups = parseInt(TroisCoupsString);
const QuatreCoups = parseInt(QuatreCoupsString);
const CinqCoups = parseInt(CinqCoupsString);
const SixCoups = parseInt(SixCoupsString);
const Perdu = parseInt(PerduString);
const LettresBienPlacees = parseInt(LettresBienPlaceesString);
const LettresMalPlacees = parseInt(LettresMalPlaceesString);
const LettresNonTrouve = parseInt(LettresNonTrouveString);
return {
dernierePartie: dernierePartie === "null" ? null : new Date(dernierePartie),
partiesJouees: UnCoup + DeuxCoups + TroisCoups + QuatreCoups + CinqCoups + SixCoups + Perdu,
partiesGagnees: UnCoup + DeuxCoups + TroisCoups + QuatreCoups + CinqCoups + SixCoups,
repartition: {
1: UnCoup,
2: DeuxCoups,
3: TroisCoups,
4: QuatreCoups,
5: CinqCoups,
6: SixCoups,
"-": Perdu,
},
lettresRepartitions: {
bienPlace: LettresBienPlacees,
malPlace: LettresMalPlacees,
nonTrouve: LettresNonTrouve,
},
};
}
}

150
ts/statistiquesDisplayer.ts Normal file
View file

@ -0,0 +1,150 @@
import CopieHelper from "./copieHelper";
import SauvegardeStats from "./entites/sauvegardeStats";
export default class StatistiquesDisplayer {
public static genererResumeTexte(texte: string): HTMLElement {
const area = document.createElement("div");
const titre = document.createElement("h3");
titre.innerText = "Résumé de la partie";
titre.appendChild(CopieHelper.creerBoutonPartage("fin-de-partie-panel-resume-bouton", "Partager"));
area.appendChild(titre);
const resumeArea = document.createElement("pre");
resumeArea.id = "fin-de-partie-panel-resume";
resumeArea.innerHTML = texte;
area.appendChild(resumeArea);
return area;
}
public static genererHtmlStats(stats: SauvegardeStats): HTMLElement {
const statsArea = document.createElement("div");
statsArea.className = "stats-area";
const titre = document.createElement("h3");
titre.innerText = "Statistiques";
titre.appendChild(CopieHelper.creerBoutonPartage("fin-de-partie-panel-stats-bouton", "Partager"));
statsArea.appendChild(titre);
const statsParties = document.createElement("div");
statsParties.className = "stats-parties";
const max = this.getMax(stats.repartition);
statsParties.appendChild(this.creerBar("1", stats.repartition[1], max));
statsParties.appendChild(this.creerBar("2", stats.repartition[2], max));
statsParties.appendChild(this.creerBar("3", stats.repartition[3], max));
statsParties.appendChild(this.creerBar("4", stats.repartition[4], max));
statsParties.appendChild(this.creerBar("5", stats.repartition[5], max));
statsParties.appendChild(this.creerBar("6", stats.repartition[6], max));
statsParties.appendChild(this.creerBar("-", stats.repartition["-"], max));
statsArea.appendChild(statsParties);
const statsNumeriques = document.createElement("div");
statsNumeriques.className = "stats-numeriques-area";
statsNumeriques.appendChild(this.creerStatNumerique("Victoires", stats.partiesGagnees, stats.partiesJouees));
statsNumeriques.appendChild(this.creerStatNumerique("Moyenne", this.getMoyenne(stats.repartition)));
statsNumeriques.appendChild(this.creerStatNumerique('Lettres <span class="emoji-carre-rouge">🟥</span>', stats.lettresRepartitions.bienPlace));
statsNumeriques.appendChild(this.creerStatNumerique('Lettres <span class="emoji-cercle-jaune">🟡</span>', stats.lettresRepartitions.malPlace));
statsNumeriques.appendChild(this.creerStatNumerique('Lettres <span class="emoji-carre-bleu">🟦</span>', stats.lettresRepartitions.nonTrouve));
statsArea.appendChild(statsNumeriques);
return statsArea;
}
private static creerBar(label: string, valeur: number, max: number): HTMLElement {
const ligne = document.createElement("div");
ligne.className = "stats-ligne";
const labelDiv = document.createElement("div");
labelDiv.className = "stats-label";
labelDiv.innerText = label;
ligne.appendChild(labelDiv);
const barAreaDiv = document.createElement("div");
barAreaDiv.className = "stats-bar-area";
const barDiv = document.createElement("div");
barDiv.className = "stats-bar";
const longueurEnPourcent = Math.round((valeur / max) * 100);
if (valeur === max) barDiv.classList.add("bar-max");
barDiv.style.width = longueurEnPourcent === 0 ? "0px" : `calc(${longueurEnPourcent}% - 2px)`;
barAreaDiv.appendChild(barDiv);
ligne.appendChild(barAreaDiv);
const valeurDiv = document.createElement("div");
valeurDiv.className = "stats-valeur";
valeurDiv.innerText = valeur.toString();
ligne.appendChild(valeurDiv);
return ligne;
}
private static creerStatNumerique(label: string, valeur: number, valeurSecondaire?: number): HTMLElement {
const caseDiv = document.createElement("div");
caseDiv.className = "stats-numerique-case";
const valeurDiv = document.createElement("div");
valeurDiv.className = "stats-numerique-case-valeur";
valeurDiv.innerText = valeur.toLocaleString("fr-FR", { maximumFractionDigits: 2 });
caseDiv.appendChild(valeurDiv);
if (valeurSecondaire !== undefined) {
const secondaireDiv = document.createElement("div");
secondaireDiv.className = "stats-numerique-case-secondaire";
secondaireDiv.innerText = valeurSecondaire.toLocaleString("fr-FR", { maximumFractionDigits: 2 });
caseDiv.appendChild(secondaireDiv);
}
const labelDiv = document.createElement("div");
labelDiv.className = "stats-numerique-case-label";
labelDiv.innerHTML = label;
caseDiv.appendChild(labelDiv);
return caseDiv;
}
private static getMax(repartition: { 1: number; 2: number; 3: number; 4: number; 5: number; 6: number; "-": number }): number {
return Math.max(repartition[1], repartition[2], repartition[3], repartition[4], repartition[5], repartition[6], repartition["-"]);
}
private static getMoyenne(repartition: { 1: number; 2: number; 3: number; 4: number; 5: number; 6: number; "-": number }): number {
return (
(repartition[1] * 1 + repartition[2] * 2 + repartition[3] * 3 + repartition[4] * 4 + repartition[5] * 5 + repartition[6] * 6 + repartition["-"] * 6) /
(repartition[1] + repartition[2] + repartition[3] + repartition[4] + repartition[5] + repartition[6] + repartition["-"])
);
}
public static genererResumeTexteStatistiques(stats: SauvegardeStats): string {
const max = this.getMax(stats.repartition);
return `🟡 Statistiques de #SUTOM 🟡
1/6 - ${this.genererBarTexte(stats.repartition[1], max)} ${stats.repartition[1]}
2/6 - ${this.genererBarTexte(stats.repartition[2], max)} ${stats.repartition[2]}
3/6 - ${this.genererBarTexte(stats.repartition[3], max)} ${stats.repartition[3]}
4/6 - ${this.genererBarTexte(stats.repartition[4], max)} ${stats.repartition[4]}
5/6 - ${this.genererBarTexte(stats.repartition[5], max)} ${stats.repartition[5]}
6/6 - ${this.genererBarTexte(stats.repartition[6], max)} ${stats.repartition[6]}
-/6 - ${this.genererBarTexte(stats.repartition["-"], max)} ${stats.repartition["-"]}
Moy. : ${this.getMoyenne(stats.repartition).toLocaleString("fr-FR", { maximumFractionDigits: 2 })}
${stats.lettresRepartitions.bienPlace}🟥- ${stats.lettresRepartitions.malPlace}🟡- ${stats.lettresRepartitions.nonTrouve}🟦`;
}
private static genererBarTexte(valeur: number, max: number): string {
if (valeur === 0) return "";
const caractere = valeur === max ? "🟥" : "🟦";
const longueurEnNbChars = Math.round((valeur / max) * 8);
return longueurEnNbChars === 0 ? caractere : caractere.repeat(longueurEnNbChars);
}
}

View file

@ -10,36 +10,49 @@ export default class ThemeManager {
const root = document.documentElement;
switch (theme) {
case Theme.Clair:
root.style.setProperty("--couleur-bien-place", "#e7002a");
root.style.setProperty("--couleur-mal-place", "#ffbd00");
root.style.setProperty("--couleur-fond-rgb", "255, 254, 246");
root.style.setProperty("--couleur-police", "#000000");
root.style.setProperty("--couleur-bordure", "#000000");
root.style.setProperty("--couleur-icone", "rgb(55, 55, 55)");
break;
case Theme.ClairAccessible:
root.style.setProperty("--couleur-bien-place", "#096800");
root.style.setProperty("--couleur-mal-place", "#db7c00");
root.style.setProperty("--couleur-fond-rgb", "255, 254, 246");
root.style.setProperty("--couleur-fond-rgb", "245, 245, 220");
root.style.setProperty("--couleur-police", "#000000");
root.style.setProperty("--couleur-bordure", "#000000");
root.style.setProperty("--couleur-bordure", "rgb(55, 55, 55)");
root.style.setProperty("--couleur-icone", "rgb(55, 55, 55)");
break;
case Theme.SombreAccessible:
root.style.setProperty("--couleur-bien-place", "#096800");
root.style.setProperty("--couleur-mal-place", "#db7c00");
root.style.setProperty("--couleur-fond-rgb", "43, 43, 43");
root.style.setProperty("--couleur-police", "#ffffff");
root.style.setProperty("--couleur-bordure", "#ffffff");
root.style.setProperty("--couleur-icone", "rgb(200, 200, 200)");
root.style.setProperty("--couleur-lettre-speciale", "rgb(210, 210, 210)");
root.style.setProperty("--couleur-lettre-survole", "rgb(140, 140, 140)");
root.style.setProperty("--couleur-lettre-speciale-survole", "rgb(140, 140, 140)");
break;
default:
root.style.setProperty("--couleur-bien-place", "#e7002a");
root.style.setProperty("--couleur-mal-place", "#ffbd00");
root.style.setProperty("--couleur-fond-rgb", "43, 43, 43");
root.style.setProperty("--couleur-police", "#ffffff");
root.style.setProperty("--couleur-bordure", "#ffffff");
root.style.setProperty("--couleur-bordure", "rgb(200, 200, 200)");
root.style.setProperty("--couleur-icone", "rgb(200, 200, 200)");
root.style.setProperty("--couleur-lettre-speciale", "rgb(75, 75, 75)");
root.style.setProperty("--couleur-lettre-survole", "rgba(75, 75, 75, 0.65)");
root.style.setProperty("--couleur-lettre-speciale-survole", "rgba(75, 75, 75, 0.65)");
}
switch (theme) {
case Theme.ClairAccessible:
case Theme.SombreAccessible:
root.style.setProperty("--couleur-bien-place", "rgb(9, 104, 0)");
root.style.setProperty("--couleur-mal-place", "rgb(219, 124, 0)");
break;
default:
root.style.setProperty("--couleur-bien-place", "rgb(231, 0, 42)");
root.style.setProperty("--couleur-mal-place", "rgb(255, 189, 0)");
}
switch (theme) {
case Theme.ClairAccessible:
root.style.setProperty("--couleur-lettre-survole-bien-place", "rgb(5, 61, 0)");
root.style.setProperty("--couleur-lettre-survole-mal-place", "rgb(128, 72, 0)");
break;
case Theme.SombreAccessible:
root.style.setProperty("--couleur-lettre-survole-bien-place", "rgba(9, 104, 0, 0.65)");
root.style.setProperty("--couleur-lettre-survole-mal-place", "rgba(219, 124, 0, 0.65)");
break;
case Theme.Clair:
root.style.setProperty("--couleur-lettre-survole-bien-place", "rgb(153, 0, 28)");
root.style.setProperty("--couleur-lettre-survole-mal-place", "rgb(153, 112, 0)");
default:
root.style.setProperty("--couleur-lettre-survole-bien-place", "rgba(231, 0, 42, 0.65)");
root.style.setProperty("--couleur-lettre-survole-mal-place", "rgba(255, 189, 0, 0.65)");
}
}
}

View file

@ -21,7 +21,7 @@ function start() {
motTrouve =
!(motAnalyse[0] === motAnalyse[0].toUpperCase()) &&
motAnalyse.length >= 6 &&
motAnalyse.length <= 9 &&
motAnalyse.length <= 10 &&
!motAnalyse.includes("!") &&
!motAnalyse.includes(" ") &&
!motAnalyse.includes("-") &&

View file

@ -85,7 +85,7 @@ fs.readFile("data/motsATrouve.txt", "UTF8", function (erreur, contenu) {
(mot) =>
mot &&
mot.length >= 6 &&
mot.length <= 9 &&
mot.length <= 10 &&
!mot.includes("!") &&
!mot.includes(" ") &&
!mot.includes("-") &&

View file

@ -45,6 +45,7 @@ function ecrireListeNettoyee(dictionnaire) {
}
fs.readFile("data/mots.txt", "UTF8", function (erreur, contenu) {
console.log("Chargement de la liste des mots");
//console.log(erreur);
var dictionnaire = contenu
.split("\n")
@ -60,7 +61,7 @@ fs.readFile("data/mots.txt", "UTF8", function (erreur, contenu) {
(mot) =>
!(mot[0] === mot[0].toUpperCase()) &&
mot.length >= 6 &&
mot.length <= 9 &&
mot.length <= 10 &&
!mot.includes("!") &&
!mot.includes(" ") &&
!mot.includes("-") &&
@ -74,6 +75,7 @@ fs.readFile("data/mots.txt", "UTF8", function (erreur, contenu) {
.filter(function (elem, index, self) {
return index === self.indexOf(elem);
});
console.log("Tri du dictionnaire");
dictionnaire.sort((a, b) => {
if (a.length < b.length) return -1;
if (a.length > b.length) return 1;
@ -87,7 +89,7 @@ fs.readFile("data/mots.txt", "UTF8", function (erreur, contenu) {
ecrireListeNettoyee(dictionnaire);
ecrireDictionnaire(dictionnaire);
let longueurs = [6, 7, 8, 9];
let longueurs = [6, 7, 8, 9, 10];
let initialesPossibles = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "L", "M", "N", "O", "P", "R", "S", "T", "U", "V"];
for (let longueur of longueurs) {
for (let initiale of initialesPossibles) {

View file

@ -54,7 +54,7 @@ fs.readFile("data/motsATrouve.txt", "UTF8", function (erreur, contenu) {
(mot) =>
mot &&
mot.length >= 6 &&
mot.length <= 9 &&
mot.length <= 10 &&
!mot.includes("!") &&
!mot.includes(" ") &&
!mot.includes("-") &&

View file

@ -14,12 +14,12 @@ fs.readFile("data/motsATrouve.txt", "UTF8", function (erreur, contenu) {
let motClean = mot.normalize("NFD").replace(/\p{Diacritic}/gu, "");
let longueur = motClean.length;
if (lettres[initiale] === undefined) lettres[initiale] = { 6: 0, 7: 0, 8: 0, 9: 0 };
if (lettres[initiale] === undefined) lettres[initiale] = { 6: 0, 7: 0, 8: 0, 9: 0, 10: 0 };
lettres[initiale][longueur.toString()]++;
}
console.log(" | 6 | 7 | 8 | 9 |");
console.log(" | 6 | 7 | 8 | 9 | 10 |");
for (let lettre in lettres) {
let stats = lettres[lettre];
console.log(
@ -32,6 +32,8 @@ fs.readFile("data/motsATrouve.txt", "UTF8", function (erreur, contenu) {
stats["8"].toString().padStart(3) +
" | " +
stats["9"].toString().padStart(3) +
" |" +
stats["10"].toString().padStart(3) +
" |"
);
}

View file

@ -26,7 +26,7 @@ fs.readFile("data/motsATrouve.txt", "UTF8", function (erreur, contenu) {
(mot) =>
mot &&
mot.length >= 6 &&
mot.length <= 9 &&
mot.length <= 10 &&
!mot.includes("!") &&
!mot.includes(" ") &&
!mot.includes("-") &&