Compare commits
33 Commits
186b3839f0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ca8ce10644 | |||
| ce835fb93c | |||
| 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)
|
||||
|
||||
#### 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.
|
||||
18
R4.01_R4.A.10/miniprojet/groupes.md
Normal file
@@ -0,0 +1,18 @@
|
||||
## 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 / Haffa BAKHOUCHE
|
||||
- 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
|
||||
- Ayoub ANHDIRE / Algassimou Pellel DIALLO
|
||||
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
|
||||
}
|
||||
}
|
||||