Sujet 3

  1. Bienvenue au 3e sujet
  2. Révisions
    1. Temps d’exécution
    2. Boucle événements, affichage
    3. Promesse
    4. Nb d'états d'une promesse
    5. Liste d'états d'une promesse
    6. Promesse rompue
    7. Enchaînement
    8. Une action après l'autre
    9. Où utiliser await ?
    10. Où utiliser await (2) ?
    11. je_te_rendrais_ton_livre
  3. AJAX
    1. Client / serveur
    2. Page HTML complexe
    3. Nombre requêtes
    4. Page réelle
    5. Requêtes AJAX
    6. Ajax: exemple recherche Google
    7. Ajax: Recherche Google - console
    8. Ajax: commentaires
    9. AJAX
    10. simple_fetch()
    11. fetch.html
    12. Requête dans la console
    13. Paramètres GET
    14. Requête avec paramètre GET
    15. Coté serveur
    16. Fetch sur localhost
    17. AJAX localhost
  4. Miam - saison 1 : La rencontre
    1. Click terrain
    2. Correction
    3. Position page
    4. Déplacer
    5. Déplacement x et y
    6. Correction
    7. Petits ajustements
    8. Correction
    9. Souris
    10. Les inséparables
    11. Correction
  5. Miam - saison 2 : La séparation
    1. Que vaut une promesse ?
    2. miam-serveur.php
    3. PHP paramètres URL
    4. Paramètres GET
    5. URL complète
    6. data.json
    7. Requêtes chat et souris
    8. Requêtes en JS
    9. Sélection chat / souris
    10. Correction
    11. await
    12. Requête dans le JS
    13. Correction
    14. Mouvement !
    15. Correction
    16. try / catch
    17. Entre deux machines:
  6. Miam - saison 3 : Consommée
    1. Correction
    2. Retour au point de départ
    3. Correction

1. Bienvenue au 3e sujet

1.1 Bienvenue au 3e sujet

Pour votre progression, c'est très important de finir le sujet précédent avant de commencer celui-ci.

Vous devez travailler connectés (lien « Connexion » en haut à droite de la page). Sinon, vous rencontrerez des problèmes d'avancement et vous risquez d'être comptés absent. En étant connectés vous retrouverez l'endroit où vous en étiez à la séance suivante.

2. Révisions

2.1 Révisions

Commençons par une rapide révision des notions des sujets précédents.

Révisions

2.2 Temps d’exécution


<script>
console.log('abc');
setTimeout(()=>{console.log('def');},5000);
console.log('ghi');
</script>


Révisions

2.3 Boucle événements, affichage


<script>
    console.log('abc');
    document.body.style.backgroundColor='red';
// "bla bla"
document.addEventListener('click',()=>{
console.log('def');
document.body.style.backgroundColor='green';
while(true);
}); console.log('ghi'); </script>

Révisions

2.4 Promesse

La fonction votez_pour_moi() retourne une promesse.
On suppose qu'on a déjà écrit:

let p=votez_pour_moi();

Que faut-il écrire pour afficher « Incroyable! » dans la console, si la promesse est tenue ?

Révisions

2.5 Nb d'états d'une promesse

Dans combien d'états différents peut se trouver une promesse ?

Révisions

2.6 Liste d'états d'une promesse

Quels sont les 3 états d'une promesse ?

Révisions

2.7 Promesse rompue

La fonction je_t_aimerais_pour_toujours() retourne une promesse.
On suppose qu'on a déjà écrit:

let p=je_t_aimerais_pour_toujours();

Que faut-il écrire pour afficher « Snif snif. » dans la console, si la promesse est rompue ?
(utilisez une fonction fléchée sans paramètre et avec des accolades)

Révisions

2.8 Enchaînement

La fonction « fais_tes_devoirs » retourne une promesse.
Quand les devoirs sont faits, on veut appeler une fonction « fini ».
Si les devoirs n'ont pas été faits,  on veut appeler une fonction « echec ».

Écrivez le JS sans utiliser de variable intermédiaire.


Révisions

2.9 Une action après l'autre

Dans la question précédente, on a juste géré les devoirs:

fais_tes_devoirs().then(fini).catch(echec);

Maintenant, on voudrait aussi que la chambre soit rangée, mais uniquement après que les devoirs soient finis. Ensuite, après les devoirs et le rangement, on voudrait afficher « Ouf, enfin libre ! » dans la console.
On dispose d'une fonction « range_ta_chambre », qui retourne une promesse et qui ne prend pas de paramètre.
On veut utiliser cette ligne pour enchaîner:

fais_tes_devoirs().then(fini).then(()=>{console.log('Ouf, enfin libre!')}).catch(echec);

Voici la fonction fini:

function fini(){
console.log('Les devoirs sont finis.');
XYZ
}

Que faut-il écrire à la place de XYZ ?

Révisions

2.10 Où utiliser await ?

<script XYZ>
     ICI
</script>

