diff --git a/R4.01_R4.A.10/README.md b/R4.01_R4.A.10/README.md
index 6e2786d..c3c6f44 100644
--- a/R4.01_R4.A.10/README.md
+++ b/R4.01_R4.A.10/README.md
@@ -3,4 +3,7 @@
 #### Semaine 1 
 [Compléments de javascript](cours/jscomp.pdf) javascript, [tp1](./td_tp/tp1)
 
+#### Semaine 2 
+[DOM](cours/dom.pdf), [tp2](./td_tp/tp2)
+
 
diff --git a/R4.01_R4.A.10/cours/dom.pdf b/R4.01_R4.A.10/cours/dom.pdf
new file mode 100644
index 0000000..f5834c3
Binary files /dev/null and b/R4.01_R4.A.10/cours/dom.pdf differ
diff --git a/R4.01_R4.A.10/td_tp/tp2/README.md b/R4.01_R4.A.10/td_tp/tp2/README.md
new file mode 100644
index 0000000..1ae0688
--- /dev/null
+++ b/R4.01_R4.A.10/td_tp/tp2/README.md
@@ -0,0 +1,211 @@
+# TP javascript : DOM
+
+Un peu [d'aide](./aide.md).
+#### Ex1 : manipulation du dom
+
+Vous devez  compléter les [sources](./src/ex1)  d'un jeu qui consiste  à essayer
+d'éteindre  toutes les  lumières d'une  grille en  utilisant la  règle suivante.
+Quand on allume/éteint une lumière, on allume/éteint aussi ses voisines.
+
+<div align="center">
+<img src="./img/lights.png">
+</div>
+
+Chaque lumière, via l'interface dataset, possède un numéro (de 0 à size^2-1).
+#### Ex2 : une vue html à partir de données js
+
+Le fichier [data.js](./src/ex2/data.js) déclare un tableau contenant le classement de la ligue de football,  récupéré depuis l'api [the sport db](https://www.thesportsdb.com/).
+
+```js
+let data = 	[
+	{
+	"idStanding":"2282066",
+	"intRank":"1",
+	"idTeam":"133714",
+	"strTeam":"Paris SG",
+	"strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/rwqrrq1473504808.png/tiny",
+	"idLeague":"4334",
+	"strLeague":"French Ligue 1",
+	"strSeason":"2022-2023",
+	"strForm":"WLWWW",
+	"strDescription":"Promotion - Champions League (Group Stage)",
+	"intPlayed":"18",
+	"intWin":"15",
+	"intLoss":"1",
+	"intDraw":"2",
+	"intGoalsFor":"48",
+	"intGoalsAgainst":"13",
+	"intGoalDifference":"35",
+	"intPoints":"47",
+	"dateUpdated":"2023-01-12 23:01:12"
+	},
+	...
+]
+```
+
+Chaque entrée est une objet json contenant différents champs. 
+
+Le but de l'exercice est de construire une vue de ces données sous forme d'une table html.
+
+<div align="center">
+<img src="./img/ligue1.png">
+</div>
+
+Un [squelette](./src/ex2/index.html)
+
+- la colonne du rang sera triable.
+- un champ de recherche permettra de filtrer les lignes du classement selon le nom d'équipe.
+
+
+> - Il faut itérer le tableau data, construire et ajouter au  `tbody`  de la table les lignes nécessaires.
+> - Ajouter ensuite le traitement évenementielle pour le tri de la première colonne. On pourra effacer et recréer la vue.
+> - Ajouter la possibilité de recherche à partir de champ de recherche.
+
+#### Ex3 : modele MVC
+Le but est d'écrire une todolist en javascript, en respectant le pattern MVC. Il est important de mener à bout cet exercice, car il nous servira
+de fil rouge notamment en ajoutant une api rest et ajax pour la communication avec le service.
+
+<div align="center">
+<img src="./img/todo.png">
+</div>
+
+Le contrôleur a accès à la vue et au modèle.
+
+##### Le modèle
+Cette "classe" (rien à compléter) utilise l'objet [localStorage](https://developer.mozilla.org/fr/docs/Web/API/Window/localStorage) pour sauvegarder la todolist.
+Chaque todo est un objet json de la forme 
+
+```js
+{ id : 1 , text : "apprendre le js", done : false }
+```
+
+La liste des méthodes publiques de cette classe
+
+```js
+/** @brief return the todolist
+ *  @param filter "all" or "active" or "done"
+ *  @return  array of todos
+ */
+getTodos(filter)
+
+/** @brief add (and save) a new todo with todoText
+ *  @param todoText : text of the new todo
+ *  @return  the new todo 
+ */
+add(todoText)
+
+/** @brief delete a todo 
+ *  @param id id of the todo 
+ */
+delete(id)
+
+/** @brief update a todo 
+ *  @param id id of the todo 
+ */
+edit(id,updatedText)
+
+/** @brief toggle  a todo (done<->active)
+ *  @param id id of the todo 
+ *  @param updatedText  text of the todo
+ */
+toggle(id)
+```
+
+##### La vue
+Cette "classe" permet au contrôleur de gérer la vue. 
+
+
+Liste des méthodes publiques
+
+```js
+/** @brief change the active tab (all,active or done)
+ *  @param filter the active tab (all, active or done)
+ */
+setFilterTabs(filter)
+
+/** @brief update the todo list with todos
+ *  @param todos array of todo
+ */ 
+renderTodoList(todos)
+```
+
+Le contrôleur peut s'abonner (notification de la vue au acontrôleur) aux événements suivant :
+
+add/delete/edit/toggle   todo : avec `bind{Add|Delete|Edit|Toggle}Todo`
+
+Ces méthodes d'abonnement prennent en paramètre une fonction du contrôleur qui est appelé par la vue pour lui notifier
+une requête.
+
+```js
+/** @brief subscribe to add event 
+ *  @param handler function(text)
+ */
+bindAddTodo(handler)
+
+/** @brief suscribe to delete event 
+ *  @param handler function(id) 
+ */
+bindDeleteTodo(handler)
+
+/** @brief suscribe to edit event
+ *  @param handler function(id,text)
+ */ 
+bindEditTodo(handler)
+
+/** @brief suscribe to toggle event
+ *  @param handler function(id)
+ */ 
+bindToggleTodo(handler)
+```
+
+##### Le contrôleur
+C'est lui qui gére le routage. Il y a trois urls possibles `index.html/#/{all|active|done}` (listener sur l'évenement 
+		dom `hashchange`.
+
+Liste des méthodes 
+```js
+/** @brief get the todolist from the model
+ *         and use the view to render 
+ */ 
+filterTodoList()
+
+/** @brief binding function called by the view
+ *         to add a new todo
+ *  @param text text of the new todo
+ */
+addTodo(text)
+
+/** @brief binding function called by the view
+ *         to delete a todo
+ *  @param id id of the todo
+*/
+deleteTodo(id)
+
+/** @brief binding function to toggle the state
+ *         of a todo
+ *  @param id id of the todo
+ */
+toggleTodo(id)
+
+/** @brief binding function to change the text
+ *         of a todo
+ *  @param id id of the todo
+ *  @param changedText new text for the todo
+*/
+editTodo(id,changedText)
+```
+
+Par exemple, lorsque l'utilisateur ajoute une todo :
+
+1. la vue est sensible à l'événement de soumission du formulaire,
+2. la fonction reflexe récupére le texte saisie,
+3. elle appelle la fonction avec laquelle le contrôleur s'est abonné, en lui passant le texte,
+4. la fonction du contrôleur `addTodo` récupére le texte,
+5. le contrôleur demande au modèle la création d'une nouvelle todo,
+6. le contrôleur demande un rafraichissement de la vue.
+
+
+#### Travail à faire
+- Compléter la méthode `bindDeleteTodo` de la vue.
+- Compléter la méthode `bindToggleTodo` de la vue.
+-  Compléter la méthode `bindEditTodo` de la vue.
diff --git a/R4.01_R4.A.10/td_tp/tp2/aide.md b/R4.01_R4.A.10/td_tp/tp2/aide.md
new file mode 100644
index 0000000..f2ce6f8
--- /dev/null
+++ b/R4.01_R4.A.10/td_tp/tp2/aide.md
@@ -0,0 +1,132 @@
+#### Selection d'éléments
+
+```js
+// le premier 
+document.querySelector(".box")
+
+// ou tous
+document.querySelectorAll(".box")
+
+// avec l'id
+
+document.getElementById("toto")
+
+// selection d'un élément dans un autre
+
+let container = document.querySeletor('.container')
+container.querySelector('.box')
+```
+
+#### Traverser le dom
+
+```js
+var box = document.querySelector(".box")
+box.nextElementSibling
+box.previousElementSibling
+box.parentElement
+box.childNodes
+```
+
+#### Création/insertion d'éléments
+
+```js
+let p = document.createElement('p')
+p.textContent="blabla"
+document
+   .getElementById("myDiv")
+   .appendChild(p)
+
+div.replaceChildren(p)
+```
+
+
+#### Gestionnaire évènementiels
+
+```js
+document.querySelector(".button").addEventListener("click", (e) => { /* ... */ })
+document.querySelector(".button").addEventListener("mouseenter", (e) => { /* ... */ })
+document.addEventListener("keyup", (e) => { /* ... */ })
+```
+
+#### window/document prêt
+
+```js
+
+document.addEventListener("DOMContentLoaded",() = > { .....})
+// le dom a été construit (on n'attend pas le chargement du css, images, etc.
+
+windon.addEventListener('load',()=>{...})
+// le dom est prêt et toutes les ressources ont été chargées.
+```
+
+#### dataset
+```html
+<div id="user" data-id="1234567890" data-user="carinaanand" data-date-of-birth>
+  Carina Anand
+</div>
+```
+
+```javascript
+const el = document.querySelector("#user");
+
+// el.id === 'user'
+// el.dataset.id === '1234567890'
+// el.dataset.user === 'carinaanand'
+// el.dataset.dateOfBirth === ''
+```
+#### Local storage
+
+```js
+
+localStorage.setItem('monChat', 'Tom')
+let cat =  localStorage.getItem('myCat')
+localStorage.clear()
+```
+
+#### Limiter les appels successifs à une fonction
+
+```js
+function debounce(f,wait)
+{
+	let timeout 
+	return function(...args){
+		clearTimeout(timeout)
+		timeout=setTimeout(()=>f(...args),wait)
+	}
+}
+```
+
+#### Gestion du css depuis le DOM
+
+```js
+// Select the first .box and change its text color to #000
+document.querySelector(".box").style.color = "#000";
+
+// Set color to #000 and background to red
+var box = document.querySelector(".box")
+box.style.color = "#000"
+box.style.backgroundColor = "red"
+
+// Set all styles at once (and override any existing styles)
+box.style.cssText = "color: #000; background-color: red"
+
+
+// Hide and show an element by changing "display" to block and none
+document.querySelector(".box").style.display = "none"
+document.querySelector(".box").style.display = "block"
+
+// Add, remove, and the toggle the "focus" class
+var box = document.querySelector(".box")
+box.classList.add("focus")
+box.classList.remove("focus")
+box.classList.toggle("focus")
+
+
+box.classList.add("focus", "highlighted")
+box.classList.remove("focus", "highlighted")
+
+box.classList.add("focus", "highlighted")
+box.classList.remove("focus", "highlighted")
+
+```
+
diff --git a/R4.01_R4.A.10/td_tp/tp2/img/lights.png b/R4.01_R4.A.10/td_tp/tp2/img/lights.png
new file mode 100644
index 0000000..917547e
Binary files /dev/null and b/R4.01_R4.A.10/td_tp/tp2/img/lights.png differ
diff --git a/R4.01_R4.A.10/td_tp/tp2/img/ligue1.png b/R4.01_R4.A.10/td_tp/tp2/img/ligue1.png
new file mode 100644
index 0000000..a73f1c4
Binary files /dev/null and b/R4.01_R4.A.10/td_tp/tp2/img/ligue1.png differ
diff --git a/R4.01_R4.A.10/td_tp/tp2/img/todo.png b/R4.01_R4.A.10/td_tp/tp2/img/todo.png
new file mode 100644
index 0000000..7478a61
Binary files /dev/null and b/R4.01_R4.A.10/td_tp/tp2/img/todo.png differ
diff --git a/R4.01_R4.A.10/td_tp/tp2/src/ex1/app.js b/R4.01_R4.A.10/td_tp/tp2/src/ex1/app.js
new file mode 100644
index 0000000..6aa4937
--- /dev/null
+++ b/R4.01_R4.A.10/td_tp/tp2/src/ex1/app.js
@@ -0,0 +1,72 @@
+// variables de vue
+
+const button = document.querySelector("input[type=submit]")
+const select = document.querySelector("select")
+const board = document.querySelector(".board")
+const message = document.querySelector("#message")
+let lights = null 
+
+// state <-> tableau de booléen pour 
+// stocker l'état des lumières
+// neighbors <-> tableau des voisins de chaque lumière
+
+let state = []
+let neighbors = []
+
+newBoard = ( (size )  => {
+	let frag = document.createDocumentFragment()
+
+	// TODO
+	// la fonction crée dans le dom la grille des lumière
+	
+	board.replaceChildren(frag)
+})
+
+calcNeighbours = ( (size) =>{
+
+	let neighbors = [];
+	for(let i = 0; i < size; i ++){
+		for(let j = 0; j < size; j++){
+			let v = [];
+			v.push(i*size + j);
+			if ( ( j -  1) >= 0) v.push(i*size + j - 1);
+			if ( ( j +  1) <  size) v.push(i*size + j + 1);
+			if ( (i - 1 ) >= 0) v.push(size*(i-1) + j );
+			if ( (i + 1 ) <  size) v.push(size*(i+1) + j);
+			neighbors.push(v)
+		}
+	}
+	return neighbors
+})
+
+
+function play(){
+	let size = select.value
+	newBoard(size)
+	neighbors = calcNeighbours(size)
+	lights = document.querySelectorAll(".board span")
+	state =  Array.from({length: size*size}, () => Math.random() < 0.5 ? true:false);
+	state.forEach((el,i)=>{
+		if (el) lights[i].classList.toggle("off") 
+	})
+	
+		message.textContent = "";
+}
+
+// abonnements 
+
+document
+	.querySelector(".board")
+	.addEventListener("click", ev => {
+
+		// TODO
+		// permet de choisir d'éteindre/allumer une lumière et
+		// ses voisines
+		// Il faut en outre detecter la fin de partie
+
+	});
+
+button.addEventListener("click",play)
+
+play()
+
diff --git a/R4.01_R4.A.10/td_tp/tp2/src/ex1/index.html b/R4.01_R4.A.10/td_tp/tp2/src/ex1/index.html
new file mode 100644
index 0000000..e724aaf
--- /dev/null
+++ b/R4.01_R4.A.10/td_tp/tp2/src/ex1/index.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<meta charset="UTF-8">
+		<title></title>
+		<!-- Centered viewport -->
+		<link
+	  rel="stylesheet"
+   href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css"
+   >
+   <link rel="stylesheet" href="style.css" type="text/css" media="screen" title="no title" charset="utf-8">
+	</head>
+	<body>
+		<main>
+			<h4>Lights</h4>
+			<p>Le but est d'éteindre l'ensemble des lumières.</p>
+
+			<p id="message"></p>
+			<article>
+					<div class="board">
+						<!--div class="row">
+						<span data-num="0"></span>
+						<span data-num="1"></span>
+						<span data-num="2"></span>
+						</div>
+						<div class="row">
+						<span data-num="3"></span>
+						<span data-num="4"></span>
+						<span data-num="5"></span>
+						</div>
+						<div class="row">
+						<span data-num="6"></span>
+						<span data-num="7"></span>
+						<span data-num="8"></span-->
+						</div>
+					</div>
+			</article>
+			<div role="group">
+				<select  aria-label="Select size" required>
+					<option selected value="3">3x3</option>
+					<option value="5">5x5</option>
+					<option value="7">7x7</option>
+				</select>
+				<input type="submit" value="New Game">
+			</div>
+		</main>
+		<script src="app.js"></script>
+	</body>
+</html>
diff --git a/R4.01_R4.A.10/td_tp/tp2/src/ex1/style.css b/R4.01_R4.A.10/td_tp/tp2/src/ex1/style.css
new file mode 100644
index 0000000..2d2be44
--- /dev/null
+++ b/R4.01_R4.A.10/td_tp/tp2/src/ex1/style.css
@@ -0,0 +1,50 @@
+/*.grid {
+	display:grid;
+	grid-template-columns : 100px 100px 100px;
+	grid-template-rows: auto;
+	grid-gap: 10px;
+}*/
+.board > div {
+	display : flex;
+}
+.board  span {
+	display: inline-block;
+	vertical-align:baseline;
+	width: 100px;
+	height: 100px;
+	/*padding : 1em;*/
+	margin : 0.1em;
+	cursor: pointer;
+	border: 1px solid #444;
+	border-radius: 50px;
+	background: radial-gradient(rgb(150,250,150), #00ee00);
+}
+
+.board span.off {
+	background: #009900;
+}
+.board span.off:hover {
+  background: #3AB903;
+}
+
+.board span:hover {
+background: #3AB903;
+background: radial-gradient(rgb(66, 255, 66), #065006);
+}
+
+
+
+
+
+
+
+
+/*
+.wrapper {
+	display : flex;
+	justify-content: space-around;
+}
+.wrapper .grid {
+	flex-basis : auto;
+}
+*/
diff --git a/R4.01_R4.A.10/td_tp/tp2/src/ex2/data.js b/R4.01_R4.A.10/td_tp/tp2/src/ex2/data.js
new file mode 100644
index 0000000..a8ecae3
--- /dev/null
+++ b/R4.01_R4.A.10/td_tp/tp2/src/ex2/data.js
@@ -0,0 +1,23 @@
+let data = 	[
+	{"idStanding":"2282066","intRank":"1","idTeam":"133714","strTeam":"Paris SG","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/rwqrrq1473504808.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"WLWWW","strDescription":"Promotion - Champions League (Group Stage)","intPlayed":"18","intWin":"15","intLoss":"1","intDraw":"2","intGoalsFor":"48","intGoalsAgainst":"13","intGoalDifference":"35","intPoints":"47","dateUpdated":"2023-01-12 23:01:12"},
+	{"idStanding":"2282067","intRank":"2","idTeam":"133822","strTeam":"Lens","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/3pxoum1598797195.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DWDWW","strDescription":"Promotion - Champions League (Group Stage)","intPlayed":"18","intWin":"12","intLoss":"1","intDraw":"5","intGoalsFor":"31","intGoalsAgainst":"13","intGoalDifference":"18","intPoints":"41","dateUpdated":"2023-01-12 23:01:12"},
+	{"idStanding":"2282068","intRank":"3","idTeam":"133707","strTeam":"Marseille","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/uutsyt1473504764.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"WWWWW","strDescription":"Promotion - Champions League (Qualification)","intPlayed":"18","intWin":"12","intLoss":"3","intDraw":"3","intGoalsFor":"36","intGoalsAgainst":"15","intGoalDifference":"21","intPoints":"39","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282069","intRank":"4","idTeam":"133719","strTeam":"Rennes","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/ypturx1473504818.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"LWLWD","strDescription":"Promotion - Europa League (Group Stage)","intPlayed":"18","intWin":"10","intLoss":"4","intDraw":"4","intGoalsFor":"35","intGoalsAgainst":"20","intGoalDifference":"15","intPoints":"34","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282070","intRank":"5","idTeam":"133823","strTeam":"Monaco","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/819x3z1655593495.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DWWLW","strDescription":"Promotion - Europa Conference League (Qualification)","intPlayed":"18","intWin":"10","intLoss":"4","intDraw":"4","intGoalsFor":"35","intGoalsAgainst":"25","intGoalDifference":"10","intPoints":"34","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282071","intRank":"6","idTeam":"133715","strTeam":"Lorient","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/sxsttw1473504748.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DWLDL","strDescription":"","intPlayed":"18","intWin":"9","intLoss":"4","intDraw":"5","intGoalsFor":"30","intGoalsAgainst":"26","intGoalDifference":"4","intPoints":"32","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282072","intRank":"7","idTeam":"133711","strTeam":"Lille","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/2giize1534005340.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DDWWD","strDescription":"","intPlayed":"18","intWin":"9","intLoss":"5","intDraw":"4","intGoalsFor":"30","intGoalsAgainst":"24","intGoalDifference":"6","intPoints":"31","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282073","intRank":"8","idTeam":"133713","strTeam":"Lyon","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/blk9771656932845.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DLWDL","strDescription":"","intPlayed":"18","intWin":"7","intLoss":"7","intDraw":"4","intGoalsFor":"27","intGoalsAgainst":"21","intGoalDifference":"6","intPoints":"25","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282074","intRank":"9","idTeam":"134713","strTeam":"Clermont Foot","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/wrytst1426871249.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"WWLLD","strDescription":"","intPlayed":"18","intWin":"7","intLoss":"7","intDraw":"4","intGoalsFor":"22","intGoalsAgainst":"26","intGoalDifference":"-4","intPoints":"25","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282075","intRank":"10","idTeam":"133712","strTeam":"Nice","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/msy7ly1621593859.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"WLDDW","strDescription":"","intPlayed":"18","intWin":"6","intLoss":"6","intDraw":"6","intGoalsFor":"22","intGoalsAgainst":"20","intGoalDifference":"2","intPoints":"24","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282076","intRank":"11","idTeam":"133934","strTeam":"Stade de Reims","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/xcrw1b1592925946.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"WDWDW","strDescription":"","intPlayed":"18","intWin":"5","intLoss":"4","intDraw":"9","intGoalsFor":"21","intGoalsAgainst":"23","intGoalDifference":"-2","intPoints":"24","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282077","intRank":"12","idTeam":"133703","strTeam":"Toulouse","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/3kqxs61547893229.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"WWLLL","strDescription":"","intPlayed":"18","intWin":"6","intLoss":"8","intDraw":"4","intGoalsFor":"28","intGoalsAgainst":"33","intGoalDifference":"-5","intPoints":"22","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282078","intRank":"13","idTeam":"134789","strTeam":"Troyes","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/swuwpq1426544753.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"LWDLD","strDescription":"","intPlayed":"18","intWin":"4","intLoss":"8","intDraw":"6","intGoalsFor":"29","intGoalsAgainst":"35","intGoalDifference":"-6","intPoints":"18","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282079","intRank":"14","idTeam":"133861","strTeam":"Nantes","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/8r0dab1598797469.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DWDDL","strDescription":"","intPlayed":"18","intWin":"3","intLoss":"6","intDraw":"9","intGoalsFor":"18","intGoalsAgainst":"24","intGoalDifference":"-6","intPoints":"18","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282080","intRank":"15","idTeam":"133709","strTeam":"Montpellier","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/spxtvu1473504783.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"LLWDD","strDescription":"","intPlayed":"18","intWin":"5","intLoss":"11","intDraw":"2","intGoalsFor":"28","intGoalsAgainst":"37","intGoalDifference":"-9","intPoints":"17","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282081","intRank":"16","idTeam":"133702","strTeam":"Ajaccio","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/qpxvwy1473505505.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"LLWDW","strDescription":"","intPlayed":"18","intWin":"4","intLoss":"11","intDraw":"3","intGoalsFor":"15","intGoalsAgainst":"27","intGoalDifference":"-12","intPoints":"15","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282082","intRank":"17","idTeam":"133704","strTeam":"Brest","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/z69be41598797026.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DLLWL","strDescription":"Relegation - Ligue 2","intPlayed":"18","intWin":"3","intLoss":"10","intDraw":"5","intGoalsFor":"18","intGoalsAgainst":"33","intGoalDifference":"-15","intPoints":"14","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282083","intRank":"18","idTeam":"134788","strTeam":"Auxerre","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/lzdtbf1658753355.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"LLLLD","strDescription":"Relegation - Ligue 2","intPlayed":"18","intWin":"3","intLoss":"11","intDraw":"4","intGoalsFor":"16","intGoalsAgainst":"40","intGoalDifference":"-24","intPoints":"13","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282084","intRank":"19","idTeam":"133882","strTeam":"Strasbourg","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/yuxtyy1464540071.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"DLLDL","strDescription":"Relegation - Ligue 2","intPlayed":"18","intWin":"1","intLoss":"8","intDraw":"9","intGoalsFor":"22","intGoalsAgainst":"33","intGoalDifference":"-11","intPoints":"12","dateUpdated":"2023-01-12 23:01:13"},
+	{"idStanding":"2282085","intRank":"20","idTeam":"134709","strTeam":"Angers","strTeamBadge":"https://www.thesportsdb.com/images/media/team/badge/445gc21622560255.png/tiny","idLeague":"4334","strLeague":"French Ligue 1","strSeason":"2022-2023","strForm":"LLLLL","strDescription":"Relegation - Ligue 2","intPlayed":"18","intWin":"2","intLoss":"14","intDraw":"2","intGoalsFor":"16","intGoalsAgainst":"39","intGoalDifference":"-23","intPoints":"8","dateUpdated":"2023-01-12 23:01:13"}
+]
+
diff --git a/R4.01_R4.A.10/td_tp/tp2/src/ex2/index.html b/R4.01_R4.A.10/td_tp/tp2/src/ex2/index.html
new file mode 100644
index 0000000..c40ed47
--- /dev/null
+++ b/R4.01_R4.A.10/td_tp/tp2/src/ex2/index.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html lang="fr">
+	<head>
+		<meta charset="UTF-8">
+		<meta name="viewport" content="initial-scale=1,witdh=device-width">  
+
+
+		<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
+		<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" integrity="sha512-q3eWabyZPc1XTCmF+8/LuE1ozpg5xxn7iO89yfSOd5/oKvyqLngoNGsx8jq92Y8eXJ/IRxQbEC+FGSYxtk2oiw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
+
+		<!--link rel="stylesheet" href="./style.css"-->
+
+		<title>Classement Ligue 1</title>
+	</head>
+	<body class="m-6">
+		<main class="container">
+			<h3 class="title is-3 has-text-primary-dark">
+				Classement Ligue 1 de football<span class="tag" id="date"></span>
+
+				<div class="block control has-icons-left is-inline-block is-pulled-right">
+					<input class="input" type="search" placeholder="Equipe" id="myInput">
+					<span class="icon is-small is-left">
+						<i class="fas fa-search"></i>
+					</span>
+				</div>
+			</h3>
+
+
+
+			<table class="table is-fullwidth is-hoverable">
+				<thead>
+					<tr>
+						<th><abbr title="Position">Pos</abbr><a id="sort" href="#"><span  class="icon"><i class="fas fa-sort"></i></span></a></th>
+						<th></th>
+						<th>Team</th>
+						<th><abbr title="Played">Pld</abbr></th>
+						<th><abbr title="Won">W</abbr></th>
+						<th><abbr title="Drawn">D</abbr></th>
+						<th><abbr title="Lost">L</abbr></th>
+						<th><abbr title="Goals for">GF</abbr></th>
+						<th><abbr title="Goals against">GA</abbr></th>
+						<th><abbr title="Goal difference">GD</abbr></th>
+						<th><abbr title="Points">Pts</abbr></th>
+					</tr>
+				</thead>
+				<tfoot>
+					<tr>
+						<th><abbr title="Position">Pos</abbr></th>
+						<th></th>
+						<th>Team</th>
+						<th><abbr title="Played">Pld</abbr></th>
+						<th><abbr title="Won">W</abbr></th>
+						<th><abbr title="Drawn">D</abbr></th>
+						<th><abbr title="Lost">L</abbr></th>
+						<th><abbr title="Goals for">GF</abbr></th>
+						<th><abbr title="Goals against">GA</abbr></th>
+						<th><abbr title="Goal difference">GD</abbr></th>
+						<th><abbr title="Points">Pts</abbr></th>
+					</tr>
+				</tfoot>
+				<tbody></tbody>
+			</table>
+		</main>
+		<script src="data.js"></script>
+		<script src="script.js"></script>
+	</body>
+</html>
diff --git a/R4.01_R4.A.10/td_tp/tp2/src/ex2/script.js b/R4.01_R4.A.10/td_tp/tp2/src/ex2/script.js
new file mode 100644
index 0000000..259af41
--- /dev/null
+++ b/R4.01_R4.A.10/td_tp/tp2/src/ex2/script.js
@@ -0,0 +1,74 @@
+// TODO
+
+
+
+
+renderTable=((mountPoint,standings,keys)=>{
+	let frag = document.createDocumentFragment();
+
+	for (team  of standings){
+		let tr = document.createElement("tr");
+		for (key of keys){
+
+			let td = document.createElement("td");
+			let elt;
+			if (key === "strTeamBadge"){
+		elt = document.createElement("img");
+				elt.src= team[key];
+
+			}else{
+				elt = team[key];
+			}
+			td.append(elt)
+			tr.append(td);
+		}
+		frag.append(tr);
+	}
+	mountPoint.replaceChildren();
+	mountPoint.append(frag);
+
+
+})
+
+sortAndFilter=((standings,sort,filter)=>{
+
+	return standings
+		.filter((team)=>team.strTeam.toLowerCase().includes(filter.toLowerCase()))
+		.sort((teamA,teamB)=>(teamA.intRank - teamB.intRank)*sort)
+
+})
+
+debounce=((f,time)=>{
+	let timer
+	return function(...args){
+	clearTimeout(timer)
+	timer = setTimeout(()=>f(...args),time)
+	
+	}
+
+})
+window.onload = (()=>{
+	let sort = 1;
+	let text = "";
+	let tbody = document.querySelector("tbody");
+	const keys = ["intRank","strTeam","strTeamBadge","intPlayed","intWin","intLoss","intDraw","intGoalsFor"
+		,"intGoalsAgainst","intGoalDifference","intPoints"];
+
+	renderTable(tbody,data,keys);
+
+	document
+		.querySelector("#sort")
+		.addEventListener("click",(e)=>{
+		sort = - sort;
+		renderTable(tbody,sortAndFilter(data,sort,text),keys);
+	})
+	document
+		.querySelector("input")
+		.addEventListener("input",debounce((e)=>{
+		 text = e.target.value
+		console.log("MAJ")
+		renderTable(tbody,sortAndFilter(data,sort,text),keys);
+
+		},1000))
+
+})
diff --git a/R4.01_R4.A.10/td_tp/tp2/src/ex3/css/style.css b/R4.01_R4.A.10/td_tp/tp2/src/ex3/css/style.css
new file mode 100644
index 0000000..465a430
--- /dev/null
+++ b/R4.01_R4.A.10/td_tp/tp2/src/ex3/css/style.css
@@ -0,0 +1,32 @@
+:focus-visible {outline:none;}
+
+.strike {
+	text-decoration-line : line-through;
+}
+
+#loader.is-loading {
+	position: fixed;
+	z-index: 999;
+	overflow: show;
+	margin: auto;
+	top: 0;
+	left: 0;
+	bottom: 0;
+	right: 0;
+}
+
+#loader.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;
+}
diff --git a/R4.01_R4.A.10/td_tp/tp2/src/ex3/index.html b/R4.01_R4.A.10/td_tp/tp2/src/ex3/index.html
new file mode 100644
index 0000000..1a0f2e5
--- /dev/null
+++ b/R4.01_R4.A.10/td_tp/tp2/src/ex3/index.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en">
+
+	<head>
+		<meta charset="utf-8" />
+		<meta name="viewport" content="width=device-width, initial-scale=1" />
+
+		<title>Todo</title>
+		<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
+		<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" integrity="sha512-q3eWabyZPc1XTCmF+8/LuE1ozpg5xxn7iO89yfSOd5/oKvyqLngoNGsx8jq92Y8eXJ/IRxQbEC+FGSYxtk2oiw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
+		<link rel="stylesheet" href="./css/style.css">
+	</head>
+
+	<body>
+		<div class="container content is-max-desktop p-4">
+
+			<div id="loader"></div>
+			<div class="has-text-centered">
+			</div>
+
+			<!-- formulaire pour la saisie -->
+
+			<form id="add_form" class="block">
+				<p class="control has-icons-left is-expanded">
+					<input autocomplete="off" id="input_todo" class="input is-large is-expanded" type="text" placeholder="Enter Todo">
+					<span class="icon is-left">
+						<i class="fas fa-plus" aria-hidden="true"></i>
+					</span>
+				</p>
+			</form>
+
+			<!-- filtrage de l'affichage -->
+
+			<div id="todos_filter" class="tabs is-small is-centered">
+				<ul>
+					<li class="is-active"><a href="#/" id="all">All</a></li>
+					<li><a href="#/active" id="active">Active</a></li>
+					<li><a href="#/done" id="done">Done</a></li>
+				</ul>
+			</div>
+			<!-- Affichage des todos -->
+			<section id="todos_list">
+			</section>
+
+	</body>
+	<script type="module" src="./js/app.js"></script>
+
+</html>
diff --git a/R4.01_R4.A.10/td_tp/tp2/src/ex3/js/app.js b/R4.01_R4.A.10/td_tp/tp2/src/ex3/js/app.js
new file mode 100644
index 0000000..b97d13a
--- /dev/null
+++ b/R4.01_R4.A.10/td_tp/tp2/src/ex3/js/app.js
@@ -0,0 +1,6 @@
+import Controller from './controller.js'
+import Model from './model.js'
+import View from './view.js'
+
+
+const app = new Controller(new Model(),new View())
diff --git a/R4.01_R4.A.10/td_tp/tp2/src/ex3/js/controller.js b/R4.01_R4.A.10/td_tp/tp2/src/ex3/js/controller.js
new file mode 100644
index 0000000..bb027a8
--- /dev/null
+++ b/R4.01_R4.A.10/td_tp/tp2/src/ex3/js/controller.js
@@ -0,0 +1,54 @@
+export default class Controller {
+	constructor(model, view) {
+		this.model = model
+		this.view = view
+		this.filter = "all"
+		this.view.bindAddTodo(this.addTodo.bind(this))
+		this.view.bindDeleteTodo(this.deleteTodo.bind(this))
+		this.view.bindToggleTodo(this.toggleTodo.bind(this))
+		this.view.bindEditTodo(this.editTodo.bind(this))
+
+		/** Routage **/
+		this.routes = ['all','active','done'];
+
+		/** Routage **/
+		window.addEventListener("load",this.routeChanged.bind(this));
+		window.addEventListener("hashchange",this.routeChanged.bind(this));
+
+	}
+
+	routeChanged(){
+		let route =  window.location.hash.replace(/^#\//,'');
+			this.filter = this.routes.find( r => r === route) || 'all';
+			this.filterTodoList();
+
+		}
+
+
+	filterTodoList () {
+		let todos = this.model.getTodos(this.filter)
+		this.view.renderTodoList(todos)
+		this.view.setFilterTabs(this.filter)
+	}
+
+	addTodo(text)  {
+		let todo = this.model.add(text)
+		this.filterTodoList()
+	}
+
+	deleteTodo(id)  {
+		this.model.delete(parseInt(id))
+		this.filterTodoList()
+	}
+
+	toggleTodo(id)  {
+		this.model.toggle(parseInt(id))
+		this.filterTodoList()
+	}
+
+	editTodo(id, text)  {
+		this.model.edit(parseInt(id),text)
+		this.filterTodoList()
+	}
+}
+
diff --git a/R4.01_R4.A.10/td_tp/tp2/src/ex3/js/model.js b/R4.01_R4.A.10/td_tp/tp2/src/ex3/js/model.js
new file mode 100644
index 0000000..7e4c7f8
--- /dev/null
+++ b/R4.01_R4.A.10/td_tp/tp2/src/ex3/js/model.js
@@ -0,0 +1,77 @@
+
+
+
+
+
+const todos = [ 
+	{ 
+		id : 1, 
+		text : "apprendre le javascript",
+		done : false
+
+	},
+	{ 
+		id : 2, 
+		text : "Faire des maths",
+		done : false
+
+	}]
+
+export default class Model {
+	constructor() {
+		//this.todos = JSON.parse(localStorage.getItem('todos')) || []
+		this.todos = JSON.parse(localStorage.getItem('todos')) || todos 
+	}
+
+	_commit(todos) {
+		localStorage.setItem('todos', JSON.stringify(todos))
+	}
+
+	getTodos(filter){
+		if (filter == "active")
+			return this.todos.filter(todo => !todo.done)
+		if (filter == "done")
+			return this.todos.filter(todo => todo.done)
+
+		return this.todos
+	}
+
+	add(todoText) {
+		const todo = {
+			id: this.todos.length > 0 ? this.todos[this.todos.length - 1].id + 1 : 1,
+			text: todoText,
+			done : false,
+		}
+
+		this.todos.push(todo)
+		this._commit(this.todos)
+		return todo 
+	}
+
+	edit(id, updatedText) {
+	let todo = this.todos.find(t => t.id === id)
+	todo.text = updatedText
+	//	this.todos = this.todos.map(todo =>
+	//		todo.id === id ? { id: todo.id, text: updatedText, done: todo.done} : todo
+	//	)
+
+		this._commit(this.todos)
+	}
+:q!
+
+	delete(id) {
+		this.todos = this.todos.filter(todo => todo.id !== id)
+
+		this._commit(this.todos)
+	}
+
+	toggle(id) {
+		let todo = this.todos.find(t => t.id === id)
+		todo.done = !todo.done
+		//this.todos = this.todos.map(todo =>
+		//	todo.id === id ? { id: todo.id, text: todo.text, done: !todo.done } : todo
+		//)
+		this._commit(this.todos)
+	}
+}
+
diff --git a/R4.01_R4.A.10/td_tp/tp2/src/ex3/js/view.js b/R4.01_R4.A.10/td_tp/tp2/src/ex3/js/view.js
new file mode 100644
index 0000000..038b9eb
--- /dev/null
+++ b/R4.01_R4.A.10/td_tp/tp2/src/ex3/js/view.js
@@ -0,0 +1,95 @@
+export default class View {
+	constructor() {
+		this.form   = document.querySelector("#add_form")
+		this.input  = document.querySelector("#input_todo")
+		this.list   = document.querySelector("#todos_list")
+		this.tabs   = document.querySelector("#todos_filter")
+		this.loader = document.querySelector("#loader")
+	}
+
+	_getTodo() {
+		return this.input.value
+	}
+
+	_resetInput() {
+		this.input.value = ''
+	}
+
+	_getNewTodoElement(todo){
+		let div = document.createElement("div")
+		div.classList.add("box","is-flex","is-align-items-center","m-2")
+		div.dataset.id = todo.id
+
+		let input = document.createElement("input")
+		input.type = "checkbox"
+		input.classList.add("mr-3")
+
+		let span = document.createElement("p")
+		span.classList.add("mb-0","is-size-3","mr-auto")
+		span.textContent = todo.text
+		span.setAttribute("contenteditable",true)
+
+
+		let button = document.createElement("button")
+		button.classList.add("delete","is-large")
+
+
+		if (todo.done){
+			span.classList.add("strike")
+			input.checked = true
+		}
+
+		div.append(input,span,button)
+
+		return div
+
+	}
+
+	setLoader(){
+		this.loader.classList.add("is-loading")
+	}
+	unsetLoader(){
+		this.loader.classList.remove("is-loading")
+	}
+
+	setFilterTabs(filter){
+		let li = this.tabs.querySelectorAll("li")
+		li.forEach( tab => {
+			tab.classList.remove("is-active")
+		})
+		let active = this.tabs.querySelector(`#${filter}`)
+		active.parentNode.classList.add("is-active")
+	}
+
+	renderTodoList(todos) {
+		let list = new DocumentFragment()
+		for (let todo of todos){
+			list.appendChild(this._getNewTodoElement(todo))
+		}
+		this.list.replaceChildren(list)
+	}
+
+	/** Abonnements événements **/
+
+	bindAddTodo(handler) {
+		this.form.addEventListener("submit", (e=>{
+			e.preventDefault()
+			let text = this._getTodo()
+			handler(text)
+			this._resetInput()
+		}))
+	}
+
+
+	bindDeleteTodo(handler) {
+		// TODO
+	}
+
+	bindEditTodo(handler) {
+		// TODO
+	}
+
+	bindToggleTodo(handler) {
+		// TODO
+	}
+}