Compare commits
31 Commits
186b3839f0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 88af050bea | |||
| 2373233899 | |||
| 8239d8024d | |||
| 1586d02094 | |||
| 2fe4bab887 | |||
| eb792edb4d | |||
| 1a450491c6 | |||
| a472e34bbe | |||
| 0490924e3d | |||
| db7896d2de | |||
| bd2243e28a | |||
| 1c7d7f636a | |||
| 1fe20f3f3b | |||
| 7f4884a90d | |||
| 532f73309c | |||
| 129a0c321c | |||
| ef4ddb1420 | |||
| da6a720f46 | |||
| 6d7a783d73 | |||
| 6cb1a005fd | |||
| d99fa137ec | |||
| 534fc8ace1 | |||
| 783e91969c | |||
| 226c55aca3 | |||
| 755c3d4e2e | |||
| c875a397ad | |||
| 5034bf1248 | |||
| 322c2a7e7f | |||
| 7aa9c51104 | |||
| 324f310e38 | |||
| 8536d0b1bb |
BIN
R1.02/cours/js.pdf
Normal file
43
R3.01/README.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Programmation WEB coté serveur R3.01
|
||||||
|
Ce dépôt contient les ressources utilisées
|
||||||
|
(cours/td/tp) dans la ressource R3.01.
|
||||||
|
|
||||||
|
**Les objectifs**
|
||||||
|
|
||||||
|
> Savoir développer une application Web côté serveur, en utilisant le langage PHP.
|
||||||
|
|
||||||
|
Les notions suivantes seront abordées :
|
||||||
|
|
||||||
|
- Le langage php
|
||||||
|
On se limitera aux bases du langages. La couche objet sera présenté très succintement.
|
||||||
|
Toute la suite sera illustré avec PHP.
|
||||||
|
|
||||||
|
- Interaction avec le client
|
||||||
|
URL (Uniform Resource Locator), requêtes, formulaires, transmission des paramètres, des données, etc.
|
||||||
|
|
||||||
|
- Applications Web à états
|
||||||
|
Cookies et sessions
|
||||||
|
|
||||||
|
- Organisation de l’accès aux données
|
||||||
|
Bases de données, annuaires, services Web, etc.
|
||||||
|
|
||||||
|
- Introduction à la programmation objet en PHP
|
||||||
|
Introduction au principe MVC
|
||||||
|
|
||||||
|
- Sensibilisation à la sécurité des applications web
|
||||||
|
|
||||||
|
## Calendrier
|
||||||
|
|
||||||
|
| Semaine | Cours | TD/TP |
|
||||||
|
| ------------------ | -------------------------------------------------------- | ------------------ |
|
||||||
|
| 1 : 16/03 - 20/03| [Bases du langages](./cours/cm_bases_php.pdf) | |
|
||||||
|
|
||||||
|
## Les TPS
|
||||||
|
|
||||||
|
#### TP1 : Bases du langage PHP
|
||||||
|
Le [tp1](./tp/tp1)
|
||||||
|
permet de se familiariser avec le langage PHP.
|
||||||
|
À chaque exercice correspond un sous répertoire avec
|
||||||
|
des fichiers à compléter.
|
||||||
|
|
||||||
|
|
||||||
BIN
R3.01/cours/cm_bases_php.pdf
Normal file
@@ -4,6 +4,19 @@
|
|||||||
[Compléments de javascript](cours/jscomp.pdf) javascript, [tp1](./td_tp/tp1)
|
[Compléments de javascript](cours/jscomp.pdf) javascript, [tp1](./td_tp/tp1)
|
||||||
|
|
||||||
#### Semaine 2
|
#### Semaine 2
|
||||||
[DOM](cours/dom.pdf), [tp2](./td_tp/tp2), [tpmvc](td_tp/tp2mvc)
|
[DOM](cours/dom.pdf), [tp2](./td_tp/tp2), [tp2mvc](td_tp/tp2mvc)
|
||||||
|
|
||||||
|
#### Semaine 3
|
||||||
|
[DOM](cours/dom.pdf), [tp3](./td_tp/tp3)
|
||||||
|
|
||||||
|
#### Semaine 4
|
||||||
|
[Promesses, Ajax, API de données](cours/ajax.pdf), [tp4](./td_tp/tp4)
|
||||||
|
|
||||||
|
#### Semaine 5
|
||||||
|
[Programmation déclarative, RIOT.js](cours/riot.pdf), [tp5](./td_tp/tp5)
|
||||||
|
#### Semaine 6
|
||||||
|
[API REST](cours/api.pdf), service firebase [tp6](./td_tp/tp6)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
BIN
R4.01_R4.A.10/cours/ajax.pdf
Normal file
BIN
R4.01_R4.A.10/cours/api.pdf
Normal file
BIN
R4.01_R4.A.10/cours/riot.pdf
Normal file
54
R4.01_R4.A.10/miniprojet/README.md
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
## Thème
|
||||||
|
|
||||||
|
Écrire une application web en javascript qui permet de consulter les données de
|
||||||
|
parcours sup (vœux de poursuite d'études et de réorientation dans l'enseignement
|
||||||
|
supérieur et réponses des établissements) afin d'aider un(e) lycéén(e) à faire ses choix.
|
||||||
|
|
||||||
|
|
||||||
|
- La partie cliente devra être réaliser au moyen du framework [riotjs](https://riot.js.org/).
|
||||||
|
- Les données seront consommées au moyen de [l'api](https://data.enseignementsup-recherche.gouv.fr/explore/dataset/fr-esr-parcoursup/api/?timezone=Europe%2FBerlin&sort=tri) opendata. (cf [méthodologie](https://data.enseignementsup-recherche.gouv.fr/api/datasets/1.0/fr-esr-parcoursup/attachments/methodologie_opendata_2025pdf/))
|
||||||
|
- La localisation utilisera openstreet map et l'api [Leaflet](https://leafletjs.com/).
|
||||||
|
- Les graphiques seront réalisés au moyen de [chart css](https://chartscss.org/).
|
||||||
|
|
||||||
|
## Fonctionnalités
|
||||||
|
#### Recherche des formations par mots clés, avec localisation sur une carte.
|
||||||
|
|
||||||
|
[<a href="img/search.png">exemple</a>]
|
||||||
|
#### Vue d'une formation particulière, avec accès aux données de parcourssup, entre autres :
|
||||||
|
- les données lors de la phase d'admission,
|
||||||
|
- les données lors de la phase complémentaire,
|
||||||
|
- le profils des admis,
|
||||||
|
- l'évolution de la selectivité, des mentions au bac depuis 2020.
|
||||||
|
|
||||||
|
[<a href="img/formation.png">exemple</a>, <a href="img/profil.png">exemple</a>,
|
||||||
|
<a href="img/profil1.png">exemple</a>]
|
||||||
|
|
||||||
|
#### Aide au choix d'orientation
|
||||||
|
|
||||||
|
L'utilisateur doit pouvoir selectionner des formations/filières afin de les comparer et estimer
|
||||||
|
ses chances d'intégrations en fonction de la série de son bac et de sa moyenne en terminale.
|
||||||
|
|
||||||
|
[<a href="img/selection1.png">exemple</a>
|
||||||
|
, <a href="img/selection2.png">exemple</a>]
|
||||||
|
|
||||||
|
Les choix de l'utilisateur devront au moins être persistant localement. Vous pouvez, losque tout fonctionne,
|
||||||
|
mettre en oeuvre une persistance avec [firebase](https://firebase.com).
|
||||||
|
|
||||||
|
## Réalisation
|
||||||
|
1. Une partie de votre travail consiste à comprendre comment fonctionne l'api (entrées/sorties), et comment l'utiliser. N'hésitez pas à encapsuler/abstraire les informations de l'api dans votre propre modèle.
|
||||||
|
2. Le but est d'utiliser la programmation déclarative et par composants de RIOT.js. Il faut donc l'architecturer et l'organiser en
|
||||||
|
conséquence. En particulier, il faut profiter pleinement de l'utilisation de composants le plus génériques possibles.
|
||||||
|
3. Il faut utiliser des urls routables. Riot.js vient avec un [router](https://github.com/riot/route) très simple d'utilisation.
|
||||||
|
Je vous expliquerai comment mettre en oeuvre la réécriture d'url avec apache.
|
||||||
|
4. Il est possible d'utiliser le pattern observable, avec une bibliothèque adhoc ([exemple](https://github.com/riot/observable))
|
||||||
|
5. Dans le depot GIT de votre projet, il faut une description des composants, leurs entrées, leurs fonctions, et comment
|
||||||
|
ils communiquent avec le reste du monde.
|
||||||
|
|
||||||
|
## Attendus
|
||||||
|
Un mail (sujet S4WEB) m'indiquant les informations suivantes :
|
||||||
|
|
||||||
|
- l'url de votre projet sous la forme : http://dwarves.iut-fbleau.fr/~login/parcoursup
|
||||||
|
- l'url du dépôt GIT avec vos sources sur dwarves.iut-fbleau.fr
|
||||||
|
- les noms du binôme.
|
||||||
|
|
||||||
|
Le tout est à finaliser avant le Vendredi 03 avril 2025, 18 heures. Une soutenance aura lieu la semaine du 30/03.
|
||||||
17
R4.01_R4.A.10/miniprojet/groupes.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
## Groupes projet
|
||||||
|
- Rayan Lankechari / Ibrahima Bah
|
||||||
|
- Demrici-Özmen Canpolat / Eliot Maxime
|
||||||
|
- Ozvann ABRAHAM / Karl FRAMERY
|
||||||
|
- SOLAR Dimitri / ZAABAY Tarehi
|
||||||
|
- Nicolas Miekisiak / Loïc Sainton
|
||||||
|
- Torreblanca Mathys / Poitou Enzo
|
||||||
|
- WIECZOREK Valentin / PLOUVIER Luka
|
||||||
|
- Kouami Kpeglo / Alexis Fort
|
||||||
|
- Ugo Faye / Mathis Leclere
|
||||||
|
- Nathan Couchot / Théo Anastasio / Adrien Rabot
|
||||||
|
- Pierre COURTEHOUX / Lukas SIMOES
|
||||||
|
- CAMILLE Cléo / Warrhen Roland
|
||||||
|
- Aylane Sehl / Séri-Khane / ??
|
||||||
|
- Arwa Ben Fraj / Bounni Loubelo Benicia
|
||||||
|
- Hicham Cherifi / Wael Atik
|
||||||
|
- DERQSI BILAL / CAMILLE LECHAVLIER
|
||||||
BIN
R4.01_R4.A.10/miniprojet/img/formation.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/profil.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/profil1.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/search.png
Normal file
|
After Width: | Height: | Size: 238 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/selection1.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
BIN
R4.01_R4.A.10/miniprojet/img/selection2.png
Normal file
|
After Width: | Height: | Size: 102 KiB |
84
R4.01_R4.A.10/td_tp/tp3/README.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
#### Ex1 : modele MVC et pattern strategy
|
||||||
|
|
||||||
|
> Stratégie est un patron de conception comportemental qui permet de définir une
|
||||||
|
> famille d’algorithmes, de les mettre dans des classes séparées et de rendre
|
||||||
|
> leurs objets interchangeables. (source wikipédia)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Le but est de réaliser ce pattern de conception dans le jeu très simple du chifoumi.
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="./img/chifoumi.png">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Pour l'instant, le jeu utilise le pattern MVC. Le modèle, qui calcule le coup de l'ordinateur utilise
|
||||||
|
un tirage aléatoire. On veut pouvoir utilisé d'autres méthodes, qui utilisent l'historique des coups
|
||||||
|
joués par le joueur.
|
||||||
|
|
||||||
|
1. Modifiez le modèle et la vue pour que le jeu affiche le pourcentage de victoires, matchs nuls et défaites du joueur depuis le début
|
||||||
|
de la partie.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
On veut maintenant séparer la façon dont l'ordinateur choisit son coup de la logique du jeu. Le modèle utilisera
|
||||||
|
une stratégie donnée.
|
||||||
|
|
||||||
|
2. Créez un sous-répétoires `stratégies` dans lequel on stokera les différentes stratégies disponibles.
|
||||||
|
|
||||||
|
Les différentes stratégies réaliseront l'interface
|
||||||
|
|
||||||
|
```js
|
||||||
|
getChoice(playerHistory)
|
||||||
|
```
|
||||||
|
|
||||||
|
qui calcule le coup de l'ordinateur, en fonction de l'historique des différents coups du joueur.
|
||||||
|
|
||||||
|
3. Ecrivez 3 stratégies simples `RandomStrategy.js` (stratégie initiale), `CopyPlayerStrategy.js`
|
||||||
|
(joue le même coup que le joueur) `CounterMostUsedStrategy.js` (joue le coup le plus joué par le joueur) :
|
||||||
|
|
||||||
|
Chaque fichier implémente la stratégie correspondante sous la forme :
|
||||||
|
```js
|
||||||
|
export default class RandomStrategy {
|
||||||
|
getChoice(playerHistory) {
|
||||||
|
....
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Mettez à jour le modèle pour qu'il puisse utiliser une stratégie donnée.
|
||||||
|
5. testez dans le main.
|
||||||
|
|
||||||
|
#### Ajout d'une stratégie
|
||||||
|
|
||||||
|
On va ajouter une stratégie markovienne d'ordre 1. On fait
|
||||||
|
l'hyptohése que le coup du joueur dépend de son coup précedent (ordre 1).
|
||||||
|
On met à jour au fur et à mesure la matrice de transitions entre
|
||||||
|
les coups joué par le joueur. Par exemple,
|
||||||
|
|
||||||
|
```js
|
||||||
|
{
|
||||||
|
rock: { rock: 2, paper: 5, scissors: 1 },
|
||||||
|
paper: { rock: 1, paper: 3, scissors: 4 },
|
||||||
|
scissors: { rock: 6, paper: 1, scissors: 2 }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
signifie qu'après paper, le joueur a joué 1 fois rock, 3 fois paper, et 4 fois scissors.
|
||||||
|
|
||||||
|
On se sert de la matrice pour contrer la prédiction.
|
||||||
|
|
||||||
|
5. Écrivezz cette stratégie, et testez la.
|
||||||
|
|
||||||
|
#### Stratégie mixte
|
||||||
|
Le but est d'implanter une stratégie mixte. On se donne une probabilité `p`.
|
||||||
|
|
||||||
|
- la stratégie markovienne est jouée avec la probabilité `p`.
|
||||||
|
- la stratégie aléatoire est jouée avec la probabilité `1-p`
|
||||||
|
|
||||||
|
Écrivez la stratégie mixte, et testez.
|
||||||
|
|
||||||
|
#### Stratégie adaptative
|
||||||
|
La probabilité `p` dépend du score. On l'adapte au fur et à mesure. Si on gagne beaucoup (trop),
|
||||||
|
on joue au hasard, sinon avec Markov.
|
||||||
|
|
||||||
BIN
R4.01_R4.A.10/td_tp/tp3/img/chifoumi.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
41
R4.01_R4.A.10/td_tp/tp3/src/css/style.css
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
.game {
|
||||||
|
}
|
||||||
|
.choices{
|
||||||
|
display : flex;
|
||||||
|
align-items:stretch;
|
||||||
|
height:8em;
|
||||||
|
}
|
||||||
|
.choices > span:nth-child(2) {
|
||||||
|
margin-left:3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.choices span i{
|
||||||
|
font-size: 2em;
|
||||||
|
cursor: pointer;
|
||||||
|
border: none;
|
||||||
|
margin-right:0.25em;
|
||||||
|
transition: transform 0.2s, color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#player,#computer{
|
||||||
|
font-size: 3em;
|
||||||
|
border: none;
|
||||||
|
margin-right:0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.choices span i:hover {
|
||||||
|
color:#398712
|
||||||
|
}
|
||||||
|
|
||||||
|
#player {
|
||||||
|
color:#398712
|
||||||
|
}
|
||||||
|
|
||||||
|
#computer {
|
||||||
|
color:#D93526;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
40
R4.01_R4.A.10/td_tp/tp3/src/index.html
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="fr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Pierre Feuille Ciseaux</title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css"
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css">
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<h1>
|
||||||
|
Pierre - Feuille - Ciseaux
|
||||||
|
</h1>
|
||||||
|
<div class="choices">
|
||||||
|
<span>
|
||||||
|
<i data-choice="rock" class="far fa-hand-rock"></i>
|
||||||
|
<i data-choice="paper" class="far fa-hand-paper"></i>
|
||||||
|
<i data-choice="scissors" class="far fa-hand-scissors"></i>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
<span id="player"></span>
|
||||||
|
<span id="computer"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="game">
|
||||||
|
<p id="score"></p>
|
||||||
|
<p id="result"></p>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<script type="module" src="js/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
18
R4.01_R4.A.10/td_tp/tp3/src/js/controller.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export default class GameController {
|
||||||
|
constructor(model, view) {
|
||||||
|
this.model = model;
|
||||||
|
this.view = view;
|
||||||
|
|
||||||
|
this.view.bindPlay(this.handlePlay);
|
||||||
|
this.view.displayScore(this.model.getScore());
|
||||||
|
}
|
||||||
|
|
||||||
|
handlePlay = (playerChoice) => {
|
||||||
|
const computerChoice = this.model.getComputerChoice();
|
||||||
|
const result = this.model.getResult(playerChoice, computerChoice);
|
||||||
|
|
||||||
|
this.view.displayResult(playerChoice, computerChoice, result);
|
||||||
|
this.view.displayScore(this.model.getScore());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
9
R4.01_R4.A.10/td_tp/tp3/src/js/main.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import GameModel from "./model.js";
|
||||||
|
import GameView from "./view.js";
|
||||||
|
import GameController from "./controller.js";
|
||||||
|
|
||||||
|
const app = new GameController(
|
||||||
|
new GameModel(),
|
||||||
|
new GameView()
|
||||||
|
);
|
||||||
|
|
||||||
29
R4.01_R4.A.10/td_tp/tp3/src/js/model.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
export default class GameModel {
|
||||||
|
constructor() {
|
||||||
|
this.choices = ["rock", "paper", "scissors"];
|
||||||
|
this.score = { player: 0, computer: 0};
|
||||||
|
}
|
||||||
|
|
||||||
|
getComputerChoice() {
|
||||||
|
const index = Math.floor(Math.random() * this.choices.length);
|
||||||
|
return this.choices[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
getResult(player, computer) {
|
||||||
|
if (player === computer) return "égalité";
|
||||||
|
|
||||||
|
if (
|
||||||
|
(player === "rock" && computer === "scissors") ||
|
||||||
|
(player === "paper" && computer === "rock") ||
|
||||||
|
(player === "scissors" && computer === "paper")
|
||||||
|
) {
|
||||||
|
this.score.player++;
|
||||||
|
return "gagné";
|
||||||
|
}
|
||||||
|
this.score.computer++;
|
||||||
|
return "perdu";
|
||||||
|
}
|
||||||
|
getScore() {
|
||||||
|
return this.score;
|
||||||
|
}
|
||||||
|
}
|
||||||
50
R4.01_R4.A.10/td_tp/tp3/src/js/view.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
export default class GameView {
|
||||||
|
#icons = {
|
||||||
|
"rock" : "far fa-hand-rock",
|
||||||
|
"paper" : "far fa-hand-paper",
|
||||||
|
"scissors" : "far fa-hand-scissors"
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.resultEl = document.getElementById("result");
|
||||||
|
this.scoreEl = document.getElementById("score");
|
||||||
|
this.buttons = document.querySelectorAll(".choices span i");
|
||||||
|
this.player = document.getElementById("player");
|
||||||
|
this.computer = document.getElementById("computer");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bindPlay(handler) {
|
||||||
|
this.buttons.forEach(button => {
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
handler(button.dataset.choice);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
displayResult(player, computer, result) {
|
||||||
|
let i = document.createElement("i");
|
||||||
|
i.classList.add(...this.#icons[player].split(' '))
|
||||||
|
this.player.replaceChildren(i)
|
||||||
|
|
||||||
|
i = document.createElement("i");
|
||||||
|
i.classList.add(...this.#icons[computer].split(' '))
|
||||||
|
this.computer.replaceChildren(i)
|
||||||
|
|
||||||
|
|
||||||
|
this.resultEl.innerHTML =
|
||||||
|
`Joueur <i class="${this.#icons[player]}"></i>, ordinateur <i class="${this.#icons[computer]}"></i> → ${result}`;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
displayScore(score) {
|
||||||
|
|
||||||
|
let winRate = ((score.player * 100 / score.count) || 0).toFixed(2)
|
||||||
|
let lossRate = ((score.computer * 100 / score.count) ||0).toFixed(2)
|
||||||
|
let drawRate = (100 - winRate - lossRate).toFixed(2)
|
||||||
|
|
||||||
|
this.scoreEl.textContent =
|
||||||
|
`Score — Joueur: ${score.player} | Ordinateur: ${score.computer}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
100
R4.01_R4.A.10/td_tp/tp4/README.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# TP javascript : Ajax, promesses.
|
||||||
|
|
||||||
|
Vous disposez tous, à la racine de votre compte, d'un répertoire `public_html` qui est servi (http/https) par le serveur dwarves.
|
||||||
|
Pour accéder à vos pages, utilisez l'url :
|
||||||
|
|
||||||
|
> http(s)://dwarves.[arda|iut-fbleau.fr]/~login
|
||||||
|
|
||||||
|
Pour tester les différentes api http, vous pouvez utilisez la commande `curl`.
|
||||||
|
|
||||||
|
|
||||||
|
#### Ex1
|
||||||
|
Le but est de faire une recherche de film en utilisant l'api [OMDb](https://www.omdbapi.com/) (Open Movie Database).
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Son utilisation nécessite une clé. Vous pouvez utiliser la mienne `2fcb2848`. Si
|
||||||
|
la limite de requêtes est atteinte, générez votre propre clé.
|
||||||
|
|
||||||
|
|
||||||
|
Complétez le [code](./src/ex1).
|
||||||
|
- J'ai volontairement utilisé des modules. Il vous faut donc tester l'application avec
|
||||||
|
http.
|
||||||
|
|
||||||
|
- Écrire la fonction du modèle
|
||||||
|
```js
|
||||||
|
getMovies(search)
|
||||||
|
```
|
||||||
|
Cette fonction renvoie une promesse qui permet de faire une requête ajax vers l'api OMDb. Pour ajax,
|
||||||
|
utilisez d'abord l'interface `fetch`, ~~puis l'objet `XMLHttpRequest`~~.
|
||||||
|
|
||||||
|
- Écrire la fonction du contrôleur
|
||||||
|
```js
|
||||||
|
async search(searchMovie)
|
||||||
|
```
|
||||||
|
qui permet de faire une recherche. Cette fonction utilise évidemment la fonction précédente `getMovies`.
|
||||||
|
|
||||||
|
- Écrire la fonction de la vue
|
||||||
|
```js
|
||||||
|
renderList(movies)
|
||||||
|
```
|
||||||
|
qui construit la liste des films affichés. La structure attendue est
|
||||||
|
```html
|
||||||
|
<li>
|
||||||
|
<a href="http://www.imdb.com/title/tt0068646" target="_blank">The Godfather</a><span>1972</span>
|
||||||
|
</li>
|
||||||
|
```
|
||||||
|
#### Ex2
|
||||||
|
Le but, dans un premier temps, est d'écrire une interface, avec ajax, qui permette de jouer au jeu suivant :
|
||||||
|
|
||||||
|
- On a une grille dans laquelle sont cachés des renards (on en connait le nombre initial).
|
||||||
|
|
||||||
|
- A chaque tour, on choisit de tirer sur un case. On a l'alternative suivante :
|
||||||
|
- on tue un renard,
|
||||||
|
- on récupére les nombres de renards sur la grille sur la ligne, colonne et diagonales de la case.
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="img/renard1.png">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Le jeu utilise un serveur http à l'url suivante
|
||||||
|
|
||||||
|
```
|
||||||
|
https://dwarves.iut-fbleau.fr/foxes/foxes.php
|
||||||
|
```
|
||||||
|
|
||||||
|
Voici les urls du jeu :
|
||||||
|
|
||||||
|
```
|
||||||
|
https://dwarves.iut-fbleau.fr/foxes/foxes.php?new&size=10&foxes=10
|
||||||
|
```
|
||||||
|
|
||||||
|
initialise la partie , avec la taille de la grille et le nombre de renards
|
||||||
|
dont la position est aléatoire (le script utilise des sessions de cinq minutes).
|
||||||
|
|
||||||
|
La réponse normale, en json :
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"status":"ok","foxes":10,"tries":0}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
https://dwarves.iut-fbleau.fr/foxes/foxes.php?X=1&Y=3
|
||||||
|
```
|
||||||
|
|
||||||
|
permet de tirer sur une case. Le serveur répond
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"status":"ok","foxes":2,"tries":1}
|
||||||
|
```
|
||||||
|
|
||||||
|
`foxes` vaut -1 si le coup est gagnant, sinon donne le nombre de renards de la colonne, ligne et diagonales de la case.
|
||||||
|
|
||||||
|
|
||||||
|
En cas de victoire, le status est `win`. En cas d'erreur, le status est `nok`.
|
||||||
|
|
||||||
|
|
||||||
|
**Remarque**
|
||||||
|
|
||||||
|
Ajouter l'option `credentials` à vos requêtes pour utiliser la session en cours.
|
||||||
35
R4.01_R4.A.10/td_tp/tp4/aide.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#### XMLHttpRequest
|
||||||
|
```js
|
||||||
|
let xhr = new XMLHttpRequest()
|
||||||
|
xhr.open("GET","mon/url")
|
||||||
|
|
||||||
|
xhr.onload = ...
|
||||||
|
xhr.onerror = ...
|
||||||
|
|
||||||
|
xhr.send()
|
||||||
|
```
|
||||||
|
#### Promise
|
||||||
|
```js
|
||||||
|
|
||||||
|
let promise = new Promise ((resolve,reject) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve("OK")
|
||||||
|
},1000)
|
||||||
|
})
|
||||||
|
promise.then(data => ....)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### async/await
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
function getUser(email){
|
||||||
|
return new Promise(...)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateUser(){
|
||||||
|
|
||||||
|
let user = await getUser(...)
|
||||||
|
console.log(user)
|
||||||
|
}
|
||||||
|
```
|
||||||
BIN
R4.01_R4.A.10/td_tp/tp4/img/renard1.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
R4.01_R4.A.10/td_tp/tp4/img/search.png
Normal file
|
After Width: | Height: | Size: 320 KiB |
86
R4.01_R4.A.10/td_tp/tp4/src/ex1/css/style.css
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
main {
|
||||||
|
position: absolute;
|
||||||
|
top: 2rem;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, 0);
|
||||||
|
}
|
||||||
|
.loader {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
|
margin: 6rem 0 0;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: #FFFAAA;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
label span {
|
||||||
|
text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
margin: 1rem 0 0;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
font-weight: 300;
|
||||||
|
padding: 0.8rem 1rem;
|
||||||
|
color: white;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
transition: all 0.3s;
|
||||||
|
box-shadow: 1px 1px 2px rgba(0,0,0, 0.3);
|
||||||
|
-moz-appearance:none;
|
||||||
|
-webkit-appearance:none;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
input:focus {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
margin: 1rem 0 2rem;
|
||||||
|
}
|
||||||
|
ul li {
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
margin: 1px 0;
|
||||||
|
line-height: 1.4rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
align-content: space-between;
|
||||||
|
text-shadow: 1px 1px 2px rgba(0,0,0,0.8);
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
box-shadow: 0 0 2px rgba(0,0,0, 0.3);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
ul li:hover,
|
||||||
|
ul li:active,
|
||||||
|
ul li:focus {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
ul li a {
|
||||||
|
margin: 0 0.6rem 0 0;
|
||||||
|
text-decoration: none;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
ul li span {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: 'Helvetica Neue', Helvetica, Arial;
|
||||||
|
font-weight: 300;
|
||||||
|
background-size: cover;
|
||||||
|
background-attachment: fixed;
|
||||||
|
background-image: -webkit-radial-gradient(ellipse farthest-corner at top, #661141, #000000);
|
||||||
|
background-image: radial-gradient(ellipse farthest-corner at top, #661141, #000000);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
13
R4.01_R4.A.10/td_tp/tp4/src/ex1/img/puff.svg
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!-- By Sam Herbert (@sherb), for everyone. More @ http://goo.gl/7AJzbL -->
|
||||||
|
<svg width="44" height="44" viewBox="0 0 44 44" xmlns="http://www.w3.org/2000/svg" stroke="#fff">
|
||||||
|
<g fill="none" fill-rule="evenodd" stroke-width="2">
|
||||||
|
<circle cx="22" cy="22" r="1">
|
||||||
|
<animate attributeName="r" begin="0s" dur="1.8s" values="1; 20" calcMode="spline" keyTimes="0; 1" keySplines="0.165, 0.84, 0.44, 1" repeatCount="indefinite"/>
|
||||||
|
<animate attributeName="stroke-opacity" begin="0s" dur="1.8s" values="1; 0" calcMode="spline" keyTimes="0; 1" keySplines="0.3, 0.61, 0.355, 1" repeatCount="indefinite"/>
|
||||||
|
</circle>
|
||||||
|
<circle cx="22" cy="22" r="1">
|
||||||
|
<animate attributeName="r" begin="-0.9s" dur="1.8s" values="1; 20" calcMode="spline" keyTimes="0; 1" keySplines="0.165, 0.84, 0.44, 1" repeatCount="indefinite"/>
|
||||||
|
<animate attributeName="stroke-opacity" begin="-0.9s" dur="1.8s" values="1; 0" calcMode="spline" keyTimes="0; 1" keySplines="0.3, 0.61, 0.355, 1" repeatCount="indefinite"/>
|
||||||
|
</circle>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
28
R4.01_R4.A.10/td_tp/tp4/src/ex1/index.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="initial-scale=1,width=device-width">
|
||||||
|
<link rel="stylesheet" type="text/css" href="./css/style.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- vue html -->
|
||||||
|
<main>
|
||||||
|
<label>
|
||||||
|
<span>Search a movie</span>
|
||||||
|
<input type="input" placeholder="28 Days Later..">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="loader">
|
||||||
|
<img src="./img/puff.svg">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="error"></p>
|
||||||
|
<div id="list-movies">
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
<script type="module" src="./module/app.js"></script>
|
||||||
|
</html>
|
||||||
7
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/app.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
import model from '../module/model.js'
|
||||||
|
import Controller from '../module/controller.js'
|
||||||
|
import View from '../module/view.js'
|
||||||
|
|
||||||
|
const view = new View()
|
||||||
|
const app = new Controller(view,model)
|
||||||
26
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/controller.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
class Controller {
|
||||||
|
constructor(view,model){
|
||||||
|
this.view = view
|
||||||
|
this.model = model
|
||||||
|
|
||||||
|
this.loading = false
|
||||||
|
this.lastSearch = null
|
||||||
|
this.error = null
|
||||||
|
this.results = []
|
||||||
|
|
||||||
|
this.view.setLoading(false)
|
||||||
|
this.view.bindSearch(this.search.bind(this))
|
||||||
|
}
|
||||||
|
reset() {
|
||||||
|
this.loading = false
|
||||||
|
this.error = null
|
||||||
|
this.results = []
|
||||||
|
}
|
||||||
|
|
||||||
|
async search(searchMovie) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Controller
|
||||||
|
|
||||||
12
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/helpers.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Returns a function, that, as long as it continues to be invoked, will not
|
||||||
|
// be triggered. The function will be called after it stops being called for
|
||||||
|
// N milliseconds.
|
||||||
|
function debounce(fn, wait) {
|
||||||
|
let timeout
|
||||||
|
|
||||||
|
return (...args) => {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
timeout = setTimeout(() => fn(...args), wait)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default debounce
|
||||||
9
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/model.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
let apiKey = '2fcb2848'
|
||||||
|
|
||||||
|
let model = {
|
||||||
|
getMovies(search){
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default model
|
||||||
44
R4.01_R4.A.10/td_tp/tp4/src/ex1/module/view.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import debounce from "./helpers.js"
|
||||||
|
|
||||||
|
class View {
|
||||||
|
constructor(){
|
||||||
|
this.listMovies = document.querySelector("#list-movies")
|
||||||
|
this.inputSearch = document.querySelector("input")
|
||||||
|
this.loader = document.querySelector(".loader")
|
||||||
|
this.message = document.querySelector("p.error")
|
||||||
|
}
|
||||||
|
|
||||||
|
#getInput(){
|
||||||
|
return this.inputSearch.value
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(loading){
|
||||||
|
if (loading)
|
||||||
|
this.loader.style.display = "block"
|
||||||
|
else
|
||||||
|
this.loader.style.display = "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
renderMessage(error){
|
||||||
|
this.message.style.display = "block"
|
||||||
|
this.message.textContent = error
|
||||||
|
}
|
||||||
|
|
||||||
|
renderList(movies){
|
||||||
|
let ul = document.createElement("ul")
|
||||||
|
|
||||||
|
movies.forEach((movie)=>{
|
||||||
|
// TODO
|
||||||
|
})
|
||||||
|
|
||||||
|
this.listMovies.replaceChildren(ul)
|
||||||
|
}
|
||||||
|
|
||||||
|
bindSearch(handler){
|
||||||
|
this.inputSearch.addEventListener("input",debounce((e)=>{
|
||||||
|
handler(this.#getInput())
|
||||||
|
},500))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default View
|
||||||
36
R4.01_R4.A.10/td_tp/tp4/src/ex2/app.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import loader from './loader.js';
|
||||||
|
const board = document.querySelector("div.board");
|
||||||
|
const form = document.querySelector("form");
|
||||||
|
const message = document.querySelector("#message");
|
||||||
|
const ld = new loader(document.getElementById("loader"));
|
||||||
|
|
||||||
|
let url = 'https://dwarves.iut-fbleau.fr/foxes/foxes.php';
|
||||||
|
|
||||||
|
let foxes;
|
||||||
|
let size;
|
||||||
|
|
||||||
|
let newBoard = ( (size ) => {
|
||||||
|
let frag = document.createDocumentFragment();
|
||||||
|
for (let i = 0; i< size; i++){
|
||||||
|
let div = document.createElement("div");
|
||||||
|
for(let j = 0; j < size; j++){
|
||||||
|
let span = document.createElement("span");
|
||||||
|
span.dataset.num = size*i + j;
|
||||||
|
div.appendChild(span);
|
||||||
|
}
|
||||||
|
frag.appendChild(div);
|
||||||
|
}
|
||||||
|
board.replaceChildren(frag);
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
form.addEventListener("submit", (async (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
// TODO
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
board.addEventListener("click", ( async (ev) => {
|
||||||
|
// TODO
|
||||||
|
}))
|
||||||
54
R4.01_R4.A.10/td_tp/tp4/src/ex2/css/style.css
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
article {
|
||||||
|
text-align : center;
|
||||||
|
}
|
||||||
|
.board > div {
|
||||||
|
display : flex;
|
||||||
|
}
|
||||||
|
article .board {
|
||||||
|
display : inline-block;
|
||||||
|
}
|
||||||
|
.board span {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align:baseline;
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
/*padding : 1em;*/
|
||||||
|
margin : 0.1em;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid #444;
|
||||||
|
/*background: radial-gradient(rgb(150,250,150), #00ee00);*/
|
||||||
|
}
|
||||||
|
.board span:hover {
|
||||||
|
background: #3AB903;
|
||||||
|
background: radial-gradient(rgb(66, 255, 66), #065006);
|
||||||
|
}
|
||||||
|
|
||||||
|
.board span.on {
|
||||||
|
background: #009900;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.is-loading {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 999;
|
||||||
|
overflow: show;
|
||||||
|
margin: auto;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
div.is-loading:after {
|
||||||
|
animation: spinAround 500ms infinite linear;
|
||||||
|
border: 2px solid hsl(0deg, 0%, 86%);
|
||||||
|
border-radius: 9999px;
|
||||||
|
border-right-color: transparent;
|
||||||
|
border-top-color: transparent;
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
top: calc(50% - 5em);
|
||||||
|
left: calc(50% - 5em);
|
||||||
|
width: 10em;
|
||||||
|
height: 10em;
|
||||||
|
border-width: 0.25em;
|
||||||
|
}
|
||||||
35
R4.01_R4.A.10/td_tp/tp4/src/ex2/foxes.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title></title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css"
|
||||||
|
>
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<div id="loader" ></div>
|
||||||
|
|
||||||
|
<hgroup>
|
||||||
|
<h4>Foxes</h4>
|
||||||
|
<p>Le but est de tuer tous les renards en minimisant le nombre de coups joués.</p>
|
||||||
|
</hgroup>
|
||||||
|
<p id="message"></p>
|
||||||
|
<article>
|
||||||
|
<div class="board">
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
<form>
|
||||||
|
<fieldset role="group">
|
||||||
|
<input type="text" name="size" placeholder="Size">
|
||||||
|
<input type="text" name="foxes" placeholder="Foxes">
|
||||||
|
<input type="submit" value="New Game">
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
<script type="module" src="app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
12
R4.01_R4.A.10/td_tp/tp4/src/ex2/loader.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
class loader {
|
||||||
|
constructor(node){
|
||||||
|
this.node = node
|
||||||
|
}
|
||||||
|
set(loading){
|
||||||
|
if (loading)
|
||||||
|
this.node.classList.add("is-loading")
|
||||||
|
else
|
||||||
|
this.node.classList.remove("is-loading")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default loader
|
||||||
39
R4.01_R4.A.10/td_tp/tp5/README.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# TP javascript : riot.js
|
||||||
|
|
||||||
|
La [documentation](https://riot.js.org/) de riot.js
|
||||||
|
|
||||||
|
#### Ex1
|
||||||
|
Voici un [exemple](./src/ex1) d'application (todo list) écrite avec riot.js.
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="./img/todo.png">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Le but est de comprendre le code pour prendre en main Riot.js
|
||||||
|
|
||||||
|
- Complétez la fonction `add` qui permet d'ajouter une tâche.
|
||||||
|
- Faites en sorte que le bouton `clear done` soit desactivé quand il n'y a pas de tâches accomplies.
|
||||||
|
- Complétez la fonction `clear` qui retire les tâches accomplies.
|
||||||
|
- Mettez en oeuvre les filtres `All`, `Active` et `Done`.
|
||||||
|
- Complétez le code qui permet de supprimer une tâche.
|
||||||
|
- Ajoutez le nombre de tâches restantes.
|
||||||
|
- Modifiez le code pour que les tâches soient stockées localement (utilisez l'objet `localStorage`).
|
||||||
|
|
||||||
|
#### Ex2
|
||||||
|
Voici un [exemple](./src/ex2) d'application (radar chart) écrite avec riot.js.
|
||||||
|
<div align="center">
|
||||||
|
<img src="./img/radar.png">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
- Complétez les méthodes `add` et `remove`.
|
||||||
|
- Créez un nouveau composant `slider.riot`, qui vous utiliserez dans app.riot
|
||||||
|
|
||||||
|
#### Ex3
|
||||||
|
L'api DummyJSON permet de manipuler des données fictives. Ecrire, avec riotjs, une application qui permet :
|
||||||
|
|
||||||
|
- de récuperer depuis l'url `https://dummyjson.com/products` une liste de produits
|
||||||
|
- pour chaque produit, afficher le titre, prix et image.
|
||||||
|
- ajouter un filtre `prix < 100`, et un bouton `voir détails`.
|
||||||
|
|
||||||
BIN
R4.01_R4.A.10/td_tp/tp5/img/radar.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
R4.01_R4.A.10/td_tp/tp5/img/todo.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
28
R4.01_R4.A.10/td_tp/tp5/src/ex1/index.html
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<!doctype html>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Riot todo</title>
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<link rel="stylesheet" href="todo.css">
|
||||||
|
<script src="todo.riot" type="riot"></script>
|
||||||
|
<script src="https://unpkg.com/riot@10.1.2/riot+compiler.min.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<todo />
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
riot.compile().then(() => {
|
||||||
|
riot.mount('todo', {
|
||||||
|
title: 'I want to behave!',
|
||||||
|
todos: [
|
||||||
|
{ title: 'Avoid excessive caffeine', done: false ,id : 0},
|
||||||
|
{ title: 'Be less provocative', done: true , id:1},
|
||||||
|
{ title: 'Be nice to people', done: true, id:2}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
</html>
|
||||||
112
R4.01_R4.A.10/td_tp/tp5/src/ex1/todo.css
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'myriad pro', sans-serif;
|
||||||
|
font-size: 20px;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
todo {
|
||||||
|
display: block;
|
||||||
|
max-width: 500px;
|
||||||
|
margin: 5% auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
form input {
|
||||||
|
font-size: 85%;
|
||||||
|
padding: .4em;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: #1FADC5;
|
||||||
|
border: 1px solid rgba(0,0,0,.2);
|
||||||
|
font-size: 75%;
|
||||||
|
color: #fff;
|
||||||
|
padding: .4em 1.2em;
|
||||||
|
border-radius: 2em;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0 .23em;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[disabled] {
|
||||||
|
background-color: #ddd;
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style-type: none;
|
||||||
|
padding: .2em 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.completed {
|
||||||
|
text-decoration: line-through;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.filters {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
/* position: absolute;*/
|
||||||
|
right: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a,.filters li span {
|
||||||
|
color: inherit;
|
||||||
|
/* margin: 3px;*/
|
||||||
|
margin-right:10px;
|
||||||
|
padding: 3px 7px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size : 0.75em;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.filters li a:hover {
|
||||||
|
border-color: rgba(175, 47, 47, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters li a.selected {
|
||||||
|
border-color: rgba(175, 47, 47, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
li .destroy {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 10px;
|
||||||
|
bottom: 0;
|
||||||
|
/* width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin: auto 0;
|
||||||
|
font-size: 30px;*/
|
||||||
|
color: #cc9a9a;
|
||||||
|
/* margin-bottom: 11px;*/
|
||||||
|
transition: color 0.2s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
li .destroy:hover {
|
||||||
|
color: #af5b5e;
|
||||||
|
}
|
||||||
|
|
||||||
|
li .destroy:after {
|
||||||
|
content: '\274c';
|
||||||
|
}
|
||||||
|
|
||||||
|
li:hover .destroy {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
63
R4.01_R4.A.10/td_tp/tp5/src/ex1/todo.riot
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
<todo>
|
||||||
|
<h3>{ props.title }</h3>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li each={ todo in state.todos } key = {todo.id}>
|
||||||
|
<label class={ todo.done ? 'completed' : '' }>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked = { todo.done }
|
||||||
|
onclick = { () => toggle(todo) } />
|
||||||
|
{ todo.title }
|
||||||
|
</label>
|
||||||
|
<span class="destroy" onclick={() => remove(todo)}></span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<form onsubmit={ add }>
|
||||||
|
<input oninput={ edit } />
|
||||||
|
<button disabled={ !state.text }>
|
||||||
|
Add #{ state.todos.length + 1 }
|
||||||
|
</button>
|
||||||
|
<button onclick={ clear }>Clear done</button>
|
||||||
|
<ul class="filters">
|
||||||
|
<li><span>0 todos left</span> </li>
|
||||||
|
<li> <a href="#"> All</a></li>
|
||||||
|
<li> <a href="#">Active</a></li>
|
||||||
|
<li> <a href="#">Done</a></li>
|
||||||
|
</ul>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
onBeforeMount(props, state) {
|
||||||
|
// initial state
|
||||||
|
this.state = {
|
||||||
|
todos: props.todos,
|
||||||
|
text: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
edit(e) {
|
||||||
|
// update only the text state
|
||||||
|
this.update({
|
||||||
|
text: e.target.value
|
||||||
|
})
|
||||||
|
},
|
||||||
|
clear(e) {
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
},
|
||||||
|
add(e) {
|
||||||
|
// TODO
|
||||||
|
},
|
||||||
|
toggle(todo) {
|
||||||
|
todo.done = !todo.done
|
||||||
|
this.update()
|
||||||
|
},
|
||||||
|
remove(todo){
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</todo>
|
||||||
18
R4.01_R4.A.10/td_tp/tp5/src/ex2/README.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Riot4 Radar Chart
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
First of all, plz execute this commands.
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
$ cd ../
|
||||||
|
$ yarn install
|
||||||
|
$ yarn radar
|
||||||
|
```
|
||||||
|
|
||||||
|
Perhaps the browser will open automatically and the application should start.
|
||||||
|
|
||||||
|
URL: [http://127.0.0.1:8080/](http://127.0.0.1:8080/)
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
demo: [CodeSandbox](https://codesandbox.io/embed/riot4-template-0f6i0)
|
||||||
62
R4.01_R4.A.10/td_tp/tp5/src/ex2/app.riot
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<app>
|
||||||
|
<h2>{ props.title }</h2>
|
||||||
|
<div id="radar">
|
||||||
|
|
||||||
|
<!-- Use the component -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
is="polygraph"
|
||||||
|
stats={ state.stats } />
|
||||||
|
|
||||||
|
|
||||||
|
<!-- controls -->
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div each={ (stat, index) in state.stats }>
|
||||||
|
<label>{ stat.label }</label>
|
||||||
|
<input type="range"
|
||||||
|
onchange={ changeValue }
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
data-index={ index }
|
||||||
|
value={ stat.value }>
|
||||||
|
<span>{ stat.value }</span>
|
||||||
|
<button onclick={ remove } class="remove" value={ index }>X</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- add item -->
|
||||||
|
<form id="add">
|
||||||
|
<input type="text"
|
||||||
|
name="newlabel"
|
||||||
|
value={ state.newLabel }
|
||||||
|
oninput={ inputStat }>
|
||||||
|
<button onclick={ add }>Add a Stat</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
state: {
|
||||||
|
newLabel: '',
|
||||||
|
stats: []
|
||||||
|
},
|
||||||
|
onBeforeMount(props, state) {
|
||||||
|
state.stats = props.stats
|
||||||
|
},
|
||||||
|
add(e) {
|
||||||
|
// TODO
|
||||||
|
},
|
||||||
|
remove(stat) {
|
||||||
|
// TODO
|
||||||
|
},
|
||||||
|
inputStat(e) {
|
||||||
|
this.state.newLabel = e.target.value
|
||||||
|
},
|
||||||
|
changeValue(e) {
|
||||||
|
this.state.stats[e.target.dataset.index].value = e.target.value
|
||||||
|
this.update()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</app>
|
||||||
31
R4.01_R4.A.10/td_tp/tp5/src/ex2/components/polygraph.riot
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<polygraph>
|
||||||
|
<g>
|
||||||
|
<polygon points={ points() }></polygon>
|
||||||
|
<circle cx="200" cy="200" r="160"></circle>
|
||||||
|
<text
|
||||||
|
each={ (stat, index) in state.stats }
|
||||||
|
index={ index }
|
||||||
|
stat={ stat }
|
||||||
|
total={ state.stats.length }
|
||||||
|
is="text" />
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
state: {
|
||||||
|
stats: []
|
||||||
|
},
|
||||||
|
onBeforeMount(props, state) {
|
||||||
|
state.stats = props.stats
|
||||||
|
},
|
||||||
|
// a computed property for the polygon's points
|
||||||
|
points() {
|
||||||
|
const total = this.state.stats.length
|
||||||
|
return this.state.stats.map((stat, i) => {
|
||||||
|
const point = valueToPoint(stat.value, i, total)
|
||||||
|
return point.x + ',' + point.y
|
||||||
|
}).join(' ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</polygraph>
|
||||||
25
R4.01_R4.A.10/td_tp/tp5/src/ex2/components/text.riot
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<text x={ state.point.x } y={ state.point.y }>
|
||||||
|
{ props.stat.label }
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
state: {
|
||||||
|
point: ''
|
||||||
|
},
|
||||||
|
onBeforeMount(props, state) {
|
||||||
|
state.point = valueToPoint(
|
||||||
|
+props.stat.value + 10,
|
||||||
|
props.index,
|
||||||
|
props.total
|
||||||
|
)
|
||||||
|
},
|
||||||
|
onBeforeUpdate(props, state) {
|
||||||
|
state.point = valueToPoint(
|
||||||
|
+props.stat.value + 10,
|
||||||
|
props.index,
|
||||||
|
props.total
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</text>
|
||||||
13
R4.01_R4.A.10/td_tp/tp5/src/ex2/helper.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
const valueToPoint = (value, index, total) => {
|
||||||
|
const x = 0
|
||||||
|
const y = -value * 1.6
|
||||||
|
const angle = Math.PI * 2 / total * index
|
||||||
|
const cos = Math.cos(angle)
|
||||||
|
const sin = Math.sin(angle)
|
||||||
|
const tx = x * cos - y * sin + 200
|
||||||
|
const ty = x * sin + y * cos + 200
|
||||||
|
return {
|
||||||
|
x: tx,
|
||||||
|
y: ty
|
||||||
|
}
|
||||||
|
}
|
||||||