Que faut-il écrire à la place de XYZ pour pouvoir utiliser « await » à l'endroit indiqué « ICI » ?

Révisions

2.11 Où utiliser await (2) ?

<script>
XYZ function exemple() {
ICI
}
</script>

Que faut-il écrire à la place de XYZ pour pouvoir utiliser await à l'endroit indiqué « ICI » ?

Révisions

2.12 je_te_rendrais_ton_livre


<script type="module">
console.log('début');
XYZ
console.log('merci');
</script>

La fonction je_te_rendrais_ton_livre() retourne une promesse.
Que faut-il écrire écrire à la place de « XYZ » pour que « merci » ne soit affiché qu'une fois que le livre rendu ?

3. AJAX

3.1 AJAX

Dans ces pages, on va apprendre comment faire des requêtes à partir du JS vers un serveur.

AJAX

3.2 Client / serveur

Dans cet exemple, l'utilisateur clique sur un lien « http://musique.org/page.html » et une page HTML très simple s'affiche.



AJAX

3.3 Page HTML complexe

En pratique les pages HTML font référence à d'autres ressources (CSS, JS, images, ...) qui se trouvent sur le serveur.

Par exemple: page.html

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8"/>
<title>Musique</title>
</head>
<body>
<h1>Musicien</h1>
<img src="photo.jpg" alt="photo"/>
<script src="exemple.js"></script>
</body>
</html>




AJAX

3.4 Nombre requêtes

Ouvrez la page suivant dans un autre onglet:
https://moodle.iutv.univ-paris13.fr/img/bjs2/page-html2.html
Ouvrez la console, puis cliquez sur l'onglet « réseau » des outils de dev (au-dessus de la console).
Ensuite appuyez sur le bouton pour recharger la page, tout en appuyant sur la touche Maj (important!).

Vous voyez toutes les requêtes réseau qui sont faites.
A chaque requête un fichier (HTML, image ou CSS) est téléchargé.

Combien de fichiers sont au total téléchargés par le navigateur ?
(y compris la page HTML)

AJAX

3.5 Page réelle

Recommencez avec la page suivante, aussi dans un autre onglet:
https://moodle.iutv.univ-paris13.fr

Regardez bien toutes les requêtes. Essayez de voir quel type de données correspondent à chaque requête.

Combien de requêtes sont faites au total ?


AJAX

3.6 ¤ Requêtes AJAX

Toutes le requêtes qu'on a vu, sont faites au chargement de la page.
Le navigateur reçoit le HTML initial et le traite. Quand il y trouve des ressources externes (images, JS, CSS, ...); il les télécharge.
Dans le premier exemple, quand le navigateur voit ceci:

		<img id="chat" src="chat.png" alt="chat"/>

il déclenche le téléchargement de https://moodle.iutv.univ-paris13.fr/img/bjs2/chat.png

AJAX: à partir du JS

Les interactions avec le serveur sont aussi possibles après le chargement de la page:

Voyons quelques exemples.

AJAX

3.7 ¤ Ajax: exemple recherche Google



Quand un utilisateur commence à taper une recherche dans Google, Google lui propose une liste de suggestions.

Il est impossible d'inclure dans la page d'origine toutes les suggestions possibles. Le navigateur va donc demander au serveur la liste de suggestions au fur et à mesure que l'utilisateur écrit. Ceci se fait sans recharger la page.

Dans ce qu'on a vu jusqu'à maintenant, les interactions avec le serveur se faisaient au moment du chargement de la page.

Ici, la liste des suggestions est cherchée à partir du serveur, à partir du JS et après la fin du chargement de la page.

AJAX

3.8 Ajax: Recherche Google - console

Ouvrez https://www.google.fr
Ouvrez la console.
Vérifiez que l'option « XHR » est bien activée:

Tapez une recherche. Vous devriez voir des suggestions au fur et à mesure que vous tapez.
Vous devriez aussi voir des requêtes Ajax dans la console.

Les informations envoyées au serveur se trouvent dans l'URL des requêtes.

Essayez de trouver le texte que vous avez tapé.

Vous devriez voir des URLs un peu comme ceci:
https://www.google.fr/.../search?XYZ=le-texte-que-vous-avez-tape

Que voyez vous à la place de XYZ ?

AJAX

3.9 ¤ Ajax: commentaires

Dans cet exemple, l'utilisateur appuie sur un bouton « J'aime » dans les commentaires d'une vidéo sur Youtube. Son action est enregistrée sur le serveur, mais la page ne se recharge pas.

Le fonctionnement habituel en HTML du bouton consiste à envoyer une requête POST et afficher une nouvelle page. Le rechargement de la page est désagréable pour l'utilisateur. C'est lent, la vidéo s'interrompt, il perd sa position dans la page...

Ici, la requête sur le serveur se fait sans rechargement de la page.

AJAX

3.10 ¤ AJAX


AJAX veut dire « Asynchronous JavaScript and XML »

