Compare commits

..

29 Commits

Author SHA1 Message Date
88af050bea maj grps 2026-03-21 15:21:09 +01:00
2373233899 maj grps 2026-03-20 14:30:20 +01:00
8239d8024d maj grps 2026-03-20 14:13:50 +01:00
1586d02094 maj grps 2026-03-20 09:29:42 +01:00
2fe4bab887 maj liste cours 2026-03-20 09:28:55 +01:00
eb792edb4d en route 2026-03-20 09:26:06 +01:00
1a450491c6 maj 2026-03-18 14:10:08 +01:00
a472e34bbe todo riot, avec router 2026-03-16 14:22:17 +01:00
0490924e3d api cm 2026-03-13 13:52:08 +01:00
db7896d2de tp6 2026-03-13 12:08:01 +01:00
bd2243e28a groupes projet 2026-03-09 16:11:08 +01:00
1c7d7f636a ajout exo tp5 2026-03-09 14:55:40 +01:00
1fe20f3f3b groupes projet 2026-03-09 09:23:32 +01:00
7f4884a90d typo 2026-02-24 13:36:21 +01:00
532f73309c typo 2026-02-24 13:30:01 +01:00
129a0c321c typo 2026-02-24 13:26:40 +01:00
ef4ddb1420 miniprojet 2026-02-24 11:49:23 +01:00
da6a720f46 riotjs 2026-02-20 12:08:56 +01:00
6d7a783d73 pb directory 2026-02-20 12:05:24 +01:00
6cb1a005fd tp5 2026-02-20 12:04:02 +01:00
d99fa137ec typo 2026-02-16 11:16:10 +01:00
534fc8ace1 exo2 2026-02-16 11:14:11 +01:00
783e91969c js 2026-02-14 20:19:44 +01:00
226c55aca3 oups 2026-02-13 15:41:09 +01:00
755c3d4e2e typo 2026-02-13 11:23:51 +01:00
c875a397ad typo 2026-02-13 11:22:25 +01:00
5034bf1248 image 2026-02-13 11:19:37 +01:00
322c2a7e7f README 2026-02-13 11:17:37 +01:00
7aa9c51104 tp4 2026-02-13 11:16:49 +01:00
72 changed files with 5750 additions and 0 deletions

BIN
R1.02/cours/js.pdf Normal file

Binary file not shown.

43
R3.01/README.md Normal file
View 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 laccè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.

Binary file not shown.

View File

@@ -6,4 +6,17 @@
#### Semaine 2 #### Semaine 2
[DOM](cours/dom.pdf), [tp2](./td_tp/tp2), [tp2mvc](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)

Binary file not shown.

BIN
R4.01_R4.A.10/cours/api.pdf Normal file

Binary file not shown.

Binary file not shown.

View 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.

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View 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).
![movie](img/search.png?style=centerme)
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.

View 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)
}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

View 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;
}

View 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

View 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>

View 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)

View 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

View 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

View File

@@ -0,0 +1,9 @@
let apiKey = '2fcb2848'
let model = {
getMovies(search){
// TODO
}
}
export default model

View 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

View 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
}))

View 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;
}

View 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>

View 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