AJAX

3.11 ¤ simple_fetch()

En pratique, les requêtes Ajax peuvent se faire de différentes manières:

Le choix ici, est d'utiliser simple_fetch(), une fonction qu'on vous fournit, qui est une version simplifié de fetch. Elle vous aidera à débuter.


AJAX

3.12 fetch.html

Créez le fichier fetch.html suivant:

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8"/>
<title>Fetch</title>
<script src="https://moodle.iutv.univ-paris13.fr/img/bjs2/bjs2-js-lib.js"></script>
</head>
<body>
<h1>Fetch</h1>
<script type="module">
</script>
</body>
</html>

Tapez dans la console:

simple_fetch('https://moodle.iutv.univ-paris13.fr/img/bjs2/sujet-3-fetch-1.php');

En premier est affiché la valeur de retour de simple_fetch(...).
Quel est son type ?

AJAX

3.13 Requête dans la console

En pratique, on a souvent besoin de regarder ce qu'il se passe dans une requête.
Allez sur la page fetch.html où vous avez tapé dans la console:

simple_fetch('https://moodle.iutv.univ-paris13.fr/img/bjs2/sujet-3-fetch-1.php');
À l'intérieur de la console, trouvez la requête (XHR GET -- flèche rouge) et ouvrez-là.
Cliquez sur les différents onglets de la requête (attention, les onglets sont tout en haut de l'affichage de la requête).
Le serveur a renvoyé "Bonjour!".
Comment s'appelle l'onglet qui contient cette valeur renvoyée « Bonjour! » ?

AJAX

3.14 ¤ Paramètres GET

Paramètres GET

On peut envoyer des informations au serveur, tout simplement en les mettant dans l'URL d'une requête.
On commence les paramètre GET avec un « ? ». Ensuite on met le nom (abc) et sa valeur (def) associée:
http://exemple.org/ma-page.php?abc=def
Si on veut ajouter plusieurs valeurs, on utilise un « & »:
http://exemple.org/ma-page.php?abc=def&rst=uuuuu

Écrivez l'URL suivante en ajoutant un paramètre GET appelé « nom » et ayant comme valeur « Tom »

https://moodle.iutv.univ-paris13.fr/img/bjs2/sujet-3-fetch-1.php

AJAX

3.15 Requête avec paramètre GET

Dans la console de la page fetch.html, appelez simple_fetch avec l'URL:
https://moodle.iutv.univ-paris13.fr/img/bjs2/sujet-3-fetch-1.php?nom=Tom

Quelle est la réponse du serveur ?

AJAX

3.16 Coté serveur

Dans votre répertoire ~/public_html
créez le fichier cote-serveur.php :

<?php
// Autoriser les requêtes AJAX à partir de n'importe où
header('Access-Control-Allow-Origin: *');
// Dire au navigateur qu'on lui envoie du JSON (et pas du HTML)
header('Content-Type: application/json');
// Message où on concatène le paramètre GET (s'il existe).
$message='Hello'.(isset($_GET['nom']) ? ' '.$_GET['nom'] : '!');
// Convertir le message en JSON (pas très utile ici, mais en général ça l'est).
echo json_encode($message);
?>

Ouvrez le fichier dans votre navigateur avec:
http://localhost/~__user_nb__/cote-serveur.php

AJAX

3.17 Fetch sur localhost

Dans la console de la page fetch.html (et pas la page PHP) essayez de faire une requête avec simple_fetch sur
http://localhost/~__user_nb__/cote-serveur.php?nom=Tom

Dans la console, regardez la requête. Que contient la "Réponse" ?

AJAX

3.18 AJAX localhost

Ici, en TP, votre serveur est sur la même machine que le navigateur (client).
Mais, pour ne pas s'emmêler, il vaut mieux s'imaginer que c'est des machines différentes:


4. Miam - saison 1 : La rencontre

4.1 Miam - saison 1 : La rencontre

Dans les pages suivantes, on va retrouver notre petit chat tout mignon.

Créez les images suivantes (attention elles sont un peu différentes que celles utilisées avant):

herbe.jpg

chat2.svg


Créez le fichier miam.html suivant:

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8"/>
<title>Miam</title>
<script src="https://moodle.iutv.univ-paris13.fr/img/bjs2/bjs2-js-lib.js"></script>
<link rel="stylesheet" href="miam.css"/>
</head>
<body>
<div id="terrain">
<img id="chat" src="chat2.svg"/>
</div>
<script src="miam.js"></script>
</body>
</html>

miam.js

// Vide pour l'instant

miam.css

#terrain {
position: relative;
width: 800px;
height: 800px;
background-image: url(herbe.jpg);
}
#chat {
position: absolute;
top: 0px;
left: 0px;
transition: top 1s, left 1s;
}


Miam - saison 1 : La rencontre

4.2 Click terrain

Créez deux variables « terrain » et « chat » contenant les éléments DOM ayant les id « terrain » et « chat ».

Quand l'utilisateur clique sur le terrain (et uniquement le terrain, ou ce qui est contenu dedans), affichez « click » dans la console.

Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur, puis copiez-le ici pour que votre enseignant puisse le relire plus tard:


Miam - saison 1 : La rencontre

4.3 Correction

const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');

terrain.addEventListener('click',function(){
console.log('click');
});

Miam - saison 1 : La rencontre

4.4 Position page

const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');

terrain.addEventListener('click',XYZ);

Que faut-il écrire à la place de XYZ pour afficher dans la console la coordonnée X de l'endroit sur la page où l'utilisateur à cliqué ?
N'utilisez pas de fonction fléchée, et utilisez les noms habituels.

Miam - saison 1 : La rencontre

4.5 Déplacer

Ce pauvre chat s'ennuie. Il a envie de bouger!

pageX et pageY donnent des coordonnées par rapport au coin en haut à gauche de la page.
Ici on va utiliser offsetX et offsetY qui donnent des coordonnées par rapport au coin en haut à gauche du terrain.

Supposons qu'on ait déjà :

const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');

terrain.addEventListener('click',function(event){
let x=event.offsetX;
XYZ
});

Que faut-il écrire à la place de XYZ pour déplacer la position du chat à l'endroit cliqué ?
(juste pour la position X)
N'oubliez pas les unités. Utilisez les variables définies.
Essayez dans votre code d'abord.

Miam - saison 1 : La rencontre

4.6 Déplacement x et y

Complétez le programme pour que le chat se déplace à l'endroit cliqué à la fois sur x et y.
Utilisez bien une variable "x" et "y" pour la position du chat.

Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur, puis copiez-le ici pour que votre enseignant puisse le relire plus tard:


Miam - saison 1 : La rencontre

4.7 Correction

const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');

terrain.addEventListener('click',function(event){
console.log('click');
let x=event.offsetX;
let y=event.offsetY;
chat.style.left=x+'px';
chat.style.top =y+'px';
});

Miam - saison 1 : La rencontre

4.8 Petits ajustements

Le chat est content de bouger, mais maintenant, il n’arrête pas de sortir de son terrain. En plus, il ne vas pas exactement à l'endroit cliqué.

Important: Mettez ce code dans votre gestionnaire d'événements:
(si vous le mettez en dehors, il s’exécutera au chargement, et les valeurs seront à 0)

const rectChat  =  chat.getBoundingClientRect();

rectChat.width et rectChat.height vous donnent la largeur et la hauteur de l'image du chat.
En les utilisant,

Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur, puis copiez-le ici pour que votre enseignant puisse le relire plus tard:

Miam - saison 1 : La rencontre

4.9 Correction

const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');

terrain.addEventListener('click',function(event){
console.log('click');
const rectChat = chat.getBoundingClientRect();

let x=event.offsetX;
let y=event.offsetY;

if(x< rectChat.width/2 ){x= rectChat.width/2;}
if(x>800-rectChat.width/2 ){x=800-rectChat.width/2;}
if(y< rectChat.height/2){y= rectChat.height/2;}
if(y>600-rectChat.height/2){y=600-rectChat.height/2;}

chat.style.left=(x-rectChat.width /2)+'px';
chat.style.top =(y-rectChat.height/2)+'px';
});

Miam - saison 1 : La rencontre

4.10 Souris

Le chat se sent seul sur son terrain.
Ajoutons une souris.

Créez le fichier

souris2.svg

Ajoutez là dans le HTML après  le chat, avec un id "souris".
Copiez le CSS du chat et adaptez.
Dans ce fichier CSS faites que la souris commence à top: 500px;   left: 600px;

Miam - saison 1 : La rencontre

4.11 Les inséparables

En dupliquant différents bout de code, faites en sorte que, quand vous cliquez quelque-part dans le terrain, le chat et la souris se placent au même endroit !

Le chat et la souris sont devenus inséparables!

Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur, puis copiez-le ici pour que votre enseignant puisse le relire plus tard:


Miam - saison 1 : La rencontre

4.12 Correction

const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');

terrain.addEventListener('click',function(event){
console.log('click');
const rectChat = chat.getBoundingClientRect();
const rectSouris = souris.getBoundingClientRect();

let x=event.offsetX;
let y=event.offsetY;

if(x< rectChat.width/2 ){x= rectChat.width/2;}
if(x>800-rectChat.width/2 ){x=800-rectChat.width/2;}
if(y< rectChat.height/2){y= rectChat.height/2;}
if(y>600-rectChat.height/2){y=600-rectChat.height/2;}

if(x< rectSouris.width/2 ){x= rectSouris.width/2;}
if(x>800-rectSouris.width/2 ){x=800-rectSouris.width/2;}
if(y< rectSouris.height/2){y= rectSouris.height/2;}
if(y>600-rectSouris.height/2){y=600-rectSouris.height/2;}

chat.style.left=(x-rectChat.width /2)+'px';
chat.style.top =(y-rectChat.height/2)+'px';

souris.style.left=(x-rectSouris.width /2)+'px';
souris.style.top =(y-rectSouris.height/2)+'px';
});