View 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`.

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View 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>

View 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;
}

View 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>

View 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)

View 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>

View 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>

View 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>

View 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
}
}

View File

@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Riot.js Radar Chart</title>
<link rel="stylesheet" href="style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/riot/10.0.1/riot+compiler.min.js"></script>
<script src="helper.js"></script>
</head>
<body>
<!-- app -->
<app></app>
<!-- tags -->
<script data-src="app.riot" type="riot"></script>
<script data-src="components/polygraph.riot" type="riot"></script>
<script data-src="components/text.riot" type="riot"></script>
<script>
riot
.compile()
.then(() => {
riot.mount('app', {
title: 'Riot.js Radar Chart',
stats: [
{ label: 'A', value: 100 },
{ label: 'B', value: 100 },
{ label: 'C', value: 100 },
{ label: 'D', value: 100 },
{ label: 'E', value: 100 },
{ label: 'F', value: 100 }
]
})
})
.catch(e => {
console.error(e)
})
</script>
</body>
</html>

View File

@@ -0,0 +1,46 @@
body {
font-family: Helvetica Neue, Arial, sans-serif;
}
#radar{
display : flex;
}
svg {
width:400px;
height:400px;
}
polygon {
fill: #ED1646;
opacity: .75;
}
circle {
fill: transparent;
stroke: #999;
}
text {
font-family: Futura, Helvetica Neue, Arial, sans-serif;
font-size: 10px;
fill: #666;
}
label {
display: inline-block;
margin-left: 10px;
width: 20px;
}
#raw {
/* position: absolute;
top: 0;
left: 300px;*/
}
#add {
margin-top: 1em;
}
.remove {
margin: 0 4px;
}

View File

@@ -0,0 +1,109 @@
# TP : Une api rest pour la todo list.
Le but de l'exercice est d'écrire une api de données restful pour l'application todolist du
[tp2](../tp2mvc) ou [tp5](../tp5).
Les routes de notre api :
```
get /todo/(:id)
post /todo
delete /todo/:id
put /todo/:id
```
- Pour routage et les entrées/sorties http, on utilise [flight php](https://flightphp.com/).
- Pour les entrées/sorties avec la base données, je vous donne un son utilise l'orm [readbean php](https://www.redbeanphp.com/index.php).
Copiez le fichier `.htaccess` à la racine de vos sources pour activer la réécriture d'urls.
```apache
Require method GET POST PUT DELETE OPTIONS
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
```
Votre api pour l'instant
```php
<?php
require 'flight/Flight.php';
require 'model/model.php';
Flight::route('GET /todo(/@id)','getTodos');
Flight::route('POST /todo','addTodo');
Flight::route('DELETE /todo/@id','deleteTodo');
Flight::route('PUT /todo/@id','updateTodo');
function deleteTodo($id)
{
// TODO
}
function updateTodo($id)
{
// TODO
}
function addTodo()
{
$todo = [
"title" => Flight::request()->data->title ,
"done" => Flight::request()->data->done
];
$id = Todo::create($todo);
Flight::response()->header("Location",Flight::request()->url.$id);
$todo['id'] = $id;
Flight::json($todo,201);
}
function getTodos($id = null)
{
$filter = Flight::request()->query->filter ?? "all";
if ($id === null){
switch($filter){
case "done":
$todos = Todo::findCompleted();
break;
case "active":
$todos = Todo::findUnCompleted();
break;
default:
$todos = Todo::findAll();
}
Flight::json(
[
"results" => $todos
]
);
} else {
$todo = Todo::find($id);
if ($todo)
Flight::json($todo);
else
Flight::halt(404);
}
}
Flight::start();
```
Le modèle utilise PDO, en php. Il vous faudra créér une table avec les attributs nécessaires.
1. Complétez le fichier index.php
2. Testez votre api à la ligne de commandes en utilisant `curl`.
3. Connectez votre application todolist avec l'api.
4. Connectez votre api à l'application [todo](./src/todo-riot). Il faut
complétez le fichier api.js. Prenez soin de modifier le fichier `htaccess`.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,144 @@
<?php
declare(strict_types=1);
use flight\Engine;
use flight\net\Request;
use flight\net\Response;
use flight\net\Router;
use flight\template\View;
use flight\net\Route;
require_once __DIR__ . '/autoload.php';
/**
* The Flight class is a static representation of the framework.
*
* @license MIT, http://flightphp.com/license
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
*
* # Core methods
* @method static void start() Starts the framework.
* @method static void path(string $path) Adds a path for autoloading classes.
* @method static void stop(?int $code = null) Stops the framework and sends a response.
* @method static void halt(int $code = 200, string $message = '', bool $actuallyExit = true)
* Stop the framework with an optional status code and message.
* @method static void register(string $name, string $class, array $params = [], ?callable $callback = null)
* Registers a class to a framework method.
* @method static void unregister(string $methodName)
* Unregisters a class to a framework method.
* @method static void registerContainerHandler(callable|object $containerHandler) Registers a container handler.
*
* # Routing
* @method static Route route(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
* Maps a URL pattern to a callback with all applicable methods.
* @method static void group(string $pattern, callable $callback, callable[] $group_middlewares = [])
* Groups a set of routes together under a common prefix.
* @method static Route post(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
* Routes a POST URL to a callback function.
* @method static Route put(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
* Routes a PUT URL to a callback function.
* @method static Route patch(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
* Routes a PATCH URL to a callback function.
* @method static Route delete(string $pattern, callable|string $callback, bool $pass_route = false, string $alias = '')
* Routes a DELETE URL to a callback function.
* @method static Router router() Returns Router instance.
* @method static string getUrl(string $alias, array<string, mixed> $params = []) Gets a url from an alias
*
* @method static void map(string $name, callable $callback) Creates a custom framework method.
*
* @method static void before(string $name, Closure(array<int, mixed> &$params, string &$output): (void|false) $callback)
* Adds a filter before a framework method.
* @method static void after(string $name, Closure(array<int, mixed> &$params, string &$output): (void|false) $callback)
* Adds a filter after a framework method.
*
* @method static void set(string|iterable<string, mixed> $key, mixed $value) Sets a variable.
* @method static mixed get(?string $key) Gets a variable.
* @method static bool has(string $key) Checks if a variable is set.
* @method static void clear(?string $key = null) Clears a variable.
*
* # Views
* @method static void render(string $file, ?array<string, mixed> $data = null, ?string $key = null)
* Renders a template file.
* @method static View view() Returns View instance.
*
* # Request-Response
* @method static Request request() Returns Request instance.
* @method static Response response() Returns Response instance.
* @method static void redirect(string $url, int $code = 303) Redirects to another URL.
* @method static void json(mixed $data, int $code = 200, bool $encode = true, string $charset = "utf8", int $encodeOption = 0, int $encodeDepth = 512)
* Sends a JSON response.
* @method static void jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = "utf8", int $encodeOption = 0, int $encodeDepth = 512)
* Sends a JSONP response.
* @method static void error(Throwable $exception) Sends an HTTP 500 response.
* @method static void notFound() Sends an HTTP 404 response.
*
* # HTTP caching
* @method static void etag(string $id, ('strong'|'weak') $type = 'strong') Performs ETag HTTP caching.
* @method static void lastModified(int $time) Performs last modified HTTP caching.
*/
class Flight
{
/** Framework engine. */
private static Engine $engine;
/** Whether or not the app has been initialized. */
private static bool $initialized = false;
/**
* Don't allow object instantiation
*
* @codeCoverageIgnore
* @return void
*/
private function __construct()
{
}
/**
* Forbid cloning the class
*
* @codeCoverageIgnore
* @return void
*/
private function __clone()
{
}
/**
* Handles calls to static methods.
*
* @param string $name Method name
* @param array<int, mixed> $params Method parameters
*
* @return mixed Callback results
* @throws Exception
*/
public static function __callStatic(string $name, array $params)
{
return self::app()->{$name}(...$params);
}
/** @return Engine Application instance */
public static function app(): Engine
{
if (!self::$initialized) {
require_once __DIR__ . '/autoload.php';
self::setEngine(new Engine());
self::$initialized = true;
}
return self::$engine;
}
/**
* Set the engine instance
*
* @param Engine $engine Vroom vroom!
*/
public static function setEngine(Engine $engine): void
{
self::$engine = $engine;
}
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
use flight\core\Loader;
require_once __DIR__ . '/Flight.php';
require_once __DIR__ . '/core/Loader.php';
Loader::autoload(true, [dirname(__DIR__)]);

View File

@@ -0,0 +1,91 @@
<?php
declare(strict_types=1);
namespace flight\commands;
use Nette\PhpGenerator\ClassType;
use Nette\PhpGenerator\PhpFile;
use Nette\PhpGenerator\PhpNamespace;
class ControllerCommand extends AbstractBaseCommand
{
/**
* Construct
*
* @param array<string,mixed> $config JSON config from .runway-config.json
*/
public function __construct(array $config)
{
parent::__construct('make:controller', 'Create a controller', $config);
$this->argument('<controller>', 'The name of the controller to create (with or without the Controller suffix)');
}
/**
* Executes the function
*
* @return void
*/
public function execute(string $controller)
{
$io = $this->app()->io();
if (isset($this->config['app_root']) === false) {
$io->error('app_root not set in .runway-config.json', true);
return;
}
if (!preg_match('/Controller$/', $controller)) {
$controller .= 'Controller';
}
$controllerPath = getcwd() . DIRECTORY_SEPARATOR . $this->config['app_root'] . 'controllers' . DIRECTORY_SEPARATOR . $controller . '.php';
if (file_exists($controllerPath) === true) {
$io->error($controller . ' already exists.', true);
return;
}
if (is_dir(dirname($controllerPath)) === false) {
$io->info('Creating directory ' . dirname($controllerPath), true);
mkdir(dirname($controllerPath), 0755, true);
}
$file = new PhpFile();
$file->setStrictTypes();
$namespace = new PhpNamespace('app\\controllers');
$namespace->addUse('flight\\Engine');
$class = new ClassType($controller);
$class->addProperty('app')
->setVisibility('protected')
->setType('flight\\Engine')
->addComment('@var Engine');
$method = $class->addMethod('__construct')
->addComment('Constructor')
->setVisibility('public')
->setBody('$this->app = $app;');
$method->addParameter('app')
->setType('flight\\Engine');
$namespace->add($class);
$file->addNamespace($namespace);
$this->persistClass($controller, $file);
$io->ok('Controller successfully created at ' . $controllerPath, true);
}
/**
* Saves the class name to a file
*
* @param string $controllerName Name of the Controller
* @param PhpFile $file Class Object from Nette\PhpGenerator
*
* @return void
*/
protected function persistClass(string $controllerName, PhpFile $file)
{
$printer = new \Nette\PhpGenerator\PsrPrinter();
file_put_contents(getcwd() . DIRECTORY_SEPARATOR . $this->config['app_root'] . 'controllers' . DIRECTORY_SEPARATOR . $controllerName . '.php', $printer->printFile($file));
}
}

View File

@@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
namespace flight\commands;
use Flight;
use flight\net\Route;
/**
* @property-read ?bool $get
* @property-read ?bool $post
* @property-read ?bool $delete
* @property-read ?bool $put
* @property-read ?bool $patch
*/
class RouteCommand extends AbstractBaseCommand
{
/**
* Construct
*
* @param array<string,mixed> $config JSON config from .runway-config.json
*/
public function __construct(array $config)
{
parent::__construct('routes', 'Gets all routes for an application', $config);
$this->option('--get', 'Only return GET requests');
$this->option('--post', 'Only return POST requests');
$this->option('--delete', 'Only return DELETE requests');
$this->option('--put', 'Only return PUT requests');
$this->option('--patch', 'Only return PATCH requests');
}
/**
* Executes the function
*
* @return void
*/
public function execute()
{
$io = $this->app()->io();
if (isset($this->config['index_root']) === false) {
$io->error('index_root not set in .runway-config.json', true);
return;
}
$io->bold('Routes', true);
$cwd = getcwd();
$index_root = $cwd . '/' . $this->config['index_root'];
// This makes it so the framework doesn't actually execute
Flight::map('start', function () {
return;
});
include($index_root);
$routes = Flight::router()->getRoutes();
$arrayOfRoutes = [];
foreach ($routes as $route) {
if ($this->shouldAddRoute($route) === true) {
$middlewares = [];
if (!empty($route->middleware)) {
try {
$middlewares = array_map(function ($middleware) {
$middleware_class_name = explode("\\", get_class($middleware));
return preg_match("/^class@anonymous/", end($middleware_class_name)) ? 'Anonymous' : end($middleware_class_name);
}, $route->middleware);
} catch (\TypeError $e) {
$middlewares[] = 'Bad Middleware';
} finally {
if (is_string($route->middleware) === true) {
$middlewares[] = $route->middleware;
}
}
}
$arrayOfRoutes[] = [
'Pattern' => $route->pattern,
'Methods' => implode(', ', $route->methods),
'Alias' => $route->alias ?? '',
'Streamed' => $route->is_streamed ? 'Yes' : 'No',
'Middleware' => !empty($middlewares) ? implode(",", $middlewares) : '-'
];
}
}
$io->table($arrayOfRoutes, [
'head' => 'boldGreen'
]);
}
/**
* Whether or not to add the route based on the request
*
* @param Route $route Flight Route object
*
* @return boolean
*/
public function shouldAddRoute(Route $route)
{
$boolval = false;
$showAll = !$this->get && !$this->post && !$this->put && !$this->delete && !$this->patch;
if ($showAll === true) {
$boolval = true;
} else {
$methods = [ 'GET', 'POST', 'PUT', 'DELETE', 'PATCH' ];
foreach ($methods as $method) {
$lowercaseMethod = strtolower($method);
if (
$this->{$lowercaseMethod} === true &&
(
$route->methods[0] === '*' ||
in_array($method, $route->methods, true) === true
)
) {
$boolval = true;
break;
}
}
}
return $boolval;
}
}

Some files were not shown because too many files have changed in this diff Show More