5. Miam - saison 2 : La séparation

5.1 Miam - saison 2 : La séparation

La souris se sent mal à l'aise. Il y-a peut-être quelque-chose dans le regard ou dans le sourire de son ami qui l'inquiète un peu. Elle voudrait plus d'indépendance.

On voudrait donc que deux utilisateurs différents contrôlent le chat et la souris. Chacun sur son navigateur.
Les clicks d'un utilisateur indiqueraient la position du chat et les clicks de l'autre utilisateur indiqueraient la position de la souris.

Il faudra donc échanger des informations entre les deux navigateurs. On va le faire, en passant par un serveur, avec des requêtes AJAX.

Quand l'utilisateur chat clique, les coordonnées de son click sont envoyées (AJAX) à miam-serveur.php.
miam-serveur.php va attendre de recevoir les coordonnées de la souris avant de répondre.
La réponse contiendra à la fois les coordonnées cliquées par l'utilisateur chat et celles cliquées par l'utilisateur souris.

Récapitulons:

  1. L'utilisateur chat clique. Son JS fait une requete AJAX envoyant les coordonnées cliquées
  2. Le serveur reçoit les coordonnées du chat, mais ne répond pas tout de suite. Il attend les coordonnées de la souris
  3. L'utilisateur souris clique. Son JS fait une requete AJAX envoyant les coordonnées cliquées
  4. Le serveur reçoit les coordonnées de la souris. Il a maintenant les coordonnées des deux (chat et souris).
  5. Le serveur répond aux deux requetes AJAX en renvoyant à chacune à la fois les coordonnées du chat et celles de la souris
  6. Les deux navigateurs démarrent le mouvement.
On vous donnera le code du serveur et on fera, ensemble, le code JS du client (navigateur).

Miam - saison 2 : La séparation

5.2 ¤ Que vaut une promesse ?

Les promesses ont une valeur (du moins en JS).
Une promesse représente une opération future, qui peut renvoyer une valeur. C'est la « valeur » de la promesse.

Par exemple, lorsqu'on fait une requête AJAX avec simple_fetch, on veut pouvoir récupérer la réponse du serveur.
simple_fetch() renvoie une promesse. La réponse du serveur sera la « valeur » de la promesse. Cette valeur n'est pas disponible au départ. Elle n'est disponible que lorsque la promesse est tenue. On ne peut récupérer la valeur qu'après que la promesse soit tenue, c'est à dire dans la fonction donnée à .then() ou bien après « await ».

Les deux manières de récupérer:

1. En paramètre de la fonction:

simple_fetch('http://exemple.org/abc.php').then( (valeur)=>{console.log(valeur);});

2. Lorsqu'on utilise « await »

let valeur= await simple_fetch('http://exemple.org/abc.php');
console.log(valeur);


Miam - saison 2 : La séparation

5.3 miam-serveur.php

Créez le fichier miam-serveur.php dans ~/public_html

<?php
ini_set("xdebug.overload_var_dump", "off");
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');

$data=lire_fichier_data();

if(isset($_GET['clickChatX']) &&
isset($_GET['clickChatY'])){
$data['clickChat']=['x'=>floatval($_GET['clickChatX']),
'y'=>floatval($_GET['clickChatY'])];
}

if(isset($_GET['clickSourisX']) &&
isset($_GET['clickSourisY'])){
$data['clickSouris']=['x'=>floatval($_GET['clickSourisX']),
'y'=>floatval($_GET['clickSourisY'])];
}

if(isset($data['clickChat']) || isset($data['clickSouris'])){
ecrire_fichier_data($data);
}

if(!isset($data['clickChat']) && !isset($data['clickSouris'])){
erreur('Pas de click fourni.');
}

// Attendre que l'autre joueur clique et que le processus PHP correspondant écrive les autres coordonnées dans le fichier
$maxAttente=500;
for($i=0;isset($data['clickChat']) !== isset($data['clickSouris']) && $i<$maxAttente;$i++){
usleep(100*1000);
$data=lire_fichier_data();
}

$output=$data;

// Cas particulier. Si l'utilisateur a cliqué plusieurs fois, certains des processus en attente risquent de voir un fichier déjà vidé.
if(!isset($data['clickChat']) && !isset($data['clickSouris'])){
$output=[];
}

// Timeout.
if($i===$maxAttente){
erreur('Delai d\'attente pour l\'autre click dépasé.');
}

unset($data['clickChat' ]);
unset($data['clickSouris']);

// Attendre pour que l'autre processus ait le temps de lire, avant qu'on éfface.
// Pas très joli, mais efficace.
usleep(150*1000);
ecrire_fichier_data($data);

echo json_encode($output);

function lire_fichier_data(){
$fichier=@file_get_contents('data.json');
if($fichier===false){$fichier='[]';}
$res=json_decode($fichier, JSON_OBJECT_AS_ARRAY);
if($res===null){$res=[];}
return $res;
}

function ecrire_fichier_data($data){
$ok=@file_put_contents('data.json',json_encode($data));
if(!$ok){
erreur('Problème d\'écriture dans data.json. Avez-vous crée le fichier et donné les droits requis ?');
}
}

function erreur($message){
http_response_code(500);
echo json_encode(['erreur'=>$message]);
die(1);
}

// Debugger (il faut d'abord créer un fichier "log" avec les droits d'écriture)
function log_dump(...$args)
{
ob_start();
var_dump(...$args);
$content = ob_get_contents();
ob_end_clean();
file_put_contents('log','######'.date('r').':'.getmypid().': '.$content,FILE_APPEND);
}

?>

Miam - saison 2 : La séparation

5.4 PHP paramètres URL

En PHP, comment s’appelle la variable qui permet de récupérer les paramètres qui se trouvent dans l'URL ?
On demande juste son nom.

Miam - saison 2 : La séparation

5.5 Paramètres GET

En PHP, la variable globale $_GET permet de récuperer les paramètres de l'URL.
Essayons de découvrir dans le code PHP, quels paramètres attend miam-serveur.php.
Regardez rapidement le début du code.
Donnez un seul des noms de paramètre attendus.

Miam - saison 2 : La séparation

5.6 URL complète

Ouvrez miam-serveur.php dans votre navigateur:
http://localhost/~__user_nb__/miam-serveur.php

Vous devriez vois une erreur : « Pas de click fourni. »
Maintenant, dans la barre d'adresses de votre navigateur, ajoutez à la fin de cette URL les paramètres
GET pour le chat (en inventant des valeurs)
Quand vous avez trouvé la bonne URL, vous devriez avoir une erreur différente:
« Problème d'écriture dans data.json. Avez-vous crée le fichier et donné les droits requis ? »

Écrivez votre URL ci-dessous:

Miam - saison 2 : La séparation

5.7 data.json

Pour fonctionner, miam-serveur.php a besoin d'écrire dans un fichier appelé « data.json »
Dans vote terminal, allez dans le répertoire de miam-serveur.php (normalement ~/public_html) et créez le fichier vide avec : « touch data.json »

miam-serveur.php est executé par le serveur web (Apache). Le serveur web a un utilisateur différent que vous.
Pour que le serveur web puisse écrire dans ce fichier, il faut permettre aux autres utilisateurs d'écrire dans data.json

Quelle commande faut-il taper pour changer les droits sur ce fichier ?

Miam - saison 2 : La séparation

5.8 Requêtes chat et souris

Maintenant, essayez l'URL à nouveau:
http://localhost/~__user_nb__/miam-serveur.php?clickChatX=123&clickChatY=123
Au lieu d'avoir un message d'erreur, la requête devrait attendre.

Laissez là attendre et ouvrez un autre onglet avec le click de la souris:
http://localhost/~__user_nb__/miam-serveur.php?clickSourisX=222&clickSourisY=222

Dès que la 2e requête se lance, les deux requêtes devraient aboutir et retourner les deux coordonnées.

Rappel:


Miam - saison 2 : La séparation

5.9 Requêtes en JS

Ouvrez deux onglets avec le fichier miam.html
Ouvrez la console dans les deux.

Que faut-il taper pour faire la requête du chat avec simple_fetch ?
Essayez dans la console avant de répondre.
Inventez des valeurs pour les coordonnées.


Miam - saison 2 : La séparation

5.10 Sélection chat / souris

Donc dans la console de chaque onglet , il fallait faire quelque chose du type:

simple_fetch('http://localhost/~__user_nb__/miam-serveur.php?clickChatX=123&clickChatY=123');
simple_fetch('http://localhost/~__user_nb__/miam-serveur.php?clickSourisX=321&clickSourisY=321');
Sélection chat / souris

Il faut que chaque utilisateur choisisse si il veut le chat ou la souris.
Ajoutez un select dans le HTML, avant le terrain:

		<select id="joueur">
<option value="chat">Chat</option>
<option value="souris">Souris</option>
</select>

A l'intérieur du gestionnaire d’événements click, déterminez le joueur. Vous pouvez utiliser la propriété « .value » comme pour un champs texte.
Affichez le joueur dans la console.

Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur, puis copiez-le ici pour que votre enseignant puisse le relire plus tard:

Miam - saison 2 : La séparation

5.11 Correction

const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');

terrain.addEventListener('click',function(event){
console.log('click');
let joueur=document.getElementById('joueur').value;
console.log(joueur);
const rectChat = chat.getBoundingClientRect();
const rectSouris = souris.getBoundingClientRect();

let x=event.offsetX;
let y=event.offsetY;

if(x< rectChat.width/2 ){x= rectChat.width/2;}
if(x>800-rectChat.width/2 ){x=800-rectChat.width/2;}
if(y< rectChat.height/2){y= rectChat.height/2;}
if(y>600-rectChat.height/2){y=600-rectChat.height/2;}

if(x< rectSouris.width/2 ){x= rectSouris.width/2;}
if(x>800-rectSouris.width/2 ){x=800-rectSouris.width/2;}
if(y< rectSouris.height/2){y= rectSouris.height/2;}
if(y>600-rectSouris.height/2){y=600-rectSouris.height/2;}

chat.style.left=(x-rectChat.width /2)+'px';
chat.style.top =(y-rectChat.height/2)+'px';

souris.style.left=(x-rectSouris.width /2)+'px';
souris.style.top =(y-rectSouris.height/2)+'px';
});

Miam - saison 2 : La séparation

5.12 await

Dans le gestionnaire d'événements, on va vouloir utiliser simple_fetch avec « await » pour faire la requete AJAX.
Pour pouvoir utiliser « await », que faut-il écrire à la place de XYZ ?

terrain.addEventListener('click',XYZ function(event){

Miam - saison 2 : La séparation

5.13 Requête dans le JS

On veut ajouter la requete simple_fetch dans le code JS.

Pour ça:

Pour tester, vous devez cliquer dans deux onglets différents, l'un chat, l'autre souris.

Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur, puis copiez-le ici pour que votre enseignant puisse le relire plus tard:

Miam - saison 2 : La séparation

5.14 Correction

const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');

terrain.addEventListener('click',async function(event){
console.log('click');
let joueur=document.getElementById('joueur').value;
console.log(joueur);
const rectChat = chat.getBoundingClientRect();
const rectSouris = souris.getBoundingClientRect();

let x=event.offsetX;
let y=event.offsetY;

if(x< rectChat.width/2 ){x= rectChat.width/2;}
if(x>800-rectChat.width/2 ){x=800-rectChat.width/2;}
if(y< rectChat.height/2){y= rectChat.height/2;}
if(y>600-rectChat.height/2){y=600-rectChat.height/2;}

if(x< rectSouris.width/2 ){x= rectSouris.width/2;}
if(x>800-rectSouris.width/2 ){x=800-rectSouris.width/2;}
if(y< rectSouris.height/2){y= rectSouris.height/2;}
if(y>600-rectSouris.height/2){y=600-rectSouris.height/2;}

let url='http://localhost/~__user_nb__/miam-serveur.php';
if(joueur==='chat'){
url+='?clickChatX='+x+'&clickChatY='+y;
}
else{
url+='?clickSourisX='+x+'&clickSourisY='+y;
}
reponse=await simple_fetch(url);
console.log(reponse);

chat.style.left=(x-rectChat.width /2)+'px';
chat.style.top =(y-rectChat.height/2)+'px';

souris.style.left=(x-rectSouris.width /2)+'px';
souris.style.top =(y-rectSouris.height/2)+'px';
});


Miam - saison 2 : La séparation

5.15 Mouvement !

On y est presque !

Regardez bien la réponse du serveur. C'est un objet, contenant deux objets.
Utilisez ces informations à la place de x et y pour mettre à jour les coordonnées du chat et de la souris.

Écrivez le code, vérifiez qu'il fonctionne dans votre navigateur, puis copiez-le ici pour que votre enseignant puisse le relire plus tard:


Miam - saison 2 : La séparation

5.16 Correction

const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');

terrain.addEventListener('click',async function(event){
console.log('click');
let joueur=document.getElementById('joueur').value;
console.log(joueur);
const rectChat = chat.getBoundingClientRect();
const rectSouris = souris.getBoundingClientRect();

let x=event.offsetX;
let y=event.offsetY;

if(x< rectChat.width/2 ){x= rectChat.width/2;}
if(x>800-rectChat.width/2 ){x=800-rectChat.width/2;}
if(y< rectChat.height/2){y= rectChat.height/2;}
if(y>600-rectChat.height/2){y=600-rectChat.height/2;}

if(x< rectSouris.width/2 ){x= rectSouris.width/2;}
if(x>800-rectSouris.width/2 ){x=800-rectSouris.width/2;}
if(y< rectSouris.height/2){y= rectSouris.height/2;}
if(y>600-rectSouris.height/2){y=600-rectSouris.height/2;}

let url='http://localhost/~__user_nb__/miam-serveur.php';
if(joueur==='chat'){
url+='?clickChatX='+x+'&clickChatY='+y;
}
else{
url+='?clickSourisX='+x+'&clickSourisY='+y;
}
reponse=await simple_fetch(url);
console.log(reponse);

chat.style.left=(reponse.clickChat.x-rectChat.width/2)+'px';
chat.style.top =(reponse.clickChat.y-rectChat.height/2)+'px';

souris.style.left=(reponse.clickSouris.x-rectSouris.width/2)+'px';
souris.style.top =(reponse.clickSouris.y-rectSouris.height/2)+'px';
});

Miam - saison 2 : La séparation

5.17 try / catch

Ca y'est ! Notre souris est enfin indépendante. Ouf, il était temps. Elle trouvait que le regard du chat devenait insistant.

Il nous reste un petit soucis. Entre le chat et la souris, il peut parfois y avoir des problèmes.
Dans notre code aussi... des problèmes comme de promesses non tenues.

Modifiez votre code de cette manière:

  let reponse;
try{
reponse=await simple_fetch(url);
}catch(e){
console.log('erreur',e);
alert(e.erreur);
return;
}

Miam - saison 2 : La séparation

5.18 Entre deux machines:

Vous pouvez faire fonctionner votre programme à partir de deux machines de la salle TP.
Il s'agit d'utiliser le code d'une seule personne. Il suffit donc qu'un des deux ait écrit le code (ici ces sera "vous"), l'autre personne (votre "voisin") ne fait que se connecter sur votre site.

6. Miam - saison 3 : Consommée

6.1 Miam - saison 3 : Consommée

La souris avait raison de se méfier. Ce chat a un petit creux.

Cette partie est juste pour s'amuser.
Si vous êtes en retard (ou si êtes trop angoissés par ce qu'il pourrait arriver à la souris), vous pouvez passer.

On veut déterminer si le chat attrape la souris. Pour ça, on va faire des vérifications toutes les 50 millisecondes.

setInterval, qu'on a déjà utilisé, permet d'appeler une fonction régulièrement.

En utilisant:

  const rectChat  =  chat.getBoundingClientRect();
const rectSouris=souris.getBoundingClientRect();

determinez les conditions pour que ces deux rectangles se touchent.

Affichez alors « Miam » dans un alert.

Miam - saison 3 : Consommée

6.2 Correction

setInterval(()=>{
const rectChat = chat.getBoundingClientRect();
const rectSouris=souris.getBoundingClientRect();
//console.log(rectChat);
if(rectChat.right > rectSouris.left &&
rectChat.left < rectSouris.right &&
rectChat.bottom > rectSouris.top &&
rectChat.top < rectSouris.bottom )
{
alert('Miam');
}
},50);

Miam - saison 3 : Consommée

6.3 Retour au point de départ

On veut que le chat et la souris retournent au point de départ après.

Effacez les valeurs top, left des deux (chat et souris) en utilisant « null ».

Le retour pose problème. D'autres collisions chat-souris vont survenir. Pour les éviter, créez une variable « partieEnCours ».
Si la partie n'est pas en cours, ne faites pas de vérification de collision.
Après avoir affiché « Miam », mettez partieEnCours à false et remettez la à true après 1200ms (setTimeout).


Miam - saison 3 : Consommée

6.4 Correction

const terrain=document.getElementById('terrain');
const chat =document.getElementById('chat');
const souris =document.getElementById('souris');
let partieEnCours=true;

terrain.addEventListener('click',async function(e){
let joueur=document.getElementById('joueur').value;
console.log(joueur);

const rectChat = chat.getBoundingClientRect();
const rectSouris=souris.getBoundingClientRect();
let x=event.offsetX;
let y=event.offsetY;

let rect;
if(joueur==='chat'){rect= chat.getBoundingClientRect();}
else {rect=souris.getBoundingClientRect();}

if(x< rect.width/2 ){x= rect.width/2;}
if(x>800-rect.width/2 ){x=800-rect.width/2;}
if(y< rect.height/2){y= rect.height/2;}
if(y>600-rect.height/2){y=600-rect.height/2;}

let url='http://localhost/~__user_nb__/miam-serveur.php';
if(joueur==='chat'){
url+='?clickChatX='+x+'&clickChatY='+y;
}
else{
url+='?clickSourisX='+x+'&clickSourisY='+y;
}

let reponse;
try{
reponse=await simple_fetch(url);
}catch(e){
console.log('erreur',e);
alert(e.erreur);
return;
}

chat.style.left=(reponse.clickChat.x-rectChat.width/2)+'px';
chat.style.top =(reponse.clickChat.y-rectChat.height/2)+'px';

souris.style.left=(reponse.clickSouris.x-rectSouris.width/2)+'px';
souris.style.top =(reponse.clickSouris.y-rectSouris.height/2)+'px';
});

setInterval(()=>{
if(partieEnCours===false){return;}
const rectChat = chat.getBoundingClientRect();
const rectSouris=souris.getBoundingClientRect();
//console.log(rectChat);
if(rectChat.right > rectSouris.left &&
rectChat.left < rectSouris.right &&
rectChat.bottom > rectSouris.top &&
rectChat.top < rectSouris.bottom )
{
alert('Miam');
partieEnCours=false;
setTimeout(()=>partieEnCours=true,1200);
chat.style.left=null;
chat.style.top =null;
souris.style.left=null;
souris.style.top =null;

}
},50);