diff -urN cahier-de-prepa6.2.0/agenda.php cahier-de-prepa8.0.1/agenda.php
--- cahier-de-prepa6.2.0/agenda.php	2016-10-01 23:26:29.713892510 +0200
+++ cahier-de-prepa8.0.1/agenda.php	2018-10-15 17:50:34.840788232 +0200
@@ -9,7 +9,7 @@
 //////////////////////////////////////////////
 // Validation de la requête : jour et année //
 //////////////////////////////////////////////
-if ( isset($_REQUEST['mois']) && is_numeric($mois = $_REQUEST['mois']) && $mois > 1608 && $mois != date('ym') )  {
+if ( isset($_REQUEST['mois']) && is_numeric($mois = $_REQUEST['mois']) && $mois > 1000 && $mois != date('ym') )  {
   $debutmois = mktime(0,0,0,$mois%100,1,(int)($mois/100));
   $auj = 0;
 }
@@ -35,33 +35,24 @@
 $r = $resultat->fetch_row();
 $protection = $r[0];
 $resultat->free();
-if ( ( $protection > $autorisation ) && ( $autorisation > 0 ) )  {
-  debut($mysqli,'Agenda','Vous n\'avez pas accès à cette page.',$autorisation,'');
-  $mysqli->close();
-  fin();
-}
-// Connexion nécessaire si protection
-if ( $protection && !$autorisation )  {
-  $titre = 'Agenda';
-  $actuel = '';
-  include('login.php');
-}
-// Mode édition pour les professeurs
-$edition = ( $autorisation == 4 );
+$edition = acces($protection,0,'Agenda',"agenda",$mysqli);
 
 ////////////
 /// HTML ///
 ////////////
 $mois = array('','Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre');
-debut($mysqli,'Agenda - '.$mois[idate('m',$debutmois)].' '.date('Y',$debutmois). ( ( $edition && $protection ) ? "<span class=\"icon-lock$protection\"></span>" : '' ),$message,$autorisation,'agenda',false,'datetimepicker');
+$icone = ( $edition && $protection ) ? '<span class="icon-lock"></span>' : '';
+debut($mysqli,'Agenda - '.$mois[idate('m',$debutmois)].' '.date('Y',$debutmois).$icone,$message,$autorisation,'agenda',false,'datetimepicker');
 
 // Contrôles généraux, seulement en mode édition
 if ( $edition )  {
 ?>
-  <a class="icon-aide general" data-id="page" title="Aide pour l'édition de l'agenda"></a>
-  <a class="icon-prefs general" data-id="prefs" title="Modifier les préférences de l'agenda"></a>
-  <a class="icon-ajoute general" data-id="evenement" title="Ajouter un nouvel événement à l'agenda"></a>
-  <a class="icon-ajout-colle general" data-id="deplacement-colle" title="Ajouter un nouveau déplacement de colle"></a>
+  <div id="icones">
+    <a class="icon-ajout-colle formulaire" title="Ajouter un nouveau déplacement de colle"></a>
+    <a class="icon-ajoute formulaire" title="Ajouter un nouvel événement à l'agenda"></a>
+    <a class="icon-prefs formulaire" title="Modifier les préférences de l'agenda"></a>
+    <a class="icon-aide" title="Aide pour l'édition de l'agenda"></a>
+  </div>
 
 <?php
 }
@@ -257,7 +248,7 @@
         $classe = ' evnmt_suite evnmt_suivi';
       }
       // Affichage
-      echo "            <td><p id=\"e$id\" class=\"evnmt evnmt{$evenements[$id]['type']}$classe\">{$evenements[$id]['titre']}</p></td>\n";
+      echo "            <td><p id=\"e$id\" class=\"modifevnmt evnmt{$evenements[$id]['type']}$classe\">{$evenements[$id]['titre']}</p></td>\n";
     }
     echo "          </tr>\n";
   }
@@ -289,69 +280,90 @@
     $resultat->free();
   }
   
-  // Select sur la protection
-  $select_protection = str_replace("\"$protection\"","\"$protection\" selected",'
-      <option value="0">Visible de tous</option>
-      <option value="1">Visible pour les connectés</option>
-      <option value="2">Visible pour les élèves, colleurs, profs</option>
-      <option value="3">Visible pour les colleurs et les profs</option>
-      <option value="4">Visible pour les profs uniquement</option>');
-
+  // Options du select multiple d'accès
+  $select_protection = '
+          <option value="0">Accès public</option>
+          <option value="6">Utilisateurs identifiés</option>
+          <option value="1">Invités</option>
+          <option value="2">Élèves</option>
+          <option value="3">Colleurs</option>
+          <option value="4">Administratifs</option>
+          <option value="5">Professeurs</option>
+          <option value="32">Agenda désactivé</option>';
+  $p = $protection;
+  if ( ( $p == 0 ) || ( $p == 32 ) )
+    $sel_protection = str_replace("\"$p\"","\"$p\" selected",$select_protection);
+  else  {
+    $sel_protection = str_replace('"6"','"6" selected',$select_protection);
+    for ( $a=1; $a<6; $a++ )
+      if ( ( ($p-1)>>($a-1) & 1 ) == 0 )
+        $sel_protection = str_replace("\"$a\"","\"$a\" selected",$sel_protection);
+  }
+  
   // Récupération du nombre d'événements affichés sur la page d'accueil
   $resultat = $mysqli->query('SELECT val FROM prefs WHERE nom=\'nb_agenda_index\'');
+  $mysqli->close();
   $r = $resultat->fetch_row();
   $n = $r[0];
   $resultat->free();
 ?>
- 
-  <form id="form-evenement">
-    <h3 class="edition">Modification d'événement</h3>
+
+  <form id="form-evnmt" data-action="agenda-elems">
+    <h3 class="edition">Modifier un événement</h3>
     <p class="ligne"><label for="type">Type&nbsp;:</label>
-      <select id="type" name="type"><?php echo $select_types; ?></select>
+      <select name="type"><?php echo $select_types; ?></select>
       <a class="icon-edite" href="agenda-types">&nbsp;</a>
     </p>
     <p class="ligne"><label for="matiere">Matière&nbsp;:</label>
-      <select id="matiere" name="matiere"><option value="0">Pas de matière</option><?php echo $select_matieres; ?></select>
+      <select name="matiere"><option value="0">Pas de matière</option><?php echo $select_matieres; ?></select>
     </p>
-    <p class="ligne"><label for="debut">Début&nbsp;: </label><input type="text" id="debut" name="debut" value="" size="15"></p>
-    <p class="ligne"><label for="fin">Fin&nbsp;: </label><input type="text" id="fin" name="fin" value="" size="15"></p>
-    <p class="ligne"><label for="jours">Date(s) seulement&nbsp;: </label><input type="checkbox" id="jours" name="jours" value="1"></p>
-    <textarea name="texte" class="edithtml" rows="10" cols="100" data-placeholder="Texte associé à l'événement (non obligatoire)"></textarea>
-    <input type="hidden" name="table" value="agenda">
+    <p class="ligne"><label for="debut">Début&nbsp;: </label><input type="text" name="debut" value="" size="15"></p>
+    <p class="ligne"><label for="fin">Fin&nbsp;: </label><input type="text" name="fin" value="" size="15"></p>
+    <p class="ligne"><label for="jours">Date(s) seulement&nbsp;: </label><input type="checkbox" name="jours" value="1"></p>
+    <textarea name="texte" class="edithtml" rows="10" cols="100" placeholder="Texte associé à l'événement (non obligatoire)"></textarea>
     <input type="hidden" name="id" value="">
   </form>
+   
+  <form id="form-ajoute" data-action="agenda-elems">
+    <h3 class="edition">Ajouter un événement</h3>
+    <p class="ligne"><label for="type">Type&nbsp;:</label>
+      <select name="type"><?php echo $select_types; ?></select>
+      <a class="icon-edite" href="agenda-types">&nbsp;</a>
+    </p>
+    <p class="ligne"><label for="matiere">Matière&nbsp;:</label>
+      <select name="matiere"><option value="0">Pas de matière</option><?php echo $select_matieres; ?></select>
+    </p>
+    <p class="ligne"><label for="debut">Début&nbsp;: </label><input type="text" name="debut" value="" size="15"></p>
+    <p class="ligne"><label for="fin">Fin&nbsp;: </label><input type="text" name="fin" value="" size="15"></p>
+    <p class="ligne"><label for="jours">Date(s) seulement&nbsp;: </label><input type="checkbox" name="jours" value="1"></p>
+    <textarea name="texte" class="edithtml" rows="10" cols="100" placeholder="Texte associé à l'événement (non obligatoire)"></textarea>
+  </form>
   
-  <form id="form-deplacement-colle">
+  <form id="form-ajout-colle" data-action="deplcolle">
     <h3 class="edition">Nouveau déplacement de colle</h3>
     <p>Ce formulaire spécial donne la possibilité de créer une annulation (si l'<i>ancien horaire</i> est le seul renseigné), un rattrapage (si le <i>nouvel horaire</i> est le seul renseigné) ou un déplacement (si les deux horaires sont renseignés) de colle de façon automatique. Dans le cas d'un déplacement, deux événements sont créés. La salle est facultative. Le(s) événement(s) créé(s) sont ensuite modifiables en éditant le texte. Pour chaque horaire, régler l'heure à «&nbsp;0h00&nbsp;» permet de n'afficher que la date.</p>
     <p class="ligne"><label for="matiere">Matière&nbsp;:</label>
-      <select id="matiere" name="matiere"><?php echo $select_matieres; ?></select>
-    </p>
-    <p class="ligne"><label for="colleur">Colleur&nbsp;: </label><input type="text" data-placeholder="(obligatoire)" id="colleur" name="colleur" value="" size="20"></p>
-    <p class="ligne"><label for="groupe">Groupe&nbsp;: </label><input type="text" data-placeholder="(obligatoire)" id="groupe" name="groupe" value="" size="10"></p>
-    <p class="ligne"><label for="ancien">Ancien horaire&nbsp;: </label>
-      <input type="text" data-placeholder="(non obligatoire)" id="ancien" name="ancien" value="" size="15">
-      <a class="icon-ferme"></a>
+      <select name="matiere"><?php echo $select_matieres; ?></select>
     </p>
-    <p class="ligne"><label for="ancien">Nouvel horaire&nbsp;: </label>
-      <input type="text" data-placeholder="(non obligatoire)" id="nouveau" name="nouveau" value="" size="15">
-      <a class="icon-ferme"></a>
-    </p>
-    <p class="ligne"><label for="salle">Salle de rattrapage&nbsp;: </label><input type="text" data-placeholder="(non obligatoire)" id="salle" name="salle" value="" size="10"></p>
-    <input type="hidden" name="table" value="deplcolle">
+    <p class="ligne"><label for="colleur">Colleur&nbsp;: </label><input type="text" placeholder="(obligatoire)" name="colleur" value="" size="20"></p>
+    <p class="ligne"><label for="groupe">Groupe&nbsp;: </label><input type="text" placeholder="(obligatoire)" name="groupe" value="" size="10"></p>
+    <p class="ligne"><label for="ancien">Ancien horaire&nbsp;: </label><input type="text" placeholder="(non obligatoire)" name="ancien" value="" size="15"></p>
+    <p class="ligne"><label for="ancien">Nouvel horaire&nbsp;: </label><input type="text" placeholder="(non obligatoire)" name="nouveau" value="" size="15"></p>
+    <p class="ligne"><label for="salle">Salle de rattrapage&nbsp;: </label><input type="text" placeholder="(non obligatoire)" name="salle" value="" size="10"></p>
   </form>
 
-  <form id="form-prefs">
+  <form id="form-prefs" data-action="prefsglobales">
     <h3 class="edition">Modifier les préférences de l'agenda</h3>
     <p class="ligne"><label for="protection_agenda">Accès&nbsp;: </label>
-      <select id="protection_agenda" name="protection_agenda"><?php echo $select_protection; ?>
+      <select name="protection_agenda[]" multiple><?php echo $sel_protection; ?>
       </select>
     </p>
+    <p>Pour modifier les semaines de l'année, il faut vous rendre à la <a href="planning">gestion du planning annuel</a>.</p>
     <p class="ligne"><label for="nb_agenda_index">Nombre d'événements affichés sur la page d'accueil&nbsp;: </label></p>
     <p class="ligne">&nbsp;
-      <input type="text" id="nb_agenda_index" name="nb_agenda_index" value="<?php echo $n; ?>" size="3">
+      <input type="text" name="nb_agenda_index" value="<?php echo $n; ?>" size="3">
     </p>
-    <input type="hidden" name="table" value="prefs">
+    <p>Les différents types d'événements sont modifiables sur une <a href="agenda-types">page spécifique</a>.</p>
   </form>
   
   <div id="aide-page">
@@ -373,15 +385,17 @@
       <li>le <em>nombre d'événements affichés sur la page d'accueil</em></li>
     </ul>
     <p>Des détails sont donnés dans l'aide du formulaire de modification.</p>
+    <h4>Types d'événements</h4>
+    <p>Les types d'événements ne sont pas modifiables sur cette page, mais sur une <a href="agenda-types">page spécifique</a>.</p>
     <h4>Visibilité des événements</h4>
-    <p>Attention, contrairement aux informations, aux documents et aux programmes de colles, les événements sont obligatoirement visibles par tous ceux qui ont accès à l'agenda. Il n'est pas possible de cacher un événement pour le faire apparaître plus tard. Cette fonctionnalité est prévue pour la prochaine version, en cours d'année.</p>
+    <p>Attention, contrairement aux informations, aux documents et aux programmes de colles, les événements sont obligatoirement visibles par tous ceux qui ont accès à l'agenda. Il n'est pas possible de cacher un événement pour le faire apparaître plus tard. Cette fonctionnalité est prévue pour une prochaine version.</p>
     <h4>Affichage sur la page d'accueil</h4>
     <p>Si des événements sont disponibles dans les 7 jours qui viennent, ils sont automatiquement affichés en haut de la page d'accueil, avant toute information. Cette fonctionnalité est désactivable en réglant le <em>nombre d'événements affichés sur la page d'accueil</em> à zéro dans les préférences de l'agenda.</p>
     <h4>Matières et droits</h4>
     <p>Seuls les professeurs peuvent modifier l'agenda. Afin de faciliter les modifications, en particulier lors des changements d'emploi du temps concernant plusieurs matières, il n'y a pas d'impossibilité d'ajouter/modifier un événement concernant une autre matière. Par ailleurs, toute matière concernée doit avoir été créée à la page de gestion des <a href="matieres">matières</a>.</p>
   </div>
 
-  <div id="aide-evenement">
+  <div id="aide-ajoute">
     <h3>Aide et explications</h3>
     <p>Ce formulaire permet d'ajouter ou de modifier un événement de l'agenda. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
     <h4>Type, matière</h4>
@@ -400,7 +414,7 @@
     <p>Le texte doit être formaté en HTML&nbsp;: par exemple, chaque bloc de texte doit être encadré par &lt;p&gt; et &lt;/p&gt;. Il peut donc contenir des liens vers les autres pages du site, vers des documents du site, vers le web... Des boutons sont fournis pour aider au formattage (ainsi qu'une aide).</p>
   </div>
 
-  <div id="aide-deplacement-colle">
+  <div id="aide-ajout-colle">
     <h3>Aide et explications</h3>
     <p>Ce formulaire permet d'ajouter un déplacement de colle dans l'agenda. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
     <p>L'ajout de déplacement de colle est en réalité un simple raccourci pratique qui conduit à l'ajout d'un ou deux événements, en fonction des dates données. Le texte sera généré automatiquement à partir des informations saisies. Le ou les deux événements seront ensuite modifiable comme tous les autres événements.</p>
@@ -428,15 +442,12 @@
       <li>l'<em>accès</em> à l'agenda (voir ci-dessous).</li>
       <li>le <em>nombre d'événements affichés sur la page d'accueil</em>&nbsp;: les événements qui se situent dans les 7 jours prochains s'affichent automatiquement en haut de la page d'accueil du Cahier de Prépa, dans la limite du nombre égal à cette valeur. En particulier, mettre zéro supprime cet affichage automatique. La valeur par défaut est 10.</li>
     </ul>
-    <p>L'<em>accès</em> à l'agenda peut être choisi parmi cinq possibilités&nbsp;:</p>
+    <p>L'accès à l'agenda peut être protégé. Trois choix sont possibles&nbsp;:</p>
     <ul>
-      <li><em>Visible de tous</em>&nbsp;: accessible de tout visiteur, sans identification.</li>
-      <li><em>Visible pour les connectés</em>&nbsp;: accessible de tout utilisateur mais uniquement après identification (utilisateurs de type invité, élève, colleur ou professeur).</li>
-      <li><em>Visible pour les élèves, colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type élève, colleur ou professeur.</li>
-      <li><em>Visible pour les colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type colleur ou professeur.</li>
-      <li><em>Visible pour les professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type professeur</li>
+      <li><em>Accès public</em>&nbsp;: agenda accessible de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: agenda accessible uniquement par les utilisateurs identifiés, en fonction de leur type de compte. Un cadenas <span class="icon-lock"></span> est alors affiché dans le titre de la page.</li>
+      <li><em>Agenda invisible</em>&nbsp;: agenda entièrement invisible pour les utilisateurs autres que les professeurs. Un cadenas <span class="icon-locktotal"></span> est alors affiché dans le titre de la page.</li>
     </ul>
-    <p>Les comptes utilisateurs sont à définir ou modifier sur la page de <a href="utilisateurs">gestion des utilisateurs</a>.</p>
   </div>
 
   <p id="log"></p>
@@ -445,7 +456,5 @@
 
 <?php
 }
-
-$mysqli->close();
 fin($edition,$mathjax);
 ?>
diff -urN cahier-de-prepa6.2.0/agenda-types.php cahier-de-prepa8.0.1/agenda-types.php
--- cahier-de-prepa6.2.0/agenda-types.php	2016-08-30 17:24:26.391152661 +0200
+++ cahier-de-prepa8.0.1/agenda-types.php	2018-10-14 17:44:20.702417813 +0200
@@ -10,28 +10,31 @@
 // Autorisation //
 //////////////////
 
-// Accès aux professeurs connectés uniquement
+// Accès aux professeurs connectés uniquement. Redirection pour les autres.
+if ( $autorisation < 5 )  {
+  header("Location: https://$site");
+  exit();
+}
 $mysqli = connectsql();
-if ( !$autorisation )  {
+// Si connexion light : on doit répéter son mot de passe pour aller plus loin
+// login.php contient fin()
+if ( $_SESSION['light'] )  {
   $titre = 'Types d\'événements de l\'agenda';
-  $actuel = false;
+  $actuel = 'agenda';
   include('login.php');
 }
-elseif ( $autorisation < 4 )  {
-  debut($mysqli,'Types d\'événements de l\'agenda','Vous n\'avez pas accès à cette page.',$autorisation,' ');
-  $mysqli->close();
-  fin();
-}
 
 //////////////
 //// HTML ////
 //////////////
-debut($mysqli,"Agenda - Types d'événements",$message,4,'agenda',false,'colpick');
+debut($mysqli,"Agenda - Types d'événements",$message,5,'agenda',false,'colpick');
 echo <<<FIN
 
-  <a class="icon-aide general" data-id="page" title="Aide pour les modifications des types d'événements"></a>
-  <a class="icon-annule general" onclick="history.back()" title="Retour à l'agenda"></a>
-  <a class="icon-ajoute general" data-id="ajoute" title="Ajouter un nouveau type d'événements"></a>
+  <div id="icones">
+    <a class="icon-ajoute formulaire" title="Ajouter un nouveau type d'événements"></a>
+    <a class="icon-annule" onclick="history.back()" title="Retour à l'agenda"></a>
+    <a class="icon-aide" title="Aide pour les modifications des types d'événements"></a>
+  </div>
 
   <h2 class="edition">Modifier les types d'événements</h2>
 
@@ -39,6 +42,7 @@
 
 // Récupération
 $resultat = $mysqli->query('SELECT t.id, ordre, nom, cle, couleur, COUNT(a.id) AS nb FROM `agenda-types` AS t LEFT JOIN agenda AS a ON type = t.id GROUP BY t.id ORDER BY ordre');
+$mysqli->close();
 $max = $resultat->num_rows;
 while ( $r = $resultat->fetch_assoc() )  {
   $id = $r['id'];
@@ -51,23 +55,21 @@
     $suppr = "\n    <a class=\"icon-supprime\" title=\"Supprimer ce type d'événements\"></a>";
     $indication = '';
   }
-  // $monte = "\n    <a class=\"icon-monte\"".( ( $r['ordre'] < 4 ) ? ' style="display:none;"' : '' ).' title="Déplacer ce type d\'événements vers le haut"></a>';
-  // $descend = "\n    <a class=\"icon-descend\"".( ( $r['ordre'] == $max ) ? ' style="display:none;"' : '' ).' title="Déplacer ce type d\'événements vers le bas"></a>';
   $monte = ( $r['ordre'] == 1 ) ? ' style="display:none;"' : '';
   $descend = ( $r['ordre'] == $max ) ? ' style="display:none;"' : '';
   echo <<<FIN
 
   <article data-id="agenda-types|$id">
-    <a class="icon-aide" data-id="type" title="Aide pour l'édition de ce type d'événements"></a>
+    <a class="icon-ok" title="Valider les modifications"></a>
+    <a class="icon-aide" title="Aide pour l'édition de ce type d'événements"></a>
     <a class="icon-monte"$monte title="Déplacer ce type d'événements vers le haut"></a>
     <a class="icon-descend"$descend title="Déplacer ce type d'événements vers le bas"></a>$suppr
+    <h3 class="edition">${r['nom']}</h3>
     <form>
-      <a class="icon-ok" title="Valider les modifications"></a>
-      <h3 class="edition">${r['nom']}</h3>
       <p>Ce type d'événements correspond à ${r['nb']} événement$s dans l'agenda.$indication</p>
-      <p class="ligne"><label for="nom$id">Nom&nbsp;: </label><input type="input" id="nom$id" name="nom" value="${r['nom']}" size="50"></p>
-      <p class="ligne"><label for="cle$id">Clé&nbsp;: </label><input type="input" id="cle$id" name="cle" value="${r['cle']}" size="50"></p>
-      <p class="ligne"><label for="couleur$id">Couleur&nbsp;: </label><input type="input" id="couleur$id" name="couleur" value="${r['couleur']}" size="6"></p>
+      <p class="ligne"><label for="nom$id">Nom&nbsp;: </label><input type="text" id="nom$id" name="nom" value="${r['nom']}" size="50"></p>
+      <p class="ligne"><label for="cle$id">Clé&nbsp;: </label><input type="text" id="cle$id" name="cle" value="${r['cle']}" size="50"></p>
+      <p class="ligne"><label for="couleur$id">Couleur&nbsp;: </label><input type="text" id="couleur$id" name="couleur" value="${r['couleur']}" size="6"></p>
     </form>
   </article>
 
@@ -78,14 +80,13 @@
 // Aide et formulaire d'ajout
 ?>
 
-  <form id="form-ajoute">
+  <form id="form-ajoute" action="ajout-agenda-types">
     <h3 class="edition">Ajouter un nouveau type d'événements</h3>
     <div>
-      <input type="input" class="ligne" name="nom" value="" size="50" data-placeholder="Nom pour l'affichage (Commence par majuscule, singulier)">
-      <input type="input" class="ligne" name="cle" value="" size="50" data-placeholder="Clé pour les adresses web (Un seul mot, minuscules ou sigle, singulier)">
-      <input type="input" class="ligne" name="couleur" value="" size="6" data-placeholder="Couleur des événements (code RRGGBB)">
+      <input type="text" class="ligne" name="nom" value="" size="50" placeholder="Nom pour l'affichage (Commence par majuscule, singulier)">
+      <input type="text" class="ligne" name="cle" value="" size="50" placeholder="Clé pour les adresses web (Un seul mot, minuscules ou sigle, singulier)">
+      <input type="text" class="ligne" name="couleur" value="" size="6" placeholder="Couleur des événements (code RRGGBB)">
     </div>
-    <input type="hidden" name="table" value="agenda-types">
   </form>
 
   <div id="aide-page">
@@ -102,7 +103,7 @@
     <p>Les types <em>Déplacement de colle</em> et <em>Rattrapage de colle</em> sont utilisé par la possibilité d'ajout automatique d'un déplacement de colle dans l'agenda. Il est donc impossible de les supprimer, mais l'on peut tout à fait les déplacer voire les renommer (ce qui n'est pas une bonne idée), et bien sûr modifier la couleur.</p>
   </div>
 
-  <div id="aide-type">
+  <div id="aide-agenda-types">
     <h3>Aide et explications</h3>
     <p>Le <em>nom</em> sera affiché au début de chaque événement, dans le calendrier et dans les informations récentes. Il doit s'agit d'un nom singulier et commençant par une majuscule. Il peut être relativement long. Par exemple&nbsp;: «&nbsp;Annulation de cours&nbsp;», «&nbsp;Interrogation de cours&nbsp;» (si vous souhaitez les annoncer :-) )</p>
     <p>La <em>clé</em> sera affichée dans le menu déroulant de recherche, précédé de «&nbsp;les&nbsp», ainsi que dans l'adresse des pages qui affichent ce type d'événements&nbsp;: il faut donc que ce soit un pluriel, pas trop long, en un mot, sans majucule au début (sauf s'il le faut). Par exemple, «&nbsp;annulations&nbsp;», «&nbsp;interros&nbsp;».</p>
@@ -132,16 +133,18 @@
   <script type="text/javascript" src="js/colpick.min.js"></script>
   <script type="text/javascript">
 $( function() {
+  // Sélecteurs de couleurs pour les formulaire affichés au chargement
+  $('[name="couleur"]').colpick();
   // Envoi par appui sur Entrée
   $('input,select').on('keypress',function (e) {
-    if ( e.which == 13 )
+    if ( e.which == 13 ) {
       $(this).parent().parent().children('a.icon-ok').click();
+      return false;
+    }
   });
 });
   </script>
 
 <?php
-
-$mysqli->close();
 fin(true);
 ?>
diff -urN cahier-de-prepa6.2.0/ajax.php cahier-de-prepa8.0.1/ajax.php
--- cahier-de-prepa6.2.0/ajax.php	2017-08-25 03:29:03.302921678 +0200
+++ cahier-de-prepa8.0.1/ajax.php	2018-10-16 22:52:36.147237651 +0200
@@ -6,1256 +6,1864 @@
 // Fonctions
 include('fonctions.php');
 
-// Connexion (tous utilisateurs)
-if ( isset($_REQUEST['login']) && isset($_REQUEST['motdepasse']) )  {
-  $mysqli = connectsql();
-  // Récupération des logins/mdp dans la base MySQL et comparaison
-  $motdepasse = sha1($_REQUEST['motdepasse']);
-  $resultat = $mysqli->query("SELECT id, login, autorisation, matieres, timeout FROM utilisateurs WHERE mdp = '$motdepasse'");
-  while ( $r = $resultat->fetch_assoc() )
-    if ( $r['login'] == $_REQUEST['login'] )  {
-      // Interdiction de garder son identifiant de session
-      session_regenerate_id(true);
-      $_SESSION = array();
-      // Interdiction de pouvoir se connecter aux autres site sur le même serveur
-      $_SESSION[md5("$site")] = true;
-      // Pour vérification/utilisation aux connexions ultérieures
-      $_SESSION['client'] = $_SERVER['HTTP_USER_AGENT'];
-      $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
-      $_SESSION['login'] = $_POST['login'];
-      $_SESSION['id'] = $r['id'];
-      $_SESSION['autorisation'] = $autorisation = $r['autorisation'];
-      $_SESSION['matieres'] = $r['matieres'];
-      $_SESSION['timeout'] = $r['timeout'];
-      $_SESSION['time'] = time()+$_SESSION['timeout']+14400*($_SESSION['timeout'] == 0);
-      // Pour sécurisation des requêtes AJAX
-      $_SESSION['csrf-token'] = ( isset($_REQUEST['csrf-token']) ) ? $_REQUEST['csrf-token'] : md5(uniqid(rand(), true)); 
-      break;
-    }
-  $resultat->free();
-  $mysqli->close();
-  if ( !$autorisation )
-    exit('{"etat":"login_nok","message":"Mauvais couple identifiant/mot de passe"}');
-  // Si paramètre "connexion", appel par le bouton de connexion (et non
-  // reconnexion après requête AJAX). Recharge immédiate, donc $_SESSION
-  if ( isset($_REQUEST['connexion']) )
-    exit($_SESSION['message'] = '{"etat":"ok","message":"Connexion réussie"}');
-}
-// Demande de déconnexion (tous utilisateurs)
-if ( isset($_REQUEST['deconnexion']) )  {
+// Vérification du token CSRF
+if ( !isset($_REQUEST['csrf-token']) || isset($_SESSION['csrf-token']) && ( $_REQUEST['csrf-token'] != $_SESSION['csrf-token'] ) )
+  exit('{"etat":"nok","message":"Accès non autorisé"}');
+
+// Récupération de l'action
+if ( !isset($_REQUEST['action']) || !in_array($action = $_REQUEST['action'],array('deconnexion', 'courriel', 'prefsperso', 'notes', 'ajout-notes', 'releve-notes', 'infos', 'ajout-info', 'reps', 'ajout-rep', 'docs', 'ajout-doc', 'colles', 'ajout-colle', 'cdt-elems', 'cdt-types', 'cdt-raccourcis', 'ajout-cdt-raccourci', 'pages', 'ajout-page', 'matieres', 'ajout-matiere', 'utilisateur', 'utilisateurs','ajout-utilisateurs', 'utilisateur-matiere', 'utilisateurs-matieres', 'groupes', 'ajout-groupe', 'planning', 'agenda-elems', 'deplcolle', 'agenda-types', 'ajout-agenda-types', 'prefsmatiere', 'prefsglobales')) )
+  exit('{"etat":"nok","message":"Aucune action effectuée"}');
+
+// Demande de déconnexion
+if ( $action == 'deconnexion' )  {
+  // Écriture de la déconnexion dans le fichier de log
+  logconnect(3,$_SESSION['login']);
   // Suppression du cookie et des données de session
   $_SESSION = array();
   setcookie(session_name(),'',time()-3600);
+  setcookie('loginpermanent','',time()-3600);
   session_regenerate_id(true);
   // Recharge immédiate, donc besoin de $_SESSION['message']
   exit($_SESSION['message'] = '{"etat":"ok","message":"Déconnexion réussie"}');
 }
-// Vérification du token CSRF
-if ( !isset($_REQUEST['csrf-token']) || isset($_SESSION['csrf-token']) && ( $_REQUEST['csrf-token'] != $_SESSION['csrf-token'] ) )
-  exit('{"etat":"nok","message":"Accès non autorisé"}');
 // Si non autorisé, la session a dû expirer : il faut se reconnecter
-if ( !$autorisation )
-  exit( ( isset($_REQUEST['recupdoc']) ) ? '{"etat":"login_"}' : '{"etat":"login"}');
-// Connexion à la base de données
-$mysqli = connectsql(true);
+if ( $autorisation == 0 )
+  exit('{"etat":"login"}');
+
+///////////////////////
+// Envoi de courriel //
+///////////////////////
+if ( ( $action == 'courriel' ) && connexionlight() && $_SESSION['mailenvoi'] && isset($_REQUEST['id-copie']) && isset($_REQUEST['sujet']) && isset($_REQUEST['texte']) )  {
+
+  // Vérification des données
+  if ( !strlen($sujet = $_REQUEST['sujet']) )
+    exit('{"etat":"nok","message":"Pas de sujet : courriel non envoyé"}');
+  elseif ( !strlen($texte = $_REQUEST['texte']) )
+    exit('{"etat":"nok","message":"Pas de texte : courriel non envoyé"}');
+  // Vérification de l'adresse électronique
+  $mysqli = connectsql();
+  $resultat = $mysqli->query("SELECT mailexp, mail FROM utilisateurs WHERE id = ${_SESSION['id']}");
+  $u = $resultat->fetch_assoc();
+  $resultat->free();
+  if ( !strlen($u['mailexp']) || !filter_var($u['mail'],FILTER_VALIDATE_EMAIL) )
+    exit('{"etat":"nok","message":"Compte mal réglé&nbsp;: nom ou adresse d\'expédition manquants"}');
+  // Récupération des destinataires, comptes valides uniquement
+  $resultat = $mysqli->query("SELECT id, IF(LENGTH(nom),CONCAT(nom,' ',prenom),login) AS nom, mail
+                              FROM utilisateurs WHERE mail > '' AND mdp > '0' AND id != ${_SESSION['id']} ORDER BY autorisation DESC, nom");
+  $mysqli->close();
+  while ( $r = $resultat->fetch_assoc() )
+    $utilisateurs[$r['id']] = $r;
+  $resultat->free();
+  $dests = '';
+  $ids = explode(',',$_REQUEST['id-copie']);
+  foreach ( $ids as $i )
+    if ( isset($utilisateurs[$i]) )  {
+      $dests .= '=?UTF-8?B?'.base64_encode($utilisateurs[$i]['nom']).'?= <'.$utilisateurs[$i]['mail'].'>, ';
+      unset($utilisateurs[$i]);
+    }
+  if ( !strlen($dests) )
+    exit('{"etat":"nok","message":"Pas de destinataire valide : courriel non envoyé"}');
+  // Fabrication du mail
+  $dests = substr($dests,0,-2);
+  $bcc = ( isset($_REQUEST['copie']) ) ? "${u['mailexp']} <${u['mail']}>, " : '';
+  if ( strlen($_REQUEST['id-bcc']) )
+    $ids = explode(',',$_REQUEST['id-bcc']);
+    foreach ( $ids as $i )
+      if ( isset($utilisateurs[$i]) )  {
+        $bcc .= '=?UTF-8?B?'.base64_encode($utilisateurs[$i]['nom']).'?= <'.$utilisateurs[$i]['mail'].'>, ';
+        unset($utilisateurs[$i]);
+      }
+  $bcc = ( strlen($bcc) ) ? 'Bcc: '.substr($bcc,0,-2) : '';
+  mail($dests,'=?UTF-8?B?'.base64_encode($sujet).'?=',$texte,'From: =?UTF-8?B?'.base64_encode($u['mailexp']).'?= <nepasrepondre'.strstr($mailadmin,'@').">\r\nReply-To: =?UTF-8?B?".base64_encode($u['mailexp'])."?= <${u['mail']}>\r\nContent-type: text/plain; charset=UTF-8\r\n$bcc","-f${u['mail']}");
+  // Message de confirmation d'envoi
+  $n1 = substr_count($dests,'<');
+  $n2 = substr_count($bcc,'<') - isset($_REQUEST['copie']);
+  if ( $n2 )
+    $message = 'La courriel a été envoyé à '.($n1+$n2).' destinataires (dont '.$n2.' en copie cachée).';
+  else
+    $message = ( $n1 > 1 ) ? "LE courriel a été envoyé à $n1 destinataires." : 'Le courriel a été envoyé à 1 destinataire.';
+  exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"$message\"}");
+
+}
+
+//////////////////////////////
+// Préférences personnelles //
+//////////////////////////////
+elseif ( ( $action == 'prefsperso' ) && ( $autorisation > 1 ) )  {
+
+  // Vérification obligatoire du mot de passe
+  if ( !isset($_REQUEST['mdp']) )
+    exit('{"etat":"nok","message":"Mot de passe incorrect"}');
+  // L'adresse électronique sert dans les cas 1 et 4
+  $mysqli = connectsql(true);
+  $resultat = $mysqli->query("SELECT mdp, mail FROM utilisateurs WHERE id = ${_SESSION['id']}");
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+  if ( ( sha1($mdp.$_REQUEST['mdp']) != $r['mdp'] ) && ( sha1($_REQUEST['mdp']) != $r['mdp'] ) )
+    exit('{"etat":"nok","message":"Mot de passe incorrect"}');
+  // Passage de la connexion light à normale si besoin
+  if ( $_SESSION['light'] )  {
+    $_SESSION['light'] = false;
+    // Écriture de la connexion dans le fichier de log
+    logconnect(5,$_SESSION['login']);
+  }
   
-//////////////////////////////////////////////////////
-// Préférences : accès élève, colleur et professeur //
-//////////////////////////////////////////////////////
-if ( ( $autorisation > 1 ) and isset($_REQUEST['prefs']) )  { 
-  // Premier cadre de modifications de prefs.php
-  if ( isset($_REQUEST['prenom']) && isset($_REQUEST['nom']) && isset($_REQUEST['mail1']) && isset($_REQUEST['mail2']) && isset($_REQUEST['mdp']) )  {
-    $resultat = $mysqli->query("SELECT mdp, mail FROM utilisateurs WHERE id = ${_SESSION['id']}");
-    $s = $resultat->fetch_assoc();
-    $resultat->free();
+  // Premier cadre de modifications de prefs.php : indentité
+  if ( ( $_REQUEST['id'] == 1 ) && isset($_REQUEST['prenom']) && isset($_REQUEST['nom']) && isset($_REQUEST['mail1']) && isset($_REQUEST['mail2']) )  {
     $requete_mail = '';
-    if ( sha1($_REQUEST['mdp']) != $s['mdp'] )
-      $message = '{"etat":"nok","message":"Mot de passe incorrect"}';
-    elseif ( !strlen($prenom = trim($_REQUEST['prenom'])) || !strlen($nom = trim($_REQUEST['nom'])) || !strlen($mail = strtolower(trim($_REQUEST['mail1']))) )
-      $message = '{"etat":"nok","message":"Votre compte n\'a pas pu être modifié. Prénom, nom et adresse électronique non vides nécessaires."}';
-    elseif ( ( $mail != $s['mail'] ) && strlen($_REQUEST['mail2']) )  {
+    if ( !strlen($prenom = trim($_REQUEST['prenom'])) || !strlen($nom = trim($_REQUEST['nom'])) || !strlen($mail = mb_strtolower(trim($_REQUEST['mail1']))) )
+      exit('{"etat":"nok","message":"Votre compte n\'a pas été modifié. Le prénom, le nom et l\'adresse électronique doivent rester non vides."}');
+    if ( ( $mail != $r['mail'] ) && strlen($_REQUEST['mail2']) )  {
       if ( !filter_var($mail,FILTER_VALIDATE_EMAIL) )
-        $message = '{"etat":"nok","message":"Adresse électronique non valide"}';
-      elseif ( $mail != $_REQUEST['mail2'] )
-        $message = '{"etat":"nok","message":"Adresses électroniques différentes"}';
-      else  {
-        // Vérification que l'adresse n'existe pas déjà
-        $resultat = $mysqli->query("SELECT GROUP_CONCAT(mail) AS mails FROM utilisateurs WHERE id != ${_SESSION['id']} AND LENGTH(mail)");
-        $s = $resultat->fetch_assoc();
-        $resultat->free();
-        if ( in_array($mail,explode(',',$s['mails'])) )
-          $message = '{"etat":"nok","message":"Adresse électronique déjà prise"}';
-        else 
-          $requete_mail = ', mail = \''.$mysqli->real_escape_string($mail).'\'';
-      }
-    }
-    if ( !strlen($message) )  {
-      $prenom = ucfirst($mysqli->real_escape_string($prenom));
-      $nom = ucfirst($mysqli->real_escape_string($nom));
-      $message = ( requete('utilisateurs',"UPDATE utilisateurs SET nom = '$nom', prenom = '$prenom'$requete_mail WHERE id = ${_SESSION['id']}",$mysqli) ) ? '{"etat":"ok","message":"Préférences modifiées"}' : '{"etat":"nok","message":"Votre compte n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
+        exit('{"etat":"nok","message":"Adresse électronique non valide"}');
+      if ( $mail != mb_strtolower(trim($_REQUEST['mail2'])) )
+        exit('{"etat":"nok","message":"Adresses électroniques différentes"}');
+      // Vérification que l'adresse n'existe pas déjà
+      $resultat = $mysqli->query("SELECT GROUP_CONCAT(mail) FROM utilisateurs WHERE id != ${_SESSION['id']} AND mail > ''");
+      $r = $resultat->fetch_row();
+      $resultat->free();
+      if ( in_array($mail,explode(',',$r[0])) )
+        exit('{"etat":"nok","message":"Adresse électronique non disponible"}');
+      $requete_mail = ', mail = \''.$mysqli->real_escape_string($mail).'\'';
+    }
+    // Nettoyage des données envoyées
+    $prenom = mb_convert_case(strip_tags($mysqli->real_escape_string($prenom)),MB_CASE_TITLE);
+    $nom = mb_convert_case(strip_tags($mysqli->real_escape_string($nom)),MB_CASE_TITLE);
+    if ( requete('utilisateurs',"UPDATE utilisateurs SET nom = '$nom', prenom = '$prenom'$requete_mail WHERE id = ${_SESSION['id']}",$mysqli) )
+      exit('{"etat":"ok","message":"Vos préférences ont été modifiées."}');
+    exit('{"etat":"nok","message":"Votre compte n\'a pas été modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+  
+  // Deuxième cadre de modifications de prefs.php : mot de passe
+  if ( ( $_REQUEST['id'] == 2 ) && isset($_REQUEST['mdp1']) && isset($_REQUEST['mdp2']) )  {
+    if ( !strlen($mdp1 = $_REQUEST['mdp1']) || ( $mdp1 != $_REQUEST['mdp2'] ) )
+      exit('{"etat":"nok","message":"Nouveau mot de passe et confirmation différents"}');
+    // Token de connexion automatique
+    if ( strlen($permconn = $_SESSION['permconn']) )
+      for ( $i = 0; $i < 10; $i++ )
+        $permconn .= '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'[random_int(0,61)];
+    if( requete('utilisateurs','UPDATE utilisateurs SET mdp = \''.sha1($mdp.$mdp1)."', permconn = '$permconn' WHERE id = ${_SESSION['id']}",$mysqli) )  {
+      if ( strlen($permconn) )
+        setcookie('loginpermanent',$permconn,time()+31536000);
+      exit('{"etat":"ok","message":"Votre mot de passe a été modifié."}');
     }
+    exit('{"etat":"nok","message":"Votre mot de passe n\'a pas été modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
   }
-  // Deuxième cadre de modifications de prefs.php
-  elseif ( isset($_REQUEST['login']) && isset($_REQUEST['mdp1']) && isset($_REQUEST['mdp2']) && isset($_REQUEST['timeout']) && isset($_REQUEST['mdp']) && ctype_digit($timeout = $_REQUEST['timeout']) )  {
-    $resultat = $mysqli->query("SELECT mdp FROM utilisateurs WHERE id = ${_SESSION['id']}");
-    $s = $resultat->fetch_assoc();
-    $resultat->free();
+
+  // Troisième cadre de modifications de prefs.php : connexion
+  if ( ( $_REQUEST['id'] == 3 ) && isset($_REQUEST['login']) && isset($_REQUEST['timeout']) && ctype_digit($timeout = $_REQUEST['timeout']) )  {
     $requete_login = '';
-    if ( sha1($_REQUEST['mdp']) != $s['mdp'] )
-      $message = '{"etat":"nok","message":"Mot de passe incorrect"}';
-    if ( !strlen($login = trim($_REQUEST['login'])) )
-      $message = '{"etat":"nok","message":"Votre compte n\'a pas pu être modifié. Identifiant non vide nécessaire."}';
-    elseif ( strlen($mdp1 = $_REQUEST['mdp1']) && ( $mdp1 != $_REQUEST['mdp2'] ) )
-      $message = '{"etat":"nok","message":"Nouveau mot de passe et confirmation différents"}';
-    elseif ( $login != $_SESSION['login'] )  {
+    if ( !strlen($login = mb_strtolower(str_replace(' ','_',strip_tags(trim($_REQUEST['login']))))) )
+      exit('{"etat":"nok","message":"Votre compte n\'a pas été modifié. L\'identifiant doit être non vide."}');
+    if ( $login != $_SESSION['login'] )  {
       // Vérification que le login n'existe pas déjà
-      $resultat = $mysqli->query("SELECT GROUP_CONCAT(login) AS logins FROM utilisateurs WHERE id != ${_SESSION['id']}");
-      $s = $resultat->fetch_assoc();
+      $resultat = $mysqli->query("SELECT GROUP_CONCAT(login) FROM utilisateurs WHERE id != ${_SESSION['id']}");
+      $r = $resultat->fetch_row();
       $resultat->free();
-      if ( in_array($login,explode(',',$s['logins'])) )
-        $message = '{"etat":"nok","message":"Adresse électronique déjà prise"}';
-      else 
-        $requete_login = ', login = \''.$mysqli->real_escape_string($login).'\'';
-    }
-    if ( !strlen($message) )  {
-      $requete_mdp = ( strlen($mdp1) ) ? ', mdp = "'.sha1($mdp1).'"' : '';
-      if ( requete('utilisateurs',"UPDATE utilisateurs SET timeout = $timeout$requete_login$requete_mdp WHERE id = ${_SESSION['id']}",$mysqli) )  {
-        $message = '{"etat":"ok","message":"Préférences modifiées"}';
-        if ( strlen($requete_login) )
-          $_SESSION['login'] = $login;
-      }
-      else
-        $message = '{"etat":"nok","message":"Votre compte n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
+      if ( in_array($login,explode(',',$r[0])) )
+        exit('{"etat":"nok","message":"Votre compte n\'a pas été modifié. L\'identifiant saisi est déjà utilisé."}');
+      $requete_login = ', login = \''.$mysqli->real_escape_string($login).'\'';
+    }
+    // Token de connexion automatique
+    if ( !strlen($permconn = $_SESSION['permconn']) && isset($_REQUEST['permconn']) )
+      for ( $i = 0; $i < 10; $i++ )
+        $permconn .= '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'[random_int(0,61)];
+    elseif ( !isset($_REQUEST['permconn']) )
+      $permconn = '';
+    $timeout = ( $timeout > 15 ) ? $timeout : 900;
+    if ( requete('utilisateurs',"UPDATE utilisateurs SET timeout = $timeout$requete_login, permconn = '$permconn' WHERE id = ${_SESSION['id']}",$mysqli) )  {
+      // Mise à jour des données de session et cookies
+      $_SESSION['timeout'] = $timeout;
+      $_SESSION['time'] = time()+$_SESSION['timeout'];
+      $_SESSION['permconn'] = $permconn;
+      setcookie('loginpermanent',$permconn,( strlen($permconn) ) ? time()+31536000 : time()-3600);
+      if ( strlen($requete_login) )
+        $_SESSION['login'] = $login;
+      exit('{"etat":"ok","message":"Vos préférences de connexion ont été modifiées."}');
     }
+    exit('{"etat":"nok","message":"Votre compte n\'a pas été modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
   }
-}
 
-if ( $autorisation > 2 )  {
-/////////////////////////////////////////
-// Accès colleur et professeur : notes //
-/////////////////////////////////////////
-  if ( ( isset($_REQUEST['notes']) or isset($_REQUEST['supprime']) and ( $_REQUEST['table'] == 'notes' ) ) and isset($_REQUEST['id']) and preg_match('/\d*-\d*/',$_REQUEST['id']) )  {
-    // id : semaine-matiere
-    list($semaine,$matiere) = explode('-',$_REQUEST['id']);
+  // Quatrième cadre de modifications de prefs.php : envoi de courriel
+  if ( ( $_REQUEST['id'] == 4 ) && isset($_REQUEST['mailexp']) )  {
+    // Compte élève : modification possible seulement si envoi de courriel déjà possible
+    if ( $autorisation == 2 )  {
+      if ( $_SESSION['mailenvoi'] == 0 )
+        exit('{"etat":"nok","message":"Ce réglage n\'est pas autorisé."}');
+      if ( !strlen($mailexp = strip_tags(trim($_REQUEST['mailexp']))) )
+        exit('{"etat":"nok","message":"Votre compte n\'a pas été modifié. Le nom d\'expéditeur doit être non vide."}');
+      if( requete('utilisateurs','UPDATE utilisateurs SET mailexp = \''.$mysqli->real_escape_string($mailexp).'\', mailcopie = '.intval(isset($_REQUEST['mailcopie']))." WHERE id = ${_SESSION['id']}",$mysqli) )
+        exit('{"etat":"ok","message":"Vos préférences d\'envoi de mail ont été modifiées."}');
+      exit('{"etat":"nok","message":"Votre compte n\'a pas été modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}' );
+    }
+    // Autres types de comptes
+    // Interdiction de vider mailexp si on souhaite garder l'envoi possible
+    $mailexp = strip_tags(trim($_REQUEST['mailexp']));
+    if ( ( $mailenvoi = intval(isset($_REQUEST['mailenvoi'])) ) && !strlen($mailexp) )
+      exit('{"etat":"nok","message":"Votre compte n\'a pas été modifié. Le nom d\'expéditeur doit être non vide."}');
+    // Interdiction d'envoyer si l'adresse électronique est vide
+    if ( $mailenvoi && !strlen($r['mail']) )
+      exit('{"etat":"nok","message":"Votre compte n\'a pas été modifié. Il est impossible d\'envoyer des courriels sans adresse électronique."}');
+    if ( requete('utilisateurs','UPDATE utilisateurs SET mailexp = \''.$mysqli->real_escape_string($mailexp)."', mailenvoi = $mailenvoi, mailcopie = ".intval(isset($_REQUEST['mailcopie'])).', mailliste = '.intval(isset($_REQUEST['mailliste']))." WHERE id = ${_SESSION['id']}",$mysqli) )  {
+      $_SESSION['mailenvoi'] = $mailenvoi;
+      exit('{"etat":"ok","message":"Vos préférences d\'envoi de mail ont été modifiées."}');
+    }
+    exit('{"etat":"nok","message":"Votre compte n\'a pas été modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
 
-    // Vérification que l'identifiant est valide
-    $resultat = $mysqli->query("SELECT SUM(colleur = ${_SESSION['id']}) AS n, GROUP_CONCAT(IF(colleur != ${_SESSION['id']},n.eleve,NULL)) AS eleves
-                                FROM semaines AS s LEFT JOIN (SELECT * FROM notes WHERE matiere = $matiere) AS n ON n.semaine = s.id
-                                WHERE s.id = $semaine AND s.colle GROUP BY s.id");
-    if ( in_array($matiere,explode(',',$_SESSION['matieres'])) && ( $resultat->num_rows ) )  {
-      $r = $resultat->fetch_assoc();
-      $resultat->free();
-      // Suppression
-      if ( isset($_REQUEST['supprime']) )
-        $message = ( requete('notes',"DELETE FROM notes WHERE semaine = $semaine AND colleur = ${_SESSION['id']} AND matiere = $matiere",$mysqli) ) ? "{\"etat\":\"ok\",\"message\":\"${r['n']} notes supprimées\"}" : '{"etat":"nok","message":"Les notes à supprimer n\'ont pas pu l\'être. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
+}
 
-      // Ajout/Modification
-      else  {
-        $dejanotes = explode(',',$r['eleves']);
-        $effacer = $r['n'];
-        // Récupération des élèves associés à la matière
-        $resultat = $mysqli->query("SELECT GROUP_CONCAT(id) FROM utilisateurs WHERE autorisation = 2 AND FIND_IN_SET($matiere,matieres)");
-        $r = $resultat->fetch_row();
-        $resultat->free();
-        $eleves = explode(',',$r[0]);
-        $notes = array('0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','0,5','1,5','2,5','3,5','4,5','5,5','6,5','7,5','8,5','9,5','10,5','11,5','12,5','13,5','14,5','15,5','16,5','17,5','18,5','19,5','abs');
-        $requete = array();
-        // Vérification des notes envoyées et préparation de la requête
-        foreach ( $_REQUEST AS $eleve => $note )
-          if ( in_array($eleve=substr($eleve,1),$eleves) && in_array($note, $notes,true) && !in_array($eleve,$dejanotes) )
-            $requete[] = "($semaine,$eleve,${_SESSION['id']},$matiere,'$note')";
-        // Suppression des notes de la semaine à modifier
-        if ( $effacer )
-          requete('notes',"DELETE FROM notes WHERE semaine = $semaine AND colleur = ${_SESSION['id']} AND matiere = $matiere",$mysqli);
-        // Ajout
-        $message = $_SESSION['message'] = requete('notes','INSERT INTO notes (semaine,eleve,colleur,matiere,note) VALUES '.implode(',',$requete),$mysqli) ? '{"etat":"ok","message":"'.count($requete).' notes '.(($effacer)?'modifiées':'ajoutées').'"}' : '{"etat":"nok","message":"Aucune note n\'a pu être ajoutée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-      }
-      // Mise à jour des champs 'notes' dans la table 'matieres' (pour le menu)
-      $mysqli->query('UPDATE matieres SET notes = IF((SELECT id FROM notes WHERE matiere = matieres.id LIMIT 1),1,0)');
-    }
-    else 
-      $message = '{"etat":"nok","message":"Identifiant non valide"}';
-  }
-
-//////////////////////////////////////////////////////////////////
-// Accès colleur et professeur : mailexp, mail (depuis mail.php)//
-//////////////////////////////////////////////////////////////////
-  elseif ( isset($_REQUEST['table']) && ( $_REQUEST['table'] == 'mailprefs' ) && isset($_REQUEST['champ']) && isset($_REQUEST['val']) )  {
-    if ( !strlen($val = $_REQUEST['val']) )
-      $message = '{"etat":"nok","message":"Cette valeur ne peut pas être vide."}';
-    elseif ( $_REQUEST['champ'] == 'mailexp' )
-      $message = ( requete('utilisateurs','UPDATE utilisateurs SET mailexp = "'.$mysqli->real_escape_string($val)."\" WHERE id = ${_SESSION['id']}",$mysqli) ) ? '{"etat":"ok","message":"Nom d\'expéditeur modifié"}' : '{"etat":"nok","message":"Le nom d\'expéditeur n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-    elseif ( ( $_REQUEST['champ'] == 'mailcopy' ) && in_array($val,array(0,1)) )
-      $message = ( requete('utilisateurs',"UPDATE utilisateurs SET mailcopy = $val WHERE id = ${_SESSION['id']}",$mysqli) ) ? '{"etat":"ok","message":"Mise en copie modifiée"}' : '{"etat":"nok","message":"La mise en copie n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
+///////////////////////////////////////////////////////////
+// Modification de notes (accès colleurs et professeurs) //
+///////////////////////////////////////////////////////////
+elseif ( ( $action == 'notes' ) && ( ( $autorisation == 3 ) || ( $autorisation == 5 ) ) && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
+
+  // Connexion normale obligatoire
+  connexionlight();
+  $mysqli = connectsql(true);
+
+  // Vérification que l'identifiant est valide
+  $resultat = $mysqli->query("SELECT matiere, DATE_FORMAT(jour,'%w%Y%m%e') AS date, DATE_FORMAT(jour,'%Y-%m-%d') AS jour, TIME_FORMAT(heure,'%k:%i') AS heure, duree, releve>0 AS releve 
+                              FROM heurescolles WHERE id = $id AND colleur = ${_SESSION['id']}");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+  $matiere = $r['matiere'];
+  
+  // Suppression
+  if ( isset($_REQUEST['supprime']) && ( $r['releve'] == 0 ) )  {
+    $resultat = $mysqli->query("SELECT * FROM notes WHERE heure = $id");
+    $n = $resultat->num_rows;
+    $resultat->free();
+    exit( requete('notes',"DELETE FROM notes WHERE heure = $id",$mysqli)
+       && requete('heurescolles',"DELETE FROM heurescolles WHERE id = $id",$mysqli)
+       && $mysqli->query('UPDATE matieres SET notes = IF((SELECT id FROM notes WHERE matiere = matieres.id LIMIT 1),1,0)')
+          ? "{\"etat\":\"ok\",\"message\":\"Les $n notes du <em>".format_date($r['date']).'</em> ont été supprimées."}'
+          : "{\"etat\":\"nok\",\"message\":\"Les $n notes du <em>".format_date($r['date']).'</em> n\'ont pas été supprimées. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+  
+  // Modification
+  if ( isset($_REQUEST['jour']) && isset($_REQUEST['heure']) )  {
+    
+    // Récupération des notes déjà existantes cette semaine
+    $resultat = $mysqli->query("SELECT semaine, GROUP_CONCAT(eleve) AS dejanotes,
+                                GROUP_CONCAT(IF(heure=$id,eleve,NULL)) AS eleves, GROUP_CONCAT(IF(heure=$id,CONCAT(note,'-',id),NULL) SEPARATOR '|' ) AS notes
+                                FROM notes WHERE semaine = (SELECT semaine FROM notes WHERE heure = $id LIMIT 1) AND matiere = $matiere");
+    $s = $resultat->fetch_assoc();
+    $resultat->free();
+    $semaine = $s['semaine'];
+    $notesperso = array_combine(explode(',',$s['eleves']),explode('|',$s['notes']));
+    $dejanotes = $s['dejanotes'];
+    $notes = array('0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','0,5','1,5','2,5','3,5','4,5','5,5','6,5','7,5','8,5','9,5','10,5','11,5','12,5','13,5','14,5','15,5','16,5','17,5','18,5','19,5','abs','nn');
+    $requete_heures = $requete_notes = $message = array();
+    $etat = 'ok';
+    
+    // Validation du jour
+    if ( $r['jour'] != ( $jour = preg_filter('/(\d{2})\/(\d{2})\/(\d{4})/','$3-$2-$1',$_REQUEST['jour']) ) )  {
+      // Vérification que le jour est bien dans la semaine prévue
+      $resultat = $mysqli->query("SELECT DATEDIFF('$jour',debut) FROM semaines WHERE id = $semaine OR id = $semaine+1");
+      $s = $resultat->fetch_row();
+      if ( ( $s[0] >= 0 ) && ( ( $resultat->num_rows == 1 ) && ( $s[0] < 7 ) || ( $s = $resultat->fetch_row() ) && ( $s[0] < 0 ) ) )
+        $requete_heures[] = "jour = '$jour'";
+      $resultat->free();
+    }
+    // Validation de l'heure
+    if ( $r['heure'] != ( $heure = preg_filter('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['heure']) ) && strlen($heure) )
+      $requete_heures[] = "heure = '$heure'";
+    
+    // Si colle déjà relevée : modification des notes déjà mises uniquement
+    if ( $r['releve'] == 1 )  {
+      foreach ( $notesperso as $eleve => $note )
+        if ( isset($_REQUEST["e$eleve"]) && in_array($newnote = $_REQUEST["e$eleve"], $notes, true) && ( $newnote != strstr($note,'-',true) ) )
+          $requete_notes[] = "UPDATE notes SET note = '$newnote' WHERE id = ". substr(strstr($note,'-'),1);
+    }
+    // Si colle non déjà relevée : modification de la durée possible, et
+    // modification/ajout/suppression de notes possible 
     else  {
-      if ( !filter_var($val,FILTER_VALIDATE_EMAIL) )
-        $message = '{"etat":"nok","message":"Adresse électronique non valide"}';
+      // Validation de la durée
+      if ( isset($_REQUEST['duree']) && ( $r['duree'] != ( $duree = call_user_func_array(function ($h,$m) { return intval($h)*60+intval($m); }, array_pad(explode('h',$_REQUEST['duree']),2,0)) )) )
+        $requete_heures[] = "duree = '$duree'";
+      // Validation des notes déjà mises à modifier/supprimer
+      foreach ( $notesperso as $eleve => $note )  {
+        if ( !isset($_REQUEST["e$eleve"]) || !in_array($newnote = $_REQUEST["e$eleve"], $notes, true) )
+          $requete_notes[] = 'DELETE FROM notes WHERE id = '. substr(strstr($note,'-'),1);
+        elseif ( $newnote != strstr($note,'-',true) )
+          $requete_notes[] = "UPDATE notes SET note = '$newnote' WHERE id = ". substr(strstr($note,'-'),1);
+      }
+      // Récupération des élèves associés à la matière
+      $resultat = $mysqli->query("SELECT GROUP_CONCAT(id) FROM utilisateurs WHERE autorisation = 2 AND FIND_IN_SET($matiere,matieres)");
+      $s = $resultat->fetch_row();
+      $resultat->free();
+      // Insertion pour les élèves non déjà notés
+      $elevesdispos = array_diff(explode(',',$s[0]),explode(',',$dejanotes));
+      foreach ( $elevesdispos as $eleve ){
+        if ( isset($_REQUEST["e$eleve"]) && in_array($note = $_REQUEST["e$eleve"], $notes, true) )
+          $requete_notes[] = "INSERT INTO notes (semaine,heure,eleve,colleur,matiere,note) VALUES ($semaine,$id,$eleve,${_SESSION['id']},$matiere,'$note')";
+        }
+    }
+    
+    // Exécution
+    if ( $requete_heures )  {
+      if ( requete('heurescolles','UPDATE heurescolles SET '.implode(', ',$requete_heures)." WHERE id = $id",$mysqli) ) 
+        $message[] = 'La colle du <em>'.format_date($r['date']).'</em> a été modifiée.';
       else  {
-        // Mail : vérification qu'il n'existe pas déjà
-        $resultat = $mysqli->query("SELECT GROUP_CONCAT(mail) AS mails FROM utilisateurs WHERE id != ${_SESSION['id']} AND LENGTH(mail)");
-        $s = $resultat->fetch_assoc();
-        $resultat->free();
-        if ( in_array($mail = strtolower($mysqli->real_escape_string($val)),explode(',',$s['mails'])) )
-          $message = '{"etat":"nok","message":"Adresse électronique déjà prise"}';
-        elseif ( requete('utilisateurs',"UPDATE utilisateurs SET mail = '$mail' WHERE id = ${_SESSION['id']}",$mysqli) )
-          $message = '{"etat":"ok","message":"Adresse électronique modifiée"}';
+        $message[] = 'La colle du <em>'.format_date($r['date']).'</em> n\'a pas été modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+        $etat = 'nok';
       }
     }
-  }
-
-/////////////////////////////////////////////////////
-// Accès colleur et professeur : envoi de courriel //
-/////////////////////////////////////////////////////
-  elseif ( isset($_REQUEST['envoimail']) && isset($_REQUEST['id-copie']) && isset($_REQUEST['sujet']) && isset($_REQUEST['texte'])  )  {
-    // Récupération des données de l'utilisateur courant
-    $resultat = $mysqli->query("SELECT mailexp, mail FROM utilisateurs WHERE id = ${_SESSION['id']}");
-    $u = $resultat->fetch_assoc();
-    $resultat->free();
-    // Récupération des destinataires
-    $resultat = $mysqli->query("SELECT id, IF(LENGTH(nom),CONCAT(nom,' ',prenom),login) AS nom, mail
-                                FROM utilisateurs WHERE LENGTH(mail) AND LENGTH(mdp)=40 AND id != ${_SESSION['id']} ORDER BY autorisation DESC, nom");
-    while ( $r = $resultat->fetch_assoc() )
-      $utilisateurs[$r['id']] = $r;
-    $resultat->free();
-    $dests = '';
-    $ids = explode(',',$_REQUEST['id-copie']);
-    foreach ( $ids as $i )
-      if ( isset($utilisateurs[$i]) )  {
-        $dests .= '=?UTF-8?B?'.base64_encode($utilisateurs[$i]['nom']).'?= <'.$utilisateurs[$i]['mail'].'>, ';
-        unset($utilisateurs[$i]);
+    if ( ( $etat == 'ok' ) && $requete_notes )  {
+      $nb_ok = 0;
+      foreach ( $requete_notes as $requete )  {
+        if ( requete('notes',$requete,$mysqli) ) 
+          $nb_ok += 1;
+        else  {
+          $message[] = 'Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'».';
+          $etat = 'nok';
+        }
       }
-    if ( !strlen($dests) )
-      $message = '{"etat":"nok","message":"Pas de destinataire valide : courriel non envoyé"}';
-    elseif ( !strlen($sujet = $_REQUEST['sujet']) )
-      $message = '{"etat":"nok","message":"Pas de sujet : courriel non envoyé"}';
-    elseif ( !strlen($texte = $_REQUEST['texte']) )
-      $message = '{"etat":"nok","message":"Pas de texte : courriel non envoyé"}';
-    else  {
-      $dests = substr($dests,0,-2);
-      $bcc = ( isset($_REQUEST['copie']) ) ? "${u['mailexp']} <${u['mail']}>, " : '';
-      if ( isset($_REQUEST['id-bcc']) )
-        $ids = explode(',',$_REQUEST['id-bcc']);
-        foreach ( $ids as $i )
-          if ( isset($utilisateurs[$i]) )  {
-            $bcc .= '=?UTF-8?B?'.base64_encode($utilisateurs[$i]['nom']).'?= <'.$utilisateurs[$i]['mail'].'>, ';
-            unset($utilisateurs[$i]);
-          }
-      $bcc = ( strlen($bcc) ) ? 'Bcc: '.substr($bcc,0,-2) : '';
-      mail($dests,'=?UTF-8?B?'.base64_encode($sujet).'?=',$texte,'From: =?UTF-8?B?'.base64_encode($u['mailexp'])."?= <${u['mail']}>\r\nContent-type: text/plain; charset=UTF-8\r\n$bcc","-f${u['mail']}");
-      
-      // Message de confirmation d'envoi
-      $n1 = substr_count($dests,'<');
-      $n2 = substr_count($bcc,'<') - isset($_REQUEST['copie']);
-      if ( $n2 )
-        $message = 'Courriel envoyé à '.($n1+$n2).' destinataires (dont '.$n2.' en copie cachée)';
-      else
-        $message = ( $n1 > 1 ) ? "Courriel envoyé à $n1 destinataires." : 'Courriel envoyé à 1 destinataire';
-      $message = $_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"$message\"}";
+      $message[] = ( ($nb_ok == 1) ? 'Une note a été modifiée/supprimée/ajoutée.' : "$nb_ok notes ont été modifiées/supprimées/ajoutées.");
     }
+      
+    // Reconstruction du message
+    if ( !$message )
+      exit('{"etat":"nok","message":"La colle du <em>'.format_date($r['date']).'</em> n\'a pas été modifiée. Aucune modification demandée."}');
+    if ( $etat == 'nok')
+      exit('{"etat":"nok","message":"'.implode('<br>',$message).'"}');
+    // Mise à jour des champs 'notes' dans la table 'matieres' (pour le menu)
+    $mysqli->query("UPDATE matieres SET notes = IF((SELECT id FROM notes WHERE matiere = $matiere LIMIT 1),1,0) WHERE id = $matiere");
+    exit($_SESSION['message'] = '{"etat":"ok","message":"'.implode('<br>',$message).'"}');
   }
 }
 
-/////////////////////////////////
-// Accès professeur uniquement //
-/////////////////////////////////
-if ( $autorisation < 4 )  {
-  $mysqli->close();
-  if ( !strlen($message) )
-    $message = '{"etat":"nok","message":"Aucune action effectuée"}';
-  exit($message);
+//////////////////////////////////////////////////////////////
+// Ajout de notes de colles (accès colleurs et professeurs) //
+//////////////////////////////////////////////////////////////
+elseif ( ( $action == 'ajout-notes' ) && ( ( $autorisation == 3 ) || ( $autorisation == 5 ) ) && isset($_REQUEST['sid']) && ctype_digit($sid = $_REQUEST['sid']) && isset($_REQUEST['matiere']) && in_array($matiere = intval($_REQUEST['matiere']),explode(',',$_SESSION['matieres'])) && isset($_REQUEST['jour']) && strlen($jour = preg_filter('/(\d{2})\/(\d{2})\/(\d{4})/','$3-$2-$1',$_REQUEST['jour'])) && isset($_REQUEST['heure']) && isset($_REQUEST['duree']) && ( $duree = call_user_func_array(function ($h,$m) { return intval($h)*60+intval($m); }, array_pad(explode('h',$_REQUEST['duree']),2,0)) ) > 0 )  {
+
+  // Connexion normale obligatoire
+  connexionlight();
+  $mysqli = connectsql(true);
+
+  // Vérification de l'identifiant de la semaine
+  $resultat = $mysqli->query("SELECT (SELECT GROUP_CONCAT(eleve) FROM notes WHERE semaine = semaines.id AND matiere = $matiere) FROM semaines WHERE id = $sid AND colle");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant non valide"}');
+  $r = $resultat->fetch_row();
+  $resultat->free();
+  $dejanotes = $r[0];
+  
+  // Vérification que le jour est bien dans la semaine prévue
+  $resultat = $mysqli->query("SELECT DATEDIFF('$jour',debut) FROM semaines WHERE id = $sid OR id = $sid+1");
+  $r = $resultat->fetch_row();
+  if ( ( $r[0] < 0 ) || ( ( $resultat->num_rows == 1 ) && ( $r[0] >= 7 ) || ( $r = $resultat->fetch_row() ) && ( $r[0] >= 0 ) ) )
+    exit('{"etat":"nok","message":"La date saisie ne se trouve pas dans la semaine choisie."}');
+  $resultat->free();
+  // Validation de l'heure (non obligatoire)
+  $heure = preg_filter('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['heure']);
+ 
+  // Récupération des élèves associés à la matière
+  $resultat = $mysqli->query("SELECT GROUP_CONCAT(id) FROM utilisateurs WHERE autorisation = 2 AND FIND_IN_SET($matiere,matieres)");
+  $r = $resultat->fetch_row();
+  $resultat->free();
+  $elevesdispos = array_diff(explode(',',$r[0]),explode(',',$dejanotes));
+  $notes = array('0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17','18','19','20','0,5','1,5','2,5','3,5','4,5','5,5','6,5','7,5','8,5','9,5','10,5','11,5','12,5','13,5','14,5','15,5','16,5','17,5','18,5','19,5','abs','nn');
+
+  // Écriture de l'heure dans la table heurescolles
+  if ( !requete('heurescolles',"INSERT INTO heurescolles SET colleur = ${_SESSION['id']}, matiere = $matiere, jour = '$jour', heure = '$heure', duree = '$duree'",$mysqli) )
+    exit('{"etat":"nok","message":"La colle n\'a pas été ajoutée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+  $heure = $mysqli->insert_id;
+  
+  // Insertion pour les élèves non déjà notés
+  $requete = array();
+  foreach ( $elevesdispos as $eleve )
+    if ( isset($_REQUEST["e$eleve"]) && in_array($note = $_REQUEST["e$eleve"], $notes, true) )
+      $requete[] = "($sid,$heure,$eleve,${_SESSION['id']},$matiere,'$note')";
+  if ( !$requete )  {
+    requete('heurescolles',"DELETE FROM heurescolles WHERE id=$heure",$mysqli);
+    exit('{"etat":"nok","message":"La colle n\'a pas été ajoutée, car aucune note valable n\'a été saisie."}');
+  }
+  // Écriture des notes
+  if ( requete('notes','INSERT INTO notes (semaine,heure,eleve,colleur,matiere,note) VALUES '.implode(',',$requete),$mysqli) && $mysqli->query("UPDATE matieres SET notes = IF((SELECT id FROM notes WHERE matiere = $matiere LIMIT 1),1,0) WHERE id = $matiere") )
+    exit($_SESSION['message'] = '{"etat":"ok","message":"'.count($requete).' notes ont été ajoutées."}');
+  exit('{"etat":"nok","message":"Toutes les notes saisies n\'ont pas été ajoutées. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
 }
 
-///////////////////////////////////////////////
-// Récupération des répertoires et documents //
-///////////////////////////////////////////////
-if ( isset($_REQUEST['recupdoc']) )  {
-  $mats = '<option value="-1">[Choisissez une matière]</option>';
-  $reps = array( -1 =>'<option value="-1">[Choisissez une matière]</option>');
-  $docs = array( -1 => '<option value="0">[Choisissez une matière]</option>', 0 => '<option value="0">[Choisissez un répertoire]</option>' );
-  // Récupération des répertoires, avec le chemin complet
-  $resultat = $mysqli->query("SELECT r.id, r.nom, r.matiere,
-                              CONCAT( ( SELECT GROUP_CONCAT(reps.nom SEPARATOR '/') FROM reps WHERE FIND_IN_SET(reps.id,r.parents) ) ,'/') AS parents
-                              FROM reps AS r LEFT JOIN matieres AS m ON m.id = r.matiere
-                              WHERE r.nbdoc_v AND FIND_IN_SET(r.matiere,'${_SESSION['matieres']}') ORDER BY m.ordre, CONCAT(parents, r.nom)");
-  if ( $resultat->num_rows )  {
-    while ( $r = $resultat->fetch_assoc() )  {
-      if ( !isset($reps[$r['matiere']]) )  {
-        $reps[$r['matiere']] = '<option value="0">[Choisissez un répertoire]</option>';
-        $mats .= "<option value=\"${r['matiere']}\">".( ( strpos($r['parents'],'/') ) ? strstr($r['parents'],'/',true) : $r['nom'] ).'</option>';
-      }
-      $reps[$r['matiere']] .= "<option value=\"${r['id']}\">${r['parents']}${r['nom']}</option>";
-      // Récupération des documents
-      $docs[$r['id']] = '<option value="0">[Choisissez un document]</option>';
-      $res = $mysqli->query("SELECT id, nom, SUBSTRING(ext,2) AS ext FROM docs WHERE parent = ${r['id']} AND protection < 5");
-      while ( $d = $res->fetch_assoc() )  {
-        $docs[$r['id']] .= "<option value=\"${d['id']}\">${d['nom']} (${d['ext']})</option>";
-      }
-      $res->free();
+///////////////////////////////////////////////////////
+// Relève des notes de colles (accès administration) //
+///////////////////////////////////////////////////////
+elseif ( ( $action == 'releve-notes' ) && ( $autorisation == 4 ) )  {
+
+  // Connexion normale obligatoire
+  connexionlight();
+  $mysqli = connectsql(true);
+
+  // Vérification qu'il y a des colles à relever
+  $resultat = $mysqli->query('SELECT * FROM heurescolles WHERE releve=0');
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Il n\'a pas d\'heure de colle à relever aujourd\'hui."}');
+  if ( requete('heurescolles','UPDATE heurescolles SET releve=CURDATE() WHERE releve=0', $mysqli) )
+    exit($_SESSION['message'] = '{"etat":"ok","message":"De nouvelles heures de colles ont été relevées et apparaissent dans le tableau général."}');
+  exit('{"etat":"nok","message":"L\'information n\'a pas été modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+}
+
+/////////////////////////////////////////////////
+// À partir d'ici, accès professeur uniquement //
+/////////////////////////////////////////////////
+if ( ( $autorisation < 5 ) )
+  exit( '{"etat":"nok","message":"Aucune action effectuée"}' );
+// Connexion normale obligatoire
+connexionlight();
+$mysqli = connectsql(true);
+// Spécifications pour les manipulations de caractères sur 2 octets (accents)
+mb_internal_encoding('UTF-8');
+
+///////////////////////////////////
+// Modification des informations //
+///////////////////////////////////
+if ( $action == 'infos' && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
+  
+  // Vérification que l'identifiant est valide
+  $resultat = $mysqli->query("SELECT i.ordre, i.titre, cache, texte, page, cle, i.protection, p.mat, ( SELECT COUNT(*) FROM infos WHERE page = i.page ) AS max FROM infos AS i LEFT JOIN pages AS p ON i.page = p.id WHERE i.id = $id AND FIND_IN_SET(p.mat,'${_SESSION['matieres']}')");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant de page non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+
+  // Traitement d'une modification
+  if ( isset($_REQUEST['champ']) && in_array($champ = $_REQUEST['champ'],array('titre','texte')) )  {
+    $valeur = trim($mysqli->real_escape_string($_REQUEST['val']));
+    if ( ( $champ == 'texte' ) && ( !strlen($valeur) ) )
+      exit('{"etat":"nok","message":"L\'information n\'a pas été modifiée. Le texte doit être non vide."}');
+    elseif ( requete('infos',"UPDATE infos SET $champ = '$valeur' WHERE id = $id",$mysqli) )  {
+      recent($mysqli,1,$id,$r['mat'],( $champ == 'titre' ) ? array('titre'=>( $valeur ?: 'Information')) : array('texte'=>$valeur));
+      exit('{"etat":"ok","message":"L\'information a été modifiée."}');
     }
-    $resultat->free();
+    exit('{"etat":"nok","message":"L\'information n\'a pas été modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
   }
-  $mysqli->close();
-  exit(json_encode(array('etat'=>'ok_','mats'=>$mats,'reps'=>$reps,'docs'=>$docs)));
-}
 
-/////////////////////////////////////////////////////////////////////////
-// Gestion des demandes en fonction de la valeur de $_REQUEST['table'] //
-/////////////////////////////////////////////////////////////////////////
-if ( isset($_REQUEST['table']) )  {
-  // Fonction PHP pour le stockage dans la base MySQL de l'ordre "naturel" (1,2,10,11 et non 1,10,11,2)
-  // Remplace tout nombre par un nombre égal mais écrit sur 10 chiffres, complété par des zéros à gauche
-  // Utilisé pour les documents et les groupes
-  function zpad($s) {
-    return preg_replace_callback('/(\d+)/', function($m){
-      return(str_pad($m[1],10,'0',STR_PAD_LEFT)); }
-    , $s);
+  // Changement de protection
+  if ( isset($_REQUEST['champ']) && ( $_REQUEST['champ'] == 'protection' ) && ctype_digit($valeur = $_REQUEST['val']) )  {
+    if ( $r['protection'] == $valeur )
+      exit('{"etat":"nok","message":"La protection de l\'information n\'a pas été modifiée car elle est inchangée."}');
+    if ( requete('infos',"UPDATE infos SET protection = $valeur WHERE id = $id",$mysqli) )  {
+      recent($mysqli,1,$id,$r['mat'],array('protection'=>$valeur));
+      exit('{"etat":"ok","message":"L\'information a été modifiée."}');
+    }
+    exit('{"etat":"nok","message":"L\'information n\'a pas été modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
   }
 
-switch ( $table = $_REQUEST['table'] )  {
-////////////////////////////
-// Modification des pages //
-////////////////////////////
-  case 'pages':
+  // Déplacement vers le haut
+  if ( isset($_REQUEST['monte']) && ( $r['ordre'] > 1 ) )
+    exit( requete('infos',"UPDATE infos SET ordre = (2*${r['ordre']}-1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}-1) ) AND page = ${r['page']}",$mysqli) && $mysqli->query('ALTER TABLE infos ORDER BY page,ordre')
+      ? '{"etat":"ok","message":"L\'information a été déplacée."}'
+      : '{"etat":"nok","message":"L\'information n\'a pas été déplacée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+
+  // Déplacement vers le bas
+  if ( isset($_REQUEST['descend']) && ( $r['ordre'] < $r['max'] ) )
+    exit( requete('infos',"UPDATE infos SET ordre = (2*${r['ordre']}+1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}+1) ) AND page = ${r['page']}",$mysqli) && $mysqli->query('ALTER TABLE infos ORDER BY page,ordre')
+      ? '{"etat":"ok","message":"L\'information a été déplacée."}'
+      : '{"etat":"nok","message":"L\'information n\'a pas été déplacée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+
+  // Positionnement "montré" (apparaît sur la partie publique)
+  if ( isset($_REQUEST['montre']) )  {
+    if ( requete('infos',"UPDATE infos SET cache = 0 WHERE id = $id",$mysqli) )  {
+      recent($mysqli,1,$id,$r['mat'],array('titre'=>( strlen($r['titre']) ? $mysqli->real_escape_string($r['titre']) : 'Information'), 'lien'=>".?${r['cle']}", 'texte'=>$mysqli->real_escape_string($r['texte']), 'matiere'=>$r['mat'], 'protection'=>$r['protection']));
+      exit('{"etat":"ok","message":"L\'information apparaît désormais sur la partie publique."}');
+    }
+    exit('{"etat":"nok","message":"L\'information n\'a pas été diffusée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
 
-    // Vérification que l'identifiant est valide
-    // Changement de matière, suppression et déplacements interdits pour la page d'accueil (id=1)
-    if ( isset($_REQUEST['id']) && ( ctype_digit($id = $_REQUEST['id']) ) )  {
-      $resultat = $mysqli->query("SELECT ordre, mat, (SELECT COUNT(*) FROM pages WHERE mat = p.mat) AS max FROM pages AS p WHERE id = $id AND FIND_IN_SET(mat,'${_SESSION['matieres']}')");
-      if ( $resultat->num_rows )  {
-        $r = $resultat->fetch_assoc();
-        $resultat->free();
+  // Positionnement "caché" (n'apparaît pas sur la partie publique)
+  if ( isset($_REQUEST['cache']) )  {
+    if ( requete('infos',"UPDATE infos SET cache = 1 WHERE id = $id",$mysqli) )  {
+      recent($mysqli,1,$id,$r['mat']);
+      exit('{"etat":"ok","message":"L\'information n\'apparaît plus sur la partie publique mais est toujours disponible ici pour modification ou diffusion."}');
+    }
+    exit('{"etat":"nok","message":"L\'information n\'a pas été cachée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
 
-        // Traitement d'une modification globale (depuis index.php ou pages.php)
-        if ( isset($_REQUEST['titre']) && isset($_REQUEST['nom']) && isset($_REQUEST['cle']) && isset($_REQUEST['bandeau']) && isset($_REQUEST['protection']) )  {
-          if ( !strlen($_REQUEST['titre']) || !strlen($_REQUEST['cle']) || !strlen($_REQUEST['nom']) )
-            $message = '{"etat":"nok","message":"La page n\'a pas pu être modifiée. Titre, clé et nom non vides nécessaires."}';
-          else  {
-            $m = ( isset($_REQUEST['matiere']) && in_array($matiere = $_REQUEST['matiere'],explode(',',$_SESSION['matieres'])) && ( $matiere != $r['mat'] ) && ( $id > 1 ) ) ? ", mat = $matiere" : '';
-            $cle = str_replace(' ','_',trim($mysqli->real_escape_string($_REQUEST['cle'])));
-            // Vérification que la clé n'existe pas déjà
-            $resultat = $mysqli->query("SELECT cle FROM pages WHERE id != $id AND mat = ".( ( strlen($m) ) ? $matiere : $r['mat'] ));
-            if ( $resultat->num_rows )  {
-              while ( $s = $resultat->fetch_assoc() )
-                if ( $s['cle'] == $cle )  {
-                  $message = '{"etat":"nok","message":"La page n\'a pas pu être modifiée. Cette clé existe déjà et doit être unique."}';
-                  break;
-                }
-              $resultat->free();
-            }
-          }
-          if ( !strlen($message) )  {
-            $titre = trim($mysqli->real_escape_string($_REQUEST['titre']));
-            $nom = trim($mysqli->real_escape_string($_REQUEST['nom']));
-            $bandeau = trim($mysqli->real_escape_string($_REQUEST['bandeau']));
-            $protection = ( in_array($_REQUEST['protection'],array(0,1,2,3,4,5)) ) ? $_REQUEST['protection'] : 0;
-            if ( requete('pages',"UPDATE pages SET cle = '$cle', nom = '$nom', titre = '$titre', bandeau = '$bandeau', protection = $protection $m WHERE id = $id",$mysqli) )  {
-              $message = $_SESSION['message'] = '{"etat":"ok","message":"Page modifiée"}';
-              // Si $_REQUEST[matiere] existe, on vient de pages.php.
-              // Sinon de index.php donc rechargement
-              if ( !isset($_REQUEST['matiere']) )
-                $_SESSION['message'] = $message;
-              elseif ( strlen($m) )  {
-                requete('pages',"UPDATE pages SET mat = $matiere, ordre = (SELECT COUNT(*) FROM (SELECT id FROM pages AS p WHERE p.mat = $matiere) AS p1)+1 WHERE id = $id",$mysqli);
-                requete('pages',"UPDATE pages SET ordre = (ordre-1) WHERE mat = ${r['mat']} AND ordre > ${r['ordre']}",$mysqli);
-              }
-            }
-            else
-              $message = '{"etat":"nok","message":"La page n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-          }
-        }
+  // Suppression
+  if ( isset($_REQUEST['supprime']) )  {
+    if ( requete('infos',"DELETE FROM infos WHERE id = $id",$mysqli) 
+      && requete('infos',"UPDATE infos SET ordre = (ordre-1) WHERE ordre > ${r['ordre']} AND page = ${r['page']}",$mysqli) )  {
+      recent($mysqli,1,$id,$r['mat']);
+      exit('{"etat":"ok","message":"Suppression réalisée"}');
+    }
+    exit('{"etat":"nok","message":"L\'information n\'a pas été supprimée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+}
 
-        // Déplacement vers le haut
-        elseif ( isset($_REQUEST['monte']) && ( $r['ordre'] > 1+!$r['mat'] ) )
-          $message = ( requete('pages',"UPDATE pages SET ordre = (2*${r['ordre']}-1-ordre) WHERE mat = ${r['mat']} AND ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}-1) )",$mysqli)
-          ) ? '{"etat":"ok","message":"Page déplacée"}' : '{"etat":"nok","message":"La page n\'a pas pu être déplacée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-
-        // Déplacement vers le bas
-        elseif ( isset($_REQUEST['descend']) && ( $r['ordre'] < $r['max'] ) && ( $id > 1 ) )
-          $message = ( requete('pages',"UPDATE pages SET ordre = (2*${r['ordre']}+1-ordre) WHERE mat = ${r['mat']} AND ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}+1) )",$mysqli)
-          ) ? '{"etat":"ok","message":"Page déplacée"}' : '{"etat":"nok","message":"La page n\'a pas pu être déplacée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-
-        // Suppression
-        elseif ( isset($_REQUEST['supprime']) && ( $id > 1 ) )
-          $message = ( requete('pages',"DELETE FROM pages WHERE id = $id",$mysqli)
-                    && requete('infos',"DELETE FROM infos WHERE page = $id",$mysqli)
-                    && requete('pages',"UPDATE pages SET ordre = (ordre-1) WHERE mat = ${r['mat']} AND ordre > ${r['ordre']}",$mysqli)
-          ) ? '{"etat":"ok","message":"Page supprimée"}' : '{"etat":"nok","message":"La page n\'a pas pu être supprimée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-
-        // Suppression des informations
-        elseif ( isset($_REQUEST['supprime_infos']) )
-          $message = ( requete('infos',"DELETE FROM infos WHERE page = $id",$mysqli) ) ? '{"etat":"ok","message":"Informations de la page supprimées"}' : '{"etat":"nok","message":"Les informations de la page n\'ont pas pu être supprimées. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-          
-        $mysqli->query('ALTER TABLE pages ORDER BY mat,ordre');
+/////////////////////////////
+// Ajout d'une information //
+/////////////////////////////
+elseif ( ( $action == 'ajout-info' ) && isset($_REQUEST['titre']) && isset($_REQUEST['texte']) && isset($_REQUEST['protection']) && isset($_REQUEST['page']) && ctype_digit($page = $_REQUEST['page']) )  {
+  // Vérification de l'identifiant de la page
+  $resultat = $mysqli->query("SELECT cle,mat FROM pages WHERE id = $page AND FIND_IN_SET(mat,'${_SESSION['matieres']}')");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant de page non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+  // Génération de la valeur de protection
+  if ( !count($val = array_filter($_REQUEST['protection'],function($id) { return ctype_digit($id); })) )
+    exit('{"etat":"nok","message":"L\'information n\'a pas été ajoutée. La protection d\'accès est incorrecte."}');
+  if ( ( $val[0] == 0 ) || ( $val[0] == 32 ) )
+    $protection = $val[0];
+  else  {
+    $protection = 32;
+    foreach (array_slice($val,1) as $v) 
+      $protection = $protection-2**($v-1);
+  }
+  // Autres données
+  $titre = trim($mysqli->real_escape_string($_REQUEST['titre']));
+  $texte = trim($mysqli->real_escape_string($_REQUEST['texte']));
+  $cache = intval(isset($_REQUEST['cache']));
+  if ( !strlen($texte) )
+    exit('{"etat":"nok","message":"L\'information n\'a pas été ajoutée. Le texte doit être non vide."}');
+  // Écriture
+  if ( requete('infos',"UPDATE infos SET ordre = (ordre+1) WHERE page = $page",$mysqli)
+    && requete('infos',"INSERT INTO infos SET ordre = 1, page = $page, texte = '$texte', titre = '$titre', cache = $cache, protection = $protection",$mysqli) )  {
+    if ( !$cache )
+      recent($mysqli,1,$mysqli->insert_id,$r['mat'],array('titre'=>( $titre ?: 'Information'), 'lien'=>".?${r['cle']}", 'texte'=>$texte, 'matiere'=>$r['mat'], 'protection'=>$protection));
+    $mysqli->query('ALTER TABLE infos ORDER BY page,ordre');
+    exit($_SESSION['message'] = '{"etat":"ok","message":"L\'information a été ajoutée."}');
+  }
+  exit('{"etat":"nok","message":"L\'information n\'a pas été ajoutée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+}
+
+//////////////////////////////////
+// Modification des répertoires //
+//////////////////////////////////
+elseif ( ( $action == 'reps' ) && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
+  
+  // Vérification que l'identifiant est valide
+  $resultat = $mysqli->query("SELECT parent, nom, menu, protection, matiere FROM reps WHERE id = $id AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant de répertoire non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+  
+  // Traitement d'une modification unique (nom)
+  if ( isset($_REQUEST['champ']) && ( $_REQUEST['champ'] == 'nom' ) )  {
+    if ( !$r['parent'] )
+      exit('{"etat":"nok","message":"Le nom des répertoires racine des matières ne sont pas modifiables."}');
+    if ( !strlen($valeur = trim($mysqli->real_escape_string($_REQUEST['val'])) ) )
+      exit("{\"etat\":\"nok\",\"message\":\"Le répertoire <em>${r['nom']}</em> n'a pas été modifié. Le nom doit être non vide.\"}");
+    if ( !requete('reps',"UPDATE reps SET nom = '$valeur' WHERE id = $id",$mysqli) )
+      exit("{\"etat\":\"nok\",\"message\":\"Le nom du répertoire <em>${r['nom']}</em> n'a pas été modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+    exit("{\"etat\":\"ok\",\"message\":\"Le nom du répertoire <em>${r['nom']}</em> a été modifié.}");
+  }
+    
+  // Traitement d'une modification globale
+  if ( isset($_REQUEST['protection']) )  {
+    $requete = $message = array();
+    $etat = 'ok';
+    // Modification du nom, de l'affichage dans le menu uniquement si pas à la racine
+    if ( $r['parent'] && isset($_REQUEST['nom']) )  {
+      if ( strlen( $nom = trim($mysqli->real_escape_string($_REQUEST['nom'])) ) && ( $nom != $r['nom'] ) )
+        $requete[] = "nom = '$nom'";
+      $menu = intval(isset($_REQUEST['menu']));
+      if ( $menu != $r['menu'] )
+        $requete[] = "menu = $menu";
+    }
+    // Génération de la valeur de protection
+    if ( count($val = array_filter($_REQUEST['protection'],function($id) { return ctype_digit($id); })) )  {
+      if ( ( $val[0] == 0 ) || ( $val[0] == 32 ) )
+        $protection = $val[0];
+      else  {
+        $protection = 32;
+        foreach (array_slice($val,1) as $v) 
+          $protection = $protection-2**($v-1);
+      }
+      if ( $protection != $r['protection'] )
+        $requete[] = "protection = $protection";
+    }
+    if ( $requete )  {
+      if ( requete('reps','UPDATE reps SET '.implode(', ',$requete)." WHERE id = $id",$mysqli) ) 
+        $message[] = "Le répertoire <em>${r['nom']}</em> a été modifié.";
+      else  {
+        $message[] = "Le répertoire <em>${r['nom']}</em> n'a pas été modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+        $etat = 'nok';
       }
-      else
-        $message = '{"etat":"nok","message":"Identifiant non valide"}';
     }
-     
-    // Nouvelle page
-    elseif ( isset($_REQUEST['titre']) && isset($_REQUEST['nom']) && isset($_REQUEST['cle']) && isset($_REQUEST['bandeau']) && isset($_REQUEST['matiere']) && isset($_REQUEST['protection']) && in_array($matiere = $_REQUEST['matiere'],explode(',',$_SESSION['matieres'])) )  {
-      if ( !strlen($_REQUEST['titre']) || !strlen($_REQUEST['cle']) || !strlen($_REQUEST['nom']) )
-        $message = '{"etat":"nok","message":"La page n\'a pas pu être ajoutée. Titre, clé et nom non vides nécessaires."}';
-      else  {
-        $titre = trim($mysqli->real_escape_string($_REQUEST['titre']));
-        $bandeau = trim($mysqli->real_escape_string($_REQUEST['bandeau']));
-        $cle = str_replace(' ','_',trim($mysqli->real_escape_string($_REQUEST['cle'])));
-        $nom = trim($mysqli->real_escape_string($_REQUEST['nom']));
-        $protection = ( in_array($_REQUEST['protection'],array(0,1,2,3,4,5)) ) ? $_REQUEST['protection'] : 0;
-        // Vérification que la clé n'existe pas déjà
-        $resultat = $mysqli->query("SELECT id FROM pages WHERE cle = '$cle' AND mat = $matiere");
-        if ( $resultat->num_rows )  {
-          $message = '{"etat":"nok","message":"La page n\'a pas pu être ajoutée. Cette clé existe déjà et doit être unique."}';
-          $resultat->free();
+    // Déplacement du répertoire si $_REQUEST['parent'] non nul et si pas à la racine
+    if ( $r['parent'] && isset($_REQUEST['parent']) && !in_array($parent = intval($_REQUEST['parent']),array(0,$id,$r['parent'])) )  {
+      // Vérification du répertoire parent
+      $resultat = $mysqli->query("SELECT parents, matiere FROM reps WHERE id = $parent AND FIND_IN_SET(matiere,'${_SESSION['matieres']}') AND NOT FIND_IN_SET($id,parents)");
+      if ( $resultat->num_rows)  {
+        $s = $resultat->fetch_assoc();
+        $resultat->free();
+        $mat = $s['matiere'];
+        $parents = "${s['parents']},$parent";
+        if ( requete('reps',"UPDATE reps SET matiere = $mat, parent = $parent, parents = '$parents' WHERE id = $id",$mysqli)
+          && requete('reps',"UPDATE reps SET matiere = $mat, parents = '$parents,$id' WHERE parent = $id",$mysqli)
+          && requete('docs',"UPDATE docs SET matiere = $mat, parents = '$parents,$id' WHERE parent = $id",$mysqli)
+          && $mysqli->query('UPDATE matieres SET docs = (SELECT IF(COUNT(*),1,0) FROM docs WHERE matiere = matieres.id AND protection < 32)') )
+          $message[] = "Le répertoire <em>${r['nom']}</em> a été déplacé.";
+        else  {
+          $message[] = "Le répertoire <em>${r['nom']}</em> n'a pas été déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+          $etat = 'nok';
         }
-        else 
-          $message = ( requete('pages',"INSERT INTO pages SET cle = '$cle', nom = '$nom', mat = $matiere, titre = '$titre',
-                                        bandeau = '$bandeau', protection = $protection, ordre = (SELECT IFNULL(MAX(ordre)+1,1)
-                                        FROM pages AS p WHERE p.mat = $matiere)",$mysqli)
-          ) ? '{"etat":"ok","message":"Page ajoutée"}' : '{"etat":"nok","message":"La page n\'a pas pu être ajoutée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
+      }
+      else  {
+        $message[] = "Le répertoire <em>${r['nom']}</em> n'a pas été déplacé. Identifiant de répertoire parent non valide.";
+        $etat = 'nok';
       }
     }
-    break;
-
-///////////////////////////////////
-// Modification des informations //
-///////////////////////////////////
-  case 'infos':
+    // Reconstruction du message
+    if ( $message )  {
+      // Mise à jour de l'ordre des répertoires
+      $mysqli->query('ALTER TABLE reps ORDER BY parents,nom');
+      if ( $etat == 'nok')
+        exit('{"etat":"nok","message":"'.implode(', ',$message).'"}');
+      // Modification de la matière si protection du répertoire racine modifié
+      if ( !$r['parent'] && $r['matiere'] && $protection != $r['protection'] )
+        requete('matieres',"UPDATE matieres SET docs_protection = $protection WHERE id = ${r['matiere']}",$mysqli);
+      exit($_SESSION['message'] = '{"etat":"ok","message":"'.implode(', ',$message).'"}');
+    }
+    exit("{\"etat\":\"nok\",\"message\":\"Le répertoire <em>${r['nom']}</em> n'a pas été modifié. Aucune modification demandée.\"}");
+  }
   
-    // Vérification que l'identifiant est valide
-    if ( isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
-      $resultat = $mysqli->query("SELECT i.ordre, i.titre, cache, texte, page, cle, protection, mat, ( SELECT COUNT(*) FROM infos WHERE page = i.page ) AS max FROM infos AS i LEFT JOIN pages AS p ON i.page = p.id WHERE i.id = $id AND FIND_IN_SET(p.mat,'${_SESSION['matieres']}')");
-      if ( $resultat->num_rows )  {
-        $r = $resultat->fetch_assoc();
-        $resultat->free();
-
-        // Traitement d'une modification
-        if ( isset($_REQUEST['champ']) && in_array($champ = $_REQUEST['champ'],array('titre','texte')) )  {
-          $valeur = trim($mysqli->real_escape_string($_REQUEST['val']));
-          if ( ( $champ == 'texte' ) && ( !strlen($valeur) ) )
-            $message = '{"etat":"nok","message":"L\'information n\'a pas pu être modifiée. Texte non vide nécessaire."}';
-          elseif ( requete('infos',"UPDATE infos SET $champ = '$valeur' WHERE id = $id",$mysqli) )  {
-            $message = '{"etat":"ok","message":"Information modifiée"}';
-            if ( !$r['cache'] )  {
-              if ( $champ == 'titre' )
-                recent($mysqli,1,$id,$r['mat'],array('titre'=>( strlen($valeur) ? $valeur : 'Information')));
-              else
-                recent($mysqli,1,$id,$r['mat'],array('texte'=>$valeur));
-            }
-          }
-          else
-            $message = '{"etat":"nok","message":"L\'information n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
-
-        // Déplacement vers le haut
-        elseif ( isset($_REQUEST['monte']) && ( $r['ordre'] > 1 ) )
-          $message = ( requete('infos',"UPDATE infos SET ordre = (2*${r['ordre']}-1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}-1) ) AND page = ${r['page']}",$mysqli)
-                    && $mysqli->query('ALTER TABLE infos ORDER BY page,ordre')
-          ) ? '{"etat":"ok","message":"Déplacement réalisé"}' : '{"etat":"nok","message":"L\'information n\'a pas pu être déplacée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-
-        // Déplacement vers le bas
-        elseif ( isset($_REQUEST['descend']) && ( $r['ordre'] < $r['max'] ) )
-          $message = ( requete('infos',"UPDATE infos SET ordre = (2*${r['ordre']}+1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}+1) ) AND page = ${r['page']}",$mysqli)
-                    && $mysqli->query('ALTER TABLE infos ORDER BY page,ordre')
-          ) ? '{"etat":"ok","message":"Déplacement réalisé"}' : '{"etat":"nok","message":"L\'information n\'a pas pu être déplacée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-
-        // Positionnement "montré" (apparaît sur la partie publique)
-        elseif ( isset($_REQUEST['montre']) )  {
-          if ( requete('infos',"UPDATE infos SET cache = 0 WHERE id = $id",$mysqli) )  {
-            $message = '{"etat":"ok","message":"L\'information apparaît désormais sur la partie publique."}';
-            recent($mysqli,1,$id,$r['mat'],array('titre'=>( strlen($r['titre']) ? $r['titre'] : 'Information'), 'lien'=>".?${r['cle']}", 'texte'=>$r['texte'], 'matiere'=>$r['mat'], 'protection'=>$r['protection']));
-          }
-          else
-            $message = '{"etat":"nok","message":"L\'information n\'a pas pu être diffusée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
-
-        // Positionnement "caché" (n'apparaît pas sur la partie publique)
-        elseif ( isset($_REQUEST['cache']) )  {
-          if ( requete('infos',"UPDATE infos SET cache = 1 WHERE id = $id",$mysqli) )  {
-            $message = '{"etat":"ok","message":"L\'information n\'apparaît plus sur la partie publique mais est toujours disponible ici pour modification ou diffusion."}';
-            recent($mysqli,1,$id,$r['mat']);
-          }
-          else
-            $message = '{"etat":"nok","message":"L\'information n\'a pas pu être cachée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
+  // Propagation des droits d'accès aux sous-répertoires et documents
+  // Les infos récentes sont modifiées dans la base, mais les flux RSS
+  // ne seront affectés qu'à la prochaine exécution de recents() et donc rss()
+  if ( isset($_REQUEST['propage']) )  {
+    if ( requete('reps',"UPDATE reps SET protection = ${r['protection']} WHERE FIND_IN_SET($id,parents)",$mysqli) && requete('docs',"UPDATE docs SET protection = ${r['protection']} WHERE FIND_IN_SET($id,parents)",$mysqli) )  {
+      // Mise à jour des informations récentes
+      requete('recents',"UPDATE recents SET protection = ${r['protection']} WHERE id IN (SELECT 3000+docs.id FROM docs WHERE FIND_IN_SET($id,parents))",$mysqli);
+      rss($mysqli, ( $r['matiere'] == 0 ) ? array(0) : array(0,$r['matiere']) );
+      // Mise à jour de la matière (menu)
+      $mysqli->query('UPDATE matieres SET docs = (SELECT IF(COUNT(*),1,0) FROM docs WHERE matiere = matieres.id AND protection < 32)');
+      exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Le réglage d'accès du répertoire <em>${r['nom']}</em> a été propagé à tous les documents et sous-répertoires qu'il contient.\"}");
+    }
+    exit("{\"etat\":\"nok\",\"message\":\"Le réglage d'accès du répertoire <em>${r['nom']}</em> n'a pas été propagé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
 
-        // Suppression
-        elseif ( isset($_REQUEST['supprime']) )  {
-          if ( requete('infos',"DELETE FROM infos WHERE id = $id",$mysqli) 
-            && requete('infos',"UPDATE infos SET ordre = (ordre-1) WHERE ordre > ${r['ordre']} AND page = ${r['page']}",$mysqli) )  {
-            $message = '{"etat":"ok","message":"Suppression réalisée"}';
-            recent($mysqli,1,$id,$r['mat']);
-          }
-          else
-            $message = '{"etat":"nok","message":"L\'information n\'a pas pu être supprimée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
-      }
-      else
-        $message = '{"etat":"nok","message":"Identifiant non valide"}';
+  // Suppression du répertoire ou de son contenu
+  if ( isset($_REQUEST['supprime']) || isset($_REQUEST['vide']) )  {
+    if ( isset($_REQUEST['supprime']) )  {
+      if ( !$r['parent'] )
+        exit('{"etat":"nok","message":"Les répertoires racine des matières ne sont pas supprimables."}');
+      $action = 'supprimé';
+      $requete = "id = $id OR";
     }
-    
-    // Nouvelle information, seulement si page valide
-    elseif ( isset($_REQUEST['page']) && ctype_digit($page = $_REQUEST['page']) )  {
-      // Vérification de l'identifiant de la page
-      $resultat = $mysqli->query("SELECT cle, protection, mat FROM pages WHERE id = $page AND FIND_IN_SET(mat,'${_SESSION['matieres']}')");
+    else  {
+      $action = 'vidé';
+      $requete = '';
+    }
+    if ( requete('reps',"DELETE FROM reps WHERE $requete FIND_IN_SET($id,parents)",$mysqli) )  {
+      // Suppression physique
+      $resultat = $mysqli->query("SELECT lien FROM docs WHERE FIND_IN_SET($id,parents)");
       if ( $resultat->num_rows )  {
-        $r = $resultat->fetch_assoc();
+        while ( $s = $resultat->fetch_row() )
+          exec("rm -rf documents/${s[0]}");
         $resultat->free();
-        // Autres données
-        $titre = trim($mysqli->real_escape_string($_REQUEST['titre']));
-        $texte = trim($mysqli->real_escape_string($_REQUEST['texte']));
-        $cache = ( isset($_REQUEST['cache']) ) ? 1 : 0;
-        if ( !strlen($texte) )
-          $message = '{"etat":"nok","message":"L\'information n\'a pas pu être ajoutée. Texte non vide nécessaire."}';
-        // Écriture
-        elseif ( requete('infos',"UPDATE infos SET ordre = (ordre+1) WHERE page = $page",$mysqli)
-          && requete('infos',"INSERT INTO infos SET ordre = 1, page = $page, texte = '$texte', titre = '$titre', cache = $cache",$mysqli) )  {
-          $message = $_SESSION['message'] = '{"etat":"ok","message":"Information ajoutée"}';
-          if ( !$cache )
-            recent($mysqli,1,$mysqli->insert_id,$r['mat'],array('titre'=>( strlen($titre) ? $titre : 'Information'), 'lien'=>".?${r['cle']}", 'texte'=>$texte, 'matiere'=>$r['mat'], 'protection'=>$r['protection']));
-          $mysqli->query('ALTER TABLE infos ORDER BY page,ordre');
-        }
-        else
-          $message = '{"etat":"nok","message":"L\'information n\'a pas pu être ajoutée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
+        if ( !requete('docs',"DELETE FROM docs WHERE FIND_IN_SET($id,parents)",$mysqli) )
+          exit("{\"etat\":\"nok\",\"message\":\"Le répertoire <em>${r['nom']}</em> n'a pas été correctement $action. Certains documents sont encore dans la base de données. Vous devriez en informer l'administrateur. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
       }
-      else
-        $message = '{"etat":"nok","message":"Identifiant non valide"}';
+      $mysqli->query('UPDATE matieres SET docs = (SELECT IF(COUNT(*),1,0) FROM docs WHERE matiere = matieres.id AND protection < 32)');
+      if ( $action == 'vidé' )
+        exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Le répertoire <em>${r['nom']}</em> a été vidé de son contenu, ainsi que tous ses sous-répertoires.\"}");
+      exit("{\"etat\":\"ok\",\"message\":\"Le répertoire <em>${r['nom']}</em> a été supprimé, ainsi que tous ses sous-répertoires et ses documents.\"}");
     }
-    break;
+    else
+      exit("{\"etat\":\"nok\",\"message\":\"Le répertoire <em>${r['nom']}</em> n'a pas été $action. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+}
 
-///////////////////////////////////////////
-// Modification des programmes de colles //
-///////////////////////////////////////////
-  case 'colles':
+///////////////////////////
+// Ajout d'un répertoire //
+///////////////////////////
+elseif ( ( $action == 'ajout-rep' ) && isset($_REQUEST['nom']) && isset($_REQUEST['protection']) && isset($_REQUEST['parent']) && strlen($nom = trim($mysqli->real_escape_string($_REQUEST['nom']))) && ctype_digit($parent=$_REQUEST['parent']) )  {
+  // Vérification du répertoire parent
+  $resultat = $mysqli->query("SELECT parents, matiere, protection FROM reps WHERE id = $parent AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant de répertoire parent non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+  $menu = intval(isset($_REQUEST['menu']));
+  // Génération de la valeur de protection
+  if ( count($val = array_filter($_REQUEST['protection'],function($id) { return ctype_digit($id); })) )  {
+    if ( ( $val[0] == 0 ) || ( $val[0] == 32 ) )
+      $protection = $val[0];
+    else  {
+      $protection = 32;
+      foreach (array_slice($val,1) as $v) 
+        $protection = $protection-2**($v-1);
+    }
+  }
+  else
+    $protection = $r['protection'];
+  if ( !requete('reps',"INSERT INTO reps SET parent = $parent, parents = '${r['parents']},$parent', nom = '$nom', matiere = ${r['matiere']}, protection = $protection, menu = $menu",$mysqli) )
+    exit('{"etat":"nok","message":"Le répertoire n\'a pas été ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+  // Mise à jour
+  $mysqli->query('ALTER TABLE reps ORDER BY parents,nom');
+  exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Le répertoire <em>$nom</em> a été ajouté.\"}");
+}
 
-    // Vérification que l'identifiant est valide
-    // L'identifiant est sous la forme semaine-matiere, pour assurer une
-    // cohérence entre les programmes entrées et ceux non entrés/supprimés
-    if ( isset($_REQUEST['id']) && ctype_digit($sid = strstr($_REQUEST['id'],'-',true)) && in_array($mid = substr(strstr($_REQUEST['id'],'-'),1), explode(',',$_SESSION['matieres'])) )  {
-      $resultat = $mysqli->query("SELECT DATE_FORMAT(s.debut,'%e/%m') AS debut, c.id, c.cache, c.texte
-                                  FROM semaines AS s LEFT JOIN colles AS c ON c.semaine = s.id
-                                  WHERE s.id = $sid AND c.matiere = $mid");
-      // Si résultat : programme de colle déjà existant
-      if ( $resultat->num_rows )  {
-        $r = $resultat->fetch_assoc();
-        $resultat->free();
+////////////////////////////////
+// Modification des documents //
+////////////////////////////////
+elseif ( ( $action == 'docs' ) && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
+  // Vérification de l'identifiant
+  $resultat = $mysqli->query("SELECT nom, protection, ext, lien, parent, matiere FROM docs WHERE id = $id AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant de document non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
 
-        // Traitement d'un ajout/d'une modification
-        if ( isset($_REQUEST['champ']) && ( $_REQUEST['champ'] == 'texte' ) )  {
-          $valeur = $mysqli->real_escape_string($_REQUEST['val']);
-          if ( !strlen($valeur) )
-            $message = '{"etat":"nok","message":"Le programme de colle n\'a pas pu être modifié. Texte non vide nécessaire."}';
-          elseif ( requete('colles',"UPDATE colles SET texte = '$valeur' WHERE id = ${r['id']}",$mysqli) )  {
-            $message = '{"etat":"ok","message":"Programme de colles modifié"}';
-            if ( !$r['cache'] )
-              recent($mysqli,2,$r['id'],$mid,array('texte'=>$valeur));
-          }
-          else
-            $message = '{"etat":"nok","message":"Le programme de colles n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
+  // Traitement d'une modification unique (nom)
+  if ( isset($_REQUEST['champ']) && ( $_REQUEST['champ'] == 'nom' ) )  {
+    if ( !strlen( $valeur = trim($_REQUEST['val']) ) )
+      exit("{\"etat\":\"nok\",\"message\":\"Le nom du document <em>${r['nom']}</em> n'a pas été modifié. Le nom doit être non vide.\"}");
+    if ( $valeur != $r['nom'] )  {
+      setlocale(LC_CTYPE, "fr_FR.UTF-8");
+      $nom = substr((str_replace(array($r['ext'],'\\','/'),array('','-','-'),$valeur)),0,100);
+      // real_escape_string seulement pour la requête SQL
+      $nouveau_nom = $mysqli->real_escape_string($nom);
+      if ( !requete('docs',"UPDATE docs SET nom = '$nouveau_nom', nom_nat = '".zpad($nouveau_nom)."' WHERE id = $id",$mysqli) )
+        exit("{\"etat\":\"nok\",\"message\":\"Le nom du document <em>${r['nom']}</em> n'a pas été modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+      exec('mv documents/'.escapeshellarg("${r['lien']}/${r['nom']}${r['ext']}").' documents/'.escapeshellarg("${r['lien']}/$nom${r['ext']}"));
+      $mysqli->query('ALTER TABLE docs ORDER BY parents,nom_nat');
+      // Modification de l'éventuelle information récente
+      if ( $r['protection'] < 32 )
+        recent($mysqli,3,$id,$r['matiere'],array('nom'=>$nouveau_nom,'ancien_nom'=>$r['nom']));
+      exit("{\"etat\":\"ok\",\"message\":\"Le nom du document <em>${r['nom']}</em> a été modifié.\"}");
+    }
+    exit("{\"etat\":\"nok\",\"message\":\"Le nom du document <em>${r['nom']}</em> n'a pas été modifié. Aucune modification demandée.\"}");
+  }
 
-        // Positionnement "montré" (apparaît sur la partie publique)
-        elseif ( isset($_REQUEST['montre']) )  {
-          if ( requete('colles',"UPDATE colles SET cache = 0 WHERE id = ${r['id']}",$mysqli) )  {
-            $message = '{"etat":"ok","message":"Le programme de colles apparaît désormais sur la partie publique."}';
-            // Fabrication des données pour les informations récentes
-            $resultat = $mysqli->query("SELECT nom, cle, colles_protection FROM matieres WHERE id = $mid");
+  // Traitement d'une modification globale
+  if ( isset($_REQUEST['nom']) && isset($_REQUEST['parent']) && isset($_REQUEST['protection']) && strlen($nom = trim($_REQUEST['nom'])) )  {
+    $requete = $message = array();
+    $etat = 'ok';
+    // Modification du nom
+    setlocale(LC_CTYPE, "fr_FR.UTF-8");
+    $nom = substr(basename(str_replace(array($r['ext'],'\\'),array('','/'),$nom)),0,100);
+    if ( $nom != $r['nom'] )  {
+      // real_escape_string seulement pour la requête SQL
+      $nouveau_nom = $mysqli->real_escape_string($nom);
+      if ( requete('docs',"UPDATE docs SET nom = '$nouveau_nom', nom_nat = '".zpad($nouveau_nom)."' WHERE id = $id",$mysqli) )  {
+        exec('mv documents/'.escapeshellarg("${r['lien']}/${r['nom']}${r['ext']}").' documents/'.escapeshellarg("${r['lien']}/$nom${r['ext']}"));
+        $message[] = "Le nom du document <em>${r['nom']}</em> a été modifié.";
+        // Modification de l'éventuelle information récente
+        if ( $r['protection'] < 32 )
+          recent($mysqli,3,$id,$r['matiere'],array('nom'=>$nouveau_nom,'ancien_nom'=>$r['nom']));
+      }
+      else  {
+        $message[] = "Le nom du document <em>${r['nom']}</em> n'a pas été modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+        $etat = 'nok';
+      }
+    }
+    // Modification de la protection
+    if ( count($val = array_filter($_REQUEST['protection'],function($id) { return ctype_digit($id); })) )  {
+      if ( ( $val[0] == 0 ) || ( $val[0] == 32 ) )
+        $protection = $val[0];
+      else  {
+        $protection = 32;
+        foreach (array_slice($val,1) as $v) 
+          $protection = $protection-2**($v-1);
+      }
+      if ( $protection != $r['protection'] )  {
+        if ( requete('docs',"UPDATE docs SET protection = $protection WHERE id = $id",$mysqli) )  {
+          $message[] = "L'accès au document <em>${r['nom']}</em> a été modifié.";
+          // Si $protection = 32, on cherche à supprimer l'info récente
+          if ( $protection == 32 )
+            recent($mysqli,3,$id,$r['matiere']);
+          // Si doc avant protégé, on crée une info récente de nouveau doc
+          elseif ( $r['protection'] == 32 )  {
+            $resultat = $mysqli->query("SELECT GROUP_CONCAT( reps.nom ORDER BY FIND_IN_SET(reps.id,docs.parents) SEPARATOR '/' ) AS path, docs.nom
+                                        FROM docs LEFT JOIN reps ON FIND_IN_SET(reps.id,docs.parents) WHERE docs.id = $id");
             $s = $resultat->fetch_assoc();
             $resultat->free();
-            recent($mysqli,2,$r['id'],$mid,array('titre'=>"Colles du ${r['debut']} en ${s['nom']}", 'lien'=>"colles?${s['cle']}&amp;n=$sid", 'texte'=>$r['texte'], 'matiere'=>$mid, 'protection'=>$s['colles_protection']));
-          }
-          else
-            $message = '{"etat":"nok","message":"Le programme de colles n\'a pas pu être diffusé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
-
-        // Positionnement "caché" (n'apparaît pas sur la partie publique)
-        elseif ( isset($_REQUEST['cache']) )  {
-          if ( requete('colles',"UPDATE colles SET cache = 1 WHERE id = ${r['id']}",$mysqli) )  {
-            $message = '{"etat":"ok","message":"Le programme de colles n\'apparaît plus sur la partie publique mais est toujours disponible ici pour modification ou diffusion."}';
-            recent($mysqli,2,$r['id'],$mid);
+            $path = $mysqli->real_escape_string("${s['path']}/${s['nom']}");
+            recent($mysqli,3,$id,$r['matiere'],array('titre'=>$path, 'lien'=>"download?id=$id", 'texte'=>"<p>Nouveau document&nbsp;: <a href=\"download?id=$id\">$path</a></p>", 'matiere'=>$r['matiere'], 'protection'=>$protection),$r['ext']);
           }
+          // Sinon, mise à jour de la protection dans les informations récentes
           else
-            $message = '{"etat":"nok","message":"Le programme de colles n\'a pas pu être caché. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
+            recent($mysqli,3,$id,$r['matiere'],array('protection'=>$protection));
         }
-
-        // Suppression
-        elseif ( isset($_REQUEST['supprime']) )  {
-          if ( requete('colles',"DELETE FROM colles WHERE id = ${r['id']}",$mysqli) )  {
-            $message = '{"etat":"ok","message":"Suppression réalisée"}';
-            recent($mysqli,2,$r['id'],$mid);
-          }
-          else
-            $message = '{"etat":"nok","message":"Le programme de colles n\'a pas pu être supprimé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
+        else  {
+          $message[] = "L'accès au document <em>${r['nom']}</em> n'a pas été modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+          $etat = 'nok';
+          $protection = $r['protection'];
         }
       }
+    }
 
-      // Si pas de résultat : nouveau programme de colle
-      else  {
-        $texte = $mysqli->real_escape_string($_REQUEST['texte']);
-        $cache = ( isset($_REQUEST['cache']) ) ? 1 : 0;
-        if ( !strlen($texte) )
-          $message = '{"etat":"nok","message":"Le programme de colles n\'a pas pu être ajouté. Texte non vide nécessaire."}';
-        elseif ( requete('colles',"INSERT INTO colles SET texte = '$texte', semaine = $sid, matiere = $mid, cache = $cache",$mysqli) )  {
-          $id = $mysqli->insert_id;
-          $mysqli->query('ALTER TABLE colles ORDER BY matiere,semaine');
-          $message = '{"etat":"ok","message":"Programme de colles ajouté"}';
-          if ( !$cache )  {
-            // Fabrication des données pour les informations récentes
-            $resultat = $mysqli->query("SELECT DATE_FORMAT(debut,'%e/%m') AS debut FROM semaines WHERE id = $sid");
-            $r = $resultat->fetch_assoc();
-            $resultat->free();
-            $resultat = $mysqli->query("SELECT nom, cle, colles_protection FROM matieres WHERE id = $mid");
+    // Déplacement dans un autre répertoire
+    if ( ctype_digit($parent = $_REQUEST['parent']) && $parent && ( $parent != $r['parent'] ) )  {
+      // Vérification du répertoire parent
+      $resultat = $mysqli->query("SELECT parents, matiere FROM reps WHERE id = $parent AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
+      if ( $resultat->num_rows )  {
+        $s = $resultat->fetch_assoc();
+        $resultat->free();
+        if ( requete('docs',"UPDATE docs SET parent = '$parent', parents = '${s['parents']},$parent', matiere = ${s['matiere']} WHERE id = $id",$mysqli) )  {
+          $message[] = "Le document <em>${r['nom']}</em> a été déplacé.";
+          // Modification de l'information récente si document visible
+          if ( $protection < 32 )  {
+            $resultat = $mysqli->query("SELECT GROUP_CONCAT( reps.nom ORDER BY FIND_IN_SET(reps.id,docs.parents) SEPARATOR '/' ) AS path, docs.nom, docs.matiere
+                                        FROM docs LEFT JOIN reps ON FIND_IN_SET(reps.id,docs.parents) WHERE docs.id = $id");
             $s = $resultat->fetch_assoc();
             $resultat->free();
-            recent($mysqli,2,$id,$mid,array('titre'=>"Colles du ${r['debut']} en ${s['nom']}", 'lien'=>"colles?${s['cle']}&amp;n=$sid", 'texte'=>$texte, 'matiere'=>$mid, 'protection'=>$s['colles_protection']));
+            recent($mysqli,3,$id,$r['matiere'],array('chemin'=>$mysqli->real_escape_string("${s['path']}/${s['nom']}"), 'matiere'=>$s['matiere']));
           }
         }
-        else
-          $message = '{"etat":"nok","message":"Le programme de colles n\'a pas pu être ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
+        else  {
+          $message[] = "Le document <em>${r['nom']}</em> n'a pas été déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».';
+          $etat = 'nok';
+        }
       }
-      
-      // Mise à jour des champs 'colles' dans la table 'matieres' (pour le menu)
-      $mysqli->query('UPDATE matieres SET colles = IF((SELECT id FROM colles WHERE matiere = matieres.id AND cache = 0 LIMIT 1),1,0)');
-    }
-    else
-      $message = '{"etat":"nok","message":"Identifiant non valide"}';
-    break;
-
-///////////////////////////////////////
-// Modification des cahiers de texte //
-///////////////////////////////////////
-  case 'cdt':
-    // Traitement d'une modification de propriétés/d'un ajout d'élément :
-    // validation préliminaire
-    if ( isset($_REQUEST['tid']) && isset($_REQUEST['jour']) && ( ctype_digit($tid = $_REQUEST['tid']) ) )  {
-      // Validation du jour de la semaine
-      $jour = preg_filter('/(\d{2})\/(\d{2})\/(\d{4})/','$3-$2-$1',$_REQUEST['jour']);
-      $resultat = $mysqli->query("SELECT id FROM semaines WHERE debut <= '$jour' AND ADDDATE(debut,7) >= '$jour' ORDER BY debut DESC LIMIT 1");
-      if ( !$resultat->num_rows )
-        $message = '{"etat":"nok","message":"Le jour choisi ne se trouve pas dans l\'année scolaire."}';
       else  {
-        $r = $resultat->fetch_assoc();
-        $semaine = $r['id'];
-        $resultat->free();
-        // Validation du type de séance
-        $resultat = $mysqli->query("SELECT id, deb_fin_pour FROM `cdt-types` WHERE id = $tid AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
-        if ( !$resultat->num_rows )
-          $message = '{"etat":"nok","message":"Type de séance non valide."}';
-        else  {
-          $r = $resultat->fetch_assoc();
+        $message[] = "Le document <em>${r['nom']}</em> n'a pas été déplacé. Identifiant de répertoire parent non valide.";
+        $etat = 'nok';
+      }
+    }
+    // Mise à jour d'un document
+    if ( isset($_FILES['fichier']['tmp_name']) && is_uploaded_file($_FILES['fichier']['tmp_name']) )  {
+      // Changement d'extension interdit
+      $ext = ( strpos($_FILES['fichier']['name'],'.') ) ? strrchr($_FILES['fichier']['name'],'.') : '';
+      if ( $ext != $r['ext'] )  {
+        $message[] = "Le document <em>${r['nom']}</em> n'a pas été mis à jour. Le fichier envoyé est d'une extension différente.";
+        $etat = 'nok';
+      }
+      // Déplacement du document uploadé au bon endroit
+      elseif ( move_uploaded_file($_FILES['fichier']['tmp_name'],"documents/${r['lien']}/${r['nom']}${r['ext']}") )  {
+        // Gestion de la taille
+        $taille = intval($_FILES['fichier']['size']/1024);
+        $taille = ( $taille < 1024 ) ? "$taille&nbsp;ko" : intval($taille/1024).'&nbsp;Mo';
+        // Modifications dans la base de données
+        requete('docs',"UPDATE docs SET upload = CURDATE(), taille = '$taille' WHERE id = $id",$mysqli);
+        // Info récente si document visible (éventuellement nouvelle)
+        if ( $protection < 32 )  {
+          // Besoin du chemin si pas d'info récente liée au document
+          $resultat = $mysqli->query("SELECT GROUP_CONCAT( reps.nom ORDER BY FIND_IN_SET(reps.id,docs.parents) SEPARATOR '/' ) AS path, docs.nom
+                                      FROM docs LEFT JOIN reps ON FIND_IN_SET(reps.id,docs.parents) WHERE docs.id = $id");
+          $s = $resultat->fetch_assoc();
           $resultat->free();
-          // Validation des horaires
-          $h_debut = $h_fin = '0:00';
-          $pour = '0000/00/00';
-          $demigroupe = 0;
-          switch ( $r['deb_fin_pour'] )  {
-            case 1: $h_fin = preg_filter('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['h_fin']);
-            case 0: $h_debut = preg_filter('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['h_debut']); $demigroupe = ( in_array($_REQUEST['demigroupe'],array(0,1)) ) ? $_REQUEST['demigroupe'] : 0; break;
-            case 2: $pour = preg_filter('/(\d{2})\/(\d{2})\/(\d{4})/','$3-$2-$1',$_REQUEST['pour']);
-            case 3: $demigroupe = ( in_array($_REQUEST['demigroupe'],array(0,1)) ) ? $_REQUEST['demigroupe'] : 0;
-            // Cas 4 et 5 : h_debut, h_fin et pour restent nuls (cf l'aide)
-          }
+          recent($mysqli,3,$id,$r['matiere'],array('maj'=>$mysqli->real_escape_string("${s['path']}/${s['nom']}")));
         }
+        $message[] = "Le document <em>${r['nom']}</em> a été mis à jour.";
+      }
+      else  {
+        $message[] = "Le document <em>$nom</em> n'a pas été mis à jour : problème d'écriture du fichier. Vous devriez en informer l'administrateur.";
+        $etat = 'nok';
       }
     }
+    // Reconstruction du message
+    if ( $message )  {
+      // Mise à jour de l'ordre des répertoires et de l'affichage des matières
+      $mysqli->query('ALTER TABLE docs ORDER BY parents,nom_nat');
+      $mysqli->query('UPDATE matieres SET docs = (SELECT IF(COUNT(*),1,0) FROM docs WHERE matiere = matieres.id AND protection < 32)');
+      if ( $etat == 'nok')
+        exit('{"etat":"nok","message":"'.implode(', ',$message).'"}');
+      exit($_SESSION['message'] = '{"etat":"ok","message":"'.implode(', ',$message).'"}');
+    }
+    exit("{\"etat\":\"nok\",\"message\":\"Le document <em>${r['nom']}</em> n'a pas été modifié. Aucune modification demandée.\"}");
+  }
+  
+  // Suppression d'un document
+  if ( isset($_REQUEST['supprime']) )  {
+    if ( !requete('docs',"DELETE FROM docs WHERE id = $id",$mysqli) )
+      exit("{\"etat\":\"nok\",\"message\":\"Le document <em>${r['nom']}</em> n'a pas été supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».');
+    // Suppression physique
+    exec("rm -rf documents/${r['lien']}");
+    recent($mysqli,3,$id,$r['matiere']);
+    $mysqli->query('UPDATE matieres SET docs = (SELECT IF(COUNT(*),1,0) FROM docs WHERE matiere = matieres.id AND protection < 32)');
+    exit("{\"etat\":\"ok\",\"message\":\"Le document <em>${r['nom']}</em> a été supprimé.\"}");
+  }
+}
 
-    // Vérification que l'identifiant est valide
-    if ( !strlen($message) && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
-      $resultat = $mysqli->query("SELECT id FROM cdt WHERE id = $id AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
-      if ( $resultat->num_rows )  {
-        $resultat->free();
+/////////////////////////
+// Ajout d'un document //
+/////////////////////////
+elseif ( ( $action == 'ajout-doc' ) && isset($_FILES['fichier']['tmp_name']) && is_uploaded_file($_FILES['fichier']['tmp_name']) && isset($_REQUEST['nom']) && isset($_REQUEST['protection']) && isset($_REQUEST['parent']) && ctype_digit($parent = $_REQUEST['parent']) )  {
+  // Vérification du répertoire parent
+  $resultat = $mysqli->query("SELECT parents, matiere, protection FROM reps WHERE id = $parent AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant de répertoire parent non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+  // Vérifications des données envoyées (on fait confiance aux utilisateurs connectés pour ne pas envoyer de scripts malsains)
+  $nom = $_FILES['fichier']['name'];
+  // $ext ne doit pas faire plus de 4 caractères sinon fichier plus accessible
+  $ext = ( strpos($nom,'.') ) ? substr(strrchr($nom,'.'),0,4) : '';
+  setlocale(LC_CTYPE, "fr_FR.UTF-8");
+  $nom = trim(substr(basename(str_replace(array($ext,'\\'),array('','/'), $_REQUEST['nom'] ?: $nom )),0,100));
+  // Génération de la valeur de protection
+  if ( count($val = array_filter($_REQUEST['protection'],function($id) { return ctype_digit($id); })) )  {
+    if ( ( $val[0] == 0 ) || ( $val[0] == 32 ) )
+      $protection = $val[0];
+    else  {
+      $protection = 32;
+      foreach (array_slice($val,1) as $v) 
+        $protection = $protection-2**($v-1);
+    }
+  }
+  else
+    $protection = $r['protection'];
+  // Création du répertoire particulier
+  $lien = substr(sha1(mt_rand()),0,15);
+  while ( is_dir("documents/$lien") )
+    $lien = substr(sha1(mt_rand()),0,15);
+  mkdir("documents/$lien");
+  // Gestion de la taille
+  $taille = intval($_FILES['fichier']['size']/1024);
+  $taille = ( $taille < 1024 ) ? "$taille&nbsp;ko" : intval($taille/1024).'&nbsp;Mo';
+  // Déplacement du document uploadé au bon endroit
+  if ( !move_uploaded_file($_FILES['fichier']['tmp_name'],"documents/$lien/$nom$ext") )
+    exit("{\"etat\":\"nok\",\"message\":\"Le document <em>$nom</em> n'a pas été ajouté : problème d'écriture du fichier. Vous devriez en informer l'administrateur.\"}");
+  // Écriture MySQL
+  if ( requete('docs',"INSERT INTO docs SET parent = $parent, parents = '${r['parents']},$parent',
+                       matiere = ${r['matiere']}, nom = '".$mysqli->real_escape_string($nom).'\', nom_nat = \''.zpad($mysqli->real_escape_string($nom))."', upload = CURDATE(),
+                       taille = '$taille', lien = '$lien', ext='".$mysqli->real_escape_string($ext)."', protection = $protection",$mysqli) )  {
+    $id = $mysqli->insert_id;
+    // Mise à jour des informations récentes
+    if ( $protection < 32 )  {
+      $resultat = $mysqli->query("SELECT GROUP_CONCAT( reps.nom ORDER BY FIND_IN_SET(reps.id,docs.parents) SEPARATOR '/' ) AS path
+                                  FROM docs LEFT JOIN reps ON FIND_IN_SET(reps.id,docs.parents) WHERE docs.id = $id");
+      $s = $resultat->fetch_assoc();
+      $resultat->free();
+      $path = $mysqli->real_escape_string("${s['path']}/$nom");
+      recent($mysqli,3,$id,$r['matiere'],array('titre'=>$path, 'lien'=>"download?id=$id", 'texte'=>"<p>Nouveau document&nbsp;: <a href=\"download?id=$id\">$path</a></p>", 'matiere'=>$r['matiere'], 'protection'=>$protection),$ext);
+      // Mise à jour de l'affichage de la matière dans le menu)
+      $mysqli->query("UPDATE matieres SET docs = 1 WHERE id = ${r['matiere']}");
+    }
+    $mysqli->query('ALTER TABLE docs ORDER BY parents,nom_nat');
+    exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Le document <em>$nom</em> a été ajouté.\"}");
+  }
+  else  {
+    // Retour en arrière
+    exec("rm -rf documents/$lien");
+    exit("{\"etat\":\"nok\",\"message\":\"Le document <em>$nom</em> n'a pas été ajouté. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+}
 
-        // Traitement d'une modification de propriétés
-        if ( isset($h_debut) )  {
-          // Écriture dans la base de données
-          if ( requete('cdt',"UPDATE cdt SET semaine = $semaine, jour = '$jour', h_debut = '$h_debut', h_fin = '$h_fin',
-                              pour = '$pour', type = $tid, demigroupe = $demigroupe WHERE id = $id", $mysqli) )  {
-            // Mise en ordre : d'abord ce qui n'a pas de fin, ensuite les
-            // séances "normales", ensuite les "pour le"
-            $mysqli->query('ALTER TABLE cdt ORDER BY jour,matiere,pour,h_debut,h_fin,type');
-            $message = '{"etat":"ok","message":"Élément du cahier de texte modifié"}';
-          }
-          else
-            $message = '{"etat":"ok","message":"Les propriétés de l\'élément du cahier de texte n\'ont pas pu être modifiées. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
+///////////////////////////////////////////
+// Modification des programmes de colles //
+///////////////////////////////////////////
+elseif ( ( $action == 'colles' ) && isset($_REQUEST['id']) && ctype_digit($sid = strstr($_REQUEST['id'],'-',true)) && in_array($mid = intval(substr(strstr($_REQUEST['id'],'-'),1)), explode(',',$_SESSION['matieres'])) )  {
+  // L'identifiant est sous la forme semaine-matiere, pour assurer une
+  // cohérence entre les programmes saisis et ceux non saisis/supprimés
+  $resultat = $mysqli->query("SELECT DATE_FORMAT(s.debut,'%e/%m') AS debut, c.id, c.cache, c.texte
+                              FROM semaines AS s LEFT JOIN colles AS c ON c.semaine = s.id
+                              WHERE s.id = $sid AND c.matiere = $mid");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+  // Chaine de mise à jour de la matière
+  $requete_maj = "UPDATE matieres SET colles = IF((SELECT id FROM colles WHERE matiere = $mid AND cache = 0 LIMIT 1),1,0) WHERE id = $mid AND colles_protection < 32";
 
-        // Traitement d'une modification de texte
-        elseif ( isset($_REQUEST['champ']) && ( $_REQUEST['champ'] == 'texte' ) )  {
-          if ( !strlen($valeur = $mysqli->real_escape_string($_REQUEST['val'])) )
-            $message = '{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas pu être modifié. Texte non vide nécessaire."}';
-          elseif ( requete('cdt',"UPDATE cdt SET texte = '$valeur' WHERE id = $id",$mysqli) )  {
-            $message = '{"etat":"ok","message":"Élément du cahier de texte modifié"}';
-          }
-          else
-            $message = '{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
+  // Traitement d'une modification unique (texte)
+  if ( isset($_REQUEST['champ']) && ( $_REQUEST['champ'] == 'texte' ) )  {
+    $valeur = $mysqli->real_escape_string($_REQUEST['val']);
+    if ( !strlen($valeur) )
+      exit("{\"etat\":\"nok\",\"message\":\"Le programme de colle de la semaine du ${r['debut']} n'a pas été modifié. Le texte doit être non vide.\"}");
+    if ( requete('colles',"UPDATE colles SET texte = '$valeur' WHERE id = ${r['id']}",$mysqli) )  {
+      if ( !$r['cache'] )
+        recent($mysqli,2,$r['id'],$mid,array('texte'=>$valeur));
+      exit("{\"etat\":\"ok\",\"message\":\"Le programme de colle de la semaine du ${r['debut']} a été modifié.\"}");
+    }
+    exit("{\"etat\":\"nok\",\"message\":\"Le programme de colle de la semaine du ${r['debut']} n'a pas été modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
 
-        // Positionnement "montré" (apparaît sur la partie publique)
-        elseif ( isset($_REQUEST['montre']) )  {
-          if ( requete('cdt',"UPDATE cdt SET cache = 0 WHERE id = $id",$mysqli) )  {
-            $message = '{"etat":"ok","message":"L\'élément du cahier de texte apparaît désormais sur la partie publique."}';
-          }
-          else
-            $message = '{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas pu être diffusé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
+  // Positionnement "montré" (apparaît sur la partie publique)
+  if ( isset($_REQUEST['montre']) )  {
+    if ( requete('colles',"UPDATE colles SET cache = 0 WHERE id = ${r['id']}",$mysqli) )  {
+      // Fabrication des données pour les informations récentes
+      $resultat = $mysqli->query("SELECT nom, cle, colles_protection FROM matieres WHERE id = $mid");
+      $s = $resultat->fetch_assoc();
+      $resultat->free();
+      recent($mysqli,2,$r['id'],$mid,array('titre'=>"Colles du ${r['debut']} en ".$mysqli->real_escape_string($s['nom']), 'lien'=>"colles?${s['cle']}&amp;n=$sid", 'texte'=>$mysqli->real_escape_string($r['texte']), 'matiere'=>$mid, 'protection'=>$s['colles_protection']));
+      $mysqli->query($requete_maj);
+      exit("{\"etat\":\"ok\",\"message\":\"Le programme de colle de la semaine du ${r['debut']} apparaît désormais sur la partie publique.\"}");
+    }
+    exit("{\"etat\":\"nok\",\"message\":\"Le programme de colle de la semaine du ${r['debut']} n'a pas été diffusé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
 
-        // Positionnement "caché" (n'apparaît pas sur la partie publique)
-        elseif ( isset($_REQUEST['cache']) )  {
-          if ( requete('cdt',"UPDATE cdt SET cache = 1 WHERE id = $id",$mysqli) )  {
-            $message = '{"etat":"ok","message":"L\'élément du cahier de texte n\'apparaît plus sur la partie publique mais est toujours disponible ici pour modification ou diffusion."}';
-          }
-          else
-            $message = '{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas pu être caché. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
+  // Positionnement "caché" (n'apparaît pas sur la partie publique)
+  if ( isset($_REQUEST['cache']) )  {
+    if ( requete('colles',"UPDATE colles SET cache = 1 WHERE id = ${r['id']}",$mysqli) )  {
+      recent($mysqli,2,$r['id'],$mid);
+      $mysqli->query($requete_maj);
+      exit("{\"etat\":\"ok\",\"message\":\"Le programme de colle de la semaine du ${r['debut']} n'apparaît plus sur la partie publique mais est toujours disponible ici pour modification ou diffusion.\"}");
+    }
+    exit("{\"etat\":\"nok\",\"message\":\"Le programme de colle de la semaine du ${r['debut']} n'a pas été caché. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
 
-        // Suppression
-        elseif ( isset($_REQUEST['supprime']) )  {
-          if ( requete('cdt',"DELETE FROM cdt WHERE id = $id",$mysqli) )  {
-            $message = '{"etat":"ok","message":"Suppression réalisée"}';
-          }
-          else
-            $message = '{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas pu être supprimé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
-      }
-      else
-        $message = '{"etat":"nok","message":"Identifiant non valide"}';
+  // Suppression
+  if ( isset($_REQUEST['supprime']) )  {
+    if ( requete('colles',"DELETE FROM colles WHERE id = ${r['id']}",$mysqli) )  {
+      recent($mysqli,2,$r['id'],$mid);
+      $mysqli->query($requete_maj);
+      exit("{\"etat\":\"ok\",\"message\":\"Le programme de colle de la semaine du ${r['debut']} a été supprimé.\"}");
     }
-    
-    // Nouvel élément du cahier de texte
-    elseif ( !strlen($message) && isset($demigroupe) && isset($_REQUEST['matiere']) && in_array($matiere = $_REQUEST['matiere'], explode(',',$_SESSION['matieres'])) )  {
-      $texte = $mysqli->real_escape_string($_REQUEST['texte']);
-      $cache = ( isset($_REQUEST['cache']) ) ? 1 : 0;
-      if ( !strlen($texte) )
-        $message = '{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas pu être ajouté. Texte non vide nécessaire."}';
-      elseif ( is_null($h_debut) )
-        $message = '{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas pu être ajouté. Horaire de début non vide nécessaire."}';
-      elseif ( is_null($h_fin) )
-        $message = '{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas pu être ajouté. Horaire de fin non vide nécessaire."}';
-      elseif ( is_null($pour) )
-        $message = '{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas pu être ajouté. Date d\'échéance non vide nécessaire."}';
+    exit("{\"etat\":\"nok\",\"message\":\"Le programme de colle de la semaine du ${r['debut']} n'a pas été supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+}
+
+////////////////////////////////////
+// Ajout d'un programme de colles //
+////////////////////////////////////
+elseif ( ( $action == 'ajout-colle' ) && isset($_REQUEST['id']) && ctype_digit($sid = strstr($_REQUEST['id'],'-',true)) && in_array($mid = intval(substr(strstr($_REQUEST['id'],'-'),1)), explode(',',$_SESSION['matieres'])) )  {
+  // L'identifiant est sous la forme semaine-matiere, pour assurer une
+  // cohérence entre les programmes saisis et ceux non saisis/supprimés
+  $resultat = $mysqli->query("SELECT DATE_FORMAT(debut,'%e/%m') AS debut FROM semaines WHERE id = $sid AND colle");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+  // Validation des données
+  $texte = $mysqli->real_escape_string($_REQUEST['texte']);
+  $cache = intval(isset($_REQUEST['cache']));
+  if ( !strlen($texte) )
+    exit("{\"etat\":\"nok\",\"message\":\"Le programme de colle de la semaine du ${r['debut']} n'a pas été ajouté. Le texte doit être non vide.\"}");
+  if ( requete('colles',"INSERT INTO colles SET texte = '$texte', semaine = $sid, matiere = $mid, cache = $cache",$mysqli) )  {
+    $id = $mysqli->insert_id;
+    $mysqli->query('ALTER TABLE colles ORDER BY matiere,semaine');
+    if ( !$cache )  {
+      // Fabrication des données pour les informations récentes
+      $resultat = $mysqli->query("SELECT nom, cle, colles_protection FROM matieres WHERE id = $mid");
+      $s = $resultat->fetch_assoc();
+      $resultat->free();
+      recent($mysqli,2,$id,$mid,array('titre'=>"Colles du ${r['debut']} en ".$mysqli->real_escape_string($s['nom']), 'lien'=>"colles?${s['cle']}&amp;n=$sid", 'texte'=>$texte, 'matiere'=>$mid, 'protection'=>$s['colles_protection']));
+      // Mise à jour de la matière
+      $mysqli->query("UPDATE matieres SET colles = IF((SELECT id FROM colles WHERE matiere = $mid AND cache = 0 LIMIT 1),1,0) WHERE id = $mid AND colles_protection < 32");
+    }
+    exit("{\"etat\":\"ok\",\"message\":\"Le programme de colle de la semaine du ${r['debut']} a été ajouté.\"}");
+  }
+  exit("{\"etat\":\"nok\",\"message\":\"Le programme de colle de la semaine du ${r['debut']} n'a pas été ajouté. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+}
+
+/////////////////////////////////////////////////////////
+// Modification ou ajout des éléments cahiers de texte //
+/////////////////////////////////////////////////////////
+elseif ( $action == 'cdt-elems' )  {
+
+  // Traitement d'une modification de propriétés/d'un ajout d'élément :
+  // validation préliminaire
+  if ( isset($_REQUEST['tid']) && ctype_digit($tid = $_REQUEST['tid']) && isset($_REQUEST['jour']) )  {
+    // Validation du jour de la semaine
+    $jour = preg_filter('/(\d{2})\/(\d{2})\/(\d{4})/','$3-$2-$1',$_REQUEST['jour']);
+    $resultat = $mysqli->query("SELECT id FROM semaines WHERE debut <= '$jour' AND debut >= SUBDATE('$jour',7) ORDER BY debut DESC LIMIT 1");
+    if ( !$resultat->num_rows )
+      exit('{"etat":"nok","message":"Le jour choisi ne se trouve pas dans l\'année scolaire. S\'il s\'agit d\'une erreur, il faut peut-être contacter l\'administrateur."}');
+    $r = $resultat->fetch_assoc();
+    $semaine = $r['id'];
+    $resultat->free();
+    // Validation du type de séance
+    $resultat = $mysqli->query("SELECT id, deb_fin_pour FROM `cdt-types` WHERE id = $tid AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
+    if ( !$resultat->num_rows )
+      exit('{"etat":"nok","message":"Type de séance non valide."}');
+    $r = $resultat->fetch_assoc();
+    $resultat->free();
+    // Validation des horaires
+    $h_debut = $h_fin = '0:00';
+    $pour = '0000/00/00';
+    $demigroupe = 0;
+    switch ( $r['deb_fin_pour'] )  {
+      case 1: $h_fin = preg_filter('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['h_fin']);
+      case 0: $h_debut = preg_filter('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['h_debut']); $demigroupe = intval($_REQUEST['demigroupe'] == 1); break;
+      case 2: $pour = preg_filter('/(\d{2})\/(\d{2})\/(\d{4})/','$3-$2-$1',$_REQUEST['pour']);
+      case 3: $demigroupe = intval($_REQUEST['demigroupe'] == 1);
+      // Cas 4 et 5 : h_debut, h_fin et pour restent nuls (cf l'aide)
+    }
+  }
+
+  // Vérification que l'identifiant est valide
+  if ( isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
+    $resultat = $mysqli->query("SELECT id, type, matiere FROM cdt WHERE id = $id AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
+    if ( !$resultat->num_rows )
+      exit('{"etat":"nok","message":"Identifiant de l\'élément de cahier de texte non valide"}');
+    $r = $resultat->fetch_assoc();
+    $resultat->free();
+
+    // Traitement d'une modification de propriétés
+    if ( isset($h_debut) )  {
       // Écriture dans la base de données
-      elseif ( requete('cdt',"INSERT INTO cdt SET matiere = $matiere, semaine = $semaine, jour = '$jour', h_debut = '$h_debut', h_fin = '$h_fin',
-                              pour = '$pour', type = $tid, texte = '$texte', demigroupe = $demigroupe, cache = $cache", $mysqli) )  {
+      if ( requete('cdt',"UPDATE cdt SET semaine = $semaine, jour = '$jour', h_debut = '$h_debut', h_fin = '$h_fin',
+                          pour = '$pour', type = $tid, demigroupe = $demigroupe WHERE id = $id", $mysqli) )  {
+        // Mise à jour du compte d'éléments si modification du type
+        if ( $tid != $r['type'] )  {
+          $mysqli->query("UPDATE `cdt-types` SET nb = nb-1 WHERE id = ${r['type']}");
+          $mysqli->query("UPDATE `cdt-types` SET nb = nb+1 WHERE id = $tid");
+        }
         // Mise en ordre : d'abord ce qui n'a pas de fin, ensuite les
         // séances "normales", ensuite les "pour le"
         $mysqli->query('ALTER TABLE cdt ORDER BY jour,matiere,pour,h_debut,h_fin,type');
-        $message = $_SESSION['message'] = '{"etat":"ok","message":"Élément du cahier de texte ajouté"}';
+        exit('{"etat":"ok","message":"Les propriétés de l\'élément du cahier de texte ont été modifiées."}');
       }
-      else
-        $message = '{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas pu être ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
+      exit('{"etat":"nok","message":"Les propriétés de l\'élément du cahier de texte n\'ont pas été modifiées. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
     }
-    
-    // Mise à jour des champs 'cdt' dans la table 'matieres' (pour le menu)
-    $mysqli->query('UPDATE matieres SET cdt = IF((SELECT id FROM cdt WHERE matiere = matieres.id AND cache = 0 LIMIT 1),1,0)');
-    // Mise à jour des champs 'nb' et nb_v dans la table 'cdt-types'
-    $mysqli->query('UPDATE `cdt-types` SET
-                    nb = (SELECT COUNT(cdt.id) FROM cdt WHERE cdt.type = `cdt-types`.id),
-                    nb_v = (SELECT COUNT(cdt.id) FROM cdt WHERE cdt.type = `cdt-types`.id AND cdt.cache = 0)');
-    break;
-
-////////////////////////////////////////////////////////////
-// Modification des types de séances des cahiers de texte //
-////////////////////////////////////////////////////////////
-  case 'cdt-types':
-  
-    // Vérification que l'identifiant est valide
-    if ( isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
-      $resultat = $mysqli->query("SELECT ordre, titre, nb, cle, matiere, (SELECT COUNT(*) FROM `cdt-types` WHERE matiere = c.matiere) AS max FROM `cdt-types` AS c WHERE id = $id AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
-      if ( $resultat->num_rows )  {
-        $r = $resultat->fetch_assoc();
-        $resultat->free();
 
-        // Traitement d'une modification
-        if ( isset($_REQUEST['titre']) && isset($_REQUEST['cle']) && isset($_REQUEST['deb_fin_pour']) )  {
-          $titre = ucfirst($mysqli->real_escape_string($_REQUEST['titre']));
-          $cle = str_replace(' ','_',trim($mysqli->real_escape_string($_REQUEST['cle'])));
-          $deb_fin_pour = ( in_array($_REQUEST['deb_fin_pour'],array(0,1,2,3,4,5)) ) ? $_REQUEST['deb_fin_pour'] : 1;
-          if ( !strlen($titre) || !strlen($cle) )
-            $message = '{"etat":"nok","message":"Le type de séance n\'a pas pu être modifié. Texte et clé non vides nécessaires."}';
-          elseif ( $cle != $r['cle'] )  {
-            // Vérification que la clé n'existe pas déjà
-            $resultat = $mysqli->query("SELECT cle FROM `cdt-types` WHERE id != $id AND matiere = ${r['matiere']}");
-            if ( $resultat->num_rows )  {
-              while ( $r = $resultat->fetch_assoc() )
-                if ( $r['cle'] == $cle )  {
-                  $message = '{"etat":"nok","message":"Le type de séance n\'a pas pu être modifié. Cette clé existe déjà et doit être unique."}';
-                  break;
-                }
-              $resultat->free();
-            }
-          }
-          if ( !strlen($message) )  {
-            if ( requete('cdt-types',"UPDATE `cdt-types` SET titre = '$titre', cle = '$cle', deb_fin_pour = $deb_fin_pour WHERE id = $id",$mysqli) )
-              $message = $_SESSION['message'] = '{"etat":"ok","message":"Type de séance modifié"}';
-            else
-              $message = '{"etat":"nok","message":"Le type de séance n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-          }
-        }
-        
-        // Déplacement vers le haut
-        elseif ( isset($_REQUEST['monte']) && ( $r['ordre'] > 1 ) )
-          $message = ( requete('cdt-types',"UPDATE `cdt-types` SET ordre = (2*${r['ordre']}-1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}-1) ) AND matiere = ${r['matiere']}",$mysqli)
-                    && $mysqli->query('ALTER TABLE `cdt-types` ORDER BY matiere,ordre')
-          ) ? '{"etat":"ok","message":"Déplacement réalisé"}' : '{"etat":"nok","message":"Le type de séance n\'a pas pu être déplacé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-
-        // Déplacement vers le bas
-        elseif ( isset($_REQUEST['descend']) && ( $r['ordre'] < $r['max'] ) )
-          $message = ( requete('cdt-types',"UPDATE `cdt-types` SET ordre = (2*${r['ordre']}+1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}+1) ) AND matiere = ${r['matiere']}",$mysqli)
-                    && $mysqli->query('ALTER TABLE `cdt-types` ORDER BY matiere,ordre')
-          ) ? '{"etat":"ok","message":"Déplacement réalisé"}' : '{"etat":"nok","message":"Le type de séance n\'a pas pu être déplacé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-
-        // Suppression
-        elseif ( isset($_REQUEST['supprime']) )  {
-          if ( $r['max'] == 1 )
-             $message = '{"etat":"nok","message":"Le type de séance n\'a pas pu être supprimé. Il faut obligatoirement en garder au moins un."}';
-          elseif ( requete('cdt',"DELETE FROM cdt WHERE type = $id",$mysqli) && requete('cdt-types',"DELETE FROM `cdt-types` WHERE id = $id",$mysqli) 
-            && requete('cdt-types',"UPDATE `cdt-types` SET ordre = (ordre-1) WHERE ordre > ${r['ordre']} AND matiere = ${r['matiere']}",$mysqli) )  {
-            $message = '{"etat":"ok","message":"Suppression réalisée"}';
-          }
-          else
-            $message = '{"etat":"nok","message":"Le type de séance n\'a pas pu être supprimé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
+    // Traitement d'une modification de texte
+    if ( isset($_REQUEST['champ']) && ( $_REQUEST['champ'] == 'texte' ) )  {
+      if ( !strlen($valeur = $mysqli->real_escape_string($_REQUEST['val'])) )
+        exit('{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas été modifié. Le texte doit être non vide."}');
+      if ( requete('cdt',"UPDATE cdt SET texte = '$valeur' WHERE id = $id",$mysqli) )
+        exit('{"etat":"ok","message":"Le texte de l\'élément du cahier de texte a été modifié."}');
+      exit('{"etat":"nok","message":"Le texte de l\'élément du cahier de texte n\'a pas été modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+    }
+
+    // Positionnement "montré" (apparaît sur la partie publique)
+    if ( isset($_REQUEST['montre']) )  {
+      if ( requete('cdt',"UPDATE cdt SET cache = 0 WHERE id = $id",$mysqli) )  {
+        $mysqli->query("UPDATE matieres SET cdt = IF((SELECT id FROM cdt WHERE matiere = ${r['matiere']} AND cache = 0 LIMIT 1),1,0) WHERE id = ${r['matiere']} AND cdt_protection < 32");
+        exit('{"etat":"ok","message":"L\'élément du cahier de texte apparaît désormais sur la partie publique."}');
       }
-      else
-        $message = '{"etat":"nok","message":"Identifiant non valide"}';
+      exit('{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas été diffusé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
     }
-    
-    // Nouveau type de séance
-    elseif ( isset($_REQUEST['titre']) && isset($_REQUEST['cle']) && isset($_REQUEST['deb_fin_pour']) && isset($_REQUEST['matiere']) && in_array($matiere = $_REQUEST['matiere'],explode(',',$_SESSION['matieres']))  )  {
-      $titre = ucfirst($mysqli->real_escape_string($_REQUEST['titre']));
-      $cle = str_replace(' ','_',trim($mysqli->real_escape_string($_REQUEST['cle'])));
-      $deb_fin_pour = ( in_array($_REQUEST['deb_fin_pour'],array(0,1,2,3,4,5)) ) ? $_REQUEST['deb_fin_pour'] : 1;
-      if ( !strlen($titre) || !strlen($cle) )
-        $message = '{"etat":"nok","message":"Le type de séance n\'a pas pu être ajouté. Texte et clé non vides nécessaires."}';
-      else  {
-        // Vérification que la clé n'existe pas déjà
-        $resultat = $mysqli->query("SELECT cle FROM `cdt-types` WHERE matiere = $matiere");
-        if ( $resultat->num_rows )  {
-          while ( $r = $resultat->fetch_assoc() )
-            if ( $r['cle'] == $cle )  {
-              $message = '{"etat":"nok","message":"Le type de séance n\'a pas pu être ajouté. Cette clé existe déjà et doit être unique."}';
-              break;
-            }
-          $resultat->free();
-        }
+
+    // Positionnement "caché" (n'apparaît pas sur la partie publique)
+    if ( isset($_REQUEST['cache']) )  {
+      if ( requete('cdt',"UPDATE cdt SET cache = 1 WHERE id = $id",$mysqli) )  {
+        $mysqli->query("UPDATE matieres SET cdt = IF((SELECT id FROM cdt WHERE matiere = ${r['matiere']} AND cache = 0 LIMIT 1),1,0) WHERE id = ${r['matiere']} AND cdt_protection < 32");
+        exit('{"etat":"ok","message":"L\'élément du cahier de texte n\'apparaît plus sur la partie publique mais est toujours disponible ici pour modification ou diffusion."}');
       }
-      if ( !strlen($message) )  {
-        if ( requete('cdt-types',"INSERT INTO `cdt-types` SET matiere = $matiere, titre = '$titre', cle = '$cle', deb_fin_pour = $deb_fin_pour,
-                                  ordre = (SELECT max(ct.ordre)+1 FROM `cdt-types` AS ct WHERE ct.matiere = $matiere)",$mysqli) )
-          $message = $_SESSION['message'] = '{"etat":"ok","message":"Type de séance ajouté"}';
-        else
-          $message = '{"etat":"nok","message":"Le type de séance n\'a pas pu être ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
+      exit('{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas été caché. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+    }
+
+    // Suppression
+    elseif ( isset($_REQUEST['supprime']) )  {
+      if ( requete('cdt',"DELETE FROM cdt WHERE id = $id",$mysqli) )  {
+        $mysqli->query("UPDATE `cdt-types` SET nb = nb-1 WHERE id = ${r['type']}");
+        $mysqli->query("UPDATE matieres SET cdt = IF((SELECT id FROM cdt WHERE matiere = ${r['matiere']} AND cache = 0 LIMIT 1),1,0) WHERE id = ${r['matiere']} AND cdt_protection < 32");
+        exit('{"etat":"ok","message":"L\'élément du cahier de texte a été supprimé."}');
+      }
+      exit('{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas été supprimé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+    }
+  }
+  
+  // Nouvel élément du cahier de texte
+  elseif ( isset($demigroupe) && isset($_REQUEST['matiere']) && in_array($matiere = intval($_REQUEST['matiere']), explode(',',$_SESSION['matieres'])) )  {
+    $cache = intval(isset($_REQUEST['cache']));
+    if ( !strlen($texte = $mysqli->real_escape_string($_REQUEST['texte'])) )
+      exit('{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas été ajouté. Le texte doit être non vide."}');
+    if ( is_null($h_debut) )
+      exit('{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas été ajouté. L\'horaire de début doit être non vide."}');
+    if ( is_null($h_fin) )
+      exit('{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas été ajouté. L\'horaire de fin doit être non vide."}');
+    if ( is_null($pour) )
+      exit('{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas été ajouté. La date d\'échéance doit être non vide."}');
+    // Écriture dans la base de données
+    if ( requete('cdt',"INSERT INTO cdt SET matiere = $matiere, semaine = $semaine, jour = '$jour', h_debut = '$h_debut', h_fin = '$h_fin',
+                            pour = '$pour', type = $tid, texte = '$texte', demigroupe = $demigroupe, cache = $cache", $mysqli) )  {
+      // Mises à jour : nombre d'éléments du type, ordre des éléments (d'abord
+      // ce qui n'a pas de fin, ensuite les séances "normales", ensuite les
+      // "pour le"), champ cdt de la matière
+      $mysqli->query("UPDATE `cdt-types` SET nb = nb+1 WHERE id = $tid");
+      $mysqli->query('ALTER TABLE cdt ORDER BY jour,matiere,pour,h_debut,h_fin,type');
+      $mysqli->query("UPDATE matieres SET cdt = IF((SELECT id FROM cdt WHERE matiere = $matiere AND cache = 0 LIMIT 1),1,0) WHERE id = $matiere AND cdt_protection < 32");
+      exit($_SESSION['message'] = '{"etat":"ok","message":"L\'élément du cahier de texte a été ajouté."}');
+    }
+    exit('{"etat":"nok","message":"L\'élément du cahier de texte n\'a pas été ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+}
+
+///////////////////////////////////////////////////////////
+// Modification des types de séance des cahiers de texte //
+///////////////////////////////////////////////////////////
+elseif ( ( $action == 'cdt-types' ) && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
+  
+  // Vérification que l'identifiant est valide
+  $resultat = $mysqli->query("SELECT ordre, titre, nb, cle, matiere, (SELECT COUNT(*) FROM `cdt-types` WHERE matiere = c.matiere) AS max FROM `cdt-types` AS c WHERE id = $id AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant du type d\'élément de cahier de texte non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+
+  // Traitement d'une modification
+  if ( isset($_REQUEST['titre']) && isset($_REQUEST['cle']) && isset($_REQUEST['deb_fin_pour']) && in_array($deb_fin_pour = intval($_REQUEST['deb_fin_pour']),array(0,1,2,3,4,5)) )  {
+    $titre = mb_convert_case(strip_tags(trim($mysqli->real_escape_string($_REQUEST['titre']))),MB_CASE_TITLE);
+    $cle = str_replace(' ','_',trim($mysqli->real_escape_string($_REQUEST['cle'])));
+    if ( !strlen($titre) || !strlen($cle) )
+      exit("{\"etat\":\"nok\",\"message\":\"Le type de séance <em>${r['titre']}</em> n'a pas été modifié. Le texte et la clé doivent être non vides.\"}");
+    if ( $cle != $r['cle'] )  {
+      // Vérification que la clé n'existe pas déjà
+      $resultat = $mysqli->query("SELECT cle FROM `cdt-types` WHERE id != $id AND matiere = ${r['matiere']}");
+      if ( $resultat->num_rows )  {
+        while ( $r = $resultat->fetch_assoc() )
+          if ( $r['cle'] == $cle )
+            exit("{\"etat\":\"nok\",\"message\":\"Le type de séance <em>${r['titre']}</em> n'a pas été modifié. Cette clé existe déjà et doit être unique.\"}");
+        $resultat->free();
       }
     }
-    break;
+    if ( requete('cdt-types',"UPDATE `cdt-types` SET titre = '$titre', cle = '$cle', deb_fin_pour = $deb_fin_pour WHERE id = $id",$mysqli) )
+      exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Le type de séance <em>${r['titre']}</em> a été modifié.\"}");
+    exit("{\"etat\":\"nok\",\"message\":\"Le type de séance <em>${r['titre']}</em> n'a pas été modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+  
+  // Déplacement vers le haut
+  if ( isset($_REQUEST['monte']) && ( $r['ordre'] > 1 ) )
+    exit( ( requete('cdt-types',"UPDATE `cdt-types` SET ordre = (2*${r['ordre']}-1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}-1) ) AND matiere = ${r['matiere']}",$mysqli)
+            && $mysqli->query('ALTER TABLE `cdt-types` ORDER BY matiere,ordre') )
+        ? "{\"etat\":\"ok\",\"message\":\"Le déplacement du type de séance <em>${r['titre']}</em> a été réalisé.\"}"
+        : "{\"etat\":\"nok\",\"message\":\"Le type de séance <em>${r['titre']}</em> n'a pas été déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+
+  // Déplacement vers le bas
+  if ( isset($_REQUEST['descend']) && ( $r['ordre'] < $r['max'] ) )
+    exit( ( requete('cdt-types',"UPDATE `cdt-types` SET ordre = (2*${r['ordre']}+1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}+1) ) AND matiere = ${r['matiere']}",$mysqli)
+            && $mysqli->query('ALTER TABLE `cdt-types` ORDER BY matiere,ordre') )
+        ? "{\"etat\":\"ok\",\"message\":\"Le déplacement du type de séance <em>${r['titre']}</em> a été réalisé.\"}"
+        : "{\"etat\":\"nok\",\"message\":\"Le type de séance <em>${r['titre']}</em> n'a pas été déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+
+  // Suppression
+  if ( isset($_REQUEST['supprime']) )  {
+    if ( $r['max'] == 1 )
+      exit("{\"etat\":\"ok\",\"message\":\"Le type de séance <em>${r['nom']}</em> n'a pas été supprimé. Il faut obligatoirement en garder au moins un.\"}");
+    exit( ( requete('cdt',"DELETE FROM cdt WHERE type = $id",$mysqli) && requete('cdt-types',"DELETE FROM `cdt-types` WHERE id = $id",$mysqli)
+            && requete('cdt-types',"UPDATE `cdt-types` SET ordre = (ordre-1) WHERE ordre > ${r['ordre']} AND matiere = ${r['matiere']}",$mysqli) )
+        ? "{\"etat\":\"ok\",\"message\":\"Le type de séance <em>${r['titre']}</em> a été supprimé.\"}"
+        : "{\"etat\":\"nok\",\"message\":\"Le type de séance <em>${r['titre']}</em> n'a pas été supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+}
+
+////////////////////////////////////////////////////
+// Ajout d'un type de séance des cahiers de texte //
+////////////////////////////////////////////////////
+elseif ( ( $action == 'ajout-cdt-types' ) && isset($_REQUEST['titre']) && isset($_REQUEST['cle']) && isset($_REQUEST['deb_fin_pour']) && in_array($deb_fin_pour = intval($_REQUEST['deb_fin_pour']),array(0,1,2,3,4,5)) && isset($_REQUEST['matiere']) && in_array($matiere = intval($_REQUEST['matiere']),explode(',',$_SESSION['matieres'])) )  {
+  $titre = mb_convert_case(strip_tags(trim($mysqli->real_escape_string($_REQUEST['titre']))),MB_CASE_TITLE);
+  $cle = str_replace(' ','_',trim($mysqli->real_escape_string($_REQUEST['cle'])));
+  if ( !strlen($titre) || !strlen($cle) )
+    exit('{"etat":"nok","message":"Le type de séance n\'a pas été ajouté. Le texte et la clé doivent être non vides."}');
+  // Vérification que la clé n'existe pas déjà
+  $resultat = $mysqli->query("SELECT cle FROM `cdt-types` WHERE matiere = $matiere");
+  if ( $resultat->num_rows )  {
+    while ( $r = $resultat->fetch_assoc() )
+      if ( $r['cle'] == $cle )
+        exit('{"etat":"nok","message":"Le type de séance n\'a pas été ajouté. Cette clé existe déjà et doit être unique."}');
+    $resultat->free();
+  }
+  // Écriture
+  if ( requete('cdt-types',"INSERT INTO `cdt-types` SET matiere = $matiere, titre = '$titre', cle = '$cle', deb_fin_pour = $deb_fin_pour,
+                              ordre = (SELECT max(ct.ordre)+1 FROM `cdt-types` AS ct WHERE ct.matiere = $matiere)",$mysqli) )
+    exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Le type de séance <em>$titre</em> a été ajouté.\"}");
+  exit("{\"etat\":\"nok\",\"message\":\"Le type de séance <em>$titre</em> n'a pas été ajouté. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+}
     
 //////////////////////////////////////////////////////
 // Modification des raccourcis des cahiers de texte //
 //////////////////////////////////////////////////////
-  case 'cdt-seances':
+elseif ( ( $action == 'cdt-raccourcis' ) && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
   
-    // Vérification que l'identifiant est valide
-    if ( isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
-      $resultat = $mysqli->query("SELECT ordre, nom, matiere, (SELECT COUNT(*) FROM `cdt-seances` WHERE matiere = c.matiere) AS max FROM `cdt-seances` AS c WHERE id = $id AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
-      if ( $resultat->num_rows )  {
-        $r = $resultat->fetch_assoc();
-        $resultat->free();
+  // Vérification que l'identifiant est valide
+  $resultat = $mysqli->query("SELECT ordre, nom, matiere, (SELECT COUNT(*) FROM `cdt-seances` WHERE matiere = c.matiere) AS max FROM `cdt-seances` AS c WHERE id = $id AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant du raccourci de cahier de texte non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
 
-        // Traitement d'une modification
-        if ( isset($_REQUEST['nom']) && isset($_REQUEST['type']) && ctype_digit($type = $_REQUEST['type']) )  {
-          if ( !strlen($nom = $mysqli->real_escape_string($_REQUEST['nom'])) )
-            $message = '{"etat":"nok","message":"Le raccourci de séance n\'a pas pu être modifié. Nom non vide nécessaire."}';
-          else  {
-            $jour = ( in_array($_REQUEST['jour'],array(1,2,3,4,5,6,7)) ) ? $_REQUEST['jour'] : 1;
-            // Validation du type de séance
-            $resultat = $mysqli->query("SELECT id, deb_fin_pour FROM `cdt-types` WHERE id = $type AND matiere = ${r['matiere']}");
-            if ( !$resultat->num_rows )
-              $message = '{"etat":"nok","message":"Type de séance non valide."}';
-            else  {
-              $r = $resultat->fetch_assoc();
-              $resultat->free();
-              // Validation des horaires
-              $h_debut = $h_fin = '0:00';
-              $demigroupe = 0;
-              switch ( $r['deb_fin_pour'] )  {
-                case 1: $h_fin = preg_filter('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['h_fin']);
-                case 0: $h_debut = preg_filter('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['h_debut']);
-                case 2: 
-                case 3: $demigroupe = ( in_array($_REQUEST['demigroupe'],array(0,1)) ) ? $_REQUEST['demigroupe'] : 0;
-                // Cas 4 et 5 : h_debut, h_fin et pour restent nuls (cf l'aide)
-              }
-              // Écriture
-              if ( requete('cdt-seances',"UPDATE `cdt-seances` SET nom = '$nom', jour = $jour, h_debut = '$h_debut',
-                                          h_fin = '$h_fin', type = $type, demigroupe = $demigroupe WHERE id = $id",$mysqli) )
-                $message = $_SESSION['message'] = '{"etat":"ok","message":"Raccourci de séance modifié"}';
-              else
-                $message = '{"etat":"nok","message":"Le raccourci de séance n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-            }
-          }
-        }
-        
-        // Déplacement vers le haut
-        elseif ( isset($_REQUEST['monte']) && ( $r['ordre'] > 1 ) )
-          $message = ( requete('cdt-seances',"UPDATE `cdt-seances` SET ordre = (2*${r['ordre']}-1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}-1) ) AND matiere = ${r['matiere']}",$mysqli)
-                    && $mysqli->query('ALTER TABLE `cdt-seances` ORDER BY matiere,ordre')
-          ) ? '{"etat":"ok","message":"Déplacement réalisé"}' : '{"etat":"nok","message":"Le raccourci de séance n\'a pas pu être déplacé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-
-        // Déplacement vers le bas
-        elseif ( isset($_REQUEST['descend']) && ( $r['ordre'] < $r['max'] ) )
-          $message = ( requete('cdt-seances',"UPDATE `cdt-seances` SET ordre = (2*${r['ordre']}+1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}+1) ) AND matiere = ${r['matiere']}",$mysqli)
-                    && $mysqli->query('ALTER TABLE `cdt-seances` ORDER BY matiere,ordre')
-          ) ? '{"etat":"ok","message":"Déplacement réalisé"}' : '{"etat":"nok","message":"Le raccourci de séance n\'a pas pu être déplacé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-
-        // Suppression
-        elseif ( isset($_REQUEST['supprime']) )  {
-          $message = ( requete('cdt-seances',"DELETE FROM `cdt-seances` WHERE id = $id",$mysqli)
-                    && requete('cdt-seances',"UPDATE `cdt-seances` SET ordre = (ordre-1) WHERE ordre > ${r['ordre']} AND matiere = ${r['matiere']}",$mysqli)
-          ) ? '{"etat":"ok","message":"Suppression réalisée"}' : '{"etat":"nok","message":"Le raccourci de séance n\'a pas pu être supprimé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
+  // Traitement d'une modification
+  if ( isset($_REQUEST['nom']) && isset($_REQUEST['type']) && ctype_digit($type = $_REQUEST['type']) && isset($_REQUEST['jour']) && in_array( $jour = intval($_REQUEST['jour']),array(1,2,3,4,5,6,7)) )  {
+    if ( !strlen($nom = $mysqli->real_escape_string(trim($_REQUEST['nom']))) )
+      exit("{\"etat\":\"nok\",\"message\":\"Le raccourci de séance <em>${r['nom']}</em> n'a pas été modifié. Le nom doit être non vide.\"}");
+    // Validation du type de séance
+    $resultat = $mysqli->query("SELECT id, deb_fin_pour FROM `cdt-types` WHERE id = $type AND matiere = ${r['matiere']}");
+    if ( !$resultat->num_rows )
+      exit("{\"etat\":\"nok\",\"message\":\"Le raccourci de séance <em>${r['nom']}</em> n'a pas été modifié. Le type de séance n'est pas valide.\"}");
+    $r = $resultat->fetch_assoc();
+    $resultat->free();
+    // Validation des horaires
+    $h_debut = $h_fin = '0:00';
+    $demigroupe = 0;
+    switch ( $r['deb_fin_pour'] )  {
+      case 1: $h_fin = preg_filter('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['h_fin']);
+      case 0: $h_debut = preg_filter('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['h_debut']);
+      case 2: 
+      case 3: $demigroupe = intval($_REQUEST['demigroupe'] == 1);
+      // Cas 4 et 5 : h_debut, h_fin et pour restent nuls (cf l'aide)
+    }
+    // Écriture
+    if ( requete('cdt-seances',"UPDATE `cdt-seances` SET nom = '$nom', jour = $jour, h_debut = '$h_debut',
+                                h_fin = '$h_fin', type = $type, demigroupe = $demigroupe WHERE id = $id",$mysqli) )
+      exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Le raccourci de séance <em>${r['nom']}</em> a été modifié.\"}");
+    exit("{\"etat\":\"nok\",\"message\":\"Le raccourci de séance <em>${r['nom']}</em> n'a pas été modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+  
+  // Déplacement vers le haut
+  if ( isset($_REQUEST['monte']) && ( $r['ordre'] > 1 ) )
+    exit( ( requete('cdt-seances',"UPDATE `cdt-seances` SET ordre = (2*${r['ordre']}-1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}-1) ) AND matiere = ${r['matiere']}",$mysqli)
+            && $mysqli->query('ALTER TABLE `cdt-seances` ORDER BY matiere,ordre') )
+        ? "{\"etat\":\"ok\",\"message\":\"Le déplacement du raccourci de séance <em>${r['nom']}</em> a été réalisé.\"}"
+        : "{\"etat\":\"nok\",\"message\":\"Le raccourci de séance <em>${r['nom']}</em> n'a pas été déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+
+  // Déplacement vers le bas
+  if ( isset($_REQUEST['descend']) && ( $r['ordre'] < $r['max'] ) )
+    exit( ( requete('cdt-seances',"UPDATE `cdt-seances` SET ordre = (2*${r['ordre']}+1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}+1) ) AND matiere = ${r['matiere']}",$mysqli)
+            && $mysqli->query('ALTER TABLE `cdt-seances` ORDER BY matiere,ordre') )
+        ? "{\"etat\":\"ok\",\"message\":\"Le déplacement du raccourci de séance <em>${r['nom']}</em> a été réalisé.\"}"
+        : "{\"etat\":\"nok\",\"message\":\"Le raccourci de séance <em>${r['nom']}</em> n'a pas été déplacé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+
+  // Suppression
+  if ( isset($_REQUEST['supprime']) )
+    exit( ( requete('cdt-seances',"DELETE FROM `cdt-seances` WHERE id = $id",$mysqli)
+            && requete('cdt-seances',"UPDATE `cdt-seances` SET ordre = (ordre-1) WHERE ordre > ${r['ordre']} AND matiere = ${r['matiere']}",$mysqli) )
+        ? "{\"etat\":\"ok\",\"message\":\"Le raccourci de séance <em>${r['nom']}</em> a été supprimé.\"}"
+        : "{\"etat\":\"nok\",\"message\":\"Le raccourci de séance <em>${r['nom']}</em> n'a pas été supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+}
+
+///////////////////////////////////////////////
+// Ajout d'un raccourci des cahiers de texte //
+///////////////////////////////////////////////
+elseif ( ( $action == 'ajout-cdt-raccourci' ) && isset($_REQUEST['nom']) && isset($_REQUEST['type']) && ctype_digit($type = $_REQUEST['type']) && isset($_REQUEST['jour']) && in_array( $jour = intval($_REQUEST['jour']),array(1,2,3,4,5,6,7)) && isset($_REQUEST['matiere']) && in_array($matiere = intval($_REQUEST['matiere']),explode(',',$_SESSION['matieres'])) )  {
+  if ( !strlen($nom = $mysqli->real_escape_string(trim($_REQUEST['nom']))) )
+    exit('{"etat":"nok","message":"Le raccourci de séance n\'a pas été ajouté. Le nom doit être non vide."}');
+  // Validation du type de séance
+  $resultat = $mysqli->query("SELECT id, deb_fin_pour FROM `cdt-types` WHERE id = $type AND matiere = $matiere");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Le raccourci de séance n\'a pas été ajouté. Le type de séance n\'est pas valide."}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+  // Validation des horaires
+  $h_debut = $h_fin = '0:00';
+  $demigroupe = 0;
+  switch ( $r['deb_fin_pour'] )  {
+    case 1: $h_fin = preg_filter('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['h_fin']);
+    case 0: $h_debut = preg_filter('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['h_debut']);
+    case 2: 
+    case 3: $demigroupe = intval($_REQUEST['demigroupe'] == 1);
+    // Cas 4 et 5 : h_debut, h_fin et pour restent nuls (cf l'aide)
+  }
+  // Écriture
+  if ( requete('cdt-seances',"INSERT INTO `cdt-seances` SET matiere = $matiere, nom = '$nom', jour = $jour,
+                              ordre = (SELECT IFNULL(max(cs.ordre)+1,1) FROM `cdt-seances` AS cs WHERE cs.matiere = $matiere),
+                              h_debut = '$h_debut', h_fin = '$h_fin', type = $type, demigroupe = $demigroupe",$mysqli) )
+    exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Le raccourci de séance <em>$nom</em> a été ajouté.\"}");
+  exit('{"etat":"nok","message":"Le raccourci de séance n\'a pas été ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+}
+
+////////////////////////////
+// Modification des pages //
+////////////////////////////
+elseif ( ( $action == 'pages' ) && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
+
+  // Vérification que l'identifiant est valide
+  $resultat = $mysqli->query("SELECT ordre, mat, nom, cle, (SELECT COUNT(*) FROM pages WHERE mat = p.mat) AS max FROM pages AS p WHERE id = $id AND FIND_IN_SET(mat,'${_SESSION['matieres']}')");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant de page non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+
+  // Traitement d'une modification globale (depuis index.php ou pages.php)
+  if ( isset($_REQUEST['titre']) && isset($_REQUEST['nom']) && isset($_REQUEST['cle']) && isset($_REQUEST['bandeau']) && isset($_REQUEST['protection']) )  {
+    $titre = strip_tags(trim($mysqli->real_escape_string($_REQUEST['titre'])));
+    $nom = strip_tags(trim($mysqli->real_escape_string($_REQUEST['nom'])));
+    $titre = mb_strtoupper(mb_substr($titre,0,1)).mb_substr($titre,1);
+    $nom = mb_strtoupper(mb_substr($nom,0,1)).mb_substr($nom,1);
+    $cle = str_replace(' ','_',strip_tags(trim($mysqli->real_escape_string($_REQUEST['cle']))));
+    if ( !strlen($_REQUEST['titre']) || !strlen($_REQUEST['nom']) || !strlen($_REQUEST['cle']) )
+      exit("{\"etat\":\"nok\",\"message\":\"La page <em>${r['nom']}</em> n'a pas été modifiée. Le titre, le nom et la clé doivent être non vides.\"}");
+    // Partie de requête si modification de matière
+    // N'existe qu'en provenance de pages.php (et non d'index.php)
+    $requete_matiere = ( isset($_REQUEST['matiere']) && in_array($matiere = intval($_REQUEST['matiere']),explode(',',$_SESSION['matieres'])) && ( $matiere != $r['mat'] ) && ( $id > 1 ) ) ? ", mat = $matiere" : '';
+    // Vérification que la clé n'existe pas déjà
+    $requete_cle = '';
+    if ( $cle != $r['cle'] )  {
+      $resultat = $mysqli->query('SELECT cle FROM pages WHERE mat = '.( strlen($requete_matiere) ? $matiere : $r['mat'] ));
+      if ( $resultat->num_rows )  {
+        while ( $s = $resultat->fetch_row() )
+          if ( $s[0] == $cle )
+            exit("{\"etat\":\"nok\",\"message\":\"La page <em>${r['nom']}</em> n'a pas été modifiée. La clé donnée existe déjà. Elle doit être différente de celles des autres pages.\"}"); 
+        $resultat->free();
+      $requete_cle = "cle = '$cle',";
       }
-      else
-        $message = '{"etat":"nok","message":"Identifiant non valide"}';
     }
-    
-    // Nouveau raccourci de séance
-    elseif ( isset($_REQUEST['nom']) && isset($_REQUEST['type']) && ctype_digit($type = $_REQUEST['type']) && isset($_REQUEST['matiere']) && in_array($matiere = $_REQUEST['matiere'],explode(',',$_SESSION['matieres'])) )  {
-      if ( !strlen($nom = $mysqli->real_escape_string($_REQUEST['nom'])) )
-        $message = '{"etat":"nok","message":"Le raccourci de séance n\'a pas pu être ajouté. Nom non vide nécessaire."}';
-      else  {
-        $jour = ( in_array($_REQUEST['jour'],array(1,2,3,4,5,6,7)) ) ? $_REQUEST['jour'] : 1;
-        // Validation du type de séance
-        $resultat = $mysqli->query("SELECT id, deb_fin_pour FROM `cdt-types` WHERE id = $type AND matiere = $matiere");
-        if ( !$resultat->num_rows )
-          $message = '{"etat":"nok","message":"Type de séance non valide."}';
-        else  {
-          $r = $resultat->fetch_assoc();
-          $resultat->free();
-          // Validation des horaires
-          $h_debut = $h_fin = '0:00';
-          $demigroupe = 0;
-          switch ( $r['deb_fin_pour'] )  {
-            case 1: $h_fin = preg_filter('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['h_fin']);
-            case 0: $h_debut = preg_filter('/(\d{1,2})h(\d{2})/','$1:$2',$_REQUEST['h_debut']);
-            case 2: 
-            case 3: $demigroupe = ( in_array($_REQUEST['demigroupe'],array(0,1)) ) ? $_REQUEST['demigroupe'] : 0;
-            // Cas 4 et 5 : h_debut, h_fin et pour restent nuls (cf l'aide)
-          }
-          // Écriture
-          if ( requete('cdt-seances',"INSERT INTO `cdt-seances` SET matiere = $matiere, nom = '$nom', jour = $jour,
-                                      ordre = (SELECT IFNULL(max(cs.ordre)+1,1) FROM `cdt-seances` AS cs WHERE cs.matiere = $matiere),
-                                      h_debut = '$h_debut', h_fin = '$h_fin', type = $type, demigroupe = $demigroupe",$mysqli) )
-            $message = $_SESSION['message'] = '{"etat":"ok","message":"Raccourci de séance ajouté"}';
-          else
-            $message = '{"etat":"nok","message":"Le raccourci de séance n\'a pas pu être ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
+    $bandeau = trim($mysqli->real_escape_string($_REQUEST['bandeau']));
+    // Génération de la valeur de protection
+    $val = array_filter($_REQUEST['protection'],function($id) { return ctype_digit($id); });
+    if ( !count($val) )
+      exit("{\"etat\":\"nok\",\"message\":\"La page <em>${r['nom']}</em> n'a pas été modifiée. La protection d'accès est incorrecte.\"}");
+    if ( ( $val[0] == 0 ) || ( $val[0] == 32 ) )
+      $protection = $val[0];
+    else  {
+      $protection = 32;
+      foreach (array_slice($val,1) as $v) 
+        $protection = $protection-2**($v-1);
+    }
+    // Écriture
+    if ( requete('pages',"UPDATE pages SET titre = '$titre', nom = '$nom', $requete_cle bandeau = '$bandeau', protection = $protection $requete_matiere WHERE id = $id",$mysqli) )  {
+      if ( strlen($requete_matiere) )  {
+        requete('pages',"UPDATE pages SET ordre = (SELECT COUNT(*) FROM (SELECT id FROM pages AS p WHERE p.mat = $matiere) AS p1) WHERE id = $id",$mysqli);
+        requete('pages',"UPDATE pages SET ordre = (ordre-1) WHERE mat = ${r['mat']} AND ordre > ${r['ordre']}",$mysqli);
+        $mysqli->query('ALTER TABLE pages ORDER BY mat,ordre');
+      }
+      if ( isset($_REQUEST['propagation']) )  {
+        requete('infos',"UPDATE infos SET protection = $protection WHERE page = $id",$mysqli);
+        // Mise à jour des informations récentes
+        requete('recents',"UPDATE recents SET protection = $protection WHERE id IN (SELECT 1000+infos.id FROM infos WHERE page = $id)",$mysqli);
+        rss($mysqli, ( $r['mat'] == 0 ) ? array(0) : array(0,$r['mat']) );
       }
+      exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"La page <em>${r['nom']}</em> a été modifiée.\"}");
     }
-    break;
+    else
+      exit("{\"etat\":\"nok\",\"message\":\"La page <em>${r['nom']}</em> n'a pas été modifiée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+
+  // Déplacement vers le haut
+  if ( isset($_REQUEST['monte']) && ( $r['ordre'] > 1+!$r['mat'] ) )
+    exit( ( requete('pages',"UPDATE pages SET ordre = (2*${r['ordre']}-1-ordre) WHERE mat = ${r['mat']} AND ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}-1) )",$mysqli) 
+            && $mysqli->query('ALTER TABLE pages ORDER BY mat,ordre') )
+        ? "{\"etat\":\"ok\",\"message\":\"Le déplacement de la page <em>${r['nom']}</em> a été réalisé.\"}"
+        : "{\"etat\":\"nok\",\"message\":\"La page <em>${r['nom']}</em> n'a pas été déplacée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+
+  // Déplacement vers le bas
+  if ( isset($_REQUEST['descend']) && ( $r['ordre'] < $r['max'] ) && ( $id > 1 ) )
+    exit( ( requete('pages',"UPDATE pages SET ordre = (2*${r['ordre']}+1-ordre) WHERE mat = ${r['mat']} AND ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}+1) )",$mysqli) 
+            && $mysqli->query('ALTER TABLE pages ORDER BY mat,ordre') )
+        ? "{\"etat\":\"ok\",\"message\":\"Le déplacement de la page <em>${r['nom']}</em> a été réalisé.\"}"
+        : "{\"etat\":\"nok\",\"message\":\"La page <em>${r['nom']}</em> n'a pas été déplacée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+
+  // Suppression
+  if ( isset($_REQUEST['supprime']) )  {
+    if ( $id == 1 )
+      exit("{\"etat\":\"nok\",\"message\":\"La page <em>${r['nom']}</em> ne peut pas être supprimée.\"}");
+    if ( requete('pages',"DELETE FROM pages WHERE id = $id",$mysqli)
+      && requete('infos',"DELETE FROM infos WHERE page = $id",$mysqli)
+      && requete('pages',"UPDATE pages SET ordre = (ordre-1) WHERE mat = ${r['mat']} AND ordre > ${r['ordre']}",$mysqli) 
+      && $mysqli->query('ALTER TABLE pages ORDER BY mat,ordre') )
+      exit("{\"etat\":\"ok\",\"message\":\"La page <em>${r['nom']}</em> a été supprimée. Les informations contenues ont été supprimées.'\"}");
+    exit("{\"etat\":\"nok\",\"message\":\"La page <em>${r['nom']}</em> n'a pas été supprimée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+
+  // Suppression des informations
+  if ( isset($_REQUEST['supprime_infos']) )
+    exit( ( requete('infos',"DELETE FROM infos WHERE pages = $id",$mysqli) )
+      ? "{\"etat\":\"ok\",\"message\":\"Les informations de la page <em>${r['nom']}</em> ont été supprimées.\"}"
+      : "{\"etat\":\"nok\",\"message\":\"Les informations de la page <em>${r['nom']}</em> n'ont pas été supprimées. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+}
+   
+//////////////////////
+// Ajout d'une page //
+//////////////////////
+elseif ( ( $action == 'ajout-page' ) && isset($_REQUEST['titre']) && isset($_REQUEST['nom']) && isset($_REQUEST['cle']) && isset($_REQUEST['bandeau']) && isset($_REQUEST['matiere']) && isset($_REQUEST['protection']) && in_array($matiere = intval($_REQUEST['matiere']),explode(',',$_SESSION['matieres'])) )  {
+  $titre = strip_tags(trim($mysqli->real_escape_string($_REQUEST['titre'])));
+  $nom = strip_tags(trim($mysqli->real_escape_string($_REQUEST['nom'])));
+  $titre = mb_strtoupper(mb_substr($titre,0,1)).mb_substr($titre,1);
+  $nom = mb_strtoupper(mb_substr($nom,0,1)).mb_substr($nom,1);
+  $cle = str_replace(' ','_',strip_tags(trim($mysqli->real_escape_string($_REQUEST['cle']))));
+  if ( !strlen($_REQUEST['titre']) || !strlen($_REQUEST['nom']) || !strlen($_REQUEST['cle']) )
+    exit('{"etat":"nok","message":"La page n\'a pas été ajoutée. Le titre, le nom et la clé doivent être non vides."}');
+  // Vérification que la clé n'existe pas déjà
+  $resultat = $mysqli->query("SELECT cle FROM pages WHERE mat = $matiere");
+  if ( $resultat->num_rows )  {
+    while ( $r = $resultat->fetch_row() )
+      if ( $r[0] == $cle )
+        exit("{\"etat\":\"nok\",\"message\":\"La page <em>$nom</em> n'a pas été ajoutée. La clé donnée existe déjà. Elle doit être différente de celles des autres pages.\"}");
+    $resultat->free();
+  }
+  $bandeau = trim($mysqli->real_escape_string($_REQUEST['bandeau']));
+  // Génération de la valeur de protection
+  $val = array_filter($_REQUEST['protection'],function($id) { return ctype_digit($id); });
+  if ( !count($val) )
+    exit("{\"etat\":\"nok\",\"message\":\"La page <em>$nom</em> n'a pas été ajoutée. La protection d'accès est incorrecte.\"}");
+  if ( ( $val[0] == 0 ) || ( $val[0] == 32 ) )
+    $protection = $val[0];
+  else  {
+    $protection = 32;
+    foreach (array_slice($val,1) as $v) 
+      $protection = $protection-2**($v-1);
+  }
+  // Écriture
+  if ( requete('pages',"INSERT INTO pages SET titre = '$titre', nom = '$nom', cle = '$cle', mat = $matiere, bandeau = '$bandeau', protection = $protection, ordre = (SELECT IFNULL(MAX(ordre)+1,1) FROM pages AS p WHERE p.mat = $matiere)",$mysqli) )
+    exit("{\"etat\":\"ok\",\"message\":\"La page <em>$nom</em> a été ajoutée.\"}");
+  exit("{\"etat\":\"nok\",\"message\":\"La page <em>$nom</em> n'a pas été ajoutée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+}
 
 ///////////////////////////////
 // Modification des matières //
 ///////////////////////////////
-  case 'matieres':
+elseif ( ( $action == 'matieres' ) && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
+
+  // Déplacements (possibles pour toute matière, contrairement au reste)
+  if ( isset($_REQUEST['monte']) || isset($_REQUEST['descend']) )  {
+    $resultat = $mysqli->query("SELECT ordre, nom, (SELECT COUNT(*) FROM matieres) AS max FROM matieres WHERE id = $id");
+    if ( !$resultat->num_rows )
+      exit('{"etat":"nok","message":"Identifiant de matière non valide"}');
+    $r = $resultat->fetch_assoc();
+    $resultat->free();
+    
+    // Déplacement vers le haut
+    if ( isset($_REQUEST['monte']) && ( $r['ordre'] > 1 ) )
+      exit( ( requete('matieres',"UPDATE matieres SET ordre = (2*${r['ordre']}-1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}-1) )",$mysqli) && $mysqli->query('ALTER TABLE matieres ORDER BY ordre') )
+        ? "{\"etat\":\"ok\",\"message\":\"Le déplacement de la matière <em>${r['nom']}</em> a été réalisé.\"}"
+        : "{\"etat\":\"nok\",\"message\":\"La matière <em>${r['nom']}</em> n'a pas été déplacée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+
+    // Déplacement vers le bas
+    if ( isset($_REQUEST['descend']) && ( $r['ordre'] < $r['max'] ) )
+      exit( ( requete('matieres',"UPDATE matieres SET ordre = (2*${r['ordre']}+1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}+1) )",$mysqli) && $mysqli->query('ALTER TABLE matieres ORDER BY ordre') )
+        ? "{\"etat\":\"ok\",\"message\":\"Le déplacement de la matière <em>${r['nom']}</em> a été réalisé.\"}"
+        : "{\"etat\":\"nok\",\"message\":\"La matière <em>${r['nom']}</em> n'a pas été déplacée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+
+  // Vérification que l'identifiant est valide
+  elseif ( !in_array($id,explode(',',$_SESSION['matieres'])) )
+    exit('{"etat":"nok","message":"Identifiant de matière non valide"}');
+  
+  $resultat = $mysqli->query("SELECT ordre, cle, nom, colles_protection, cdt_protection, docs_protection, (SELECT COUNT(*) FROM matieres) AS max FROM matieres WHERE id = $id");
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
 
-    // Déplacements (possibles pour toute matière, contrairement au reste)
-    if ( isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) && ( isset($_REQUEST['monte']) || isset($_REQUEST['descend']) ) )  {
-      $resultat = $mysqli->query("SELECT ordre, (SELECT COUNT(*) FROM matieres) AS max FROM matieres WHERE id = $id");
+  // Traitement d'une modification
+  if ( isset($_REQUEST['nom']) && isset($_REQUEST['cle']) && isset($_REQUEST['colles_protection']) && isset($_REQUEST['cdt_protection']) && isset($_REQUEST['docs_protection']) && isset($_REQUEST['notes']) && isset($_REQUEST['dureecolle']) )  {
+    $nom = strip_tags(trim($mysqli->real_escape_string($_REQUEST['nom'])));
+    $nom = mb_strtoupper(mb_substr($nom,0,1)).mb_substr($nom,1);
+    $cle = str_replace(' ','_',strip_tags(trim($mysqli->real_escape_string($_REQUEST['cle']))));
+    if ( !strlen($nom) || !strlen($cle) )
+      exit("{\"etat\":\"nok\",\"message\":\"La matière <em>${r['nom']}</em> n'a pas été modifiée. Le nom et la clé doivent être non vides.\"}");
+    // Vérification que la clé n'existe pas déjà
+    if ( $cle != $r['cle'] )  {
+      $resultat = $mysqli->query('SELECT cle FROM matieres');
       if ( $resultat->num_rows )  {
-        $r = $resultat->fetch_assoc();
+        while ( $s = $resultat->fetch_row() )
+          if ( $s[0] == $cle )
+            exit("{\"etat\":\"nok\",\"message\":\"La matière <em>${r['nom']}</em> n'a pas été modifiée. La clé donnée existe déjà. Elle doit être différente de celles des autres matières.\"}");
         $resultat->free();
-        
-        // Déplacement vers le haut
-        if ( isset($_REQUEST['monte']) && ( $r['ordre'] > 1 ) )
-          $message = ( requete('matieres',"UPDATE matieres SET ordre = (2*${r['ordre']}-1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}-1) )",$mysqli)
-                    && $mysqli->query('ALTER TABLE matieres ORDER BY ordre')
-          ) ? '{"etat":"ok","message":"Déplacement réalisé"}' : '{"etat":"nok","message":"La matière n\'a pas pu être déplacée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-
-        // Déplacement vers le bas
-        elseif ( isset($_REQUEST['descend']) && ( $r['ordre'] < $r['max'] ) )
-          $message = ( requete('matieres',"UPDATE matieres SET ordre = (2*${r['ordre']}+1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}+1) )",$mysqli)
-                    && $mysqli->query('ALTER TABLE matieres ORDER BY ordre')
-          ) ? '{"etat":"ok","message":"Déplacement réalisé"}' : '{"etat":"nok","message":"La matière n\'a pas pu être déplacée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
       }
-      else
-        $message = '{"etat":"nok","message":"Identifiant non valide"}';
     }
+    // Génération des valeurs de protection
+    $protection = array();
+    foreach ( array('colles','cdt','docs') as $fonction )  {
+      $val = array_filter($_REQUEST[$fonction.'_protection'],function($id) { return ctype_digit($id); });
+      if ( !count($val) )
+        exit("{\"etat\":\"nok\",\"message\":\"La matière <em>${r['nom']}</em> n'a pas été modifiée. Une des protections d'accès est incorrecte.\"}");
+      if ( ( $val[0] == 0 ) || ( $val[0] == 32 ) )
+        $protection[$fonction] = $val[0];
+      else  {
+        $p = 32;
+        foreach (array_slice($val,1) as $v) 
+          $p = $p-2**($v-1);
+        $protection[$fonction] = $p;
+      }
+    }
+    // Affichage des notes : 2-> désactivée ; 1-> possible (0 ou 1 dans la base)
+    $notes = ( $_REQUEST['notes'] == 2 ) ? '2' : "IF( notes=2, IF((SELECT id FROM notes WHERE matiere = $id LIMIT 1),1,0), notes)";
+    $dureecolle = intval($_REQUEST['dureecolle']) ?: 20;
+    // Écriture
+    if ( requete('matieres',"UPDATE matieres SET nom = '$nom', cle = '$cle', notes = $notes, colles_protection = ${protection['colles']}, cdt_protection = ${protection['cdt']}, docs_protection = ${protection['docs']}, dureecolle = $dureecolle WHERE id = $id",$mysqli) )  {
+      if ( $nom != $r['nom'] )
+        requete('reps',"UPDATE reps SET nom = '$nom' WHERE matiere = $id AND parent = 0",$mysqli);
+      if ( $protection['docs'] != $r['docs_protection'] )
+        requete('reps',"UPDATE reps SET protection = ${protection['docs']} WHERE matiere = $id AND parent = 0",$mysqli);
+      exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"La matière <em>${r['nom']}</em> a été modifiée.\"}");
+    }
+    else
+      exit("{\"etat\":\"nok\",\"message\":\"La matière <em>${r['nom']}</em> n'a pas été modifiée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
   
-    // Vérification que l'identifiant est valide
-    elseif ( isset($_REQUEST['id']) && in_array($id = $_REQUEST['id'],explode(',',$_SESSION['matieres'])) )  {
-      $resultat = $mysqli->query("SELECT ordre, cle, nom, colles_protection, cdt_protection, (SELECT COUNT(*) FROM matieres) AS max FROM matieres WHERE id = $id");
+  // Suppression
+  if ( isset($_REQUEST['supprime']) )  {
+    if ( $r['max'] == 1 )
+      exit("{\"etat\":\"nok\",\"message\":\"La matière <em>${r['nom']}</em> n'a pas été supprimée. Il faut obligatoirement en garder au moins une.\"}");
+    if ( requete('matieres',"DELETE FROM matieres WHERE id = $id",$mysqli) 
+      && requete('matieres',"UPDATE matieres SET ordre = (ordre-1) WHERE ordre > ${r['ordre']}",$mysqli)
+      && requete('colles',"DELETE FROM colles WHERE matiere = $id",$mysqli)
+      && requete('cdt',"DELETE FROM cdt WHERE matiere = $id",$mysqli)
+      && requete('cdt-types',"DELETE FROM `cdt-types` WHERE matiere = $id",$mysqli)
+      && requete('cdt-seances',"DELETE FROM `cdt-seances` WHERE matiere = $id",$mysqli)
+      && requete('notes',"DELETE FROM notes WHERE matiere = $id",$mysqli)
+      && requete('reps',"UPDATE reps SET matiere = 0 WHERE matiere = $id",$mysqli)
+      && requete('docs',"UPDATE docs SET matiere = 0 WHERE matiere = $id",$mysqli)
+      && requete('pages',"UPDATE pages SET mat = 0 WHERE mat = $id",$mysqli)
+      && requete('agenda',"UPDATE agenda SET matiere = 0 WHERE matiere = $id",$mysqli)
+      && requete('utilisateurs',"UPDATE utilisateurs SET matieres = TRIM(TRAILING ',' FROM REPLACE(CONCAT(matieres,','),',$id,',',')) ",$mysqli) )
+      exit("{\"etat\":\"ok\",\"message\":\"La matière <em>${r['nom']}</em> a été supprimée. Les répertoires, documents, pages d'informations et éléments d'agenda associés à la matières n'ont pas été supprimés mais déplacés dans le contexte «&nbsp;général&nbsp;»'\"}");
+    exit("{\"etat\":\"nok\",\"message\":\"La matière <em>${r['nom']}</em> n'a pas été supprimée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+
+  // Suppression des programmes de colles d'une matière
+  if ( isset($_REQUEST['supprime_colles']) )
+    exit( ( requete('colles',"DELETE FROM colles WHERE matiere = $id",$mysqli) && requete('matieres',"UPDATE matieres SET colles = 0 WHERE id = $id",$mysqli) )
+      ? "{\"etat\":\"ok\",\"message\":\"Les programmes de colle de la matière <em>${r['nom']}</em> ont été supprimés.\"}"
+      : "{\"etat\":\"nok\",\"message\":\"Les programmes de colle de la matière <em>${r['nom']}</em> n'ont pas été supprimés. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  
+  // Suppression du cahier de texte d'une matière
+  if ( isset($_REQUEST['supprime_cdt']) )
+    exit( ( requete('cdt',"DELETE FROM cdt WHERE matiere = $id",$mysqli) && requete('matieres',"UPDATE matieres SET cdt = 0 WHERE id = $id",$mysqli) )
+      ? "{\"etat\":\"ok\",\"message\":\"Le cahier de texte de la matière <em>${r['nom']}</em> a été supprimé.\"}"
+      : "{\"etat\":\"nok\",\"message\":\"Le cahier de texte de la matière <em>${r['nom']}</em> n'a pas été supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+
+  // Suppression des notes d'une matière
+  if ( isset($_REQUEST['supprime_notes']) )
+    exit( ( requete('notes',"DELETE FROM notes WHERE matiere = $id",$mysqli) && requete('matieres',"UPDATE matieres SET notes = 0 WHERE id = $id",$mysqli) )
+      ? "{\"etat\":\"ok\",\"message\":\"Les notes de la matière <em>${r['nom']}</em> ont été supprimées.\"}"
+      : "{\"etat\":\"nok\",\"message\":\"Les notes de la matière <em>${r['nom']}</em> n'ont pas été supprimées. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+
+  // Suppression des documents d'une matière
+  if ( isset($_REQUEST['supprime_docs']) )  {
+    if ( requete('reps',"DELETE FROM reps WHERE matiere = $id AND parent > 0",$mysqli) )  {
+      // Suppression physique
+      $resultat = $mysqli->query("SELECT lien FROM docs WHERE matiere = $id");
       if ( $resultat->num_rows )  {
-        $r = $resultat->fetch_assoc();
+        while ( $r = $resultat->fetch_row() )
+          exec("rm -rf documents/${r[0]}");
         $resultat->free();
-
-        // Traitement d'une modification
-        if ( isset($_REQUEST['nom']) && isset($_REQUEST['cle']) )  {
-          $nom = ucfirst($mysqli->real_escape_string($_REQUEST['nom']));
-          $cle = str_replace(' ','_',trim($mysqli->real_escape_string($_REQUEST['cle'])));
-          $colles_protection = ( in_array($_REQUEST['colles_protection'],array(0,1,2,3,4,5)) ) ? $_REQUEST['colles_protection'] : 0;
-          $cdt_protection = ( in_array($_REQUEST['cdt_protection'],array(0,1,2,3,4,5)) ) ? $_REQUEST['cdt_protection'] : 0;
-          if ( !strlen($nom) || !strlen($cle) )
-            $message = '{"etat":"nok","message":"La matière n\'a pas pu être modifiée. Nom et clé non vides nécessaires."}';
-          elseif ( $cle != $r['cle'] )  {
-            // Vérification que la clé n'existe pas déjà
-            $resultat = $mysqli->query("SELECT cle FROM matieres WHERE id != $id");
-            if ( $resultat->num_rows )  {
-              while ( $s = $resultat->fetch_assoc() )
-                if ( $s['cle'] == $cle )  {
-                  $message = '{"etat":"nok","message":"La matière n\'a pas pu être modifiée. Cette clé existe déjà et doit être unique."}';
-                  break;
-                }
-              $resultat->free();
-            }
-          }
-          if ( !strlen($message) )  {
-            if ( requete('matieres',"UPDATE matieres SET nom = '$nom', cle = '$cle', colles_protection = $colles_protection, cdt_protection = $cdt_protection WHERE id = $id",$mysqli) )  {
-              $message = $_SESSION['message'] = '{"etat":"ok","message":"Matière modifiée"}';
-              if ( $nom != $r['nom'] )
-                requete('reps',"UPDATE reps SET nom = '$nom' WHERE matiere = $id AND parent = 0",$mysqli);
-            }
-            else
-              $message = '{"etat":"nok","message":"La matière n\'a pas pu être modifiée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-          }
-        }
-        
-        // Suppression
-        elseif ( isset($_REQUEST['supprime']) )  {
-          if ( $r['max'] == 1 )
-             $message = '{"etat":"nok","message":"La matière n\'a pas pu être supprimée. Il faut obligatoirement en garder au moins une."}';
-          elseif ( requete('matieres',"DELETE FROM matieres WHERE id = $id",$mysqli) 
-                && requete('matieres',"UPDATE matieres SET ordre = (ordre-1) WHERE ordre > ${r['ordre']}",$mysqli)
-                && requete('colles',"DELETE FROM colles WHERE matiere = $id",$mysqli)
-                && requete('cdt',"DELETE FROM cdt WHERE matiere = $id",$mysqli)
-                && requete('cdt-types',"DELETE FROM `cdt-types` WHERE matiere = $id",$mysqli)
-                && requete('cdt-seances',"DELETE FROM `cdt-seances` WHERE matiere = $id",$mysqli)
-                && requete('notes',"DELETE FROM notes WHERE matiere = $id",$mysqli)
-                && requete('reps',"UPDATE reps SET matiere = 0 WHERE matiere = $id",$mysqli)
-                && requete('docs',"UPDATE docs SET matiere = 0 WHERE matiere = $id",$mysqli)
-                && requete('pages',"UPDATE pages SET mat = 0 WHERE mat = $id",$mysqli)
-                && requete('agenda',"UPDATE agenda SET matiere = 0 WHERE matiere = $id",$mysqli)
-                && requete('utilisateurs',"UPDATE utilisateurs SET matieres = TRIM(TRAILING ',' FROM REPLACE(CONCAT(matieres,','),',$id,',',')) ",$mysqli) )
-            $message = '{"etat":"ok","message":"Matière '.$r['nom'].' supprimée&nbsp;: programmes de colles, cahier de texte, notes supprimées&nbsp;; répertoires et documents passés dans le répertoire général."}';
-          else
-            $message = '{"etat":"nok","message":"La matière n\'a pas pu être supprimée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
-
-        // Suppression des programmes de colles d'une matière
-        elseif ( isset($_REQUEST['supprime_colles']) )
-          $message = ( requete('colles',"DELETE FROM colles WHERE matiere = $id",$mysqli) && requete('matieres',"UPDATE matieres SET colles = 0 WHERE id = $id",$mysqli) ) ? '{"etat":"ok","message":"Programmes de colles en '.$r['nom'].' supprimés"}' : '{"etat":"nok","message":"La suppression n\'a pas été réalisée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        
-        // Suppression du cahier de texte d'une matière
-        elseif ( isset($_REQUEST['supprime_cdt']) )
-          $message = ( requete('cdt',"DELETE FROM cdt WHERE matiere = $id",$mysqli) && requete('matieres',"UPDATE matieres SET cdt = 0 WHERE id = $id",$mysqli) ) ? '{"etat":"ok","message":"Cahier de texte en '.$r['nom'].' supprimés"}' : '{"etat":"nok","message":"La suppression n\'a pas été réalisée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-
-        // Suppression des notes d'une matière
-        elseif ( isset($_REQUEST['supprime_notes']) )
-          $message = ( requete('notes',"DELETE FROM notes WHERE matiere = $id",$mysqli) && requete('matieres',"UPDATE matieres SET notes = 0 WHERE id = $id",$mysqli) ) ? '{"etat":"ok","message":"Notes en '.$r['nom'].' supprimés"}' : '{"etat":"nok","message":"La suppression n\'a pas été réalisée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-
-        // Suppression des documents d'une matière
-        elseif ( isset($_REQUEST['supprime_docs']) )  {
-          if ( requete('reps',"DELETE FROM reps WHERE matiere = $id AND parent > 0",$mysqli) )  {
-            // Suppression physique
-            $resultat = $mysqli->query("SELECT lien FROM docs WHERE matiere = $id");
-            if ( $resultat->num_rows )  {
-              while ( $r = $resultat->fetch_row() )
-                exec("rm -rf documents/${r[0]}");
-              $resultat->free();
-              requete('docs',"DELETE FROM docs WHERE matiere = $id",$mysqli);
-            }
-            requete('matieres',"UPDATE matieres SET docs = 0 WHERE id = $id",$mysqli);
-            $message = '{"etat":"ok","message":"Documents en '.$r['nom'].' supprimés"}';
-          }
-        }
+        requete('docs',"DELETE FROM docs WHERE matiere = $id",$mysqli);
       }
-      else
-        $message = '{"etat":"nok","message":"Identifiant non valide"}';
+      requete('matieres',"UPDATE matieres SET docs = 0 WHERE id = $id",$mysqli);
+      exit("{\"etat\":\"ok\",\"message\":\"Les répertoires et documents de la matière <em>${r['nom']}</em> ont été supprimés.\"}");
     }
-    
-    // Nouvelle matière
-    elseif ( isset($_REQUEST['nom']) && isset($_REQUEST['cle']) && isset($_REQUEST['colles_protection']) && isset($_REQUEST['cdt_protection']) )  {
-      $nom = ucfirst($mysqli->real_escape_string($_REQUEST['nom']));
-      $cle = str_replace(' ','_',trim($mysqli->real_escape_string($_REQUEST['cle'])));
-      $colles_protection = ( in_array($_REQUEST['colles_protection'],array(0,1,2,3,4,5)) ) ? $_REQUEST['colles_protection'] : 0;
-      $cdt_protection = ( in_array($_REQUEST['cdt_protection'],array(0,1,2,3,4,5)) ) ? $_REQUEST['cdt_protection'] : 0;
-      if ( !strlen($nom) || !strlen($cle) )
-        $message = '{"etat":"nok","message":"La matière n\'a pas pu être ajoutée. Nom et clé non vides nécessaires."}';
-      else  {
-        // Vérification que la clé n'existe pas déjà
-        $resultat = $mysqli->query('SELECT cle FROM matieres');
-        if ( $resultat->num_rows )  {
-          while ( $r = $resultat->fetch_assoc() )
-            if ( $r['cle'] == $cle )  {
-              $message = '{"etat":"nok","message":"La matière n\'a pas pu être ajoutée. Cette clé existe déjà et doit être unique."}';
-              break;
-            }
-          $resultat->free();
-        }
-      }
-      if ( !strlen($message) )  {
-        $resultat = $mysqli->query('SELECT COUNT(*) FROM matieres');
-        $r = $resultat->fetch_row();
-        $n = $r[0];
-        $resultat->free();
-        if ( requete('matieres',"INSERT INTO matieres SET nom = '$nom', cle = '$cle', colles_protection = $colles_protection, cdt_protection = $cdt_protection, ordre = ($n+1)",$mysqli) )  {
-          $id = $mysqli->insert_id;
-          requete('reps',"INSERT INTO reps SET parent = 0, parents = '0', nom = '$nom', matiere = $id,
-                          nbrep = 0, nbrep_v = 0, nbdoc = 0, nbdoc_v = 0, protection = 0, menu = 0",$mysqli);
-          requete('cdt-types',"INSERT INTO `cdt-types` (matiere, ordre, cle, titre, deb_fin_pour) VALUES
-                               ($id, 1, 'cours', 'Cours', 1),
-                               ($id, 2, 'TD', 'Séance de travaux dirigés', 1),
-                               ($id, 3, 'TP', 'Séance de travaux pratiques', 1),
-                               ($id, 4, 'DS', 'Devoir surveillé', 1),
-                               ($id, 5, 'interros', 'Interrogation de cours', 0),
-                               ($id, 6, 'distributions', 'Distribution de document', 0),
-                               ($id, 7, 'DM', 'Devoir maison', 2)",$mysqli);
-          // On ajoute la matière uniquement aux utilisateurs qui les ont déjà toutes
-          requete('utilisateurs',"UPDATE utilisateurs SET matieres = CONCAT(matieres,',',$id) WHERE LENGTH(matieres) - LENGTH( REPLACE(matieres,',','') ) = $n",$mysqli);
-          $message = $_SESSION['message'] = '{"etat":"ok","message":"Matière ajoutée"}';
-        }
-        else
-          $message = '{"etat":"nok","message":"La matière n\'a pas pu être ajoutée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-      }
+    exit("{\"etat\":\"nok\",\"message\":\"Les documents de la matière <em>${r['nom']}</em> n'ont pas été supprimés. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+}
+  
+/////////////////////////
+// Ajout d'une matière //
+/////////////////////////
+elseif ( ( $action == 'ajout-matiere' ) && isset($_REQUEST['nom']) && isset($_REQUEST['cle']) && isset($_REQUEST['colles_protection']) && isset($_REQUEST['cdt_protection']) && isset($_REQUEST['docs_protection']) && isset($_REQUEST['notes']) && isset($_REQUEST['dureecolle']) )  {
+  $nom = strip_tags(trim($mysqli->real_escape_string($_REQUEST['nom'])));
+  $nom = mb_strtoupper(mb_substr($nom,0,1)).mb_substr($nom,1);
+  $cle = str_replace(' ','_',strip_tags(trim($mysqli->real_escape_string($_REQUEST['cle']))));
+  if ( !strlen($nom) || !strlen($cle) )
+    exit('{"etat":"nok","message":"La matière n\'a pas été ajoutée. Le nom et la clé doivent être non vides."}');
+  // Vérification que la clé n'existe pas déjà
+  $resultat = $mysqli->query('SELECT cle FROM matieres');
+  while ( $r = $resultat->fetch_row() )
+    if ( $r[0] == $cle )
+      exit("{\"etat\":\"nok\",\"message\":\"La matière <em>$nom</em> n'a pas été ajoutée. La clé donnée existe déjà. Elle doit être différente de celles des autres matières.\"}");
+  $resultat->free();
+  // Génération des valeurs de protection
+  $protection = array();
+  foreach ( array('colles','cdt','docs') as $fonction )  {
+    $val = array_filter($_REQUEST[$fonction.'_protection'],function($id) { return ctype_digit($id); });
+    if ( !count($val) )
+      exit("{\"etat\":\"nok\",\"message\":\"La matière <em>$nom</em> n'a pas été ajoutée. Une des protections d'accès est incorrecte.\"}");
+    if ( ( $val[0] == 0 ) || ( $val[0] == 32 ) )
+      $protection[$fonction] = $val[0];
+    else  {
+      $p = 32;
+      foreach (array_slice($val,1) as $v) 
+        $p = $p-2**($v-1);
+      $protection[$fonction] = $p;
     }
-    break;
+  }
+  // Affichage des notes : 2-> désactivée ; 1-> possible (0 pour l'instant dans la base)
+  $notes = ( $_REQUEST['notes'] == 2 ) ? '2' : '0';
+  $dureecolle = intval($_REQUEST['dureecolle']) ?: 20;
+  // Écriture
+  if ( requete('matieres',"INSERT INTO matieres SET nom = '$nom', cle = '$cle', notes = $notes, colles_protection = ${protection['colles']}, cdt_protection = ${protection['cdt']}, docs_protection = ${protection['docs']}, dureecolle = $dureecolle, ordre = (SELECT MAX(ordre)+1 FROM matieres AS m)",$mysqli) )  {
+    $id = $mysqli->insert_id;
+    requete('reps',"INSERT INTO reps SET parent = 0, parents = '0', nom = '$nom', matiere = $id",$mysqli);
+    requete('cdt-types',"INSERT INTO `cdt-types` (matiere, ordre, cle, titre, deb_fin_pour) VALUES
+                         ($id, 1, 'cours', 'Cours', 1),
+                         ($id, 2, 'TD', 'Séance de travaux dirigés', 1),
+                         ($id, 3, 'TP', 'Séance de travaux pratiques', 1),
+                         ($id, 4, 'DS', 'Devoir surveillé', 1),
+                         ($id, 5, 'interros', 'Interrogation de cours', 0),
+                         ($id, 6, 'distributions', 'Distribution de document', 0),
+                         ($id, 7, 'DM', 'Devoir maison', 2)",$mysqli);
+    exit("{\"etat\":\"ok\",\"message\":\"La matière <em>$nom</em> a été ajoutée.\"}");
+  }
+  exit("{\"etat\":\"nok\",\"message\":\"La matière <em>$nom</em> n'a pas été ajoutée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+}
 
-///////////////////////////////////
-// Modification des utilisateurs //
-///////////////////////////////////
-  case 'utilisateurs':
+//////////////////////////////////////////
+// Modification d'un utilisateur unique //
+//////////////////////////////////////////
+elseif ( ( $action == 'utilisateur' ) && isset($_REQUEST['modif']) && in_array($modif = $_REQUEST['modif'],array('mailenvoi','prefs','desactive','active','supprutilisateur','validutilisateur')) && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
+
+  // Vérification que l'identifiant est valide
+  // Attention, les valeurs "valide", "demande" et "invitation" sont des chaines de caractères égales à '0' ou '1'.
+  $resultat = $mysqli->query("SELECT nom, prenom, login, matieres, mail, (LENGTH(mdp)=40) AS valide, (LEFT(mdp,1)='*') AS demande, (LENGTH(mdp)=1) AS invitation, autorisation, mailenvoi, mailexp, mailliste, mailcopie FROM utilisateurs WHERE id = $id");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant non valide"}');  
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+  $compte = ( strlen($r['nom'].$r['prenom']) ) ? "de <em>${r['prenom']} ${r['nom']}</em>" : "<em>${r['login']}</em>";
+  switch ( $modif )  {
 
-    // Modification du réglage "demandes de création de compte interdite"
-    // Les demandes sont interdites si un utilisateur se trouve sans mdp
-    if ( isset($_REQUEST['creation']) )  {
-      // Récupération du réglage "création impossible"
-      $resultat = $mysqli->query('SELECT id FROM utilisateurs WHERE LENGTH(mdp) = 0');
-      if ( $creationinterdite = $resultat->num_rows )
+    // Modification de la possibilité d'envoyer des mails seulement
+    case 'mailenvoi': {
+      $val = intval( isset($_REQUEST['val']) && $_REQUEST['val'] );
+      if ( $r['autorisation'] == 1 )
+        exit('{"etat":"nok","message":"Un compte de type invité ne peut pas envoyer des courriels."}');
+      if ( $val == $r['mailenvoi'] )
+        exit('{"etat":"ok","message":"Ce réglage était déjà celui en place. Aucune modification n\'a été effectuée."}');
+      if ( $val && !strlen($r['mail']) )
+        exit('{"etat":"nok","message":"Un compte ne peut envoyer de courriel sans adresse électronique valide."}');
+      if ( requete('utilisateurs',"UPDATE utilisateurs SET mailenvoi = $val, mailexp = IF($val AND mailexp = '',CONCAT(prenom,' ',nom),mailexp) WHERE id = $id",$mysqli) )
+        exit('{"etat":"ok","message":"Les préférences d\'envoi de mail '.$compte.' ont été modifiées."}');
+      exit('{"etat":"nok","message":"Les préférences d\'envoi de mail '.$compte.' n\'ont pas été modifiées. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+    }
+
+    // Modification des données du compte (venant du formulaire de utilisateurs.php)
+    case 'prefs': {
+      $modifications = array_diff_assoc( array( 'nom'=>trim($_REQUEST['nom']), 'prenom'=>trim($_REQUEST['prenom']), 'login'=>trim($_REQUEST['login']), 'mail'=>mb_strtolower(trim($_REQUEST['mail1'])), 'mailexp'=>trim($_REQUEST['mailexp']), 'mailenvoi'=>intval(isset($_REQUEST['mailenvoi'])), 'mailcopie'=>intval(isset($_REQUEST['mailcopie'])), 'mailliste'=>intval(isset($_REQUEST['mailliste'])) ), $r);
+      if ( !$modifications )
+        exit('{"etat":"ok","message":"Les valeurs fournies étaient celles déjà enregistrées. Aucune modification n\'a été effectuée."}');
+      $requete = '';
+      if ( isset($modifications['nom']) && strlen($nom = mb_convert_case(strip_tags($mysqli->real_escape_string($modifications['nom'])),MB_CASE_TITLE)) )
+        $requete .= ", nom = '$nom'";
+      if ( isset($modifications['prenom']) && strlen($prenom = mb_convert_case(strip_tags($mysqli->real_escape_string($modifications['prenom'])),MB_CASE_TITLE)) )
+        $requete .= ", prenom = '$prenom'";
+      if ( isset($modifications['login']) && strlen($login = mb_strtolower(str_replace(' ','_',$modifications['login']))) )  {
+        // Vérification que le login n'existe pas déjà
+        $resultat = $mysqli->query("SELECT GROUP_CONCAT(login) FROM utilisateurs WHERE id != $id");
+        $s = $resultat->fetch_row();
         $resultat->free();
-      if ( $creationinterdite != isset($_REQUEST['autoriser']) )
-        $message = ( $creationinterdite ) ? '{"etat":"ok","message":"Les demandes de création de compte sont déjà interdites."}' : '{"etat":"ok","message":"Les demandes de création de compte sont déjà autorisées."}';
-      elseif ( $creationinterdite )
-        $message = ( requete('utilisateurs','DELETE FROM utilisateurs WHERE !LENGTH(mdp)',$mysqli) ) ? '{"etat":"ok","message":"Demandes de création de compte désormais autorisées"}' : '{"etat":"nok","message":"Pas de modification. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-      else
-        $message = ( requete('utilisateurs','INSERT INTO utilisateurs SET mdp = ""',$mysqli) ) ? '{"etat":"ok","message":"Demandes de création de compte désormais interdites"}' : '{"etat":"nok","message":"Pas de modification. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-    }
-
-    // Modifications de tous les utilisateurs : suppression, modification
-    // nom/prénom/adresse électronique, association à une matière, validation
-    elseif ( isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
-      // Vérification que l'identifiant est valide
-      $resultat = $mysqli->query("SELECT IF(LENGTH(nom),CONCAT(prenom,' ',nom),login) AS nom, matieres, login, mail, (LENGTH(mdp)=40) AS valide, autorisation FROM utilisateurs WHERE id = $id");
-      if ( $resultat->num_rows )  {
-        $r = $resultat->fetch_assoc();
+        if ( !in_array($login,explode(',',$s[0])) )
+          $requete .= ', login = \''.$mysqli->real_escape_string($login).'\'';
+      }
+      if ( isset($modifications['mail']) && filter_var($mail = $modifications['mail'],FILTER_VALIDATE_EMAIL) && isset($_REQUEST['mail2']) && ( $mail == mb_strtolower(trim($_REQUEST['mail2'])) ) )  {
+        // Vérification que l'adresse n'existe pas déjà
+        $resultat = $mysqli->query("SELECT GROUP_CONCAT(mail) FROM utilisateurs WHERE id != $id AND mail > ''");
+        $s = $resultat->fetch_row();
         $resultat->free();
+        if ( !in_array($mail,explode(',',$s[0])) )
+          $requete .= ', mail = \''.$mysqli->real_escape_string($r['mail'] = $mail).'\'';
+      }
+      if ( isset($modifications['mailenvoi']) && ( $r['autorisation'] > 1 ) && ( !$modifications['mailenvoi'] || strlen($r['mail']) ) )
+        $requete .= ', mailenvoi = \''.( $r['mailenvoi'] = $modifications['mailenvoi'] ).'\'';
+      if ( $r['mailenvoi'] )  {
+        if ( isset($modifications['mailexp']) && strlen($mailexp = strip_tags($mysqli->real_escape_string($modifications['mailexp']))) )
+          $requete .= ", mailexp = '$mailexp'";
+        elseif ( !strlen($r['mailexp']) )
+          $requete .= ', mailexp = CONCAT(prenom,\' \',nom)';
+        if ( isset($modifications['mailcopie']) )
+          $requete .= ", mailcopie = ${modifications['mailcopie']}";
+        if ( isset($modifications['mailliste']) && ( $r['autorisation'] > 2 ) )
+          $requete .= ", mailliste = ${modifications['mailliste']}";
+      }
+      if ( !strlen($requete) )
+        exit('{"etat":"nok","message":"Les préférences '.$compte.' n\'ont pas été modifiées, les valeurs saisies sont identiques à celles existantes."}');
+      if( requete('utilisateurs','UPDATE utilisateurs SET '.substr($requete,2)." WHERE id = $id",$mysqli) )
+        exit($_SESSION['message'] = '{"etat":"ok","message":"Les préférences '.$compte.' ont été modifiées."}');
+      exit('{"etat":"nok","message":"Les préférences '.$compte.' n\'ont pas été modifiées. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+    }
+
+    // Désactivation
+    case 'desactive': {
+      if ( ( $r['valide'] == 0 ) && ( $r['demande'] == 0 ) && ( $r['invitation'] == 0 ) )
+        exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Le compte $compte est déjà actuellement désactivé.\"}");
+      if ( ( $r['demande'] == 1 ) || ( $r['invitation'] == 1 ) )
+        exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Le compte $compte n'est pas actuellement désactivable.\"}");
+      if ( requete('utilisateurs',"UPDATE utilisateurs SET mdp = CONCAT('!',mdp) WHERE id = $id",$mysqli) )
+        exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Compte $compte désactivé\"}");
+      exit("{\"etat\":\"nok\",\"message\":\"La désactivation du compte $compte n'a pas été réalisée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+    }
+
+    // Réactivation
+    case 'active': {
+      if ( $r['valide'] == 1 )
+        exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Le compte $compte est déjà actuellement activé.\"}");
+      if ( ( $r['demande'] == 1 ) || ( $r['invitation'] == 1 ) )
+        exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Le compte $compte n'est pas actuellement activable.\"}");
+      if ( requete('utilisateurs',"UPDATE utilisateurs SET mdp = SUBSTR(mdp,2) WHERE id = $id",$mysqli) )
+        exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Compte $compte réactivé\"}");
+      exit("{\"etat\":\"nok\",\"message\":\"La réactivation du compte $compte n'a pas été réalisée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+    }
+
+    // Suppression
+    case 'supprutilisateur': {
+      if ( requete('utilisateurs',"DELETE FROM utilisateurs WHERE id = $id",$mysqli)
+        && requete('groupes',"UPDATE groupes SET utilisateurs = TRIM(BOTH ',' FROM REPLACE(CONCAT(',',utilisateurs,','),',$id,',',')) WHERE FIND_IN_SET($id,utilisateurs)",$mysqli)
+        && requete('notes',"DELETE FROM notes WHERE colleur = $id OR eleve = $id",$mysqli) )
+        exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Compte $compte supprimé\"}");
+      exit("{\"etat\":\"nok\",\"message\":\"La suppression du compte $compte n'a pas été réalisée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+    }
+
+    // Validation d'une demande
+    case 'validutilisateur': {
+      if ( $r['demande'] == 0 )
+        exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"La demande $compte a déjà été validée.\"}");
+      if ( requete('utilisateurs',"UPDATE utilisateurs SET mdp = SUBSTR(mdp,2) WHERE id = $id",$mysqli) )  {
+        mail($r['mail'],'=?UTF-8?B?'.base64_encode('[Cahier de Prépa] Compte validé').'?=',
+"Bonjour
 
-        // Suppression
-        if ( isset($_REQUEST['supprime']) )  {
-          if ( requete('utilisateurs',"DELETE FROM utilisateurs WHERE id = $id",$mysqli)
-             && requete('groupes',"UPDATE groupes SET eleves = TRIM(BOTH ',' FROM REPLACE(CONCAT(',',eleves,','),',$id,',',')) WHERE FIND_IN_SET($id,eleves)",$mysqli)
-             && requete('notes',"DELETE FROM notes WHERE colleur = $id OR eleve = $id",$mysqli) )
-            $message = $_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"${r['nom']} supprimé\"}";
-          else
-            $message = '{"etat":"nok","message":"La suppression n\'a pas été réalisée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
-
-        // Cas des comptes validés : modification et association
-        elseif ( $r['valide'] )  {
-
-          // Traitement d'une modification unique
-          if ( isset($_REQUEST['champ']) && ( $r['autorisation'] > 1 ) && in_array($champ = $_REQUEST['champ'],array('nom','prenom','mail')) && isset($_REQUEST['val']) && strlen($val = trim($_REQUEST['val'])) )  {
-            if ( ( $champ == 'nom' ) || ( $champ == 'prenom' ) )  {
-              $val = ucfirst($mysqli->real_escape_string($val));
-              $message = ( requete('utilisateurs',"UPDATE utilisateurs SET $champ = '$val' WHERE id = $id",$mysqli) ) ? '{"etat":"ok","message":"'.ucfirst($champ)." de ${r['nom']} modifié\"}" : "{\"etat\":\"nok\",\"message\":\"La modification du $champ de ${r['nom']} n'a pas pu être effectuée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}';
-            }
-            else  {
-              if ( !filter_var($val,FILTER_VALIDATE_EMAIL) )
-                $message = '{"etat":"nok","message":"Adresse électronique non valide"}';
-              else  {
-                // Adresse électronique : vérification qu'il n'existe pas déjà
-                $resultat = $mysqli->query("SELECT GROUP_CONCAT(mail) AS mails FROM utilisateurs WHERE id != $id AND LENGTH(mail)");
-                $s = $resultat->fetch_assoc();
-                $resultat->free();
-                if ( in_array($mail = strtolower($mysqli->real_escape_string($val)),explode(',',$s['mails'])) )
-                  $message = '{"etat":"nok","message":"Adresse électronique déjà prise"}';
-                elseif ( requete('utilisateurs',"UPDATE utilisateurs SET mail = '$mail' WHERE id = $id",$mysqli) )  {
-                  $message = "{\"etat\":\"ok\",\"message\":\"Adresse électronique de ${r['nom']} modifiée (utilisateur prévenu par courriel)\"}";
-                  // Récupération de l'adresse électronique du professeur connecté
-                  $resultat = $mysqli->query("SELECT mail FROM utilisateurs WHERE id = ${_SESSION['id']}");
-                  $s = $resultat->fetch_row();
-                  $resultat->free();
-                  if ( !isset($mailadmin) )
-                    $mailadmin = 'admin@cahier-de-prepa.fr';
-                  $returnpath = ( strlen($s[0]) ) ? $s[0] : $mailadmin;
-                  mail($mail,'=?UTF-8?B?'.base64_encode('[Cahier de Prépa] Changement d\'adresse électronique').'?=',
-'Bonjour
+Vous avez rempli une demande de création de compte sur le Cahier de Prépa <https://$site>, correspondant à l'identifiant ${r['login']}.
 
-L\'équipe pédagogique en charge du Cahier de Prépa <http'.($https?'s':'')."://$site> vient de modifier l'adresse électronique correspondant à l'identifiant ${r['login']}. Elle y a mis l'adresse à laquelle vous recevez ce courrier.
+Cette demande vient de recevoir une réponse favorable de la part de l'équipe pédagogique en charge du site. Vous pouvez donc désormais vous connecter avec votre identifiant et votre mot de passe.
 
-S'il s'agit d'une erreur, merci de contacter au plus vite l'administrateur du système Cahier de Prépa à l'adresse <contact@cahier-de-prepa.fr>.
+Bonne navigation sur Cahier de Prépa.
 
 Cordialement,
 -- 
 Cahier de Prépa
-",'From: =?UTF-8?B?'.base64_encode('Cahier de Prépa')."?= <$mailadmin>\r\nContent-type: text/plain; charset=UTF-8","-f$returnpath");
-                }
-              }
-            }
-          }
+",'From: =?UTF-8?B?'.base64_encode('Cahier de Prépa')."?= <$mailadmin>\r\nContent-type: text/plain; charset=UTF-8","-f$mailadmin");
+        exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"Demande de compte $compte accordée. L'élève a été prévenu par courriel.\"}");
+      }
+      exit("{\"etat\":\"nok\",\"message\":\"La validation du compte $compte n'a pas été réalisée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+    }
+  }
+}
 
-          // Modifications des associations aux matières (depuis matieres.php)
-          elseif ( isset($_REQUEST['matiere']) && ctype_digit($matiere = $_REQUEST['matiere']) )  {
-            // Vérification que l'identifiant est valide
-            $resultat = $mysqli->query("SELECT nom FROM matieres WHERE id = $matiere");
-            if ( !$resultat->num_rows )  {
-              $mysqli->close();
-              exit('{"etat":"nok","message":"Identifiant de matière non valide"}');
-            }
-            $m = $resultat->fetch_assoc();
-            $resultat->free();
-            // Si une seule matière : modifications impossibles
-            $resultat = $mysqli->query('SELECT COUNT(*) FROM matieres');
-            $s = $resultat->fetch_row();
-            if ( $s[0] == 1 )  {
-              $mysqli->close();
-              exit('{"etat":"nok","message":"Tout compte doit être associé à au moins une matière."}');
-            }
-            $resultat->free();
-            // Utilisateur désormais non associé
-            if ( $_REQUEST['ok'] == 0 )  {
-              if ( !strpos($r['matieres'],',',2) )
-                $message = '{"etat":"nok","message":"Tout compte doit être associé à au moins une matière."}';
-              elseif ( in_array($matiere,explode(',',$r['matieres'])) )
-                $message = ( requete('utilisateurs',"UPDATE utilisateurs SET matieres = TRIM(TRAILING ',' FROM REPLACE(CONCAT(matieres,','),',$matiere,',',')) WHERE id = $id",$mysqli)
-                ) ? "{\"etat\":\"ok\",\"message\":\"${r['nom']} n'est plus en ${m['nom']}\"}" : '{"etat":"nok","message":"La modification n\'a pas été réalisée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-              else
-                $messsage = "{\"etat\":\"ok\",\"message\":\"${r['nom']} n'était déjà plus en ${m['nom']}\"}";
-            }
-            // Utilisateur désormais associé
-            elseif ( $_REQUEST['ok'] == 1 )  {
-              if ( !in_array($matiere,explode(',',$r['matieres'])) )
-                $message = ( requete('utilisateurs',"UPDATE utilisateurs SET matieres = CONCAT(matieres,',',$matiere) WHERE id = $id",$mysqli)
-                ) ? "{\"etat\":\"ok\",\"message\":\"${r['nom']} est en ${m['nom']}\"}" : '{"etat":"nok","message":"La modification n\'a pas été réalisée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-              else
-                $messsage = "{\"etat\":\"ok\",\"message\":\"${r['nom']} était déjà en ${m['nom']}\"}";
-            }
-            // Mise à jour de $_SESSION['matieres'] si besoin
-            if ( $id == $_SESSION['id'] )  {
-              $resultat = $mysqli->query("SELECT matieres FROM utilisateurs WHERE id = $id");
-              $r = $resultat->fetch_assoc();
-              $resultat->free();
-              $_SESSION['matieres'] = $r['matieres'];
-            }
-          }
-        }
+//////////////////////////////////////////
+// Modification multiple d'utilisateurs // 
+//////////////////////////////////////////
+elseif ( ( $action == 'utilisateurs' ) && isset($_REQUEST['modif']) && in_array($modif = $_REQUEST['modif'],array('mailenvoi','desactive','active','supprutilisateur','validutilisateur')) && isset($_REQUEST['ids']) && strlen($ids = implode(',',array_filter(explode(',',$_REQUEST['ids']),function($id) { return ctype_digit($id); }))) )  {
+
+  // Vérification que les identifiants sont valides
+  // Attention, les valeurs "valide", "demande" et "invitation" sont des chaines de caractères égales à '0' ou '1'.
+  $resultat = $mysqli->query("SELECT id, nom, prenom, login, mail, (LENGTH(mdp)=40) AS valide, (LEFT(mdp,1)='*') AS demande, (LENGTH(mdp)=1) AS invitation, autorisation, mailenvoi, mailexp, mailliste, mailcopie FROM utilisateurs WHERE FIND_IN_SET(id,'$ids')");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiants non valides"}');
+  $message = array('ok'=>'','nok'=>'');
+  switch ( $modif )  {
+
+    // Modification de la possibilité d'envoyer des mails seulement
+    case 'mailenvoi': {
+      $val = intval( isset($_REQUEST['val']) && ( $_REQUEST['val'] == 1 ) );
+      while ( $r = $resultat->fetch_assoc() )  {
+        $compte = ( strlen($r['nom'].$r['prenom']) ) ? "de <em>${r['prenom']} ${r['nom']}</em>" : "<em>${r['login']}</em>";
+        if ( ( $r['autorisation'] == 1 ) || ( $val == $r['mailenvoi'] ) || ( $val && !strlen($r['mail']) ) )
+          $message['nok'] .= "Les préférences d'envoi de mail $compte n'ont pas été modifiées, car le compte ne peut envoyer de mail ou en envoie déjà.<br>";
+        elseif ( requete('utilisateurs',"UPDATE utilisateurs SET mailenvoi = $val, mailexp = IF($val AND mailexp = '',CONCAT(prenom,' ',nom),mailexp) WHERE id = ${r['id']}",$mysqli) )
+          $message['ok'] .= "Les préférences d'envoi de mail $compte ont été modifiées.<br>";
+        else
+          $message['nok'] .= "Les préférences d'envoi de mail $compte n'ont pas été modifiées. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»<br>';
+      }
+    }
 
-        // Cas des utilisateurs en attente de validation
-        else  {
-          if ( requete('utilisateurs',"UPDATE utilisateurs SET mail = SUBSTR(mail,2), mdp = SUBSTR(mdp,2) WHERE id = $id",$mysqli) )  {
-            $message = $_SESSION['message'] = '{"etat":"ok","message":"Utilisateur ajouté"}';
-            if ( !isset($mailadmin) )
-              $mailadmin = 'admin@cahier-de-prepa.fr';
-            mail(substr($r['mail'],1),'=?UTF-8?B?'.base64_encode('[Cahier de Prépa] Compte validé').'?=',
-'Bonjour
+    // Désactivation
+    case 'desactive': {
+      while ( $r = $resultat->fetch_assoc() )  {
+        $compte = ( strlen($r['nom'].$r['prenom']) ) ? "de <em>${r['prenom']} ${r['nom']}</em>" : "<em>${r['login']}</em>";
+        if ( ( $r['demande'] == 1 ) || ( $r['invitation'] == 1 ) || ( $r['valide'] == 0 ) )
+          $message['nok'] .= "Le compte $compte n'a pas été désactivé, car il l'est déjà ou ne peut pas l'être.<br>";
+        elseif ( requete('utilisateurs',"UPDATE utilisateurs SET mdp = CONCAT('!',mdp) WHERE id = ${r['id']}",$mysqli) )
+          $message['ok'] .= "Le compte $compte a été désactivé.<br>";
+        else
+          $message['nok'] .= "Le compte $compte n'a pas été désactivé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»<br>';
+      }
+    }
+
+    // Réactivation
+    case 'active': {
+      while ( $r = $resultat->fetch_assoc() )  {
+        $compte = ( strlen($r['nom'].$r['prenom']) ) ? "de <em>${r['prenom']} ${r['nom']}</em>" : "<em>${r['login']}</em>";
+        if ( ( $r['demande'] == 1 ) || ( $r['invitation'] == 1 ) || ( $r['valide'] == 1 ) )
+          $message['nok'] .= "Le compte $compte n'a pas été activé, car il l'est déjà ou ne peut pas l'être.<br>";
+        elseif ( requete('utilisateurs',"UPDATE utilisateurs SET mdp = SUBSTR(mdp,2) WHERE id = ${r['id']}",$mysqli) )
+          $message['ok'] .= "Le compte $compte a été réactivé.<br>";
+        else
+          $message['nok'] .= "Le compte $compte n'a pas été réactivé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»<br>';
+      }
+    }
 
-Vous avez rempli une demande de création de compte sur le Cahier de Prépa <http'.($https?'s':'')."://$site>, correspondant à l'identifiant ${r['login']}.
+    // Suppression
+    case 'supprutilisateur': {
+      while ( $r = $resultat->fetch_assoc() )  {
+        $compte = ( strlen($r['nom'].$r['prenom']) ) ? "de <em>${r['prenom']} ${r['nom']}</em>" : "<em>${r['login']}</em>";
+        if ( requete('utilisateurs',"DELETE FROM utilisateurs WHERE id = ${r['id']}",$mysqli)
+          && requete('groupes',"UPDATE groupes SET utilisateurs = TRIM(BOTH ',' FROM REPLACE(CONCAT(',',utilisateurs,','),',${r['id']},',',')) WHERE FIND_IN_SET(${r['id']},utilisateurs)",$mysqli)
+          && requete('notes',"DELETE FROM notes WHERE colleur = ${r['id']} OR eleve = ${r['id']}",$mysqli) )
+          $message['ok'] .= "Le compte $compte a été supprimé.<br>";
+        else
+          $message['nok'] .= "Le compte $compte n'a pas été supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»<br>';
+      }
+    }
+
+    // Validation d'une demande
+    case 'validutilisateur': {
+      while ( $r = $resultat->fetch_assoc() )  {
+        $compte = ( strlen($r['nom'].$r['prenom']) ) ? "de <em>${r['prenom']} ${r['nom']}</em>" : "<em>${r['login']}</em>";
+        if ( $r['demande'] == 0 )
+          $message['nok'] .= "La demande $compte a déjà été validée.<br>";
+        elseif ( requete('utilisateurs',"UPDATE utilisateurs SET mdp = SUBSTR(mdp,2) WHERE id = ${r['id']}",$mysqli) )  {
+          mail($r['mail'],'=?UTF-8?B?'.base64_encode('[Cahier de Prépa] Compte validé').'?=',
+"Bonjour
+
+Vous avez rempli une demande de création de compte sur le Cahier de Prépa <https://$site>, correspondant à l'identifiant ${r['login']}.
 
 Cette demande vient de recevoir une réponse favorable de la part de l'équipe pédagogique en charge du site. Vous pouvez donc désormais vous connecter avec votre identifiant et votre mot de passe.
 
@@ -1265,912 +1873,666 @@
 -- 
 Cahier de Prépa
 ",'From: =?UTF-8?B?'.base64_encode('Cahier de Prépa')."?= <$mailadmin>\r\nContent-type: text/plain; charset=UTF-8","-f$mailadmin");
-          }
-          else
-            $message = '{"etat":"nok","message":"La validation du compte n\'a pas été réalisée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
+          $message['ok'] .= "La demande de compte $compte a été accordée. L'élève a été prévenu par courriel.";
         }
+        else
+          $message['nok'] .= "La validation du compte $compte n'a pas été réalisée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».<br>';
       }
-      else
-        $message = '{"etat":"nok","message":"Identifiant non valide"}';
     }
 
-    // Ajout de nouveaux utilisateurs
-    elseif ( isset($_REQUEST['utilisateurs']) && isset($_REQUEST['autorisation']) && in_array($autorisation = $_REQUEST['autorisation'],array(1,2,3,4)) && isset($_REQUEST['matiere']) )  {
-      $utilisateurs = explode("\n",trim($_REQUEST['utilisateurs']));
-      // Récupération des identifiants
-      $resultat = $mysqli->query('SELECT GROUP_CONCAT(login) FROM utilisateurs');
-      $r = $resultat->fetch_row();
-      $resultat->free();
-      $logins = explode(',',$r[0]);
-      // Vérification de la matière
-      $resultat = $mysqli->query('SELECT GROUP_CONCAT(id) AS matieres FROM matieres');
-      $r = $resultat->fetch_row();
-      $resultat->free();
-      $matiere = ( isset($_REQUEST['matiere']) && in_array($_REQUEST['matiere'],explode(',',$r[0])) ) ? "0,${_REQUEST['matiere']}" : "0,${r[0]}";
-      // $m: pour le message; $n: nb de comptes ajoutés; $i: compteur de ligne
-      $m = '';
-      $n = $i = 0;
-      // Comptes invités : enregistrement immédiat
-      if ( $autorisation == 1 )
-        foreach ( $utilisateurs as $utilisateur)  {
-          $u = array_map('trim',explode(',',$utilisateur));
-          $i = $i+1;
-          if ( ( count($u) != 2 ) || !strlen($u[0]) || !strlen($u[1]) )
-            $m .= "<br>Ligne $i : mauvais paramètres";
-          elseif ( in_array($login = mb_strtolower(str_replace(' ','_',$u[0]),'UTF-8'),$logins) )
-            $m .= "<br>Ligne $i : identifiant déjà existant";
-          else  {
-            $newmdp = sha1($u[1]);
-            if ( requete('utilisateurs',"INSERT INTO utilisateurs SET login = '$login', mdp = '$newmdp', autorisation = 1, matieres = '$matiere', timeout=900",$mysqli) )  {
-              $m .= "<br>Ligne $i : ok (identifiant $login)";
-              $n = $n+1;
-            }
-            else
-              $m .= "<br>Ligne $i : erreur MySQL n°".$mysqli->errno.' «'.$mysqli->error.'»';
-          }
+  }
+  $resultat->free();
+  if ( strlen($message['ok']) )
+    exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"${message['ok']}${message['nok']}\"}");
+  exit("{\"etat\":\"nok\",\"message\":\"${message['nok']}\"}");
+}
+
+////////////////////////////////////
+// Ajout de nouveaux utilisateurs //
+////////////////////////////////////
+elseif ( ( $action == 'ajout-utilisateurs' ) && isset($_REQUEST['listeutilisateurs']) && isset($_REQUEST['autorisation']) && in_array($autorisation = intval($_REQUEST['autorisation']),array(1,2,3,4,5)) && isset($_REQUEST['saisie']) && isset($_REQUEST['matieres']) && count($matieres = array_filter($_REQUEST['matieres'],function($id) { return ctype_digit($id); })) )  {
+  
+  // Vérification des matières -- on ne garde que les identifiants existants,
+  // et on prend silencieusement par défaut l'ensemble des matières
+  $resultat = $mysqli->query('SELECT GROUP_CONCAT(id) AS matieres FROM matieres');
+  $r = $resultat->fetch_row();
+  $resultat->free();
+  $matieres = '0,'.implode(',', array_intersect($_REQUEST['matieres'],explode(',',$r[0])) ?: explode(',',$r[0]) );
+  
+  // Récupération des identifiants existants
+  $resultat = $mysqli->query('SELECT GROUP_CONCAT(login) FROM utilisateurs');
+  $r = $resultat->fetch_row();
+  $resultat->free();
+  $logins = explode(',',$r[0]);
+  
+   // Récupération des lignes
+  $utilisateurs = explode("\n",$_REQUEST['listeutilisateurs']);
+  
+  // Compteurs : $n nb de comptes ajoutés; $i compteur de ligne traitée
+  $n = $i = 0;
+  $message = '';
+  // Comptes invités : login,mdp
+  if ( $autorisation == 1 )
+    foreach ( $utilisateurs as $utilisateur)  {
+      if ( !strlen(trim($utilisateur)) )
+        continue;
+      $u = array_map('trim',explode(',',$utilisateur));
+      $i = $i+1;
+      if ( ( count($u) != 2 ) || !strlen($u[0]) || !strlen($u[1]) )
+        $message .= "<br>Ligne $i : mauvais paramètres";
+      elseif ( in_array($login = mb_strtolower(str_replace(' ','_',$u[0])),$logins) )
+        $message .= "<br>Ligne $i : identifiant <strong>$login</strong> déjà existant";
+      elseif ( requete('utilisateurs',"INSERT INTO utilisateurs SET login = '$login', mdp = '".sha1($mdp.$u[1])."', autorisation = 1, matieres = '$matieres', timeout=900",$mysqli) )  {
+        $message .= "<br>Ligne $i : ok (identifiant <strong>$login</strong>)";
+        $n = $n+1;
+      }
+      else
+        $message .= "<br>Ligne $i : erreur MySQL n°".$mysqli->errno.' «'.$mysqli->error.'»';
+    }
+  // Autres comptes : nom,prenom,mail ou nom,prenom,mdp
+  else
+    foreach ( $utilisateurs as $utilisateur)  {
+      if ( !strlen(trim($utilisateur)) )
+        continue;
+      $u = array_map('trim',explode(',',$utilisateur));
+      $i = $i+1;
+      // Nettoyage des données envoyées
+      $nom = mb_convert_case(strip_tags($mysqli->real_escape_string($u[0])),MB_CASE_TITLE);
+      $prenom = mb_convert_case(strip_tags($mysqli->real_escape_string($u[1])),MB_CASE_TITLE);
+      $login = mb_strtolower(mb_substr($prenom,0,1).str_replace(' ','_',$nom));
+      if ( ( count($u) != 3 ) || !strlen($u[0].$u[1]) || !strlen($u[2]) )
+        $message .= "<br>Ligne $i : mauvais paramètres";
+      elseif ( in_array($login,$logins) )
+        $message .= "<br>Ligne $i : identifiant <strong>$login</strong> déjà existant";
+      // Si nom,prenom,mdp
+      elseif ( $_REQUEST['saisie'] == 2 )  {
+        if ( requete('utilisateurs',"INSERT INTO utilisateurs SET login = '$login', prenom = '$prenom', nom = '$nom', mdp = '".sha1($mdp.$u[2])."', autorisation = $autorisation, matieres = '$matieres', timeout = 900",$mysqli) )  {
+          $message .= "<br>Ligne $i : ok (<strong>$prenom $nom</strong>, identifiant $login)";
+          $n = $n+1;
         }
-      // Comptes élèves, colleurs ou professeurs : mot de passe fourni ou invitation par courriel
+        else
+          $message .= "<br>Ligne $i : erreur MySQL n°".$mysqli->errno.' «'.$mysqli->error.'»';
+      }
+      // Si nom,prenom,mail
       else  {
         // Récupération des adresses électroniques
-        $resultat = $mysqli->query('SELECT GROUP_CONCAT(mail) FROM utilisateurs WHERE LENGTH(mail)');
+        $resultat = $mysqli->query('SELECT GROUP_CONCAT(mail) FROM utilisateurs WHERE mail > \'\'');
         $r = $resultat->fetch_row();
         $resultat->free();
-        foreach ( $utilisateurs as $utilisateur)  {
-          if ( !strlen(trim($utilisateur)) )
-            continue;
-          // Deux séparateurs possibles : ',' => nom-prenom-mail, ';' => nom-prenom-mdp
-          $sep = ( strpos($utilisateur.',',',') < strpos($utilisateur.';',';') ) ? ',' : ';';
-          $u = array_map('trim',explode($sep,$utilisateur));
-          $i = $i+1;
-          $mail = '';
-          if ( ( count($u) != 3 ) || !strlen($u[0].$u[1]) || !strlen($u[2]) )
-            $m .= "<br>Ligne $i : mauvais paramètres";
-          elseif ( ( $sep == ',' ) && in_array($mail = strtolower($u[2]),explode(',',$r[0])) )
-            $m .= "<br>Ligne $i : adresse électronique déjà existante";
-          elseif ( ( $sep == ',' ) && !filter_var($mail,FILTER_VALIDATE_EMAIL) )
-            $m .= "<br>Ligne $i : adresse électronique non valide";
-          else  {
-            // Nettoyage des données envoyées
-            $nom = mb_convert_case(strip_tags($mysqli->real_escape_string($u[0])),MB_CASE_TITLE,'UTF-8');
-            $prenom = mb_convert_case(strip_tags($mysqli->real_escape_string($u[1])),MB_CASE_TITLE,'UTF-8');
-            $login = mb_strtolower(mb_substr($prenom,0,1,'UTF-8').str_replace(' ','_',$nom),'UTF-8');
-            if ( in_array($login,$logins) )
-              $m .= "<br>Ligne $i : identifiant déjà existant";
-            else  {
-              $newmdp = ( $mail ) ? '*' : sha1($u[2]);
-              $mailexp = ( $autorisation > 2 ) ? "$prenom $nom" : '';
-              $mailcopy = ( $autorisation > 2 ) ? 1 : 0;
-              if ( requete('utilisateurs',"INSERT INTO utilisateurs SET login = '$login', prenom = '$prenom', nom = '$nom', mail = '$mail',
-                         mdp = '$newmdp', autorisation = $autorisation, matieres = '$matiere', timeout = 900, mailexp = '$mailexp', mailcopy = 1",$mysqli) )  {
-                $m .= "<br>Ligne $i : ok ($prenom $nom, identifiant $login)";
-                $n = $n+1;
-                if ( $mail )  {
-                  $lien = ( $https ? 'https' : 'http' )."://$site/connexion?reponseinvitation&mail=".str_replace('@','ø',$mail).'&p='.sha1($site.$mdp.$mail);
-                  // Récupération de l'adresse électronique du professeur connecté
-                  $resultat = $mysqli->query("SELECT mail FROM utilisateurs WHERE id = ${_SESSION['id']}");
-                  $s = $resultat->fetch_row();
-                  $resultat->free();
-                  if ( !isset($mailadmin) )
-                    $mailadmin = 'admin@cahier-de-prepa.fr';
-                  $returnpath = ( strlen($s[0]) ) ? $s[0] : $mailadmin;
-                  mail($mail,'=?UTF-8?B?'.base64_encode('[Cahier de Prépa] Invitation').'?=',
-'Bonjour
+        if ( in_array($mail = mb_strtolower($u[2]),explode(',',$r[0])) )
+          $message .= "<br>Ligne $i : adresse électronique déjà existante (<strong>$prenom $nom</strong>)";
+        elseif ( !filter_var($mail,FILTER_VALIDATE_EMAIL) )
+          $message .= "<br>Ligne $i : adresse électronique non valide (<strong>$prenom $nom</strong>)";
+        else  {
+          $mailenvoi = intval(isset($_REQUEST['mailenvoi']));
+          $mailexp = $mailenvoi ? "$prenom $nom" : '';
+          $mailliste = ( $mailenvoi && ( $autorisation > 2 ) ) ? intval(isset($_REQUEST['mailliste'])) : 0;
+          if ( requete('utilisateurs',"INSERT INTO utilisateurs SET login = '$login', prenom = '$prenom', nom = '$nom', mail = '$mail', mdp = '?', autorisation = $autorisation, matieres = '$matieres', timeout = 900, mailenvoi = $mailenvoi, mailcopie = $mailenvoi, mailexp = '$mailexp', mailliste = $mailliste",$mysqli) )  {
+            $message .= "<br>Ligne $i : ok (<strong>$prenom $nom</strong>, identifiant $login)";
+            $n = $n+1;
+            $lien = 'https://'.$site.'/gestioncompte?invitation&mail='.str_replace('@','__',$mail).'&p='.sha1($site.$mdp.$mail);
+            // Récupération de l'adresse électronique du professeur connecté
+            $resultat = $mysqli->query("SELECT mail FROM utilisateurs WHERE id = ${_SESSION['id']}");
+            $s = $resultat->fetch_row();
+            $resultat->free();
+            $returnpath = $s[0] ?: $mailadmin;
+            mail($mail,'=?UTF-8?B?'.base64_encode('[Cahier de Prépa] Invitation').'?=',
+"Bonjour
 
-L\'équipe pédagogique en charge du Cahier de Prépa <http'.($https?'s':'')."://$site> vous invite à les rejoindre.
+L'équipe pédagogique en charge du Cahier de Prépa <https://$site> vous invite à les rejoindre.
 
 S'il s'agit d'une erreur, merci d'ignorer simplement ce courriel.
 
 Sinon, veuillez cliquer ci-dessous pour vous rendre à la page qui vous permettra d'entrer un mot de passe :
    $lien
 
+Si ce lien ne s'ouvre pas correctement, il a peut-être été coupé lors du clic : dans ce cas, essayez à nouveau en copiant-collant le lien.
+
 Bonne navigation sur Cahier de Prépa.
 
 Cordialement,
 -- 
 Cahier de Prépa
 ",'From: =?UTF-8?B?'.base64_encode('Cahier de Prépa')."?= <$mailadmin>\r\nContent-type: text/plain; charset=UTF-8","-f$returnpath");
-                }
-              }
-              else
-                $m .= "<br>Ligne $i : erreur MySQL n°".$mysqli->errno.' «'.$mysqli->error.'»';
-            }
           }
+          else
+            $message .= "<br>Ligne $i : erreur MySQL n°".$mysqli->errno.' «'.$mysqli->error.'»';
         }
       }
-      // Fabrication du message
-      $nouveaucompte = ( $n > 1 ) ? 'nouveaux comptes' : 'nouveau compte';
-      if ( $e = $i-$n )
-        $message = "{\"etat\":\"nok\",\"message\":\"$n $nouveaucompte et $e erreur".(($e>1)?'s':'')."$m\"}";
-      else
-        $message = $_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"$n $nouveaucompte$m\"}";
     }
-    break;
+  // Fabrication du message
+  $nouveaucompte = ( $n > 1 ) ? 'nouveaux comptes' : 'nouveau compte';
+  if ( $e = $i-$n )
+    exit("{\"etat\":\"nok\",\"message\":\"<strong>$n $nouveaucompte et $e erreur".($e>1?'s':'')."</strong>$message\"}");
+  exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"<strong>$n $nouveaucompte</strong>$message\"}");
+}
 
-//////////////////////////////////
-// Modification des répertoires //
-//////////////////////////////////
-  case 'reps':
+///////////////////////////////////////////////////////////////
+// Modification d'une association utilisateur-matière unique //
+///////////////////////////////////////////////////////////////
+elseif ( ( $action == 'utilisateur-matiere' ) && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) && isset($_REQUEST['matiere']) && ctype_digit($mid = $_REQUEST['matiere']) && isset($_REQUEST['val']) )  {
+
+  // Vérification que l'identifiant de la matière est valide
+  $resultat = $mysqli->query("SELECT nom FROM matieres WHERE id = $mid");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant de matière non valide"}');
+  $r = $resultat->fetch_row();
+  $resultat->free();
+  $matiere = $r[0];
   
-    // Vérification que l'identifiant est valide
-    if ( isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
-      $resultat = $mysqli->query("SELECT parent, nom, menu, protection, matiere, nbrep+nbdoc AS nb FROM reps WHERE id = $id AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
-      if ( $resultat->num_rows )  {
-        $r = $resultat->fetch_assoc();
-        $resultat->free();
+  // Vérification que l'identifiant de l'utilisateur est valide
+  $resultat = $mysqli->query("SELECT IF(nom>'',CONCAT(prenom,' ',nom),CONCAT('<em>',login,'</em>')) AS nom, FIND_IN_SET('$mid',matieres)>0 AS matiere, LOCATE(',',matieres,3) AS multi FROM utilisateurs WHERE id = $id");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant d\'utilisateur non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+  
+  // Modification
+  $val = intval( $_REQUEST['val'] > 0 );
+  // Si rien à faire
+  if ( $r['matiere'] == $val )
+    exit('{"etat":"nok","message":"Ce réglage était déjà celui en place. Aucune modification n\'a été effectuée."}');
+  // Si une seule matière à retirer : impossible
+  if ( !$val && ( $r['multi'] == 0 ) )
+    $message = "{\"etat\":\"nok\",\"message\":\"L'utilisateur ${r['nom']} n'est associé qu'à une seule matière. Il est impossible de supprimer cette association.\"}";
+  // Association
+  elseif ( $val && requete('utilisateurs',"UPDATE utilisateurs SET matieres = CONCAT(matieres,',$mid') WHERE id = $id",$mysqli) )
+    $message = "{\"etat\":\"ok\",\"message\":\"L'utilisateur ${r['nom']} a été associé à la matière $matiere.\"}";
+  // Désassociation
+  elseif ( !$val && requete('utilisateurs',"UPDATE utilisateurs SET matieres = TRIM(TRAILING ',' FROM REPLACE(CONCAT(matieres,','),',$mid,',',')) WHERE id = $id",$mysqli)
+                 && requete('notes',"DELETE FROM notes WHERE matiere = $mid AND ( colleur = $id OR eleve = $id)",$mysqli) )
+    $message = "{\"etat\":\"ok\",\"message\":\"L'association de l'utilisateur ${r['nom']} à la matière $matiere a été supprimée.\"}";
+  else
+    exit("{\"etat\":\"nok\",\"message\":\"L'association de l'utilisateur ${r['nom']} à la matière $matiere n'a pas été modifiée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  // Mise à jour de $_SESSION['matieres'] si besoin
+  if ( $id == $_SESSION['id'] )  {
+    $resultat = $mysqli->query("SELECT matieres FROM utilisateurs WHERE id = $id");
+    $r = $resultat->fetch_row();
+    $resultat->free();
+    $_SESSION['matieres'] = $r[0];
+  }
+  exit($message);
 
-        // Traitement d'une modification unique (nom)
-        if ( isset($_REQUEST['champ']) && ( $_REQUEST['champ'] == 'nom' ) )  {
-          if ( !$r['parent'] )
-            $message = '{"etat":"nok","message":"Le nom des répertoires racine des matières ne sont pas modifiables."}';
-          elseif ( !strlen($valeur = trim($mysqli->real_escape_string($_REQUEST['val'])) ) )
-            $message = '{"etat":"nok","message":"Le répertoire n\'a pas pu être modifié. Nom non vide nécessaire."}';
-          elseif ( requete('reps',"UPDATE reps SET nom = '$valeur' WHERE id = $id",$mysqli) )  {
-            $message = '{"etat":"ok","message":"Répertoire modifié"}';
-          }
-          else
-            $message = '{"etat":"nok","message":"Le répertoire n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
-          
-        // Traitement d'une modification globale
-        elseif ( isset($_REQUEST['protection']) )  {
-          $requete = $message = array();
-          $etat = 'ok';
-          // Modification du nom, de l'affichage dans le menu uniquement si pas à la racine
-          if ( $r['parent'] && isset($_REQUEST['nom']) )  {
-            if ( strlen($nom = trim($mysqli->real_escape_string($_REQUEST['nom'])) ) && ( $nom != $r['nom'] ) )
-              $requete[] = "nom = '$nom'";
-            $menu = isset($_REQUEST['menu']) ? 1 : 0;
-            if ( $menu != $r['menu'] )
-              $requete[] = "menu = $menu";
-          }
-          $protection = ( in_array($_REQUEST['protection'],array(0,1,2,3,4,5)) ) ? $_REQUEST['protection'] : 0;
-          if ( $protection != $r['protection'] )
-            $requete[] = "protection = $protection";
-          if ( $requete )  {
-            if ( requete('reps','UPDATE reps SET '.implode(', ',$requete)." WHERE id = $id",$mysqli) ) 
-              $message[] = 'Répertoire modifié ';
-            else  {
-              $message[] = 'Le répertoire n\'a pas pu être modifié. (Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»)';
-              $etat = 'nok';
-            }
-          }
-          // Déplacement du répertoire si $_REQUEST['parent'] non nul et si pas à la racine
-          if ( ( $r['parent'] ) && isset($_REQUEST['parent']) && ctype_digit($parent = $_REQUEST['parent']) && !in_array($parent,array(0,$id,$r['parent'])) )  {
-            // Vérification du répertoire parent
-            $resultat = $mysqli->query("SELECT parents, matiere FROM reps WHERE id = $parent AND FIND_IN_SET(matiere,'${_SESSION['matieres']}') AND NOT FIND_IN_SET($id,parents)");
-            if ( $resultat->num_rows)  {
-              $s = $resultat->fetch_assoc();
-              $resultat->free();
-              $mat = $s['matiere'];
-              $parents = "${s['parents']},$parent";
-              if ( requete('reps',"UPDATE reps SET matiere = $mat, parent = $parent, parents = '$parents' WHERE id = $id",$mysqli)
-                && requete('reps',"UPDATE reps SET matiere = $mat, parents = '$parents,$id' WHERE parent = $id",$mysqli)
-                && requete('docs',"UPDATE docs SET matiere = $mat, parents = '$parents,$id' WHERE parent = $id",$mysqli) )
-                $message[] = 'Répertoire déplacé';
-              else  {
-                $message[] = 'Le répertoire n\'a pas pu être déplacé. (Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»)';
-                $etat = 'nok';
-              }
-            }
-            else  {
-              $message[] = 'Répertoire non déplacé (identifiant de répertoire parent non valide)';
-              $etat = 'nok';
-            }
-          }
-          // Propagation des droits d'accès aux sous-répertoires et documents
-          if ( isset($_REQUEST['propagation']) && ( $r['nb'] ) )  {
-            if ( requete('reps',"UPDATE reps SET protection = $protection WHERE FIND_IN_SET($id,parents)",$mysqli)
-              && requete('docs',"UPDATE docs SET protection = $protection WHERE FIND_IN_SET($id,parents)",$mysqli) ) 
-              $message[] = 'Réglage d\'accès propagé';
-            else  {
-              $message[] = 'Le réglage d\'accès n\a pas pu être propagé. (Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»)';
-              $etat = 'nok';
-            }
-          }
-          // Reconstruction du message
-          $message = $_SESSION['message'] = ( $message ) ? "{\"etat\":\"$etat\",\"message\":\"".implode(', ',$message).'"}' : '';
-        }
+}
 
-        // Suppression
-        elseif ( isset($_REQUEST['supprime']) )  {
-          if ( !$r['parent'] )
-            $message = '{"etat":"nok","message":"Les répertoires racine des matières ne sont pas supprimables."}';
-          elseif ( requete('reps',"DELETE FROM reps WHERE id = $id OR FIND_IN_SET($id,parents)",$mysqli) )  {
-            // Suppression physique
-            $resultat = $mysqli->query("SELECT lien FROM docs WHERE FIND_IN_SET($id,parents)");
-            if ( $resultat->num_rows )  {
-              while ( $r = $resultat->fetch_row() )
-                exec("rm -rf documents/${r[0]}");
-              $resultat->free();
-              requete('docs',"DELETE FROM docs WHERE FIND_IN_SET($id,parents)",$mysqli);
-            }
-            $message = '{"etat":"ok","message":"Répertoire supprimé"}';
-          }
-          else
-            $message = '{"etat":"nok","message":"Le répertoire n\'a pas pu être supprimé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
-      }
-      else
-        $message = '{"etat":"nok","message":"Identifiant non valide"}';
-    }
+////////////////////////////////////////////////////////////////
+// Modification multiple d'associations utilisateurs-matières //
+////////////////////////////////////////////////////////////////
+elseif ( ( $action == 'utilisateurs-matieres' ) && isset($_REQUEST['ids']) && strlen($ids = implode(',',array_filter(explode(',',$_REQUEST['ids']),function($id) { return ctype_digit($id); }))) && isset($_REQUEST['matiere']) && ctype_digit($mid = $_REQUEST['matiere']) && isset($_REQUEST['val']) )  {
+
+  // Vérification que l'identifiant de la matière est valide
+  $resultat = $mysqli->query("SELECT nom FROM matieres WHERE id = $mid");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant de matière non valide"}');
+  $r = $resultat->fetch_row();
+  $resultat->free();
+  $matiere = $r[0];
+  
+  // Vérification que les identifiants d'utilisateur sont valides
+  $resultat = $mysqli->query("SELECT id, nom, prenom, login, FIND_IN_SET('$mid',matieres)>0 AS matiere, LOCATE(',',matieres,3) AS multi FROM utilisateurs WHERE FIND_IN_SET(id,'$ids')");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiants d\'utilisateur non valides"}');  
+  $message = array('ok'=>'','nok'=>'');
     
-    // Nouveau répertoire
-    elseif ( isset($_REQUEST['nom']) && isset($_REQUEST['protection']) && isset($_REQUEST['parent']) && strlen($nom = trim($mysqli->real_escape_string($_REQUEST['nom']))) && ctype_digit($parent=$_REQUEST['parent']) )  {
-      $resultat = $mysqli->query("SELECT parents, matiere FROM reps WHERE id = $parent AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
-      if ( $resultat->num_rows)  {
-        $s = $resultat->fetch_assoc();
-        $resultat->free();
-        $protection = ( in_array($_REQUEST['protection'],array(0,1,2,3,4,5)) ) ? $_REQUEST['protection'] : 0;
-        $menu = isset($_REQUEST['menu']) ? 1 : 0;
-        if ( requete('reps',"INSERT INTO reps SET parent = $parent, parents = '${s['parents']},$parent', nom = '$nom', matiere = ${s['matiere']},
-                             nbrep = 0, nbrep_v = 0, nbdoc = 0, nbdoc_v = 0, protection = $protection, menu = $menu",$mysqli) )
-          $message = $_SESSION['message'] = '{"etat":"ok","message":"Répertoire ajouté"}';
-        else
-          $message = '{"etat":"nok","message":"Le répertoire n\'a pas pu être ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-      }
-      else
-        $message = '{"etat":"nok","message":"Identifiant du répertoire parent non valide"}';
+  // Modification
+  $val = intval( $_REQUEST['val'] > 0 );
+  while ( $r = $resultat->fetch_assoc() )  {
+    $compte = ( strlen($r['nom'].$r['prenom']) ) ? "de <em>${r['prenom']} ${r['nom']}</em>" : "<em>${r['login']}</em>";
+    // Si rien à faire
+    if ( $r['matiere'] == $val )
+      $message['nok'] .= "Le compte $compte n'a pas été modifié, car son association à la matière <em>$matiere</em> est déjà celle demandée.<br>";
+    // Si une seule matière à retirer : impossible
+    elseif ( !$val && ( $r['multi'] == 0 ) )
+      $message['nok'] .= "Le compte $compte n'est associé qu'à une seule matière. Il est impossible de supprimer cette association.<br>";
+    // Association
+    elseif ( $val && requete('utilisateurs',"UPDATE utilisateurs SET matieres = CONCAT(matieres,',$mid') WHERE id = ${r['id']}",$mysqli) )
+      $message['ok'] .= "Le compte $compte a été associé à la matière <em>$matiere</em>.<br>";
+    // Désassociation
+    elseif ( !$val && requete('utilisateurs',"UPDATE utilisateurs SET matieres = TRIM(TRAILING ',' FROM REPLACE(CONCAT(matieres,','),',$mid,',',')) WHERE id = ${r['id']}",$mysqli)
+                   && requete('notes',"DELETE FROM notes WHERE matiere = $mid AND ( colleur = ${r['id']} OR eleve = ${r['id']})",$mysqli) )
+      $message['ok'] .= "L'association du compte $compte à la matière <em>$matiere</em> a été supprimée.<br>";
+    else
+      $message['nok'] .= "L'association de l'utilisateur ${r['nom']} à la matière $matiere n'a pas été modifiée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'».<br>';
+    // Mise à jour de $_SESSION['matieres'] si besoin
+    if ( $r['id'] == $_SESSION['id'] )  {
+      $res = $mysqli->query("SELECT matieres FROM utilisateurs WHERE id = ${r['id']}");
+      $r = $res->fetch_row();
+      $res->free();
+      $_SESSION['matieres'] = $r[0];
     }
+  }
+  $resultat->free();
+  if ( strlen($message['ok']) )
+    exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"${message['ok']}${message['nok']}\"}");
+  exit("{\"etat\":\"nok\",\"message\":\"${message['nok']}\"}");
+}
 
-    // Mise à jour des champs nbrep/nbdoc dans la table 'reps'
-    $mysqli->query('UPDATE reps AS r SET 
-      nbrep = (SELECT COUNT(id) FROM (SELECT * FROM reps) AS r1 WHERE r.id = r1.parent),
-      nbrep_v = (SELECT COUNT(id) FROM (SELECT * FROM reps WHERE reps.protection<5) AS r2 WHERE r.id = r2.parent),
-      nbdoc = (SELECT COUNT(id) FROM docs AS d WHERE r.id = d.parent),
-      nbdoc_v = (SELECT COUNT(id) FROM docs AS d WHERE r.id = d.parent AND d.protection<5)');
-    $mysqli->query('ALTER TABLE reps ORDER BY parents,nom');
-    // Mise à jour des champs 'docs' dans la table 'matieres' (pour le menu)
-    $mysqli->query('UPDATE matieres SET docs = (SELECT IF(SUM(nbdoc_v),1-(protection=5 AND parent=0),0) FROM reps WHERE matiere = matieres.id)');
-    
-    break;
-
-////////////////////////////////
-// Modification des documents //
-////////////////////////////////
-  case 'docs':
-
-    // Vérification que l'identifiant est valide
-    if ( isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
-      $resultat = $mysqli->query("SELECT nom, protection, ext, lien, parent, matiere FROM docs WHERE id = $id AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
-      if ( $resultat->num_rows )  {
-        $r = $resultat->fetch_assoc();
-        $resultat->free();
-
-        // Traitement d'une modification unique (nom)
-        if ( isset($_REQUEST['champ']) && ( $_REQUEST['champ'] == 'nom' ) )  {
-          if ( !strlen( $valeur = trim($_REQUEST['val']) ) )
-            $message = '{"etat":"nok","message":"Le document n\'a pas pu être modifié. Nom non vide nécessaire."}';
-          elseif ( $valeur != $r['nom'] )  {
-            setlocale(LC_CTYPE, "fr_FR.UTF-8");
-            $nom = substr((str_replace(array($r['ext'],'\\','/'),array('','-','-'),$valeur)),0,100);
-            // real_escape_string seulement pour la requête SQL
-            $nouveau_nom = $mysqli->real_escape_string($nom);
-            if ( requete('docs',"UPDATE docs SET nom = '$nouveau_nom', nom_nat = '".zpad($nouveau_nom)."' WHERE id = $id",$mysqli) )  {
-              exec('mv documents/'.escapeshellarg("${r['lien']}/${r['nom']}${r['ext']}").' documents/'.escapeshellarg("${r['lien']}/$nom${r['ext']}"));
-              $message = '{"etat":"ok","message":"Nom du document modifié"}';
-              $mysqli->query('ALTER TABLE docs ORDER BY parents,nom_nat');
-              // Modification de l'éventuelle information récente
-              if ( $r['protection'] < 5 )
-                recent($mysqli,3,$id,$r['matiere'],array('nom'=>$nouveau_nom,'ancien_nom'=>$r['nom']));
-            }
-            else
-              $message = '{"etat":"nok","message":"Le nom du document n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-          }
-        }
-
-        // Traitement d'une modification globale
-        elseif ( isset($_REQUEST['nom']) && isset($_REQUEST['parent']) && isset($_REQUEST['protection']) && strlen($nom = trim($_REQUEST['nom'])) )  {
-          $requete = $message = array();
-          $etat = 'ok';
-          // Modification du nom
-          setlocale(LC_CTYPE, "fr_FR.UTF-8");
-          $nom = substr(basename(str_replace(array($r['ext'],'\\'),array('','/'),$nom)),0,100);
-          if ( $nom != $r['nom'] )  {
-            // real_escape_string seulement pour la requête SQL
-            $nouveau_nom = $mysqli->real_escape_string($nom);
-            if ( requete('docs',"UPDATE docs SET nom = '$nouveau_nom', nom_nat = '".zpad($nouveau_nom)."' WHERE id = $id",$mysqli) )  {
-              exec('mv documents/'.escapeshellarg("${r['lien']}/${r['nom']}${r['ext']}").' documents/'.escapeshellarg("${r['lien']}/$nom${r['ext']}"));
-              $message[] = 'Nom du document modifié';
-              // Modification de l'éventuelle information récente
-              if ( $r['protection'] < 5 )
-                recent($mysqli,3,$id,$r['matiere'],array('nom'=>$nouveau_nom,'ancien_nom'=>$r['nom']));
-            }
-            else  {
-              $message[] = 'Le nom du document n\'a pas pu être modifié. (Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»)';
-              $etat = 'nok';
-            }
-          }
-          // Modification de la protection
-          $protection = ( in_array($_REQUEST['protection'],array(0,1,2,3,4,5)) ) ? $_REQUEST['protection'] : 0;
-          if ( $protection != $r['protection'] )  {
-            if ( requete('docs',"UPDATE docs SET protection = $protection WHERE id = $id",$mysqli) )  {
-              $message[] = 'Accès du document modifié';
-              // Si $protection = 5, on cherche à supprimer l'info récente
-              if ( $protection == 5 )
-                recent($mysqli,3,$id,$r['matiere']);
-              // Si doc avant protégé, on crée une info récente de nouveau doc
-              elseif ( $r['protection'] == 5 )  {
-                $resultat = $mysqli->query("SELECT GROUP_CONCAT( reps.nom ORDER BY FIND_IN_SET(reps.id,docs.parents) SEPARATOR '/' ) AS path, docs.nom
-                                            FROM docs LEFT JOIN reps ON FIND_IN_SET(reps.id,docs.parents) WHERE docs.id = $id");
-                $s = $resultat->fetch_assoc();
-                $resultat->free();
-                $path = $mysqli->real_escape_string("${s['path']}/${s['nom']}");
-                recent($mysqli,3,$id,$r['matiere'],array('titre'=>$path, 'lien'=>"download?id=$id", 'texte'=>"<p>Nouveau document&nbsp;: <a href=\"download?id=$id\">$path</a></p>", 'matiere'=>$r['matiere'], 'protection'=>$protection),$r['ext']);
-              }
-            }
-            else  {
-              $message[] = 'L\'accès du document n\'a pas pu être modifié. (Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»)';
-              $etat = 'nok';
-              $protection = $r['protection'];
-            }
-          }
+/////////////////////////////////////////////
+// Modification des groupes d'utilisateurs //
+/////////////////////////////////////////////
+elseif ( ( $action == 'groupes' ) && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
+
+  // Vérification que l'identifiant est valide
+  $resultat = $mysqli->query("SELECT nom, mails, notes, utilisateurs FROM groupes WHERE id = $id");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
 
-          // Déplacement dans un autre répertoire
-          if ( ctype_digit($parent = $_REQUEST['parent']) && $parent && ( $parent != $r['parent'] ) )  {
-            // Vérification du répertoire parent
-            $resultat = $mysqli->query("SELECT parents, matiere FROM reps WHERE id = $parent AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
-            if ( $resultat->num_rows )  {
-              $s = $resultat->fetch_assoc();
-              $resultat->free();
-              if ( requete('docs',"UPDATE docs SET parent = '$parent', parents = '${s['parents']},$parent', matiere = ${s['matiere']} WHERE id = $id",$mysqli) )  {
-                $message[] = 'Document déplacé';
-                // Modification de l'information récente si document visible
-                if ( $protection < 5 )  {
-                  $resultat = $mysqli->query("SELECT GROUP_CONCAT( reps.nom ORDER BY FIND_IN_SET(reps.id,docs.parents) SEPARATOR '/' ) AS path, docs.nom, docs.matiere
-                                              FROM docs LEFT JOIN reps ON FIND_IN_SET(reps.id,docs.parents) WHERE docs.id = $id");
-                  $s = $resultat->fetch_assoc();
-                  $resultat->free();
-                  recent($mysqli,3,$id,$r['matiere'],array('chemin'=>$mysqli->real_escape_string("${s['path']}/${s['nom']}"), 'matiere'=>$s['matiere']));
-                }
-              }
-              else  {
-                $message[] = 'Le document n\'a pas pu être déplacé. (Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»)';
-                $etat = 'nok';
-              }
-            }
-            else  {
-              $message[] = 'Document non déplacé (identifiant de répertoire parent non valide)';
-              $etat = 'nok';
-            }
-          }
+  // Suppression
+  if ( isset($_REQUEST['supprime']) )  {
+    if ( requete('groupes',"DELETE FROM groupes WHERE id = $id",$mysqli) ) 
+      exit("{\"etat\":\"ok\",\"message\":\"Le groupe ${r['nom']} a été supprimé.\"}");
+    exit("{\"etat\":\"nok\",\"message\":\"Le groupe ${r['nom']} n'a pas été supprimé. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
 
-          // Mise à jour d'un document
-          if ( isset($_FILES['fichier']['tmp_name']) && is_uploaded_file($_FILES['fichier']['tmp_name']) )  {
-            // Changement d'extension interdit
-            $ext = ( strpos($_FILES['fichier']['name'],'.') ) ? strrchr($_FILES['fichier']['name'],'.') : '';
-            if ( $ext != $r['ext'] )  {
-              $message[] = 'Document non mis à jour (extension du fichier envoyé non conforme)';
-              $etat = 'nok';
-            }
-            // Déplacement du document uploadé au bon endroit
-            elseif ( move_uploaded_file($_FILES['fichier']['tmp_name'],"documents/${r['lien']}/${r['nom']}${r['ext']}") )  {
-              // Gestion de la taille
-              $taille = intval($_FILES['fichier']['size']/1024);
-              $taille = ( $taille < 1024 ) ? "$taille&nbsp;ko" : intval($taille/1024).'&nbsp;Mo';
-              // Modifications dans la base de données
-              requete('docs',"UPDATE docs SET upload = CURDATE(), taille = '$taille' WHERE id = $id",$mysqli);
-              // Info récente si document visible (éventuellement nouvelle)
-              if ( $protection < 5 )  {
-                // Besoin du chemin si pas d'info récente liée au document
-                $resultat = $mysqli->query("SELECT GROUP_CONCAT( reps.nom ORDER BY FIND_IN_SET(reps.id,docs.parents) SEPARATOR '/' ) AS path, docs.nom
-                                            FROM docs LEFT JOIN reps ON FIND_IN_SET(reps.id,docs.parents) WHERE docs.id = $id");
-                $s = $resultat->fetch_assoc();
-                $resultat->free();
-                recent($mysqli,3,$id,$r['matiere'],array('maj'=>$mysqli->real_escape_string("${s['path']}/${s['nom']}"),'p'=>$protection,'e'=>$ext));
-              }
-              $message[] = 'Document mis à jour';
-            }
-            else  {
-              $message[] = 'Document non mis à jour (problème d\'écriture du fichier)';
-              $etat = 'nok';
-            }
-          }
-          // Reconstruction du message
-          $message = $_SESSION['message'] = ( $message ) ? "{\"etat\":\"$etat\",\"message\":\"".implode(', ',$message).'"}' : '';
-        }
-        
-        // Suppression d'un document
-        elseif ( isset($_REQUEST['supprime']) )  {
-          if ( requete('docs',"DELETE FROM docs WHERE id = $id",$mysqli) )  {
-            // Suppression physique
-            exec("rm -rf documents/${r['lien']}");
-            $message = '{"etat":"ok","message":"Document supprimé"}';
-            recent($mysqli,3,$id,$r['matiere']);
-          }
-          else
-            $message = '{"etat":"nok","message":"Le document n\'a pas pu être supprimé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
-      }
-      else
-        $message = '{"etat":"nok","message":"Identifiant non valide"}';
-    }
-    
-    // Nouveau document
-    elseif ( isset($_FILES['fichier']['tmp_name']) && is_uploaded_file($_FILES['fichier']['tmp_name']) && isset($_REQUEST['nom']) && isset($_REQUEST['protection']) && isset($_REQUEST['parent']) && ctype_digit($parent = $_REQUEST['parent']) )  {
-      // Vérification du répertoire parent
-      $resultat = $mysqli->query("SELECT parents, matiere FROM reps WHERE id = $parent AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')");
-      if ( $resultat->num_rows)  {
-        $r = $resultat->fetch_assoc();
-        $resultat->free();
-        // Vérifications des données envoyées (on fait confiance aux utilisateurs connectés pour ne pas envoyer de scripts malsains)
-        $nom = $_FILES['fichier']['name'];
-        // $est ne doit pas faire plus de 4 caractères sinon fichier plus accessible
-        $ext = ( strpos($nom,'.') ) ? substr(strrchr($nom,'.'),0,4) : '';
-        setlocale(LC_CTYPE, "fr_FR.UTF-8");
-        $nom = substr(basename(str_replace(array($ext,'\\'),array('','/'), ( strlen($_REQUEST['nom']) ) ? $_REQUEST['nom'] : $nom )),0,100);
-        $protection = ( in_array($_REQUEST['protection'],array(0,1,2,3,4,5)) ) ? $_REQUEST['protection'] : 0;
-        // Création du répertoire particulier
-        $lien = substr(sha1(mt_rand()),0,15);
-        while ( is_dir("documents/$lien") )
-          $lien = substr(sha1(mt_rand()),0,15);
-        mkdir("documents/$lien");
-        // Gestion de la taille
-        $taille = intval($_FILES['fichier']['size']/1024);
-        $taille = ( $taille < 1024 ) ? "$taille&nbsp;ko" : intval($taille/1024).'&nbsp;Mo';
-        // Déplacement du document uploadé au bon endroit
-        if ( move_uploaded_file($_FILES['fichier']['tmp_name'],"documents/$lien/$nom$ext") )  {
-          // Écriture MySQL
-          if ( requete('docs',"INSERT INTO docs SET parent = $parent, parents = '${r['parents']},$parent',
-                               matiere = ${r['matiere']}, nom = '".$mysqli->real_escape_string($nom).'\', nom_nat = \''.zpad($mysqli->real_escape_string($nom))."', upload = CURDATE(),
-                               taille = '$taille', lien = '$lien', ext='".$mysqli->real_escape_string($ext)."', protection = $protection",$mysqli) )  {
-            $id = $mysqli->insert_id;
-            $message = $_SESSION['message'] = '{"etat":"ok","message":"Document ajouté"}';
-            // Mise à jour des informations récentes
-            if ( $protection < 5 )  {
-              $resultat = $mysqli->query("SELECT GROUP_CONCAT( reps.nom ORDER BY FIND_IN_SET(reps.id,docs.parents) SEPARATOR '/' ) AS path
-                                          FROM docs LEFT JOIN reps ON FIND_IN_SET(reps.id,docs.parents) WHERE docs.id = $id");
-              $s = $resultat->fetch_assoc();
-              $resultat->free();
-              $path = $mysqli->real_escape_string("${s['path']}/$nom");
-              recent($mysqli,3,$id,$r['matiere'],array('titre'=>$path, 'lien'=>"download?id=$id", 'texte'=>"<p>Nouveau document&nbsp;: <a href=\"download?id=$id\">$path</a></p>", 'matiere'=>$r['matiere'], 'protection'=>$protection),$ext);
-            }
-          }
-          else  {
-            // Retour en arrière
-            exec("rm -rf documents/$lien");
-            $message = '{"etat":"nok","message":"Le document n\'a pas pu être ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-          }
+  // Modification
+  if ( isset($_REQUEST['champ']) )  {
+    switch ( $champ = $_REQUEST['champ'] )  {
+      case 'nom':
+        if ( !strlen($val = trim($mysqli->real_escape_string($_REQUEST['val']))) )
+          exit("{\"etat\":\"nok\",\"message\":\"Le nom du groupe ${r['nom']} n'a pas été modifié&nbsp;: le nom ne peut pas être vide.\"}");
+        if ( requete('groupes',"UPDATE groupes SET nom = '$val', nom_nat = '".zpad($val)."' WHERE id = $id",$mysqli) )  {
+          $mysqli->query('ALTER TABLE groupes ORDER BY nom_nat');
+          exit("{\"etat\":\"ok\",\"message\":\"Le nom du groupe ${r['nom']} a été modifié.\"}");
         }
-        else
-          $message = '{"etat":"nok","message":"Le document n\'a pas pu  ajouté (problème d\'écriture du fichier)."}';        
-      }
-      else
-        $message = '{"etat":"nok","message":"Identifiant du répertoire parent non valide"}';
+        exit("{\"etat\":\"nok\",\"message\":\"Le nom du groupe ${r['nom']} n'a pas été modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+      case 'mails':
+      case 'notes':
+        $val = intval( $_REQUEST['val'] > 0 );
+        if ( requete('groupes',"UPDATE groupes SET $champ = $val WHERE id = $id",$mysqli) )
+          exit("{\"etat\":\"ok\",\"message\":\"Le groupe ${r['nom']} a été modifié.\"}");
+        exit("{\"etat\":\"nok\",\"message\":\"Le groupe ${r['nom']} n'a pas été modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+      case 'utilisateurs':
+        if ( !isset($_REQUEST['uids']) || !strlen($ids = implode(',',array_filter(explode(',',$_REQUEST['uids']),function($id) { return ctype_digit($id); }))) )
+          exit("{\"etat\":\"nok\",\"message\":\"La composition du groupe ${r['nom']} n'a pas été modifiée&nbsp;: le groupe ne peut pas être vide.\"}");
+        // Vérification des identifiants d'utilisateurs - tous utilisateurs autorisés
+        $resultat = $mysqli->query("SELECT GROUP_CONCAT(id) FROM utilisateurs WHERE FIND_IN_SET(id,'$ids')");
+        $s = $resultat->fetch_row();
+        $resultat->free();
+        if ( !$s[0] )
+          exit("{\"etat\":\"nok\",\"message\":\"La composition du groupe ${r['nom']} n'a pas été modifiée&nbsp;: le groupe ne peut pas être vide.\"}");
+        if ( requete('groupes',"UPDATE groupes SET utilisateurs = '${s[0]}' WHERE id = $id",$mysqli) )
+          exit("{\"etat\":\"ok\",\"message\":\"La composition du groupe ${r['nom']} a été modifié.\"}");
+        exit("{\"etat\":\"nok\",\"message\":\"La composition du groupe ${r['nom']} n'a pas été modifié. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
     }
+  }
+  exit('{"etat":"nok","message":"Champ non valide"}');
+}
 
-    // Mise à jour des champs nbdoc dans la table 'reps'
-    $mysqli->query('UPDATE reps AS r SET 
-      nbdoc = (SELECT COUNT(id) FROM docs AS d WHERE r.id = d.parent),
-      nbdoc_v = (SELECT COUNT(id) FROM docs AS d WHERE r.id = d.parent AND d.protection<5)');
-    $mysqli->query('ALTER TABLE docs ORDER BY parents,nom_nat');
-    // Mise à jour des champs 'docs' dans la table 'matieres' (pour le menu)
-    $mysqli->query('UPDATE matieres SET docs = (SELECT IF(SUM(nbdoc_v),1-(protection=5 AND parent=0),0) FROM reps WHERE matiere = matieres.id)');
-    
-    break;
+//////////////////////////////////////
+// Ajout d'un groupe d'utilisateurs //
+//////////////////////////////////////
+elseif ( ( $action == 'ajout-groupe' ) && isset($_REQUEST['nom']) && strlen($nom = trim($mysqli->real_escape_string($_REQUEST['nom']))) && isset($_REQUEST['uids']) && strlen($ids = implode(',',array_filter(explode(',',$_REQUEST['uids']),function($id) { return ctype_digit($id); })))  )  {
+
+  // Vérification des identifiants d'utilisateurs - tous utilisateurs autorisés
+  $resultat = $mysqli->query("SELECT GROUP_CONCAT(id) FROM utilisateurs WHERE FIND_IN_SET(id,'$ids')");
+  $r = $resultat->fetch_row();
+  $resultat->free();
+  if ( !$r[0] )
+    exit('{"etat":"nok","message":"Un groupe ne peut pas être vide."}');
+  // Champs mails et notes
+  $mails = intval(isset($_REQUEST['mails']));
+  $notes = intval(isset($_REQUEST['notes']));
+  // Écriture
+  if ( requete('groupes',"INSERT INTO groupes SET nom = '$nom', nom_nat = '".zpad($nom)."', mails = $mails, notes = $notes, utilisateurs = '${r[0]}'",$mysqli) )  {
+    $mysqli->query('ALTER TABLE groupes ORDER BY nom_nat');
+    exit($_SESSION['message'] = '{"etat":"ok","message":"Le groupe a été ajouté."}');
+  }
+  exit('{"etat":"nok","message":"Le groupe n\'a pas été ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+}
 
 //////////////////////////////
 // Modification du planning //
 //////////////////////////////
-  case 'semaines':
-
-    // Récupérations des donnees envoyées
-    $colles = ( isset($_REQUEST['colles']) ) ? $_REQUEST['colles'] : array();
-    $vacances = $_REQUEST['vacances'];
-    // Comparaison et modification
-    $modif = array();
-    $resultat = $mysqli->query('SELECT id, colle, vacances, DATE_FORMAT(debut,\'%d/%m/%Y\') AS debut FROM semaines');
-    while ( $r = $resultat->fetch_assoc() )  {
-      if ( !is_numeric($v = $vacances[$r['id']]) )
-        $v = 0;
-      $c = ( isset($colles[$r['id']]) && !$v ) ? 1 : 0;
-      if ( ( $c != $r['colle'] ) || ( $v != $r['vacances'] ) )  {
-        requete('semaines',"UPDATE semaines SET colle = $c, vacances = $v WHERE id = ${r['id']}",$mysqli);
-        $modif[] = "semaine du ${r['debut']}";
-      }
-    }
-    // Message à afficher
-    if ( $modif )
-      $message = '{"etat":"ok","message":"Modifications réalisées ('.implode(',',$modif).')"}';
-
-    break;
-
-///////////////////////////////////////
-// Modification des groupes d'élèves //
-///////////////////////////////////////
-  case 'groupes':
-
-    // Vérification que l'identifiant est valide
-    if ( isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
-      $resultat = $mysqli->query("SELECT nom, mailnotes, eleves FROM groupes WHERE id = $id");
-      if ( $resultat->num_rows )  {
-        $r = $resultat->fetch_assoc();
-        $resultat->free();
-
-        // Traitement d'une modification unique (nom, mailnotes)
-        if ( isset($_REQUEST['champ']) && in_array($champ = $_REQUEST['champ'],array('nom','mailnotes')) )  {
-          switch ( $champ )  {
-            case 'nom':
-              if ( !strlen($valeur = $mysqli->real_escape_string($_REQUEST['val'])) )
-                $message = '{"etat":"nok","message":"Groupe non modifié. Nom non vide nécessaire."}';
-              elseif ( requete('groupes',"UPDATE groupes SET nom = '$valeur', nom_nat = '".zpad($valeur)."' WHERE id = $id",$mysqli) )  {
-                $message = '{"etat":"ok","message":"Groupe modifié"}';
-                $mysqli->query('ALTER TABLE groupes ORDER BY nom_nat');
-              }
-              else
-                $message = '{"etat":"nok","message":"Le groupe n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-              break;
-            case 'mailnotes':
-              $valeur = ( in_array($_REQUEST['val'],array(1,2,3)) ) ? $_REQUEST['val'] : 0;
-              $message = ( requete('groupes',"UPDATE groupes SET mailnotes = $valeur WHERE id = $id",$mysqli) ) ? '{"etat":"ok","message":"Groupe modifié"}' : '{"etat":"nok","message":"Le groupe n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-          }
-        }
-
-        // Ajout/Suppression d'un élève dans le groupe
-        elseif ( isset($_REQUEST['eleve']) && isset($_REQUEST['ok']) )  {
-          // Suppression de l'élève dans le groupe
-          if ( $_REQUEST['ok'] == 0 )  {
-            if ( !strpos($r['eleves'],',') )
-              $message = '{"etat":"nok","message":"Groupe non modifié. Un utilisateur au minimum est nécessaire."}';
-            elseif( in_array($eleve = $_REQUEST['eleve'],explode(',',$r['eleves'])) )
-              $message = ( requete('groupes',"UPDATE groupes SET eleves = TRIM(BOTH ',' FROM REPLACE(CONCAT(',',eleves,','),',$eleve,',',')) WHERE id = $id",$mysqli) ) ? '{"etat":"ok","message":"Groupe modifié"}' : '{"etat":"nok","message":"Le groupe n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-          }
-          // Ajout de l'élève dans le groupe
-          elseif ( ctype_digit($eleve = $_REQUEST['eleve']) && !in_array($eleve,explode(',',$r['eleves'])) )  {
-            // Vérification de l'identifiant
-            $resultat = $mysqli->query("SELECT id FROM utilisateurs WHERE id = $eleve AND autorisation = 2 AND LENGTH(mdp)=40");
-            if ( $resultat->num_rows )  {
-              $resultat->free();
-              $message = ( requete('groupes',"UPDATE groupes SET eleves = CONCAT(eleves,',$eleve') WHERE id = $id",$mysqli) ) ? '{"etat":"ok","message":"Groupe modifié"}' : '{"etat":"nok","message":"Le groupe n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-            }
-            else
-              $message = '{"etat":"nok","message":"Groupe non modifié. Identifiant d\'élève incorrect."}';
-          }
-        }
-
-        // Suppression
-        elseif ( isset($_REQUEST['supprime']) )
-          $message = ( requete('groupes',"DELETE FROM groupes WHERE id = $id",$mysqli) ) ? '{"etat":"ok","message":"Groupe supprimé"}' : '{"etat":"nok","message":"La suppression n\'a pas été réalisée. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-      }
-      else
-        $message = '{"etat":"nok","message":"Identifiant non valide"}';
-    }
+elseif ( $action == 'planning' )  {
 
-    // Nouveau groupe
-    elseif ( isset($_REQUEST['nom']) && isset($_REQUEST['mailnotes']) && isset($_REQUEST['eleves']) && in_array($mailnotes = $_REQUEST['mailnotes'],array(1,2,3)) )  {
-      if ( !strlen($nom = $mysqli->real_escape_string($_REQUEST['nom'])) )
-        $message = '{"etat":"nok","message":"Groupe non ajouté. Nom non vide nécessaire."}';
-      else  {
-        $resultat = $mysqli->query('SELECT GROUP_CONCAT(id) FROM utilisateurs WHERE autorisation = 2 AND LENGTH(mdp)=40');
-        $r = $resultat->fetch_row();
-        $resultat->free();
-        if ( !strlen($eleves = implode(',',array_intersect(explode(',',$r[0]),explode(',',$_REQUEST['eleves'])))) )
-          $message = '{"etat":"nok","message":"Groupe non ajouté. Un ou plusieurs élèves sont nécessaires."}';
-        elseif ( requete('groupes',"INSERT INTO groupes SET nom = '$nom', nom_nat = '".zpad($nom)."', mailnotes = $mailnotes, eleves = '$eleves'",$mysqli) )  {
-          $message = $_SESSION['message'] = '{"etat":"ok","message":"Groupe ajouté"}';
-          $mysqli->query('ALTER TABLE groupes ORDER BY nom_nat');
-        }
-        else
-          $message = '{"etat":"nok","message":"Le groupe n\'a pas pu être ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-      }
+  // Récupérations des donnees envoyées
+  $colles = ( isset($_REQUEST['colles']) ) ? $_REQUEST['colles'] : array();
+  $vacances = ( isset($_REQUEST['vacances']) ) ? $_REQUEST['vacances'] : array();
+  // Comparaison et modification
+  $modif = array();
+  $resultat = $mysqli->query('SELECT id, colle, vacances, DATE_FORMAT(debut,\'%d/%m/%Y\') AS debut FROM semaines');
+  while ( $r = $resultat->fetch_assoc() )  {
+    $v = intval( ( ctype_digit($v = $vacances[$r['id']]) && ( $v < 5 ) ) ? $v : 0 );
+    $c = intval( isset($colles[$r['id']]) && !$v );
+    if ( ( $c != $r['colle'] ) || ( $v != $r['vacances'] ) )  {
+      requete('semaines',"UPDATE semaines SET colle = $c, vacances = $v WHERE id = ${r['id']}",$mysqli);
+      $modif[] = "semaine du ${r['debut']}";
     }
-    break;
+  }
+  // Message à afficher
+  exit( $modif ? '{"etat":"ok","message":"Les modifications ont été réalisées ('.implode(', ',$modif).')."}' : '{"etat":"ok","message":"Aucune modification n\'a été réalisée."}');
+}
 
 //////////////////////////////
 // Modification de l'agenda //
 //////////////////////////////
-  case 'agenda':
-    // Traitement d'une suppression
-    if ( isset($_REQUEST['supprime']) && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
-      $resultat = $mysqli->query("SELECT matiere FROM agenda WHERE id = $id");
-      if ( $resultat->num_rows )  {
-        $r = $resultat->fetch_row();
-        $resultat->free();
-        if ( requete('agenda',"DELETE FROM agenda WHERE id = $id",$mysqli) )  {
-          $message = '{"etat":"ok","message":"Suppression réalisée"}';
-          recent($mysqli,5,$id,$r[0]);
-        }
-        else
-          $message = '{"etat":"nok","message":"L\'événement n\'a pas pu être supprimé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-      }
-      else
-        $message = '{"etat":"nok","message":"Identifiant non valide"}';
-    }
+elseif ( $action == 'agenda-elems' )  {
   
-    // Traitement d'une modification/d'un ajout d'événement :
-    // validation préliminaire
-    elseif ( isset($_REQUEST['type']) && isset($_REQUEST['matiere']) && isset($_REQUEST['debut']) && isset($_REQUEST['fin']) && ( ctype_digit($tid = $_REQUEST['type']) ) && ( ctype_digit($mid = $_REQUEST['matiere']) ) )  {
-      // Validation des dates
-      $debut = $_REQUEST['debut'];
-      if ( strlen($debut) == 10 )
-        $debut .= ' 00h00';
-      elseif ( strlen($debut) == 15 )
-        $debut = substr($debut,0,11).'0'.substr($debut,11);
-      $fin = $_REQUEST['fin'];
-      if ( strlen($fin) == 10 )
-        $fin .= ' 00h00';
-      elseif ( strlen($fin) == 15 )
-        $fin = substr($fin,0,11).'0'.substr($fin,11);
-      $debut = preg_filter('/(\d{2})\/(\d{2})\/(\d{4}) (\d{1,2})h(\d{2})/','$3-$2-$1 $4:$5',$debut);
-      $fin = preg_filter('/(\d{2})\/(\d{2})\/(\d{4}) (\d{1,2})h(\d{2})/','$3-$2-$1 $4:$5',$fin);
-      if ( is_null($debut) || is_null($fin) || ( $debut > $fin ) )
-        $message = '{"etat":"nok","message":"Les dates/heures choisies ne sont pas valables."}';
-      else  {
-        // Validation des dates : l'événement doit se trouver en partie dans
-        // l'année scolaire
-        $resultat = $mysqli->query("SELECT id FROM semaines WHERE debut <= '$fin' AND ADDDATE(debut,80) >= '$debut' LIMIT 1");
-        if ( !$resultat->num_rows )
-          $message = '{"etat":"nok","message":"Les dates de l\'événement le placent hors de l\'année scolaire."}';
-        else  {
-          $resultat->free();
-          // Pour les informations récentes : jour et mois
-          $jour = substr($debut,8,2);
-          $mois = substr($debut,5,2);
-          $annee = substr($debut,2,2);
-          // Validation du type d'événement
-          $resultat = $mysqli->query("SELECT nom FROM `agenda-types` WHERE id = $tid");
-          if ( !$resultat->num_rows )
-            $message = '{"etat":"nok","message":"Type d\'événement non valide."}';
-          else  {
-            // Besoin du nom pour les informations récentes
-            $r = $resultat->fetch_row();
-            $resultat->free();
-            $type = $r[0];
-            if ( $mid )  {
-              // Validation de la matière si non nulle
-              $resultat = $mysqli->query("SELECT nom FROM matieres WHERE id = $mid");
-              if ( !$resultat->num_rows )
-                $message = '{"etat":"nok","message":"Matière non valide."}';
-              else  {
-                // Besoin du nom pour les informations récentes
-                $r = $resultat->fetch_row();
-                $resultat->free();
-                $matiere = $r[0];
-              }
-            }
-            else
-              $matiere = '';
-          }
-        }
-      }
-      // Sortie prématurée si message d'erreur
-      if ( strlen($message) )
-        break;
-      $texte = $mysqli->real_escape_string($_REQUEST['texte']);
-
-      // Vérification que l'identifiant est valide
-      if ( isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
-        $resultat = $mysqli->query("SELECT id FROM agenda WHERE id = $id");
-        if ( $resultat->num_rows )  {
-          $resultat->free();
-          // Écriture dans la base de données
-          if ( requete('agenda',"UPDATE agenda SET matiere = $mid, type = $tid, debut = '$debut', fin = '$fin', texte = '$texte' WHERE id = $id", $mysqli) )  {
-            $message = '{"etat":"ok","message":"Événement modifié"}';
-            recent($mysqli,5,$id,$mid,array('titre'=>"$jour/$mois - $type ".( strlen($matiere) ? "en $matiere " : '').'(mise à jour)', 'lien'=>"agenda?mois=$annee$mois", 'texte'=>$texte, 'matiere'=>$mid, 'protection'=>0));
-            $mysqli->query('ALTER TABLE agenda ORDER BY fin, debut');
-          }
-          else
-            $message = '{"etat":"nok","message":"L\'événement n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-        }
-        else
-          $message = '{"etat":"nok","message":"Identifiant non valide"}';
-      }
-      // Nouvel événement
-      elseif ( requete('agenda',"INSERT INTO agenda SET matiere = $mid, type = $tid, debut = '$debut', fin = '$fin', texte = '$texte'", $mysqli) )  {
-        $message = '{"etat":"ok","message":"Événement ajouté"}';
-        recent($mysqli,5,$mysqli->insert_id,$mid,array('titre'=>"$jour/$mois - $type ".( strlen($matiere) ? "en $matiere " : ''), 'lien'=>"agenda?mois=$annee$mois", 'texte'=>$texte, 'matiere'=>$mid, 'protection'=>0));
+  // Traitement d'une suppression
+  if ( isset($_REQUEST['supprime']) && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
+    $resultat = $mysqli->query("SELECT matiere FROM agenda WHERE id = $id");
+    if ( !$resultat->num_rows )
+      exit('{"etat":"nok","message":"Identifiant non valide"}');
+    $r = $resultat->fetch_row();
+    $resultat->free();
+    if ( requete('agenda',"DELETE FROM agenda WHERE id = $id",$mysqli) )  {
+      recent($mysqli,5,$id,$r[0]);
+      exit($_SESSION['message'] = '{"etat":"ok","message":"La suppression a été réalisée."}');
+    }
+    exit('{"etat":"nok","message":"L\'événement n\'a pas été supprimé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+
+  // Traitement d'une modification/d'un ajout d'événement :
+  // validation préliminaire
+  if ( isset($_REQUEST['type']) && isset($_REQUEST['matiere']) && isset($_REQUEST['debut']) && isset($_REQUEST['fin']) && ctype_digit($tid = $_REQUEST['type']) && ctype_digit($mid = $_REQUEST['matiere']) )  {
+    // Validation des dates
+    $debut = $_REQUEST['debut'];
+    if ( strlen($debut) == 10 )
+      $debut .= ' 00h00';
+    elseif ( strlen($debut) == 15 )
+      $debut = substr($debut,0,11).'0'.substr($debut,11);
+    $fin = $_REQUEST['fin'];
+    if ( strlen($fin) == 10 )
+      $fin .= ' 00h00';
+    elseif ( strlen($fin) == 15 )
+      $fin = substr($fin,0,11).'0'.substr($fin,11);
+    $debut = preg_filter('/(\d{2})\/(\d{2})\/(\d{4}) (\d{1,2})h(\d{2})/','$3-$2-$1 $4:$5',$debut);
+    $fin = preg_filter('/(\d{2})\/(\d{2})\/(\d{4}) (\d{1,2})h(\d{2})/','$3-$2-$1 $4:$5',$fin);
+    if ( is_null($debut) || is_null($fin) || ( $debut > $fin ) )
+      exit('{"etat":"nok","message":"Les dates/heures choisies ne sont pas valables."}');
+    // Validation des dates : l'événement doit se trouver en partie dans
+    // l'année scolaire
+    $resultat = $mysqli->query("SELECT id FROM semaines WHERE debut <= '$fin' AND debut >= SUBDATE('$debut',80) LIMIT 1");
+    if ( !$resultat->num_rows )
+      exit('{"etat":"nok","message":"Les dates de l\'événement le placent hors de l\'année scolaire."}');
+    $resultat->free();
+    // Pour les informations récentes : jour et mois
+    $jour = substr($debut,8,2);
+    $mois = substr($debut,5,2);
+    $annee = substr($debut,2,2);
+    // Validation du type d'événement
+    $resultat = $mysqli->query("SELECT nom FROM `agenda-types` WHERE id = $tid");
+    if ( !$resultat->num_rows )
+      exit('{"etat":"nok","message":"Type d\'événement non valide."}');
+    // Besoin du nom pour les informations récentes
+    $r = $resultat->fetch_row();
+    $resultat->free();
+    $type = $r[0];
+    if ( $mid )  {
+      // Validation de la matière si non nulle
+      $resultat = $mysqli->query("SELECT nom FROM matieres WHERE id = $mid");
+      if ( !$resultat->num_rows )
+        exit('{"etat":"nok","message":"Matière non valide."}');
+      // Besoin du nom pour les informations récentes
+      $r = $resultat->fetch_row();
+      $resultat->free();
+      $matiere = $r[0];
+    }
+    else
+      $matiere = '';
+    // Validation du texte
+    $texte = $mysqli->real_escape_string($_REQUEST['texte']);
+    // Vérification que l'identifiant est valide
+    if ( isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
+      $resultat = $mysqli->query("SELECT id FROM agenda WHERE id = $id");
+      if ( !$resultat->num_rows ) 
+        exit('{"etat":"nok","message":"Identifiant non valide"}');
+      $resultat->free();
+      // Écriture dans la base de données
+      if ( requete('agenda',"UPDATE agenda SET matiere = $mid, type = $tid, debut = '$debut', fin = '$fin', texte = '$texte' WHERE id = $id", $mysqli) )  {
+        recent($mysqli,5,$id,$mid,array('titre'=>"$jour/$mois - $type ".( strlen($matiere) ? $mysqli->real_escape_string("en $matiere ") : '').'(mise à jour)', 'lien'=>"agenda?mois=$annee$mois", 'texte'=>$texte, 'matiere'=>$mid, 'protection'=>0));
         $mysqli->query('ALTER TABLE agenda ORDER BY fin, debut');
+        exit($_SESSION['message'] = '{"etat":"ok","message":"L\'événement a été modifié."}');
       }
-      else
-        $message = '{"etat":"nok","message":"L\'événement n\'a pas pu être ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
+      exit('{"etat":"nok","message":"L\'événement n\'a pas été modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+    }
+
+    // Nouvel événement
+    if ( requete('agenda',"INSERT INTO agenda SET matiere = $mid, type = $tid, debut = '$debut', fin = '$fin', texte = '$texte'", $mysqli) )  {
+      recent($mysqli,5,$mysqli->insert_id,$mid,array('titre'=>"$jour/$mois - $type ".( strlen($matiere) ? $mysqli->real_escape_string("en $matiere ") : ''), 'lien'=>"agenda?mois=$annee$mois", 'texte'=>$texte, 'matiere'=>$mid, 'protection'=>0));
+      $mysqli->query('ALTER TABLE agenda ORDER BY fin, debut');
+      exit($_SESSION['message'] = '{"etat":"ok","message":"L\'événement a été ajouté."}');
     }
-    // Rechargement systématique
-    $_SESSION['message'] = $message;
-    break;
+    exit('{"etat":"nok","message":"L\'événement n\'a pas été ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+}
 
 //////////////////////////////////////////////////////////////
 // Modification de l'agenda : cas des déplacements de colle //
 //////////////////////////////////////////////////////////////
-  case 'deplcolle':
-    // On ne réalise ici que des ajouts d'événements. Pas de suppression, pas de
-    // modification. Événements simultanés si deux dates renseignées.
-
-    // Validation préliminaire
-    if ( !isset($_REQUEST['matiere']) || !isset($_REQUEST['colleur']) || !isset($_REQUEST['groupe']) || !ctype_digit($matiere = $_REQUEST['matiere']) || !strlen($colleur = $_REQUEST['colleur']) || !strlen($groupe = $_REQUEST['groupe']) )
-      $message = '{"etat":"nok","message":"La matière, le colleur et le groupe sont obligatoires."}';
-    else  {
-      // Validation des dates
-      $mois = array('','janvier','février','mars','avril','mai','juin','juillet','août','septembre','octobre','novembre','décembre');
-      switch ( strlen($_REQUEST['ancien']) )  {
-        case 0: $ancien_sql = ''; break;
-        case 10:
-          $ancien_sql = preg_filter('/(\d{2})\/(\d{2})\/(\d{4})/','$3-$2-$1',$_REQUEST['ancien'].' 00:00');
-          $ancien = substr($ancien_sql,8,2);
-          $ancien = (( $ancien == '01' ) ? '1er' : ltrim($ancien,'0')).' '.$mois[(int)substr($ancien_sql,5,2)];
-          break;
-        case 15: 
-        case 16:
-          $ancien_sql = preg_filter('/^(\d{2})\/(\d{2})\/(\d{4}) (\d{1,2})h(\d{2})$/','$3-$2-$1 $4:$5',$_REQUEST['ancien']);
-          $ancien = substr($ancien_sql,8,2);
-          $ancien = (( $ancien == '01' ) ? '1er' : ltrim($ancien,'0')).' '.$mois[(int)substr($ancien_sql,5,2)].' à'.str_replace(':','h',strstr($ancien_sql,' '));
-          break;
-        default: $ancien_sql = null;
-      }
-      switch ( strlen($_REQUEST['nouveau']) )  {
-        case 0: $nouveau_sql = ''; break;
-        case 10:
-          $nouveau_sql = preg_filter('/(\d{2})\/(\d{2})\/(\d{4})/','$3-$2-$1',$_REQUEST['nouveau'].' 00:00');
-          $nouveau = substr($nouveau_sql,8,2);
-          $nouveau = (( $nouveau == '01' ) ? '1er' : ltrim($nouveau,'0')).' '.$mois[(int)substr($nouveau_sql,5,2)];
-          break;
-        case 15: 
-        case 16:
-          $nouveau_sql = preg_filter('/^(\d{2})\/(\d{2})\/(\d{4}) (\d{1,2})h(\d{2})$/','$3-$2-$1 $4:$5',$_REQUEST['nouveau']);
-          $nouveau = substr($nouveau_sql,8,2);
-          $nouveau = (( $nouveau == '01' ) ? '1er' : ltrim($nouveau,'0')).' '.$mois[(int)substr($nouveau_sql,5,2)].' à'.str_replace(':','h',strstr($nouveau_sql,' '));
-          break;
-        default: $nouveau_sql = null;
-      }
-      if ( is_null($ancien_sql) || is_null($nouveau_sql) )
-        $message = '{"etat":"nok","message":"Les dates/heures choisies ne sont pas valables."}';
-      else  {
-        // Validation de la matière
-        $resultat = $mysqli->query("SELECT nom FROM matieres WHERE id = $matiere");
-        if ( !$resultat->num_rows )
-          $message = '{"etat":"nok","message":"Matière non valide."}';
-        else  {
-          $r = $resultat->fetch_row();
-          $resultat->free();
-          // Début du texte des événements
-          $texte = 'La colle du groupe '.$mysqli->real_escape_string($groupe)." en {$r[0]} avec ".$mysqli->real_escape_string($colleur);
-          // Seulement ancien horaire : annulation de colle
-          if ( strlen($ancien_sql) && !strlen($nouveau_sql) )  {
-            $validation = " '$ancien_sql' BETWEEN debut AND ADDDATE(debut,60)";
-            $texte .= " prévue le $ancien est annulée.";
-            $insertion = "($matiere,1,'$ancien_sql','$ancien_sql','<p>$texte</p>')";
-          }
-          // Seulement nouvel horaire : rattrapage de colle
-          elseif ( !strlen($ancien_sql) && strlen($nouveau_sql) )  {
-            $validation = " '$nouveau_sql' BETWEEN debut AND ADDDATE(debut,80)";
-            $texte .= " est rattrapée le $nouveau".(( strlen($_REQUEST['salle']) ) ? ' en salle '.$mysqli->real_escape_string($_REQUEST['salle']) : '').'.';
-            $insertion = "($matiere,2,'$nouveau_sql','$nouveau_sql','<p>$texte</p>')";
-          }
-          // Ancien et nouvel horaires : déplacement de colle
-          else  {
-            $validation = " '$ancien_sql' BETWEEN debut AND ADDDATE(debut,80) AND '$nouveau_sql' BETWEEN debut AND ADDDATE(debut,80)";
-            $texte .= " est déplacée du $ancien au $nouveau".(( strlen($_REQUEST['salle']) ) ? ' en salle '.$mysqli->real_escape_string($_REQUEST['salle']) : '').'.';
-            $insertion = "($matiere,1,'$ancien_sql','$ancien_sql','<p>$texte</p>'),($matiere,2,'$nouveau_sql','$nouveau_sql','<p>$texte</p>')";
-          }
-          // Validation des dates : chaque horaire doit se trouver dans l'année scolaire
-          $resultat = $mysqli->query("SELECT id FROM semaines WHERE $validation LIMIT 1");
-          if ( !$resultat->num_rows )
-            $message = '{"etat":"nok","message":"Le(s) horaire(s) fourni(s) se trouve(nt) hors de l\'année scolaire."}';
-          else  {
-            $resultat->free();
-            if ( requete('agenda',"INSERT INTO agenda (matiere,type,debut,fin,texte) VALUES $insertion", $mysqli) )  {
-              $mysqli->query('ALTER TABLE agenda ORDER BY fin, debut');
-              $message = '{"etat":"ok","message":"Déplacement de colle ajouté"}';
-              $debut = ( strlen($ancien_sql) ) ? $ancien_sql : $nouveau_sql;
-              recent($mysqli,5,$mysqli->insert_id,$matiere,array('titre'=>substr($debut,8,2).'/'.substr($debut,5,2)." - Déplacement de colle en ${r[0]}, groupe ".$mysqli->real_escape_string($groupe), 'lien'=>'agenda?mois='.substr($debut,2,2).substr($debut,5,2), 'texte'=>$texte, 'matiere'=>$mid, 'protection'=>0));
-            }
-            else
-              $message = '{"etat":"nok","message":"Le déplacement de colle n\'a pas pu être ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-          }
-        }
-      }
-    }
-    // Rechargement systématique
-    $_SESSION['message'] = $message;
-    break;
+elseif ( $action == 'deplcolle' )  {
+  
+  // On ne réalise ici que des ajouts d'événements. Pas de suppression, pas de
+  // modification. Événements simultanés si deux dates renseignées.
+
+  // Validation préliminaire
+  if ( !isset($_REQUEST['matiere']) || !isset($_REQUEST['colleur']) || !isset($_REQUEST['groupe']) || !ctype_digit($matiere = $_REQUEST['matiere']) || !strlen($colleur = $_REQUEST['colleur']) || !strlen($groupe = $_REQUEST['groupe']) )
+    exit('{"etat":"nok","message":"La matière, le colleur et le groupe sont obligatoires."}');
+
+  // Validation des dates
+  $mois = array('','janvier','février','mars','avril','mai','juin','juillet','août','septembre','octobre','novembre','décembre');
+  switch ( strlen($_REQUEST['ancien']) )  {
+    case 0: $ancien_sql = ''; break;
+    case 10:
+      $ancien_sql = preg_filter('/(\d{2})\/(\d{2})\/(\d{4})/','$3-$2-$1',$_REQUEST['ancien'].' 00:00');
+      $ancien = substr($ancien_sql,8,2);
+      $ancien = (( $ancien == '01' ) ? '1er' : ltrim($ancien,'0')).' '.$mois[(int)substr($ancien_sql,5,2)];
+      break;
+    case 15: 
+    case 16:
+      $ancien_sql = preg_filter('/^(\d{2})\/(\d{2})\/(\d{4}) (\d{1,2})h(\d{2})$/','$3-$2-$1 $4:$5',$_REQUEST['ancien']);
+      $ancien = substr($ancien_sql,8,2);
+      $ancien = (( $ancien == '01' ) ? '1er' : ltrim($ancien,'0')).' '.$mois[(int)substr($ancien_sql,5,2)].' à'.str_replace(':','h',strstr($ancien_sql,' '));
+      break;
+    default: $ancien_sql = null;
+  }
+  switch ( strlen($_REQUEST['nouveau']) )  {
+    case 0: $nouveau_sql = ''; break;
+    case 10:
+      $nouveau_sql = preg_filter('/(\d{2})\/(\d{2})\/(\d{4})/','$3-$2-$1',$_REQUEST['nouveau'].' 00:00');
+      $nouveau = substr($nouveau_sql,8,2);
+      $nouveau = (( $nouveau == '01' ) ? '1er' : ltrim($nouveau,'0')).' '.$mois[(int)substr($nouveau_sql,5,2)];
+      break;
+    case 15: 
+    case 16:
+      $nouveau_sql = preg_filter('/^(\d{2})\/(\d{2})\/(\d{4}) (\d{1,2})h(\d{2})$/','$3-$2-$1 $4:$5',$_REQUEST['nouveau']);
+      $nouveau = substr($nouveau_sql,8,2);
+      $nouveau = (( $nouveau == '01' ) ? '1er' : ltrim($nouveau,'0')).' '.$mois[(int)substr($nouveau_sql,5,2)].' à'.str_replace(':','h',strstr($nouveau_sql,' '));
+      break;
+    default: $nouveau_sql = null;
+  }
+  if ( is_null($ancien_sql) || is_null($nouveau_sql) )
+    exit('{"etat":"nok","message":"Les dates/heures choisies ne sont pas valables."}');
+
+  // Validation de la matière
+  $resultat = $mysqli->query("SELECT nom FROM matieres WHERE id = $matiere");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Matière non valide."}');
+    $r = $resultat->fetch_row();
+  $resultat->free();
+  // Début du texte des événements
+  $texte = 'La colle du groupe '.$mysqli->real_escape_string("$groupe en {$r[0]} avec $colleur");
+  // Seulement ancien horaire : annulation de colle
+  if ( strlen($ancien_sql) && !strlen($nouveau_sql) )  {
+    $validation = " '$ancien_sql' BETWEEN debut AND ADDDATE(debut,60)";
+    $texte .= " prévue le $ancien est annulée.";
+    $insertion = "($matiere,1,'$ancien_sql','$ancien_sql','<p>$texte</p>')";
+  }
+  // Seulement nouvel horaire : rattrapage de colle
+  elseif ( !strlen($ancien_sql) && strlen($nouveau_sql) )  {
+    $validation = " '$nouveau_sql' BETWEEN debut AND ADDDATE(debut,80)";
+    $texte .= " est rattrapée le $nouveau".(( strlen($_REQUEST['salle']) ) ? ' en salle '.$mysqli->real_escape_string($_REQUEST['salle']) : '').'.';
+    $insertion = "($matiere,2,'$nouveau_sql','$nouveau_sql','<p>$texte</p>')";
+  }
+  // Ancien et nouvel horaires : déplacement de colle
+  else  {
+    $validation = " '$ancien_sql' BETWEEN debut AND ADDDATE(debut,80) AND '$nouveau_sql' BETWEEN debut AND ADDDATE(debut,80)";
+    $texte .= " est déplacée du $ancien au $nouveau".(( strlen($_REQUEST['salle']) ) ? ' en salle '.$mysqli->real_escape_string($_REQUEST['salle']) : '').'.';
+    $insertion = "($matiere,1,'$ancien_sql','$ancien_sql','<p>$texte</p>'),($matiere,2,'$nouveau_sql','$nouveau_sql','<p>$texte</p>')";
+  }
+  // Validation des dates : chaque horaire doit se trouver dans l'année scolaire
+  $resultat = $mysqli->query("SELECT id FROM semaines WHERE $validation LIMIT 1");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Le(s) horaire(s) fourni(s) se trouve(nt) hors de l\'année scolaire."}');
+  $resultat->free();
+  if ( requete('agenda',"INSERT INTO agenda (matiere,type,debut,fin,texte) VALUES $insertion", $mysqli) )  {
+    $mysqli->query('ALTER TABLE agenda ORDER BY fin, debut');
+    $debut = ( strlen($ancien_sql) ) ? $ancien_sql : $nouveau_sql;
+    recent($mysqli,5,$mysqli->insert_id,$matiere,array('titre'=>substr($debut,8,2).'/'.substr($debut,5,2)." - Déplacement de colle en ".$mysqli->real_escape_string("${r[0]}, groupe $groupe"), 'lien'=>'agenda?mois='.substr($debut,2,2).substr($debut,5,2), 'texte'=>$texte, 'matiere'=>$matiere, 'protection'=>0));
+    exit($_SESSION['message'] = '{"etat":"ok","message":"Déplacement de colle ajouté"}');
+  }
+  else
+    exit('{"etat":"nok","message":"Le déplacement de colle n\'a pas été ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+}
 
 /////////////////////////////////////////////////////
 // Modification des types d'événements de l'agenda //
 /////////////////////////////////////////////////////
-  case 'agenda-types':
-  
-    // Vérification que l'identifiant est valide
-    if ( isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
-      $resultat = $mysqli->query("SELECT ordre, nom, cle, couleur, (SELECT COUNT(*) FROM `agenda-types`) AS max FROM `agenda-types` WHERE id = $id");
+elseif ( ( $action == 'agenda-types' ) && isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
+
+  // Vérification que l'identifiant est valide
+  $resultat = $mysqli->query("SELECT ordre, nom, cle, couleur, (SELECT COUNT(*) FROM `agenda-types`) AS max FROM `agenda-types` WHERE id = $id");
+  if ( !$resultat->num_rows )
+    exit('{"etat":"nok","message":"Identifiant non valide"}');
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+
+  // Traitement d'une modification
+  if ( isset($_REQUEST['nom']) && isset($_REQUEST['cle']) && isset($_REQUEST['couleur']) )  {
+    $nom = ucfirst($mysqli->real_escape_string($_REQUEST['nom']));
+    $cle = str_replace(' ','_',trim($mysqli->real_escape_string($_REQUEST['cle'])));
+    $couleur =  preg_filter('/^([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/','$1',$_REQUEST['couleur']);
+    if ( !strlen($nom) || !strlen($cle) || !$couleur )
+      exit('{"etat":"nok","message":"Le type d\'événements n\'a pas été modifié. Le nom, la clé et la couleur doivent être non vides."}');
+    if ( $cle != $r['cle'] )  {
+      // Vérification que la clé n'existe pas déjà
+      $resultat = $mysqli->query("SELECT cle FROM `agenda-types` WHERE id != $id");
       if ( $resultat->num_rows )  {
-        $r = $resultat->fetch_assoc();
+        while ( $r = $resultat->fetch_assoc() )
+          if ( $r['cle'] == $cle )
+            exit('{"etat":"nok","message":"Le type d\'événements n\'a pas été modifié. Cette clé existe déjà et doit être unique."}');
         $resultat->free();
-
-        // Traitement d'une modification
-        if ( isset($_REQUEST['nom']) && isset($_REQUEST['cle']) && isset($_REQUEST['couleur']) )  {
-          $nom = ucfirst($mysqli->real_escape_string($_REQUEST['nom']));
-          $cle = str_replace(' ','_',trim($mysqli->real_escape_string($_REQUEST['cle'])));
-          $couleur =  preg_filter('/^([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/','$1',$_REQUEST['couleur']);
-          if ( !strlen($nom) || !strlen($cle) || !$couleur )
-            $message = '{"etat":"nok","message":"Le type d\'événements n\'a pas pu être modifié. Nom, clé et couleur non vides nécessaires."}';
-          elseif ( $cle != $r['cle'] )  {
-            // Vérification que la clé n'existe pas déjà
-            $resultat = $mysqli->query("SELECT cle FROM `agenda-types` WHERE id != $id");
-            if ( $resultat->num_rows )  {
-              while ( $r = $resultat->fetch_assoc() )
-                if ( $r['cle'] == $cle )  {
-                  $message = '{"etat":"nok","message":"Le type d\'événements n\'a pas pu être modifié. Cette clé existe déjà et doit être unique."}';
-                  break;
-                }
-              $resultat->free();
-            }
-          }
-          if ( !strlen($message) )  {
-            if ( requete('agenda-types',"UPDATE `agenda-types` SET nom = '$nom', cle = '$cle', couleur = '$couleur' WHERE id = $id",$mysqli) )
-              $message = $_SESSION['message'] = '{"etat":"ok","message":"Type d\'événements modifié"}';
-            else
-              $message = '{"etat":"nok","message":"Le type d\'événements n\'a pas pu être modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-          }
-        }
-        
-        // Déplacement vers le haut
-        elseif ( isset($_REQUEST['monte']) && ( $r['ordre'] > 1 ) )
-          $message = ( requete('agenda-types',"UPDATE `agenda-types` SET ordre = (2*${r['ordre']}-1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}-1) )",$mysqli)
-                    && $mysqli->query('ALTER TABLE `agenda-types` ORDER BY ordre')
-          ) ? '{"etat":"ok","message":"Déplacement réalisé"}' : '{"etat":"nok","message":"Le type d\'événements n\'a pas pu être déplacé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-
-        // Déplacement vers le bas
-        elseif ( isset($_REQUEST['descend']) && ( $r['ordre'] < $r['max'] ) )
-          $message = ( requete('agenda-types',"UPDATE `agenda-types` SET ordre = (2*${r['ordre']}+1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}+1) )",$mysqli)
-                    && $mysqli->query('ALTER TABLE `agenda-types` ORDER BY ordre')
-          ) ? '{"etat":"ok","message":"Déplacement réalisé"}' : '{"etat":"nok","message":"Le type d\'événements n\'a pas pu être déplacé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-
-        // Suppression
-        elseif ( isset($_REQUEST['supprime']) )
-          $message =  ( requete('agenda',"DELETE FROM agenda WHERE type = $id",$mysqli)
-                     && requete('agenda-types',"DELETE FROM `agenda-types` WHERE id = $id",$mysqli) 
-                     && requete('agenda-types',"UPDATE `agenda-types` SET ordre = (ordre-1) WHERE ordre > ${r['ordre']} AND matiere = ${r['matiere']}",$mysqli)
-          ) ? '{"etat":"ok","message":"Suppression réalisée"}' : '{"etat":"nok","message":"Le type d\'événements n\'a pas pu être supprimé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
       }
-      else
-        $message = '{"etat":"nok","message":"Identifiant non valide"}';
     }
-    
-    // Nouveau type d'événements
-    elseif ( isset($_REQUEST['nom']) && isset($_REQUEST['cle']) && isset($_REQUEST['couleur']) )  {
-      $nom = ucfirst($mysqli->real_escape_string($_REQUEST['nom']));
-      $cle = str_replace(' ','_',trim($mysqli->real_escape_string($_REQUEST['cle'])));
-      $couleur =  preg_filter('/^([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/','$1',$_REQUEST['couleur']);
-      if ( !strlen($nom) || !strlen($cle) || !$couleur )
-        $message = '{"etat":"nok","message":"Le type d\'événements n\'a pas pu être ajouté. Nom, clé et couleur non vides nécessaires."}';
+    if ( requete('agenda-types',"UPDATE `agenda-types` SET nom = '$nom', cle = '$cle', couleur = '$couleur' WHERE id = $id",$mysqli) )
+      exit($_SESSION['message'] = '{"etat":"ok","message":"Le type d\'événements a été modifié."}');
+    exit('{"etat":"nok","message":"Le type d\'événements n\'a pas été modifié. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+  
+  // Déplacement vers le haut
+  if ( isset($_REQUEST['monte']) && ( $r['ordre'] > 1 ) )
+    exit( ( requete('agenda-types',"UPDATE `agenda-types` SET ordre = (2*${r['ordre']}-1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}-1) )",$mysqli)
+            && $mysqli->query('ALTER TABLE `agenda-types` ORDER BY ordre') )
+        ? '{"etat":"ok","message":"Le type d\'événements a été déplacé."}' : '{"etat":"nok","message":"Le type d\'événements n\'a pas été déplacé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+
+  // Déplacement vers le bas
+  if ( isset($_REQUEST['descend']) && ( $r['ordre'] < $r['max'] ) )
+    exit( ( requete('agenda-types',"UPDATE `agenda-types` SET ordre = (2*${r['ordre']}+1-ordre) WHERE ( ordre = ${r['ordre']} OR ordre = (${r['ordre']}+1) )",$mysqli)
+            && $mysqli->query('ALTER TABLE `agenda-types` ORDER BY ordre') )
+        ? '{"etat":"ok","message":"Le type d\'événements a été déplacé."}' : '{"etat":"nok","message":"Le type d\'événements n\'a pas été déplacé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+
+  // Suppression
+  if ( isset($_REQUEST['supprime']) )
+    exit( ( requete('agenda',"DELETE FROM agenda WHERE type = $id",$mysqli)
+            && requete('agenda-types',"DELETE FROM `agenda-types` WHERE id = $id",$mysqli) 
+            && requete('agenda-types',"UPDATE `agenda-types` SET ordre = (ordre-1) WHERE ordre > ${r['ordre']}",$mysqli) )
+        ? '{"etat":"ok","message":"La suppression a été réalisée."}' : '{"etat":"nok","message":"Le type d\'événements n\'a pas été supprimé. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+}
+  
+//////////////////////////////////////////////
+// Ajout d'un type d'événements de l'agenda //
+//////////////////////////////////////////////
+elseif ( ( $action == 'ajout-agenda-types' ) && isset($_REQUEST['nom']) && isset($_REQUEST['cle']) && isset($_REQUEST['couleur']) )  {
+  $nom = ucfirst($mysqli->real_escape_string($_REQUEST['nom']));
+  $cle = str_replace(' ','_',trim($mysqli->real_escape_string($_REQUEST['cle'])));
+  $couleur =  preg_filter('/^([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/','$1',$_REQUEST['couleur']);
+  if ( !strlen($nom) || !strlen($cle) || !$couleur )
+    exit('{"etat":"nok","message":"Le type d\'événements n\'a pas été ajouté. Le nom, la clé et la couleur doivent être non vides."}');
+  // Vérification que la clé n'existe pas déjà
+  $resultat = $mysqli->query('SELECT cle FROM `agenda-types`');
+  if ( $resultat->num_rows )  {
+    while ( $r = $resultat->fetch_assoc() )
+      if ( $r['cle'] == $cle )
+        exit('{"etat":"nok","message":"Le type d\'événements n\'a pas été ajouté. Cette clé existe déjà et doit être unique."}');
+    $resultat->free();
+  }
+  if ( requete('agenda-types',"INSERT INTO `agenda-types` SET nom = '$nom', cle = '$cle', couleur = '$couleur',
+                               ordre = (SELECT max(t.ordre)+1 FROM `agenda-types` AS t)",$mysqli) )
+    exit($_SESSION['message'] = '{"etat":"ok","message":"Le type d\'événements a été ajouté."}');
+  exit('{"etat":"nok","message":"Le type d\'événements n\'a pas été ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+}
+
+///////////////////////////////
+// Modification des matières //
+///////////////////////////////
+elseif ( ( $action == 'prefsmatiere' ) && isset($_REQUEST['id']) && in_array($id = intval($_REQUEST['id']),explode(',',$_SESSION['matieres'])) )  {
+  
+  $resultat = $mysqli->query("SELECT nom, colles_protection, cdt_protection, docs_protection, dureecolle FROM matieres WHERE id = $id");
+  $r = $resultat->fetch_assoc();
+  $resultat->free();
+  
+  // Gestion de la durée de colle
+  if ( isset($_REQUEST['dureecolle']) && ctype_digit($dureecolle = $_REQUEST['dureecolle']) )  {
+    if ( requete('matieres',"UPDATE matieres SET dureecolle = $dureecolle WHERE id = $id",$mysqli) )
+      exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"La durée de colle par élève en <em>${r['nom']}</em> a été modifiée.\"}");
+    exit("{\"etat\":\"nok\",\"message\":\"La durée de colle par élève en <em>${r['nom']}</em> n'a pas été modifiée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+
+  // Génération des valeurs de protection
+  foreach ( array('colles','cdt','docs') as $fonction )  {
+    if ( isset($_REQUEST[$fonction.'_protection']) )  {
+      $val = array_filter($_REQUEST[$fonction.'_protection'],function($id) { return ctype_digit($id); });
+      if ( !count($val) )
+        exit("{\"etat\":\"nok\",\"message\":\"La matière <em>${r['nom']}</em> n'a pas été modifiée. La protection d'accès est incorrecte.\"}");
+      if ( ( $val[0] == 0 ) || ( $val[0] == 32 ) )
+        $protection = $val[0];
       else  {
-        // Vérification que la clé n'existe pas déjà
-        $resultat = $mysqli->query('SELECT cle FROM `agenda-types`');
-        if ( $resultat->num_rows )  {
-          while ( $r = $resultat->fetch_assoc() )
-            if ( $r['cle'] == $cle )  {
-              $message = '{"etat":"nok","message":"Le type d\'événements n\'a pas pu être ajouté. Cette clé existe déjà et doit être unique."}';
-              break;
-            }
-          $resultat->free();
-        }
-      }
-      if ( !strlen($message) )  {
-        if ( requete('agenda-types',"INSERT INTO `agenda-types` SET nom = '$nom', cle = '$cle', couleur = '$couleur',
-                                     ordre = (SELECT max(t.ordre)+1 FROM `agenda-types` AS t)",$mysqli) )
-          $message = $_SESSION['message'] = '{"etat":"ok","message":"Type d\'événements ajouté"}';
-        else
-          $message = '{"etat":"nok","message":"Le type d\'événements n\'a pas pu être ajouté. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
+        $protection = 32;
+        foreach (array_slice($val,1) as $v) 
+          $protection = $protection-2**($v-1);
+      }
+      if ( $protection == $r[$fonction.'_protection'] )
+        exit('{"etat":"nok","message":"Aucune action effectuée : la protection saisie est la même que celle déjà en place."}');
+      if ( requete('matieres',"UPDATE matieres SET ${fonction}_protection = $protection WHERE id = $id",$mysqli) )  {
+        if ( $fonction == 'docs' )
+          requete('reps',"UPDATE reps SET protection = $protection WHERE matiere = $id AND parent = 0",$mysqli);
+        exit($_SESSION['message'] = "{\"etat\":\"ok\",\"message\":\"La matière <em>${r['nom']}</em> a été modifiée.\"}");
       }
+      exit("{\"etat\":\"nok\",\"message\":\"La matière <em>${r['nom']}</em> n'a pas été modifiée. Erreur MySQL n°".$mysqli->errno.', «'.$mysqli->error.'»."}');        
     }
-    break;
-
+  }
+}
+  
 ///////////////////////////////////////////
 // Modification des préférences globales //
 ///////////////////////////////////////////
-  case 'prefs':
+elseif ( $action == 'prefsglobales' )  {
 
-    // Préférences de l'agenda : protection globale et nombre d'événements sur 
-    // la page d'accueil
-    if ( isset($_REQUEST['protection_agenda']) && isset($_REQUEST['nb_agenda_index']) && in_array($protection_agenda = $_REQUEST['protection_agenda'],array(0,1,2,3,4)) && ctype_digit($nb_agenda_index = $_REQUEST['nb_agenda_index']) )
-      $message = $_SESSION['message'] = ( requete('prefs',"UPDATE prefs SET val = $protection_agenda WHERE nom='protection_agenda'",$mysqli)
-                                       && requete('prefs',"UPDATE prefs SET val = $nb_agenda_index WHERE nom='nb_agenda_index'",$mysqli)
-      ) ? '{"etat":"ok","message":"Préférences de l\'agenda modifiées"}' : '{"etat":"nok","message":"Les préférences globales de l\'agenda n\'ont pas pu être modifiées. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}';
-    break;
-
-}
+  // Préférences de l'agenda : protection globale et nombre d'événements sur 
+  // la page d'accueil
+  if ( isset($_REQUEST['protection_agenda']) && isset($_REQUEST['nb_agenda_index']) && count( $val = array_filter($_REQUEST['protection_agenda'],function($id) { return ctype_digit($id); }) ) && ctype_digit($nb_agenda_index = $_REQUEST['nb_agenda_index']) )  {
+    if ( ( $val[0] == 0 ) || ( $val[0] == 32 ) )
+      $protection = $val[0];
+    else  {
+      $protection = 32;
+      foreach (array_slice($val,1) as $v) 
+        $protection = $protection-2**($v-1);
+    }
+    if ( requete('prefs',"UPDATE prefs SET val = $protection WHERE nom='protection_agenda'",$mysqli) && requete('prefs',"UPDATE prefs SET val = $nb_agenda_index WHERE nom='nb_agenda_index'",$mysqli) )
+      exit($_SESSION['message'] = '{"etat":"ok","message":"Les préférences de l\'agenda ont été modifiées."}');
+    exit('{"etat":"nok","message":"Les préférences globales de l\'agenda n\'ont pas été modifiées. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
+  
+  // Préférence de création de compte, venant de utilisateurs.php
+  if ( isset($_REQUEST['creation_compte']) )  {
+    if ( requete('prefs','UPDATE prefs SET val = '.intval(isset($_REQUEST['autoriser'])).' WHERE nom="creation_compte"',$mysqli) )
+      exit($_SESSION['message'] = '{"etat":"ok","message":"Les préférences de l\'agenda ont été modifiées."}');
+    exit('{"etat":"nok","message":"Les préférences globales de l\'agenda n\'ont pas été modifiées. Erreur MySQL n°'.$mysqli->errno.', «'.$mysqli->error.'»."}');
+  }
 }
 
-$mysqli->close();
-if ( !strlen($message) )
-  $message = '{"etat":"nok","message":"Aucune action effectuée"}';
-exit($message);
+// Sans action
+exit('{"etat":"nok","message":"Aucune action effectuée"}');
 ?>
diff -urN cahier-de-prepa6.2.0/cdt.php cahier-de-prepa8.0.1/cdt.php
--- cahier-de-prepa6.2.0/cdt.php	2016-08-30 16:51:33.187629752 +0200
+++ cahier-de-prepa8.0.1/cdt.php	2018-10-15 17:50:40.000000000 +0200
@@ -21,14 +21,21 @@
 
 // Recherche de la matière concernée, variable $matiere
 // Si $_REQUEST['cle'] existe, on la cherche dans les matières disponibles.
-// Si $_REQUEST['cle'] existe, on la cherche dans les matières disponibles.
+// cdt=0 : cahier de texte vide, à afficher uniquement pour les profs concernés
+// cdt=1 : cahier de texte utilisé, à afficher pour les utilisateurs associés
+//  à la matière 
+// colles_protection permet de restreindre l'accès
+// colles_protection=32 : cahier de texte désactivé, pas d'affichage
 $mysqli = connectsql();
-$resultat = $mysqli->query('SELECT id, cle, nom, cdt_protection AS protection FROM matieres WHERE cdt'.( ( $autorisation == 4 ) ? " OR  FIND_IN_SET(id,'${_SESSION['matieres']}')" : '' ));
+$resultat = $mysqli->query('SELECT id, cle, nom, cdt_protection AS protection, cdt
+                            FROM matieres WHERE ( colles = 1'.( ( $autorisation == 5 ) ? " OR  FIND_IN_SET(id,'${_SESSION['matieres']}')" : '' ).' ) AND colles_protection < 32');
 if ( $resultat->num_rows )  {
   if ( !empty($_REQUEST) )  {
     while ( $r = $resultat->fetch_assoc() )
       if ( isset($_REQUEST[$r['cle']]) )  {
         $matiere = $r;
+        $mid = $matiere['id'];
+        $cle = $matiere['cle'];
         break;
       }
   }
@@ -50,23 +57,7 @@
 /////////////////////////////
 // Vérification de l'accès //
 /////////////////////////////
-// Interdiction de voir les cahiers de texte de protection élevée ou non
-// associés à ses propres matières si on est invité/élève/colleur
-// Toutes les matières sont visibles pour les professeurs (édition: cf plus bas)
-if ( $matiere['protection'] && ( $autorisation > 0 ) && ( $autorisation < 4 )
-  && ( ( $matiere['protection'] > $autorisation ) || ( !in_array($matiere['id'],explode(',',$_SESSION['matieres'])) ) ) )  {
-  debut($mysqli,"Cahier de texte - ${matiere['nom']}",'Vous n\'avez pas accès à cette page.',$autorisation,"cdt?${matiere['cle']}");
-  $mysqli->close();
-  fin();
-}
-// Connexion nécessaire si protection
-if ( $matiere['protection'] && !$autorisation )  {
-  $titre = "Cahier de texte - ${matiere['nom']}";
-  $actuel = "cdt?${matiere['cle']}";
-  include('login.php');
-}
-// Mode édition pour les professeurs sur leurs propres matières uniquement
-$edition = ( ( $autorisation == 4 ) && in_array($matiere['id'],explode(',',$_SESSION['matieres'])) );
+$edition = acces($matiere['protection'],$mid,"Cahier de texte - ${matiere['nom']}","cdt?$cle",$mysqli);
 
 //////////////////////////////////////////////////////
 // Validation de la requête : semaine(s) à afficher //
@@ -102,7 +93,7 @@
 // Récupération des types de séances
 // $select_seances est utilisé dans le bandeau de recherche
 // $seances est utilisé pour la validation de recherche de séance
-$resultat = $mysqli->query("SELECT cle FROM `cdt-types` WHERE matiere = ${matiere['id']}".( $edition ? ' AND nb' : ' AND nb_v'));
+$resultat = $mysqli->query("SELECT cle FROM `cdt-types` WHERE matiere = $mid AND nb");
 $select_seances = "\n      <option value=\"tout\">Toutes les séances</option>";
 $seances = array();
 if ( $resultat->num_rows )  {
@@ -145,17 +136,16 @@
 // Vue de la semaine en cours à partir du lundi
 // Vue de la semaine précédente et de la semaine en cours jusqu'au vendredi
 // $n est false si non trouvé (hors année scolaire)
-else  {
-  if ( ( ( $n = array_search(date('yW', strtotime('Monday this week',time()-86400)),$semaines_id) ) !== false ) )  {
-    $requete = "AND cdt.semaine >= $n";
-    $nb = ( date('N') > 5 ) ? 1 : 2;
-  }
+elseif ( ( $n = array_search(date('yW', strtotime('Monday this week',time()-86400)),$semaines_id) ) !== false )  {
+  $requete = "AND cdt.semaine >= $n";
+  $nb = ( date('N') > 5 ) ? 1 : 2;
 }
 
 ////////////
 /// HTML ///
 ////////////
-debut($mysqli,"Cahier de texte - ${matiere['nom']}". ( ( $edition && $matiere['protection'] ) ? "<span class=\"icon-lock${matiere['protection']}\"></span>" : '' ),$message,$autorisation,"cdt?${matiere['cle']}",$matiere['id'],'datetimepicker');
+$icone = ( $edition && $matiere['protection'] ) ? '<span class="icon-lock"></span>' : '';
+debut($mysqli,"Cahier de texte - ${matiere['nom']}$icone",$message,$autorisation,"cdt?$cle",$mid,'datetimepicker');
 
 // MathJax désactivé par défaut
 $mathjax = false;
@@ -164,15 +154,15 @@
 $select_semaines = str_replace("\"$n\"","\"$n\" selected",$select_semaines);
 $boutons = "
   <p id=\"recherchecdt\" class=\"topbarre\">
-    <a class=\"icon-precedent\" href=\"?${matiere['cle']}&amp;n=".max(1,$n-1)."\" title=\"Semaine précédente\"></a>
-    <a class=\"icon-suivant\" href=\"?${matiere['cle']}&amp;n=".min($n+1,$nmax)."\" title=\"Semaine suivante\"></a>
-    <a class=\"icon-voirtout\" href=\"?${matiere['cle']}&amp;tout\" title=\"Voir l'ensemble du cahier de texte\"></a>
-    <select id=\"seances\" onchange=\"window.location='?${matiere['cle']}&amp;n='+$(this).next().val()+'&amp;seances='+this.value;\">$select_seances
+    <a class=\"icon-precedent\" href=\"?$cle&amp;n=".max(1,$n-1)."\" title=\"Semaine précédente\"></a>
+    <a class=\"icon-suivant\" href=\"?$cle&amp;n=".min($n+1,$nmax)."\" title=\"Semaine suivante\"></a>
+    <a class=\"icon-voirtout\" href=\"?$cle&amp;tout\" title=\"Voir l'ensemble du cahier de texte\"></a>
+    <select id=\"seances\" onchange=\"window.location='?$cle&amp;n='+$(this).next().val()+'&amp;seances='+this.value;\">$select_seances
     </select>
-    <select id=\"semaines\" onchange=\"window.location='?${matiere['cle']}&amp;n='+this.value+'&amp;seances='+$(this).prev().val();\">$select_semaines
+    <select id=\"semaines\" onchange=\"window.location='?$cle&amp;n='+this.value+'&amp;seances='+$(this).prev().val();\">$select_semaines
     </select>
-    <input type=\"text\" value=\"$recherche\" onchange=\"window.location='?${matiere['cle']}&amp;recherche='+this.value;\" title=\"Recherche dans les textes des programmes de colles\">
-    <span class=\"icon-recherche\" onclick=\"if ( !$(this).prev().is(':visible')) $(this).prev().show(); else window.location='?${matiere['cle']}&amp;recherche='+$(this).prev().val();\"></span>
+    <input type=\"text\" value=\"$recherche\" onchange=\"window.location='?$cle&amp;recherche='+this.value;\" title=\"Recherche dans les textes des programmes de colles\">
+    <span class=\"icon-recherche\" onclick=\"if ( !$(this).prev().is(':visible')) $(this).prev().show(); else window.location='?$cle&amp;recherche='+$(this).prev().val();\"></span>
   </p>
 ";
 
@@ -201,7 +191,7 @@
                                 TIME_FORMAT(cdt.h_debut,'%kh%i') AS h_debut, TIME_FORMAT(cdt.h_fin,'%kh%i') AS h_fin, DATE_FORMAT(cdt.pour,'%d/%m/%Y') AS pour,
                                 cdt.texte, IF(cdt.demigroupe,' (en demi-groupe)','') AS demigroupe, t.titre, t.deb_fin_pour
                                 FROM cdt LEFT JOIN `cdt-types` AS t ON t.id = cdt.type
-                                WHERE cdt.matiere = ${matiere['id']} AND cdt.cache = 0 $requete");
+                                WHERE cdt.matiere = $mid AND cdt.cache = 0 $requete");
     if ( $resultat->num_rows )  {
       $compteur = 0;
       $semaine = ( $n > 0 ) ? $n-1 : 0;
@@ -220,7 +210,7 @@
             echo "\n  <h3>".generetitre($semaines[$semaine]['debut'],$semaines[$semaine]['vacances']).'</h3>';
         }
         // Élément du cahier de texte
-        $mathjax = ( $mathjax ) ? true : strpos($r['texte'],'$')+strpos($r['texte'],'\\');
+        $mathjax = $mathjax ?: boolval(strpos($r['texte'],'$')+strpos($r['texte'],'\\'));
         switch ( $r['deb_fin_pour'] )  {
           case 0: $titre = "${jours[$r['jour']]} ${r['date']} à ${r['h_debut']}&nbsp;: ${r['titre']}${r['demigroupe']}"; break;
           case 1: $titre = "${jours[$r['jour']]} ${r['date']} de ${r['h_debut']} à ${r['h_fin']}&nbsp;: ${r['titre']}${r['demigroupe']}"; break;
@@ -243,15 +233,18 @@
       echo "\n  <h2>Aucun résultat n'a été trouvé pour cette recherche.</h2>\n";
   }
   else
-    echo "\n  <h2>L'année est terminée... Bonnes vacances&nbsp;!</h2>\n  <p><a href=\"?${matiere['cle']}&amp;tout\">Revoir tout le cahier de texte</a></p>\n";
+    echo "\n  <h2>L'année est terminée... Bonnes vacances&nbsp;!</h2>\n  <p><a href=\"?$cle&amp;tout\">Revoir tout le cahier de texte</a></p>\n";
 }
 
 // Affichage professeur éditeur
 else  {
   echo <<<FIN
   
-  <a class="icon-aide general" data-id="page" title="Aide pour l'édition du cahier de texte"></a>
-  <a class="icon-ajoute general" data-id="ajoute-cdt" title="Ajouter un nouvel élément du cahier de texte"></a>
+  <div id="icones">
+    <a class="icon-ajoute formulaire" title="Ajouter un nouvel élément du cahier de texte"></a>
+    <a class="icon-prefs formulaire" title="Modifier les réglages du cahier de texte"></a>
+    <a class="icon-aide" title="Aide pour les modifications des programmes de colles"></a>
+  </div>
 $boutons
 FIN;
   if ( $n !== false )  {
@@ -262,7 +255,7 @@
                                 cdt.texte, IF(cdt.demigroupe,' (en demi-groupe)','') AS demigroupe,
                                 cdt.cache, t.id AS tid, t.titre, t.deb_fin_pour
                                 FROM cdt LEFT JOIN `cdt-types` AS t ON t.id = cdt.type
-                                WHERE cdt.matiere = ${matiere['id']} $requete");
+                                WHERE cdt.matiere = $mid $requete");
     if ( $resultat->num_rows )  {
       $compteur = 0;
       $semaine = ( $n > 0 ) ? $n-1 : 0;
@@ -281,7 +274,7 @@
             echo "\n  <h3>".generetitre($semaines[$semaine]['debut'],$semaines[$semaine]['vacances']).'</h3>';
         }
         // Élément du cahier de texte
-        $mathjax = ( $mathjax ) ? true : strpos($r['texte'],'$')+strpos($r['texte'],'\\');
+        $mathjax = $mathjax ?: boolval(strpos($r['texte'],'$')+strpos($r['texte'],'\\'));
         switch ( $r['deb_fin_pour'] )  {
           case 0: $titre = "${jours[$r['jour']]} ${r['date']} à ${r['h_debut']}&nbsp;: ${r['titre']}${r['demigroupe']}"; break;
           case 1: $titre = "${jours[$r['jour']]} ${r['date']} de ${r['h_debut']} à ${r['h_fin']}&nbsp;: ${r['titre']}${r['demigroupe']}"; break;
@@ -290,25 +283,17 @@
           case 4: $titre = "${jours[$r['jour']]} ${r['date']}"; break;
           case 5: $titre = '[Entrée hebdomadaire]';
         }
-        if ( $r['cache'] )  {
-          $classe_cache = ' class="cache"';
-          $visible = '<a class="icon-montre" title="Afficher l\'élément du cahier de texte sur la partie publique"></a>
-<a class="icon-cache" style="display:none;" title="Rendre invisible l\'élément du cahier de texte sur la partie publique"></a>';
-        }
-        else  {
-          $classe_cache = '';
-          $visible = '<a class="icon-montre" style="display:none;" title="Afficher l\'élément du cahier de texte sur la partie publique"></a>
-<a class="icon-cache" title="Rendre invisible l\'élément du cahier de texte sur la partie publique"></a>';
-        }
+        $classe_cache = ( $r['cache'] ) ? ' class="cache"' : '';
+        $bouton_cache = ( $r['cache'] ) ? '<a class="icon-montre" title="Afficher le programme de colles sur la partie publique"></a>' : '<a class="icon-cache" title="Rendre invisible le programme de colles sur la partie publique"></a>';
         $demigroupe = ( strlen($r['demigroupe']) ) ? 1 : 0;
         echo <<<FIN
 
-  <article$classe_cache data-id="cdt|${r['id']}">
-    <a class="icon-aide" data-id="cdt" title="Aide pour les modifications du cahier de texte"></a>
-    $visible
+  <article$classe_cache data-id="cdt-elems|${r['id']}">
+    <a class="icon-aide" title="Aide pour les modifications du cahier de texte"></a>
+    $bouton_cache
     <a class="icon-supprime" title="Supprimer cet élément du cahier de texte"></a>
     <p class="titrecdt edition" data-donnees='{"tid":${r['tid']},"jour":"${r['date']}","h_debut":"${r['h_debut']}","h_fin":"${r['h_fin']}","pour":"${r['pour']}","demigroupe":$demigroupe}'>$titre</p>
-    <div class="editable edithtml" data-id="cdt|texte|${r['id']}" data-placeholder="Texte de l'élément du cahier de texte">
+    <div class="editable edithtml" data-id="cdt|texte|${r['id']}" placeholder="Texte de l'élément du cahier de texte">
 ${r['texte']}
     </div>
   </article>
@@ -321,11 +306,11 @@
       echo "\n  <article><h2>Aucun résultat n'a été trouvé pour cette recherche.</h2></article>\n\n";
   }
   else
-    echo "\n  <article><h2>L'année est terminée... Bonnes vacances&nbsp;!</h2>\n  <p><a href=\"?${matiere['cle']}&amp;tout\">Revoir tout le cahier de texte</a></p></article>\n";
+    echo "\n  <article><h2>L'année est terminée... Bonnes vacances&nbsp;!</h2>\n  <p><a href=\"?$cle&amp;tout\">Revoir tout le cahier de texte</a></p></article>\n";
 
   // Récupération des boutons
   $resultat = $mysqli->query("SELECT id, nom, jour, TIME_FORMAT(h_debut,'%kh%i') AS h_debut, TIME_FORMAT(h_fin,'%kh%i') AS h_fin,
-                              type, demigroupe FROM `cdt-seances` WHERE matiere = ${matiere['id']}");
+                              type, demigroupe FROM `cdt-seances` WHERE matiere = $mid");
   $select_raccourcis = '';
   $raccourcis = array();
   if ( $resultat->num_rows )  {
@@ -339,7 +324,8 @@
     
   // Nouvelle récupération des types de séances, nécessaire car les types
   // "vides" ne sont pas récupérés précédemment
-  $resultat = $mysqli->query("SELECT id, titre, deb_fin_pour FROM `cdt-types` WHERE matiere = ${matiere['id']}");
+  $resultat = $mysqli->query("SELECT id, titre, deb_fin_pour FROM `cdt-types` WHERE matiere = $mid");
+  $mysqli->close();
   $select_seances = '';
   $seances = array();
   if ( $resultat->num_rows )  {
@@ -350,50 +336,79 @@
     $resultat->free();
   }
 
+  // Options du select multiple d'accès
+  $select_protection = '
+          <option value="0">Accès public</option>
+          <option value="6">Utilisateurs identifiés</option>
+          <option value="1">Invités</option>
+          <option value="2">Élèves</option>
+          <option value="3">Colleurs</option>
+          <option value="4">Administratifs</option>
+          <option value="5">Professeurs</option>
+          <option value="32">Fonction désactivée</option>';
+  $p = $matiere['protection'];
+  if ( ( $p == 0 ) || ( $p == 32 ) )
+    $sel_protection = str_replace("\"$p\"","\"$p\" selected",$select_protection);
+  else  {
+    $sel_protection = str_replace('"6"','"6" selected',$select_protection);
+    for ( $a=1; $a<6; $a++ )
+      if ( ( ($p-1)>>($a-1) & 1 ) == 0 )
+        $sel_protection = str_replace("\"$a\"","\"$a\" selected",$sel_protection);
+  }
   // Aide et formulaire d'ajout
 ?>
 
   <form id="form-cdt">
-    <p class="ligne"><label for="racourci">Raccourci&nbsp;:</label>
-      <select id="raccourci" name="raccourci"><?php echo $select_raccourcis; ?></select>
-      <a class="icon-edite" href="cdt-raccourcis?<?php echo $matiere['cle']; ?>">&nbsp;</a>
+    <p class="ligne">
+      <label for="racourci">Raccourci&nbsp;</label><a class="icon-edite" href="cdt-raccourcis?<?php echo $cle; ?>" title="Éditer les raccourcis de séances"></a>&nbsp;:
+      <select name="raccourci"><?php echo $select_raccourcis; ?></select>
     </p>
-    <p class="ligne"><label for="tid">Séance&nbsp;:</label>
-      <select id="tid" name="tid"><?php echo $select_seances; ?></select>
-      <a class="icon-edite" href="cdt-seances?<?php echo $matiere['cle']; ?>">&nbsp;</a>
+    <p class="ligne">
+      <label for="tid">Séance&nbsp;:</label><a class="icon-edite" href="cdt-seances?<?php echo $cle; ?>"></a>&nbsp;:
+      <select name="tid"><?php echo $select_seances; ?></select>
     </p>
-    <p class="ligne"><label for="jour">Jour&nbsp;: </label><input type="text" id="jour" name="jour" value="" size="8"></p>
-    <p class="ligne"><label for="h_debut">Heure de début&nbsp;: </label><input type="text" id="h_debut" name="h_debut" value="" size="5"></p>
-    <p class="ligne"><label for="h_fin">Heure de fin&nbsp;: </label><input type="text" id="h_fin" name="h_fin" value="" size="5"></p>
-    <p class="ligne"><label for="pour">Pour le&nbsp;: </label><input type="text" id="pour" name="pour" value="" size="8"></p>
+    <p class="ligne"><label for="jour">Jour&nbsp;: </label><input type="text" name="jour" value="" size="8"></p>
+    <p class="ligne"><label for="h_debut">Heure de début&nbsp;: </label><input type="text" name="h_debut" value="" size="5"></p>
+    <p class="ligne"><label for="h_fin">Heure de fin&nbsp;: </label><input type="text" name="h_fin" value="" size="5"></p>
+    <p class="ligne"><label for="pour">Pour le&nbsp;: </label><input type="text" name="pour" value="" size="8"></p>
     <p class="ligne"><label for="demigroupe">Séance en demi-groupe&nbsp;: </label>
-      <select id="demigroupe" name="demigroupe"><option value="0">Classe entière</option><option value="1">Demi-groupe</option></select>
+      <select name="demigroupe"><option value="0">Classe entière</option><option value="1">Demi-groupe</option></select>
     </p>
   </form>
   
-  <form id="form-ajoute-cdt">
+  <form id="form-ajoute" data-action="cdt-elems">
     <h3 class="edition">Nouvel élément du cahier de texte</h3>
-    <p class="ligne"><label for="racourci">Raccourci&nbsp;:</label>
-      <select id="raccourci" name="raccourci"><?php echo $select_raccourcis; ?></select>
-      <a class="icon-edite" href="cdt-raccourcis?<?php echo $matiere['cle']; ?>">&nbsp;</a>
+    <p class="ligne">
+      <label for="racourci">Raccourci&nbsp;</label><a class="icon-edite" href="cdt-raccourcis?<?php echo $cle; ?>" title="Éditer les raccourcis de séances"></a>&nbsp;:
+      <select name="raccourci"><?php echo $select_raccourcis; ?></select>
     </p>
-    <p class="ligne"><label for="tid">Séance&nbsp;:</label>
-      <select id="tid" name="tid"><?php echo $select_seances; ?></select>
-      <a class="icon-edite" href="cdt-seances?<?php echo $matiere['cle']; ?>">&nbsp;</a>
+    <p class="ligne">
+      <label for="tid">Séance&nbsp;</label><a class="icon-edite" href="cdt-seances?<?php echo $cle; ?>"></a>&nbsp;:
+      <select name="tid"><?php echo $select_seances; ?></select>
     </p>
-    <p class="ligne"><label for="jour">Jour&nbsp;: </label><input type="text" id="jour" name="jour" value="" size="8"></p>
-    <p class="ligne"><label for="h_debut">Heure de début&nbsp;: </label><input type="text" id="h_debut" name="h_debut" value="" size="5"></p>
-    <p class="ligne"><label for="h_fin">Heure de fin&nbsp;: </label><input type="text" id="h_fin" name="h_fin" value="" size="5"></p>
-    <p class="ligne"><label for="pour">Pour le&nbsp;: </label><input type="text" id="pour" name="pour" value="" size="8"></p>
+    <p class="ligne"><label for="jour">Jour&nbsp;: </label><input type="text" name="jour" value="" size="8"></p>
+    <p class="ligne"><label for="h_debut">Heure de début&nbsp;: </label><input type="text" name="h_debut" value="" size="5"></p>
+    <p class="ligne"><label for="h_fin">Heure de fin&nbsp;: </label><input type="text" name="h_fin" value="" size="5"></p>
+    <p class="ligne"><label for="pour">Pour le&nbsp;: </label><input type="text" name="pour" value="" size="8"></p>
     <p class="ligne"><label for="demigroupe">Séance en demi-groupe&nbsp;: </label>
-      <select id="demigroupe" name="demigroupe"><option value="0">Classe entière</option><option value="1">Demi-groupe</option></select>
+      <select name="demigroupe"><option value="0">Classe entière</option><option value="1">Demi-groupe</option></select>
     </p>
-    <textarea name="texte" class="edithtml" rows="10" cols="100" data-placeholder="Texte de l'élément du cahier de texte (obligatoire)"></textarea>
-    <p class="ligne"><label for="cache">Ne pas diffuser sur la partie publique&nbsp;: </label><input type="checkbox" id="cache" name="cache" value="1"></p>
-    <input type="hidden" name="table" value="cdt">
-    <input type="hidden" name="matiere" value="<?php echo $matiere['id']; ?>">
+    <textarea name="texte" class="edithtml" rows="10" cols="100" placeholder="Texte de l'élément du cahier de texte (obligatoire)"></textarea>
+    <p class="ligne"><label for="cache">Ne pas diffuser sur la partie publique&nbsp;: </label><input type="checkbox" name="cache" value="1"></p>
+    <input type="hidden" name="matiere" value="<?php echo $mid; ?>">
   </form>
 
+  <form id="form-prefs" data-action="prefsmatiere">
+    <h3 class="edition">Réglages du cahier de texte en <?php echo $matiere['nom']; ?></h3>
+    <p class="ligne"><label for="cdt_protection">Accès&nbsp;: </label>
+      <select name="cdt_protection[]" multiple><?php echo $sel_protection; ?>
+      </select>
+    </p>
+    <p>Si vous désactivez ici cette fonction, vous ne pourrez plus utiliser cette page. Les éléments du cahier de texte déjà saisis ne seront pas supprimés, le choix est réversible. Vous pourrez réactiver cette fonction à la <a href="matieres">gestion des matières</a>.</p>
+    <p>Pour modifier les utilisateurs concernés par cette matière, il faut vous rendre à la <a href="utilisateurs-matieres">gestion des associations utilisateurs-matières</a>.</p>
+    <input type="hidden" name="id" value="<?php echo $mid; ?>">
+  </form>
+  
   <script type="text/javascript">
     seances = <?php echo json_encode($seances); ?>;
     raccourcis = <?php echo json_encode($raccourcis); ?>;
@@ -402,18 +417,35 @@
 
   <div id="aide-page">
     <h3>Aide et explications</h3>
-    <p>Il est possible ici d'ajouter et de modifier des éléments du cahier de texte. On peut précédemment sélectionner une semaine, un type de séance, ou effectuer une recherche de texte.</p>
+    <p>Il est possible ici d'ajouter et de modifier des éléments du cahier de texte. On peut également consulter le cahier de texte, sélectionner une semaine, un type de séance, ou effectuer une recherche de texte.</p>
     <p>Chaque élément du cahier de texte contient une date, éventuellement des horaires, un type de séance, et un texte.</p>
     <p>Les horaires et le texte de chaque élément du cahier de texte, apparaissant dans une zone indiquée par des pointillés, sont modifiables individuellement en cliquant sur les boutons <span class="icon-edite"></span> et en validant avec le bouton <span class="icon-ok"></span> qui apparaît alors.</p>
-    <p>Pour ajouter un élément du cahier de texte, il faut cliquer sur le bouton <span class="icon-ajoute"></span>.</p>
+    <p>Les deux boutons généraux permettent de&nbsp;:</p>
+    <ul>
+      <li><span class="icon-ajoute"></span>&nbsp;: ouvrir un formulaire pour ajouter un nouvel élément du cahier de texte.</li>
+      <li><span class="icon-prefs"></span>&nbsp;: ouvrir un formulaire pour modifier l'accès au cahier de texte.</li>
+    </ul>
+    <h4>Gestion de l'accès</h4>
+    <p>L'accès au cahier de texte peut être protégé. Trois choix sont possibles&nbsp;:</p>
+    <ul>
+      <li><em>Accès public</em>&nbsp;: cahier accessible de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: cahier accessible uniquement par les utilisateurs identifiés, en fonction de leur type de compte et des matières auxquelles ils sont associés. Un cadenas <span class="icon-lock"></span> est alors affiché dans le titre de la page. Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Fonction désactivée</em>&nbsp;: le cahier de texte pour cette matière est complètement indisponible. La fonction n'apparaît plus dans le menu, y compris pour vous.</li>
+    </ul>
+    <p>Si vous désactivez ici cette fonction, vous ne pourrez plus utiliser cette page. Les éléments du cahier de texte ne seront pas supprimés, le choix est réversible. Vous pourrez réactiver cette fonction à la <a href="matieres">gestion des matières</a>.</p>
+    <p>Le lien de la page dans le menu est visible avant identification. Il disparaît après identification pour les utilisateurs n'ayant pas accès à la page.</p>
+    <h4>Suppression de tous les éléments du cahier de texte</h4>
+    <p>L'ensemble du cahier de texte est supprimable en un clic à la <a href="matieres">gestion des matières</a>.</p>
+    <h4>Impression/récupération</h4>
+    <p>Le cahier de texte est très facilement imprimable, avec le bouton <span class="icon-imprime"></span> du menu ou avec la commande d'impression de votre navigateur. Dans les deux cas, tout ce que ne correspond pas au cahier de texte (menu de gauche, marquages en pointillés, icônes de modification) disparaît à l'impression. Le document produit, que vous pourrez conserver en papier ou en pdf, peut constituer un document utilisable pour vous ou pour votre inspection.</p>
+    <p>Le cahier de texte n'est pas récupérable facilement en fichier éditable. La commande «&nbsp;enregistrer sous&nbsp;» de votre navigateur ne donnera pas de très bon résultats.</p>
     <h4>Autres réglages</h4>
-    <p>Vous ne pouvez modifier les cahiers de texte et leur possibilité d'accès que pour les matières qui sont associées à votre compte. Tout cela est modifiable dans la page de gestion des <a href="matieres">Matières</a>.</p>
-    <p>Vous pouvez modifier les semaines de période scolaire ou de vacances sur la page du <a href="planning">Planning annuel</a>.</p>
+    <p>Vous pouvez modifier les semaines de période scolaire ou de vacances à la <a href="planning">gestion du planning annuel</a>.</p>
   </div>
 
-  <div id="aide-cdt">
+  <div id="aide-cdt-elems">
     <h3>Aide et explications</h3>
-    <p>Les horaires et le texte de chaque élément du cahier de texte existant sont modifiable, en cliquant sur le bouton <span class="icon-edite"></span>. Chaque contenu éditable est indiqué par une bordure en pointillés.</p>
+    <p>Les horaires et le texte de chaque élément du cahier de texte existant sont modifiables séparément, en cliquant sur le bouton <span class="icon-edite"></span>. Chaque contenu éditable est indiqué par une bordure en pointillés.</p>
     <p>Une fois le texte édité, il est validé en appuyant sur le bouton <span class="icon-ok"></span>. Au contraire, un clic sur le bouton <span class="icon-annule"></span> annule l'édition.</p>
     <p>Si la connexion est inactive depuis longtemps, une identification sera à nouveau nécessaire.</p>
     <p>D'autres modifications sont possibles à l'aide des boutons disponibles pour chaque élément du cahier de texte&nbsp;:</p>
@@ -423,37 +455,51 @@
       <li><span class="icon-montre"></span>&nbsp;: afficher l'élément du cahier de texte sur la partie publique</li>
     </ul>
     <h4>Types de séances</h4>
-    <p>Les éléments du cahier de texte peuvent être catégorisées par type de séance. Ces types de séances sont modifiables sur la <a href="cdt-seances?<?php echo $matiere['cle']; ?>">page correspondante</a>. Faire ces catégories permet de&nbsp;</p>
+    <p>Les éléments du cahier de texte peuvent être catégorisées par type de séance. Ces types de séances sont modifiables sur la <a href="cdt-seances?<?php echo $cle; ?>">page correspondante</a>, accessible par le bouton <span class="icon-edite"></span> situé devant le menu de sélection du type de séance. Les types de séances sont indépendants d'une matière à l'autre. Établir ces catégories permet de&nbsp;</p>
     <ul>
       <li>obtenir des titres correspondant correctement à la séance effectuée</li>
       <li>faciliter les recherches dans le cahier de texte</li>
       <li>modifier l'affichage des horaires (horaires de début et de fin, de début seulement, ou pas d'horaire)</li>
     </ul>
-    <p>Il est possible de spécifier des types de séances du genre "Entrée quotidienne" ou "Entrée hebdomadaire". Cela se modifie sur la page de modification des <a href="cdt-seances?<?php echo $matiere['cle']; ?>">types de séances</a>.</p>
-    <h4>Modification du type de séance/des horaires</h4>
-    <p>La <em>séance</em> correspond au type de séance. Modifier la séance peut modifier immédiatement l'affichage ou non des champs suivants, selon les réglages effectués sur la page de modification des <a href="cdt-seances?<?php echo $matiere['cle']; ?>">types de séances</a>.</p>
+    <p>Il est possible de spécifier des types de séances du genre "Entrée quotidienne" ou "Entrée hebdomadaire" si vous préférez saisir votre cahier de texte sans préciser exactement les horaires ou les jours.</p>
+    <p>Lorsque vous modifiez le jour/horaire d'un élément du cahier de texte, modifier la séance peut modifier immédiatement l'affichage ou non des champs suivants, selon les réglages effectués à la <a href="cdt-seances?<?php echo $cle; ?>">gestion des types de séances</a>.</p>
     <h4>Raccourcis</h4>
-    <p>Il est possible de régler des <em>raccourcis</em> (propres à chaque matière) qui pré-rempliront les champs séances, jour, heures et demi-groupe. On peut par exemple disposer d'un raccourci &laquo;&nbsp;Cours du lundi&nbsp;&raquo; qui permettra de régler automatiquement le type de séance à Cours, le jour de la semaine au lundi de la semaine en cours, les heures de début et fin à 8h et 10h. Ces raccourcis sont modifiables sur la <a href="cdt-raccourcis?<?php echo $matiere['cle']; ?>">page correspondante</a> et apparaitront au début du formulaire de modification du type de séance/des horaires.</p>
+    <p>Il est possible de définir des <em>raccourcis</em> (propres à chaque matière) qui pré-rempliront les champs séances, jour, heures et demi-groupe. On peut par exemple disposer d'un raccourci «&nbsp;Cours du lundi&nbsp;» qui permettra de régler automatiquement le type de séance à Cours, le jour de la semaine au lundi de la semaine en cours, les heures de début et fin à 8h et 10h. Ces raccourcis sont modifiables sur la <a href="cdt-raccourcis?<?php echo $cle; ?>">page correspondante</a>, accessible par le bouton <span class="icon-edite"></span> situé devant le menu de sélection du raccourcis dans le formulaire de modification du type de séance/des horaires.</p>
     <h4>Modification du texte</h4>
     <p>Le texte doit être formaté en HTML&nbsp;: par exemple, chaque bloc de texte doit être encadré par &lt;p&gt; et &lt;/p&gt;. Il peut donc contenir des liens vers les autres pages du site, vers des documents du site, vers le web... Des boutons sont fournis pour aider au formattage (ainsi qu'une aide).</p>
   </div>
 
-  <div id="aide-ajoute-cdt">
+  <div id="aide-prefs">
+    <h3>Aide et explications</h3>
+    <p>Ce formulaire permet de modifier l'accès au cahier de texte en <?php echo $matiere['nom']; ?>. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>. Trois choix sont possibles&nbsp;:</p>
+    <ul>
+      <li><em>Accès public</em>&nbsp;: cahier accessible de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: cahier accessible uniquement par les utilisateurs identifiés, en fonction de leur type de compte et des matières auxquelles ils sont associés. Un cadenas <span class="icon-lock"></span> est alors affiché dans le titre de la page. Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Fonction désactivée</em>&nbsp;: le cahier de texte pour cette matière est complètement indisponible. La fonction n'apparaît plus dans le menu, y compris pour vous.</li>
+    </ul>
+    <p>Si vous désactivez ici cette fonction, vous ne pourrez plus utiliser cette page. Les éléments du cahier de texte ne seront pas supprimés, le choix est réversible. Vous pourrez réactiver cette fonction à la <a href="matieres">gestion des matières</a>.</p>
+    <h4>Suppression de tous les éléments du cahier de texte</h4>
+    <p>L'ensemble du cahier de texte est supprimable en un clic à la <a href="matieres">gestion des matières</a>.</p>
+    <h4>Autres réglages</h4>
+    <p>Vous pouvez modifier les semaines de période scolaire ou de vacances à la <a href="planning">gestion du planning annuel</a>.</p>
+  </div>
+
+  <div id="aide-ajoute">
     <h3>Aide et explications</h3>
     <p>Ce formulaire permet de créer un nouvel élément du cahier de texte. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-annule"></span>.</p>
-    <p>La <em>séance</em> correspond au type de séance. Modifier la séance peut modifier immédiatement l'affichage ou non des champs suivants, selon les réglages effectués sur la page de modification des <a href="cdt-seances?<?php echo $matiere['cle']; ?>">types de séances</a>.</p>
+    <p>La <em>séance</em> correspond au type de séance. Modifier la séance peut modifier immédiatement l'affichage ou non des champs suivants, selon les réglages effectués à la <a href="cdt-seances?<?php echo $cle; ?>">gestion des types de séances</a>.</p>
     <p>Le texte doit être formaté en HTML&nbsp;: par exemple, chaque bloc de texte doit être encadré par &lt;p&gt; et &lt;/p&gt;. Il peut donc contenir des liens vers les autres pages du site, vers des documents du site, vers le web... Des boutons sont fournis pour aider au formattage (ainsi qu'une aide).</p>
     <p>La case à cocher <em>Ne pas diffuser sur la partie publique</em> permet de cacher temporairement cet élément du cahier de texte, par exemple pour le diffuser ultérieurement. Si la case est cochée, l'élément du cahier de texte ne sera pas diffusé et pourra être affiché à l'aide du bouton <span class="icon-montre"></span>. Si la case est décochée, l'élément du cahier de texte sera immédiatement visible et pourra être rendu invisible à l'aide du bouton <span class="icon-cache"></span>.</p>
     <h4>Types de séances</h4>
-    <p>Les éléments du cahier de texte peuvent être catégorisées par type de séance. Ces types de séances sont modifiables sur la <a href="cdt-seances?<?php echo $matiere['cle']; ?>">page correspondante</a>. Faire ces catégories permet de&nbsp;</p>
+    <p>Les éléments du cahier de texte peuvent être catégorisées par type de séance. Ces types de séances sont modifiables sur la <a href="cdt-seances?<?php echo $cle; ?>">page correspondante</a>, accessible par le bouton <span class="icon-edite"></span> situé devant le menu de sélection du type de séance. Les types de séances sont indépendants d'une matière à l'autre. Établir ces catégories permet de&nbsp;</p>
     <ul>
       <li>obtenir des titres correspondant correctement à la séance effectuée</li>
       <li>faciliter les recherches dans le cahier de texte</li>
       <li>modifier l'affichage des horaires (horaires de début et de fin, de début seulement, ou pas d'horaire)</li>
     </ul>
-    <p>Il est possible de spécifier des types de séances du genre "Entrée journalière" ou "Entrée hebdomadaire". Cela se modifie sur la page de modification des <a href="cdt-seances?<?php echo $matiere['cle']; ?>">types de séances</a>.</p>
+    <p>Il est possible de spécifier des types de séances du genre "Entrée quotidienne" ou "Entrée hebdomadaire" si vous préférez saisir votre cahier de texte sans préciser exactement les horaires ou les jours.</p>
     <h4>Raccourcis</h4>
-    <p>Il est possible de régler des <em>raccourcis</em> (propres à chaque matière) qui pré-rempliront les champs séances, jour, heures et demi-groupe. On peut par exemple disposer d'un raccourci &laquo;&nbsp;Cours du lundi&nbsp;&raquo; qui permettra de régler automatiquement le type de séance à Cours, le jour de la semaine au lundi de la semaine en cours, les heures de début et fin à 8h et 10h. Ces raccourcis sont modifiables sur la <a href="cdt-raccourcis?<?php echo $matiere['cle']; ?>">page correspondante</a> et apparaitront au début du formulaire de modification du type de séance/des horaires.</p>
+    <p>Il est possible de définir des <em>raccourcis</em> (propres à chaque matière) qui pré-rempliront les champs séances, jour, heures et demi-groupe. On peut par exemple disposer d'un raccourci «&nbsp;Cours du lundi&nbsp;» qui permettra de régler automatiquement le type de séance à Cours, le jour de la semaine au lundi de la semaine en cours, les heures de début et fin à 8h et 10h. Ces raccourcis sont modifiables sur la <a href="cdt-raccourcis?<?php echo $cle; ?>">page correspondante</a>, accessible par le bouton <span class="icon-edite"></span> situé devant le menu de sélection du raccourcis dans le formulaire de modification du type de séance/des horaires.</p>
   </div>
 
   <p id="log"></p>
@@ -461,7 +507,5 @@
   <script type="text/javascript" src="js/datetimepicker.min.js"></script>
 <?php
 }
-
-$mysqli->close();
 fin($edition,$mathjax);
 ?>
diff -urN cahier-de-prepa6.2.0/cdt-raccourcis.php cahier-de-prepa8.0.1/cdt-raccourcis.php
--- cahier-de-prepa6.2.0/cdt-raccourcis.php	2016-08-25 01:59:51.623607008 +0200
+++ cahier-de-prepa8.0.1/cdt-raccourcis.php	2018-10-12 15:09:12.227576211 +0200
@@ -21,10 +21,11 @@
 
 // Recherche de la matière concernée, variable $matiere
 // Si $_REQUEST['cle'] existe, on la cherche dans les matières disponibles.
+// colles_protection=32 : cahier de texte désactivé, pas d'affichage
 // Accès aux professeurs connectés uniquement
 $mysqli = connectsql();
-if ( $autorisation == 4 )  {
-  $resultat = $mysqli->query("SELECT id, cle, nom FROM matieres WHERE FIND_IN_SET(id,'${_SESSION['matieres']}')");
+if ( $autorisation == 5 )  {
+  $resultat = $mysqli->query("SELECT id, cle, nom FROM matieres WHERE FIND_IN_SET(id,'${_SESSION['matieres']}') AND colles_protection < 32");
   if ( $resultat->num_rows )  {
     if ( !empty($_REQUEST) )  {
       while ( $r = $resultat->fetch_assoc() )
@@ -71,12 +72,14 @@
 //////////////
 //// HTML ////
 //////////////
-debut($mysqli,"Cahier de texte - ${matiere['nom']}",$message,4,"cdt-raccourcis?${matiere['cle']}",false,'datetimepicker');
+debut($mysqli,"Cahier de texte - ${matiere['nom']}",$message,5,"cdt-raccourcis?${matiere['cle']}",false,'datetimepicker');
 echo <<<FIN
 
-  <a class="icon-aide general" data-id="page" title="Aide pour les modifications des raccourcis de séance"></a>
-  <a class="icon-annule general" onclick="history.back()" title="Retour au cahier de texte"></a>
-  <a class="icon-ajoute general" data-id="ajoute-cdt-raccourci" title="Ajouter un nouveau raccourci de séance"></a>
+  <div id="icones">
+    <a class="icon-ajoute formulaire" title="Ajouter un nouveau raccourci de séance"></a>
+    <a class="icon-annule" onclick="history.back()" title="Retour au cahier de texte"></a>
+    <a class="icon-aide" title="Aide pour les modifications des raccourcis de séance"></a>
+  </div>
 
   <h2 class="edition">Modifier les raccourcis de séance</h2>
 
@@ -85,6 +88,7 @@
 // Récupération
 $resultat = $mysqli->query("SELECT id, ordre, nom, jour, type, demigroupe, TIME_FORMAT(h_debut,'%kh%i') AS h_debut, TIME_FORMAT(h_fin,'%kh%i') AS h_fin
                             FROM `cdt-seances` WHERE matiere = ${matiere['id']}");
+$mysqli->close();
 if ( $max = $resultat->num_rows )  {
   while ( $r = $resultat->fetch_assoc() )  {
     $id = $r['id'];
@@ -96,15 +100,15 @@
     
     echo <<<FIN
 
-  <article data-id="cdt-seances|$id">
+  <article data-id="cdt-raccourcis|$id">
     <a class="icon-aide" data-id="raccourci" title="Aide pour l'édition de ce raccourci de séance"></a>
+    <a class="icon-ok" title="Valider les modifications"></a>
     <a class="icon-monte"$monte title="Déplacer ce type de séance vers le haut"></a>
     <a class="icon-descend"$descend title="Déplacer ce type de séance vers le bas"></a>
     <a class="icon-supprime" title="Supprimer ce type de séance"></a>
     <form class="cdt-raccourcis">
-      <a class="icon-ok" title="Valider les modifications"></a>
       <h3 class="edition">${r['nom']}</h3>
-      <p class="ligne"><label for="nom0">Nom&nbsp;: </label><input type="input" id="nom0" name="nom" value="${r['nom']}" size="50"></p>
+      <p class="ligne"><label for="nom0">Nom&nbsp;: </label><input type="text" id="nom0" name="nom" value="${r['nom']}" size="50"></p>
       <p class="ligne"><label for="type0">Séance&nbsp;:</label>
         <select id="type0" name="type">$sel_seances</select>
       </p>
@@ -129,29 +133,28 @@
 // Aide et formulaire d'ajout
 ?>
 
-  <form id="form-ajoute-cdt-raccourci">
+  <form id="form-ajoute" data-action="ajout-cdt-raccourci">
     <h3 class="edition">Ajouter un nouveau raccourci de séances</h3>
     <div>
-      <p class="ligne"><label for="nom0">Nom&nbsp;: </label><input type="input" id="nom0" name="nom" value="" size="50"></p>
-      <p class="ligne"><label for="type0">Séance&nbsp;:</label>
-        <select id="type0" name="type"><?php echo $select_seances; ?></select>
-      </p>
-      <p class="ligne"><label for="jour0">Jour&nbsp;:</label>
-        <select id="jour0" name="jour"><?php echo $select_jours; ?></select>
-      </p>
-      <p class="ligne"><label for="h_debut0">Heure de début&nbsp;: </label><input type="text" id="h_debut0" name="h_debut" value="" size="5"></p>
-      <p class="ligne"><label for="h_fin0">Heure de fin&nbsp;: </label><input type="text" id="h_fin0" name="h_fin" value="" size="5"></p>
-      <p class="ligne"><label for="demigroupe0">Séance en demi-groupe&nbsp;: </label>
-        <select id="demigroupe0" name="demigroupe"><?php echo $select_dg; ?></select>
+      <p class="ligne"><label for="nom">Nom&nbsp;: </label><input type="text" name="nom" value="" size="50"></p>
+      <p class="ligne"><label for="type">Séance&nbsp;:</label>
+        <select name="type"><?php echo $select_seances; ?></select>
+      </p>
+      <p class="ligne"><label for="jour">Jour&nbsp;:</label>
+        <select name="jour"><?php echo $select_jours; ?></select>
+      </p>
+      <p class="ligne"><label for="h_debut">Heure de début&nbsp;: </label><input type="text" name="h_debut" value="" size="5"></p>
+      <p class="ligne"><label for="h_fin">Heure de fin&nbsp;: </label><input type="text" name="h_fin" value="" size="5"></p>
+      <p class="ligne"><label for="demigroupe">Séance en demi-groupe&nbsp;: </label>
+        <select name="demigroupe"><?php echo $select_dg; ?></select>
       </p>
     </div>
-    <input type="hidden" name="table" value="cdt-seances">
     <input type="hidden" name="matiere" value="<?php echo $matiere['id']; ?>">
   </form>
 
   <div id="aide-page">
     <h3>Aide et explications</h3>
-    <p>Il est possible ici d'ajouter ou de modifier des <em>raccourcis</em> pour le cahier de texte. Ces raccourcis de séance, propres à chaque matière, formeront un menu déroulant disponible lors de l'édition des horaires d'un élément du cahier de texte. Sélectionner un raccourci de séance dans ce menu déroulant pré-remplit les champs séances, jour, heures et demi-groupe. Cela permet donc d'aller plus vite lors du remplissage du cahier de texte. Les raccourcis n'apparaissent qu'en mode édition, et non dans la partie publique.</p>
+    <p>Il est possible ici d'ajouter ou de modifier des <em>raccourcis</em> pour le cahier de texte. Ces raccourcis de séance, propres à chaque matière, formeront un menu déroulant disponible lors de l'édition des horaires d'un élément du cahier de texte. Sélectionner un raccourci de séance dans ce menu déroulant pré-remplit les champs séances, jour, heures et demi-groupe. Cela permet donc d'aller plus vite lors du remplissage du cahier de texte. Les raccourcis n'apparaissent qu'en mode édition (pour vous), non devant les élèves ou autres visiteurs.</p>
     <p>On peut par exemple disposer d'un raccourci &laquo;&nbsp;Cours du lundi&nbsp;&raquo; qui permettra de régler automatiquement le type de séance à Cours, le jour de la semaine au lundi de la semaine en cours, les heures de début et fin à 8h et 10h.</p>
     <p>Le <em>nom</em> est ce qui sera affiché dans le menu d'accès aux raccourcis, visible lors de l'édition d'un élément du cahier de texte. On peut mettre ce que l'on veut.</p>
     <p>La <em>séance</em> est le type de séance qui sera automatiquement sélectionné. Les types de séances sont modifiables, indépendamment pour chaque matière, sur la page de <a href="cdt-seances?<?php echo $matiere['cle']; ?>">modification des types de séances</a>.</p>
@@ -159,10 +162,10 @@
     <p>Les raccourcis existants sont directement modifiables. Les modifications sont prises en compte après validation avec le bouton <span class="icon-ok"></span>.</p>
   </div>
 
-  <div id="aide-raccourci">
+  <div id="aide-cdt-raccourcis">
     <h3>Aide et explications</h3>
     <p>Le <em>nom</em> est ce qui sera affiché dans le menu d'accès aux raccourcis, visible lors de l'édition d'un élément du cahier de texte. On peut mettre ce que l'on veut.</p>
-    <p>La <em>séance</em> est le type de séance qui sera automatiquement sélectionné. Les types de séances sont modifiables, indépendamment pour chaque matière, sur la page de <a href="cdt-seances?<?php echo $matiere['cle']; ?>">modification des types de séances</a>.</p>
+    <p>La <em>séance</em> est le type de séance qui sera automatiquement sélectionné. Les types de séances sont modifiables, indépendamment pour chaque matière, à la <a href="cdt-seances?<?php echo $matiere['cle']; ?>">gestion de types de séances</a>.</p>
     <p>Une fois les modifications faites, il faut les valider en cliquant sur le bouton <span class="icon-ok"></span>.</p>
     <p>D'autres modifications sont possibles à l'aide des boutons disponibles pour chaque raccourci de séance&nbsp;:</p>
     <ul>
@@ -173,7 +176,7 @@
     <p>Supprimer un raccourci de séance n'a strictement aucun impact sur les éléments du cahier de texte.</p>
   </div>
 
-  <div id="aide-ajoute-cdt-raccourci">
+  <div id="aide-ajoute">
     <h3>Aide et explications</h3>
     <p>Ce formulaire permet de créer un nouveau raccourci de séance. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
     <p>Chaque raccourci de séance ne concerne qu'une matière à la fois.</p>
@@ -185,11 +188,18 @@
   
   <script type="text/javascript">
     seances = <?php echo json_encode($seances); ?>;
+    $( function() {
+      // Envoi par appui sur Entrée
+      $('input,select').on('keypress',function (e) {
+        if ( e.which == 13 ) {
+          $(this).parent().parent().children('a.icon-ok').click();
+          return false;
+        }
+      });
+    });
   </script>
   <script type="text/javascript" src="js/datetimepicker.min.js"></script>
   
 <?php
-
-$mysqli->close();
 fin(true);
 ?>
diff -urN cahier-de-prepa6.2.0/cdt-seances.php cahier-de-prepa8.0.1/cdt-seances.php
--- cahier-de-prepa6.2.0/cdt-seances.php	2016-08-23 00:29:01.217875511 +0200
+++ cahier-de-prepa8.0.1/cdt-seances.php	2018-10-12 15:03:58.728099684 +0200
@@ -21,10 +21,11 @@
 
 // Recherche de la matière concernée, variable $matiere
 // Si $_REQUEST['cle'] existe, on la cherche dans les matières disponibles.
+// colles_protection=32 : cahier de texte désactivé, pas d'affichage
 // Accès aux professeurs connectés uniquement
 $mysqli = connectsql();
-if ( $autorisation == 4 )  {
-  $resultat = $mysqli->query("SELECT id, cle, nom FROM matieres WHERE FIND_IN_SET(id,'${_SESSION['matieres']}')");
+if ( $autorisation == 5 )  {
+  $resultat = $mysqli->query("SELECT id, cle, nom FROM matieres WHERE FIND_IN_SET(id,'${_SESSION['matieres']}') AND colles_protection < 32");
   if ( $resultat->num_rows )  {
     if ( !empty($_REQUEST) )  {
       while ( $r = $resultat->fetch_assoc() )
@@ -56,12 +57,14 @@
 //////////////
 //// HTML ////
 //////////////
-debut($mysqli,"Cahier de texte - ${matiere['nom']}",$message,4,"cdt-seances?${matiere['cle']}");
+debut($mysqli,"Cahier de texte - ${matiere['nom']}",$message,5,"cdt-seances?${matiere['cle']}");
 echo <<<FIN
 
-  <a class="icon-aide general" data-id="page" title="Aide pour les modifications des types de séances"></a>
-  <a class="icon-annule general" onclick="history.back()" title="Retour au cahier de texte"></a>
-  <a class="icon-ajoute general" data-id="ajoute" title="Ajouter un nouveau type de séances"></a>
+  <div id="icones">
+    <a class="icon-ajoute formulaire" title="Ajouter un nouveau type de séances"></a>
+    <a class="icon-annule" onclick="history.back()" title="Retour au cahier de texte"></a>
+    <a class="icon-aide" title="Aide pour les modifications des types de séances"></a>
+  </div>
 
   <h2 class="edition">Modifier les types de séances</h2>
 
@@ -76,6 +79,7 @@
 ';
 // Récupération
 $resultat = $mysqli->query("SELECT id, ordre, titre, cle, deb_fin_pour, nb FROM `cdt-types` WHERE matiere = ${matiere['id']}");
+$mysqli->close();
 $max = $resultat->num_rows;
 while ( $r = $resultat->fetch_assoc() )  {
   $id = $r['id'];
@@ -86,16 +90,16 @@
   echo <<<FIN
 
   <article data-id="cdt-types|$id">
-    <a class="icon-aide" data-id="seance" title="Aide pour l'édition de ce type de séances"></a>
+    <a class="icon-aide" title="Aide pour l'édition de ce type de séances"></a>
+    <a class="icon-ok" title="Valider les modifications"></a>
     <a class="icon-monte"$monte title="Déplacer ce type de séances vers le haut"></a>
     <a class="icon-descend"$descend title="Déplacer ce type de séances vers le bas"></a>
     <a class="icon-supprime" title="Supprimer ce type de séances"></a>
     <form>
-      <a class="icon-ok" title="Valider les modifications"></a>
       <h3 class="edition">${r['titre']}</h3>
       <p>Ce type de séances correspond à ${r['nb']} élément$s du cahier de texte.</p>
-      <p class="ligne"><label for="titre$id">Titre&nbsp;: </label><input type="input" id="titre$id" name="titre" value="${r['titre']}" size="50"></p>
-      <p class="ligne"><label for="cle$id">Clé&nbsp;: </label><input type="input" id="cle$id" name="cle" value="${r['cle']}" size="50"></p>
+      <p class="ligne"><label for="titre$id">Titre&nbsp;: </label><input type="text" id="titre$id" name="titre" value="${r['titre']}" size="50"></p>
+      <p class="ligne"><label for="cle$id">Clé&nbsp;: </label><input type="text" id="cle$id" name="cle" value="${r['cle']}" size="50"></p>
       <p class="ligne"><label for="deb_fin_pour$id">Affichage d'horaires&nbsp;:</label>
         <select id="deb_fin_pour$id" name="deb_fin_pour">$dfp        </select>
       </p>
@@ -109,13 +113,13 @@
 // Aide et formulaire d'ajout
 ?>
 
-  <form id="form-ajoute">
+  <form id="form-ajoute" data-action="ajout-cdt-types">
     <h3 class="edition">Ajouter un nouveau type de séances</h3>
     <div>
-      <input type="input" class="ligne" name="titre" value="" size="50" data-placeholder="Titre pour l'affichage (Commence par majuscule, singulier)">
-      <input type="input" class="ligne" name="cle" value="" size="50" data-placeholder="Clé pour les adresses web (Un seul mot, pluriel)">
-      <p class="ligne"><label for="deb_fin_pour0">Affichage d'horaires&nbsp;:</label>
-        <select id="deb_fin_pour0" name="deb_fin_pour">
+      <input type="text" class="ligne" name="titre" value="" size="50" placeholder="Titre pour l'affichage (Commence par majuscule, singulier)">
+      <input type="text" class="ligne" name="cle" value="" size="50" placeholder="Clé pour les adresses web (Un seul mot, pluriel)">
+      <p class="ligne"><label for="deb_fin_pour">Affichage d'horaires&nbsp;:</label>
+        <select name="deb_fin_pour">
           <option value="0">Début seulement</option>
           <option value="1">Début et fin</option>
           <option value="2">Pas d'horaire mais date d'échéance</option>
@@ -125,13 +129,12 @@
         </select>
       </p>
     </div>
-    <input type="hidden" name="table" value="cdt-types">
     <input type="hidden" name="matiere" value="<?php echo $matiere['id']; ?>">
   </form>
 
   <div id="aide-page">
     <h3>Aide et explications</h3>
-    <p>Il est possible ici d'ajouter ou de modifier les types de séances du cahier de texte. Ces modifications sont propres à chaque matière.</p>
+    <p>Il est possible ici d'ajouter ou de modifier les types de séances du cahier de texte. Les modifications effectuées ici ne concernent que la matière <em><?php echo $matiere['nom']; ?></em>.</p>
     <p>Le <em>titre</em> sera affiché au début de chaque élément du cahier de texte. Il doit s'agit d'un nom singulier et commençant par une majuscule. Il peut être relativement long. Par exemple&nbsp;: «&nbsp;Séance de travaux pratiques&nbsp;», «&nbsp;Interrogation de cours&nbsp;»</p>
     <p>La <em>clé</em> sera affichée dans le menu déroulant de recherche, précédé de «&nbsp;les&nbsp», ainsi que dans l'adresse des pages qui affichent ce type de séances&nbsp;: il faut donc que ce soit un pluriel, court, en un mot, sans majucule au début (sauf s'il le faut). Par exemple, «&nbsp;TP&nbsp;», «&nbsp;interros&nbsp;».</p>
     <p>Vous pouvez choisir les valeurs affichées pour chaque type de séances grâce à l'<em>affichage d'horaires</em>&nbsp;:</p>
@@ -179,7 +182,7 @@
     <p>Seuls les types qui correspondent effectivement à des séances apparaissent dans le menu déroulant de recherche de la partie publique. Ce n'est donc pas un problème d'avoir des types qui ne servent finalement à rien, il n'apparaîtront pas sur la partie publique.</p>
   </div>
 
-  <div id="aide-seance">
+  <div id="aide-cdt-types">
     <h3>Aide et explications</h3>
     <p>Le <em>titre</em> sera affiché au début de chaque élément du cahier de texte. Il doit s'agit d'un nom singulier et commençant par une majuscule. Il peut être relativement long. Par exemple&nbsp;: «&nbsp;Séance de travaux pratiques&nbsp;», «&nbsp;Interrogation de cours&nbsp;»</p>
     <p>La <em>clé</em> sera affichée dans le menu déroulant de recherche, précédé de «&nbsp;les&nbsp;», ainsi que dans l'adresse des pages qui affichent ce type de séances&nbsp;: il faut donc que ce soit un pluriel, court, en un mot, sans majucule au début (sauf s'il le faut). Par exemple, «&nbsp;TP&nbsp;», «&nbsp;interros&nbsp;». La clé doit obligatoirement être unique (deux types de séances ne peuvent pas avoir la même clé).</p>
@@ -199,13 +202,13 @@
       <li><span class="icon-monte"></span>&nbsp;: remonter le type de séances d'un cran</li>
       <li><span class="icon-descend"></span>&nbsp;: descendre le type de séances d'un cran</li>
     </ul>
-    <p>Attention&nbsp;: supprimer un type de séances supprime aussi automatiquement tous les éléments du cahier de texte correspondant à ce type. Cela ne peut impacter que la matière concernée. Le nombre d'éléments du cahier de texte correspondant à un type est donné pour chaque type de séances.</p>
+    <p>Attention&nbsp;: supprimer un type de séances supprime aussi automatiquement tous les éléments du cahier de texte correspondant à ce type. Cela ne peut impacter que la matière <em><?php echo $matiere['nom']; ?></em>. Le nombre d'éléments du cahier de texte correspondant à un type est donné pour chaque type de séances.</p>
   </div>
 
   <div id="aide-ajoute">
     <h3>Aide et explications</h3>
     <p>Ce formulaire permet de créer un nouveau type de séances. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
-    <p>Chaque type de séances ne concerne qu'une matière à la fois.</p>
+    <p>Un type de séances créé ici ne concernera que la matière <em><?php echo $matiere['nom']; ?></em>.</p>
     <p>Le <em>titre</em> sera affiché au début de chaque élément du cahier de texte. Il doit s'agit d'un nom singulier et commençant par une majuscule. Il peut être relativement long. Par exemple&nbsp;: «&nbsp;Séance de travaux pratiques&nbsp;», «&nbsp;Interrogation de cours&nbsp;»</p>
     <p>La <em>clé</em> sera affichée dans le menu déroulant de recherche, précédé de «&nbsp;les&nbsp;», ainsi que dans l'adresse des pages qui affichent ce type de séances&nbsp;: il faut donc que ce soit un pluriel, court, en un mot, sans majucule au début (sauf s'il le faut). Par exemple, «&nbsp;TP&nbsp;», «&nbsp;interros&nbsp;». La clé doit obligatoirement être unique (deux types de séances ne peuvent pas avoir la même clé).</p>
     <p>Vous pouvez choisir les valeurs affichées pour chaque type de séances grâce à l'<em>affichage d'horaires</em>&nbsp;:</p>
@@ -224,14 +227,14 @@
   <script type="text/javascript">
 $( function() {
   // Envoi par appui sur Entrée
-  $('input,select').on('keypress',function (e) {
-    if ( e.which == 13 )
+  $('input,select').on('kdeypress',function (e) {
+    if ( e.which == 13 ) {
       $(this).parent().parent().children('a.icon-ok').click();
+      return false;
+    }
   });
 });
   </script>
 <?php
-
-$mysqli->close();
 fin(true);
 ?>
diff -urN cahier-de-prepa6.2.0/CHANGELOG.php cahier-de-prepa8.0.1/CHANGELOG.php
--- cahier-de-prepa6.2.0/CHANGELOG.php	2018-10-14 22:31:03.622207601 +0200
+++ cahier-de-prepa8.0.1/CHANGELOG.php	2018-10-17 01:26:23.807180273 +0200
@@ -1,4 +1,4 @@
-Version actuelle : 6.1.0 (25/08/17)
+Version actuelle : 8.0.1 (15/10/18)
 ===================
 Changements :
 1.0   31/08/11 Première version
@@ -195,24 +195,51 @@
   * Améliorations et uniformisations d'affichage
 6.1.0 25/08/17 Correction de bug : problème des invitations (liens avec @)
   * Nouvelle version du planning annuel pour 2017-2018
-  * La version 7 viendra courant septembre.
 6.2.0 02/09/18 Version non publiée
   * Nouvelle version du planning annuel pour 2018-2019
-
+8.0.1 15/10/18 Nouvelles fonctionnalités :
+  * Envois de mails 
+    * Envoi paramétré avec une adresse générale évitant les erreurs de transport
+    * Possibilité pour tous les utilisateurs (y compris les élèves) d'envoyer
+    des mails, réglage particulier à chaque compte
+  * Gestion des accès entièrement revue, possibilité de réglage beaucoup plus fin
+    pour la protection de l'accès de chaque ressource 
+  * Création d'un nouveau type d'utilisateur : administration
+  * Gestion des utilisateurs améliorée
+    * Validations/suppressions/modifications multiples d'utilisateurs
+    * Possibilité de "désactiver" un compte sans le supprimer, par exemple pour
+    les élèves partis en cours d'année
+    * Les groupes peuvent comprendre des utilisateurs non élèves 
+  * Protection individuelle des informations
+  * Possibilité de connexion automatique sans mot de passe pour les pages
+    en lecture seule et sans donnée sensible.
+  * Gestion des notes entièrement revue, avec un regroupement par heure et une
+    nouvelle interface de saisie et modification. Les notes de colles peuvent 
+    être relevées par l'administration. Les données récapitulatives (nombre
+    d'élèves et nombre d'heures déclarées et relevées) sont affichées.
+  * Possibilité de désactiver les fonctions cahier de texte/programme de colle/
+    notes de colles : elles disparaissent alors du menu.
+  
 ===================
 
 Todo :
 
-[ 7.0 ] Septembre 2017
-  * Modification complète des envois de mails et mise en place d'un système de forum.
-  * Possibilité pour les élèves de contacter professeurs et colleurs si autorisé.
-  * Gestion des accès entièrement revue
-  * Création d'un nouveau type d'utilisateur : administration
-  * Possibilité de modifier le type d'un utilisateur existant
-  * Gestion des utilisateurs
-    * Validations/suppressions multiples d'utilisateurs
-    * Possibilité de "désactiver" un compte sans le supprimer, pour les élèves partis en cours d'année
-  * Protection individuelle des informations
+[8.1] Janvier 2019
+  * Mode lecture
+  * Page de préférences globales : création de compte, protection globale, titre
+  * Page de statistiques des colles toutes matières confondues ?
+  * Correction des mises à jour multiples vers les informations récentes
+  * FAQ
+  * Affichage en ligne des vidéos et fichiers sonores
+  * Pièce jointe sur les mails : téléchargement, enregistrement dans un répertoire
+  hors d'atteinte, lien spécial à insérer dans le corps du mail.
+  * À côté de Cahier de Prépa : une interface spéciale pour l'administration des
+  lycées qui voudraient récupérer les notes de colles sur plusieurs classes.
+
+[ 9.0 ] Septembre 2019
+  * Meilleure visiblité des protections dans le menu
+  * Réglages des fichiers xls téléchargés (notes & utilisateurs)
+  * Mise en place d'un système de forum ?
   * Améliorations de l'agenda
     * Possibilité de montrer/cacher un événement
     * Protection individuelle des événements
@@ -221,35 +248,26 @@
     * Vue de l'agenda sous forme de liste d'événements
     * Recherche dans les événements
   * Raccourcis cdt : texte pré-défini
-  * FAQ
-
-[ 7.1 ] Janvier 2018
-  * Timeout revu. Connexion automatique sans mot de passe pour les pages sans danger.
   * Système de récupération des données/documents
   * Paramétrage des styles pour les titres, des couleurs
-  * Modification globale des répertoires ou matières : répercution sur les information récentes
-  * Notes : gestions de modifications de personnes, matières
+
 
 =======
 
 Autres remarques / propositions :
-  * Types cdt : suppression massive (idem pages)
-  * Préférences globales : création de compte, protection globale, 
-  * Suppressions (modifications ?) multiples de documents/informations
+  * Modifications multiples sur des éléments (informations, pages, types ou
+    séances de cahier de texte, documents...)
   * Tags dans les cahiers de texte
-  * Modifier les matières des utilisateurs depuis utilisateurs.php
   * Programme de colles par quinzaine
-  * Bug : suppression des notes lorsque suppression d'un compte élève/colleur
-  * Correction des mises à jour multiples vers les informations récentes
-  * agrandir les icônes
   * ajouter la durée des séances dans le cahier de texte
   * bug : interdire les "." dans les clés
-  * crayon matière dans menu -> modification de la matière
-  * meilleure compréhension des visibilités dans le menu
-  * notes : garder des notes malgré suppression de l'association colleur/matière ou élève/matière (changement de matière, Y PCSI)
+  * matière éditable depuis le menu -> modification de la matière seule (matieres.php accessible avec une clé)
   * notes : savoir combien de notes sont attribuées à un compte avant suppression (gestion des doublons)
   * news : date de création, date de mise à jour ?
   * modification de docs par les élèves
   * modification d'items de l'agenda par les élèves
   * ajout de la possibilité d'envoi de notification à chaque saisie de document/information/programme de colle
-  * prévention javascript de changement de page en cours d'édition
+  * prévention javascript de changement de page/fermeture de sous-fenêtre, en cours d'édition
+  * mise en évidence (surlignage) des champs modifiés, envoi sélectif ?
+  * correction : détection de la touche entrée sur les input/select plus globale (supprimer les copies multiples)
+  * correction : utiliser le data-matiere associé à body dans les actions ajax
diff -urN cahier-de-prepa6.2.0/colles.php cahier-de-prepa8.0.1/colles.php
--- cahier-de-prepa6.2.0/colles.php	2015-10-27 12:28:17.595628535 +0100
+++ cahier-de-prepa8.0.1/colles.php	2018-10-15 17:50:45.624901382 +0200
@@ -12,13 +12,20 @@
 
 // Recherche de la matière concernée, variable $matiere
 // Si $_REQUEST['cle'] existe, on la cherche dans les matières disponibles.
+// colles=0 : cahier de texte vide, à afficher uniquement pour les profs concernés
+// colles=1 : cahier de texte utilisé, à afficher pour les utilisateurs associés
+//  à la matière 
+// colles_protection permet de restreindre l'accès
+// colles_protection=32 : cahier de texte désactivé, pas d'affichage
 $mysqli = connectsql();
-$resultat = $mysqli->query('SELECT id, cle, nom, colles_protection AS protection FROM matieres WHERE colles'.( ( $autorisation == 4 ) ? " OR  FIND_IN_SET(id,'${_SESSION['matieres']}')" : '' ));
+$resultat = $mysqli->query('SELECT id, cle, nom, colles_protection AS protection
+                            FROM matieres WHERE ( colles = 1'.( ( $autorisation == 5 ) ? " OR  FIND_IN_SET(id,'${_SESSION['matieres']}')" : '' ).' ) AND colles_protection < 32');
 if ( $resultat->num_rows )  {
   if ( !empty($_REQUEST) )  {
     while ( $r = $resultat->fetch_assoc() )
       if ( isset($_REQUEST[$r['cle']]) )  {
         $matiere = $r;
+        $mid = $matiere['id'];
         break;
       }
   }
@@ -37,27 +44,10 @@
   fin();
 }
 
-
 /////////////////////////////
 // Vérification de l'accès //
 /////////////////////////////
-// Interdiction de voir les programmes de protection élevée ou non associés à
-// ses propres matières si on est invité/élève/colleur
-// Toutes les matières sont visibles pour les professeurs (édition: cf plus bas)
-if ( $matiere['protection'] && ( $autorisation > 0 ) && ( $autorisation < 4 )
-  && ( ( $matiere['protection'] > $autorisation ) || ( !in_array($matiere['id'],explode(',',$_SESSION['matieres'])) ) ) )  {
-  debut($mysqli,"Programme de colles - ${matiere['nom']}",'Vous n\'avez pas accès à cette page.',$autorisation,"colles?${matiere['cle']}");
-  $mysqli->close();
-  fin();
-}
-// Connexion nécessaire si protection
-if ( $matiere['protection'] && !$autorisation )  {
-  $titre = "Programme de colles - ${matiere['nom']}";
-  $actuel = "colles?${matiere['cle']}";
-  include('login.php');
-}
-// Mode édition pour les professeurs sur leurs propres matières uniquement
-$edition = ( ( $autorisation == 4 ) && in_array($matiere['id'],explode(',',$_SESSION['matieres'])) );
+$edition = acces($matiere['protection'],$mid,"Programme de colles - ${matiere['nom']}","colles?${matiere['cle']}",$mysqli);
 
 //////////////////////////////////////////////////////////////////
 // Validation de la requête : semaine(s) ou éléments à afficher //
@@ -111,17 +101,16 @@
 }
 // Vue de la semaine en cours, prochaine semaine à partir du vendredi 19h
 // $n est false si non trouvé (hors année scolaire)
-else  {
-  if ( ( ( $n = array_search(date('yW', strtotime('Monday this week',time()+104400)),$semaines) ) !== false ) )  {
-    $requete = "WHERE s.id >= $n";
-    $nb = 1+$edition;
-  }
+elseif ( ( $n = array_search(date('yW', strtotime('Monday this week',time()+104400)),$semaines) ) !== false )  {
+  $requete = "WHERE s.id >= $n";
+  $nb = 1+$edition;
 }
 
 ////////////
 /// HTML ///
 ////////////
-debut($mysqli,"Programme de colles - ${matiere['nom']}". ( ( $edition && $matiere['protection'] ) ? "<span class=\"icon-lock${matiere['protection']}\"></span>" : '' ),$message,$autorisation,"colles?${matiere['cle']}",$matiere['id']);
+$icone = ( $edition && $matiere['protection'] ) ? '<span class="icon-lock"></span>' : '';
+debut($mysqli,"Programme de colles - ${matiere['nom']}$icone",$message,$autorisation,"colles?${matiere['cle']}",$mid);
 
 // MathJax désactivé par défaut
 $mathjax = false;
@@ -161,23 +150,24 @@
   echo $boutons;
   if ( $n !== false )  {
     // Affichage des programmes de colles diffusés
-    if ( strlen($recherche) )
+    if ( strlen($recherche) > 0 )
       $resultat = $mysqli->query("SELECT DATE_FORMAT(s.debut,'%w%Y%m%e') AS debut, s.colle, s.vacances, c.texte
                                   FROM colles AS c LEFT JOIN semaines AS s ON c.semaine=s.id
-                                  WHERE c.matiere = ${matiere['id']} AND c.cache = 0 AND c.texte LIKE '%".$mysqli->real_escape_string($_REQUEST['recherche']).'%\' ORDER BY c.semaine');
+                                  WHERE c.matiere = $mid AND c.cache = 0 AND c.texte LIKE '%".$mysqli->real_escape_string($_REQUEST['recherche']).'%\' ORDER BY c.semaine');
     else
       $resultat = $mysqli->query("SELECT DATE_FORMAT(s.debut,'%w%Y%m%e') AS debut, s.colle, s.vacances, c.texte
                                   FROM semaines AS s
-                                  LEFT JOIN (SELECT texte, semaine FROM colles WHERE matiere = ${matiere['id']} AND cache = 0) AS c ON c.semaine=s.id
+                                  LEFT JOIN (SELECT texte, semaine FROM colles WHERE matiere = $mid AND cache = 0) AS c ON c.semaine=s.id
                                   $requete" );
-    if ( $resultat->num_rows )  {
+    $mysqli->close();
+    if ( $resultat->num_rows > 0 )  {
       $compteur = 0;
       while ( ( $compteur < $nb ) && ( $r = $resultat->fetch_assoc() ) )  {
-        $mathjax = ( $mathjax ) ? true : strpos($r['texte'],'$')+strpos($r['texte'],'\\');
+        $mathjax = $mathjax ?: boolval(strpos($r['texte'],'$')+strpos($r['texte'],'\\'));
         $titre = generetitre($r['debut'],$r['vacances']);
         if ( $r['colle'] )  {
           $compteur = $compteur+1;
-          $texte = ( is_null($r['texte']) ) ? '    <p>Le programme de colles de cette semaine n\'est pas défini.</p>' : $r['texte'];
+          $texte = $r['texte'] ?: '    <p>Le programme de colles de cette semaine n\'est pas défini.</p>';
         }
         else
           $texte = ( $r['vacances'] ) ? '' : '    <p>Il n\'y a pas de colles cette semaine.</p>';
@@ -202,67 +192,57 @@
 // Affichage professeur éditeur
 else  {
   echo <<<FIN
-  
-  <a class="icon-aide general" data-id="page" title="Aide pour les modifications des programmes de colles"></a>
+
+  <div id="icones">
+    <a class="icon-prefs formulaire" title="Modifier les réglages des programmes de colles"></a>
+    <a class="icon-aide" title="Aide pour les modifications des programmes de colles"></a>
+  </div>
 $boutons
 FIN;
   if ( $n !== false )  {
     // Affichage des semaines concernées
     // Le programme est identifié par le couple semaine-matière plutôt que son
-    // identifiant propre pour assurer une cohérence entre les programmes entrés
-    // et ceux non entrés/supprimés.
+    // identifiant propre pour assurer une cohérence entre les programmes saisis
+    // et ceux non saisis/supprimés.
     if ( strlen($recherche) )
       $resultat = $mysqli->query("SELECT DATE_FORMAT(s.debut,'%w%Y%m%e') AS debut, s.colle, s.vacances, c.texte, s.id, c.cache 
                                   FROM colles AS c LEFT JOIN semaines AS s ON c.semaine=s.id
-                                  WHERE c.matiere = ${matiere['id']} AND c.texte LIKE '%$recherche%' ORDER BY c.semaine");
+                                  WHERE c.matiere = $mid AND c.texte LIKE '%$recherche%' ORDER BY c.semaine");
     else
       $resultat = $mysqli->query("SELECT DATE_FORMAT(s.debut,'%w%Y%m%e') AS debut, s.colle, s.vacances, c.texte, s.id, c.cache 
                                   FROM semaines AS s
-                                  LEFT JOIN (SELECT texte, semaine, cache FROM colles WHERE matiere = ${matiere['id']}) AS c ON c.semaine=s.id
+                                  LEFT JOIN (SELECT texte, semaine, cache FROM colles WHERE matiere = $mid) AS c ON c.semaine=s.id
                                   $requete");
+    $mysqli->close();
     if ( $resultat->num_rows )  {
       $compteur = 0;
       while ( ( $compteur < $nb ) && ( $r = $resultat->fetch_assoc() ) )  {
-        $mathjax = ( $mathjax ) ? true : strpos($r['texte'],'$')+strpos($r['texte'],'\\');
+        $mathjax = $mathjax ?: boolval(strpos($r['texte'],'$')+strpos($r['texte'],'\\'));
         $titre = generetitre($r['debut'],$r['vacances']);
         if ( $r['colle'] )  {
           $compteur = $compteur+1;
           if ( is_null($r['texte']) )
             echo <<<FIN
 
-  <article id="${r['id']}-${matiere['id']}" data-id="colles|${r['id']}-${matiere['id']}">
+  <article data-id="ajoutecolle|${r['id']}-$mid">
     <h3 class="edition">$titre</h3>
-    <a class="icon-aide" data-id="colle" title="Aide pour l'édition de ce programme de colles"></a>
-    <a class="icon-montre" style="display:none;" title="Afficher le programme de colles sur la partie publique"></a>
-    <a class="icon-cache" style="display:none;" title="Rendre invisible le programme de colles sur la partie publique"></a>
-    <a class="icon-supprime" style="display:none;" title="Supprimer ce programme de colles"></a>
-    <a class="icon-ajoute" data-id="ajoute" data-remplace="${r['id']}-${matiere['id']}" title="Entrer ce programme de colles"></a>
-    <div class="editable-remplace edithtml" data-id="colles|texte|${r['id']}-${matiere['id']}" data-placeholder="Texte du programme de colles">
-      <p>Le programme de colles de cette semaine n'est pas encore défini.</p>
-    </div>
+    <a class="icon-aide" title="Aide pour la saisie d'un nouveau programme de colles"></a>
+    <a class="icon-ajoutecolle" title="Saisir ce programme de colles"></a>
+    <p>Le programme de colles de cette semaine n'est pas encore défini.</p>
   </article>
 
 FIN;
           else  {
-            if ( $r['cache'] )  {
-              $classe_cache = ' class="cache"';
-              $visible = '<a class="icon-montre" title="Afficher le programme de colles sur la partie publique"></a>
-    <a class="icon-cache" style="display:none;" title="Rendre invisible le programme de colles sur la partie publique"></a>';
-            }
-            else  {
-              $classe_cache = '';
-              $visible = '<a class="icon-montre" style="display:none;" title="Afficher le programme de colles sur la partie publique"></a>
-    <a class="icon-cache" title="Rendre invisible le programme de colles sur la partie publique"></a>';
-            }
+            $classe_cache = ( $r['cache'] ) ? ' class="cache"' : '';
+            $bouton_cache = ( $r['cache'] ) ? '<a class="icon-montre" title="Afficher le programme de colles sur la partie publique"></a>' : '<a class="icon-cache" title="Rendre invisible le programme de colles sur la partie publique"></a>';
             echo <<<FIN
 
-  <article$classe_cache data-id="colles|${r['id']}-${matiere['id']}">
+  <article$classe_cache data-id="colles|${r['id']}-$mid">
     <h3 class="edition">$titre</h3>
-    <a class="icon-aide" data-id="colle" title="Aide pour l'édition de ce programme de colles"></a>
-    $visible
+    <a class="icon-aide" title="Aide pour l'édition de ce programme de colles"></a>
+    $bouton_cache
     <a class="icon-supprime" title="Supprimer ce programme de colles"></a>
-    <a class="icon-ajoute" style="display:none;" data-id="ajoute" data-remplace="${r['id']}-${matiere['id']}" title="Entrer ce programme de colles"></a>
-    <div class="editable edithtml mat${matiere['id']}" data-id="colles|texte|${r['id']}-${matiere['id']}" data-placeholder="Texte du programme de colles">
+    <div class="editable edithtml" data-id="colles|texte|${r['id']}-$mid" placeholder="Texte du programme de colles">
 ${r['texte']}
     </div>
   </article>
@@ -271,7 +251,7 @@
           }
         }
         else  {
-          $texte = ( $r['vacances'] ) ? '' : '      <p>Il n\'y a normalement pas de colles cette semaine-là.</p><p>S\'il s\'agit d\'une erreur, cela est modifiable <a href="semaines">sur la page des semaines</a></p>';
+          $texte = ( $r['vacances'] ) ? '' : '    <p>Il n\'y a normalement pas de colles cette semaine-là.</p><p>S\'il s\'agit d\'une erreur, cela est modifiable <a href="planning">sur la page de gestion du planning annuel</a>.</p>';
           echo <<<FIN
 
   <article>
@@ -290,28 +270,78 @@
   else
     echo "\n  <article><h2>L'année est terminée.</h2>\n  <p><a href=\"?${matiere['cle']}&amp;tout\">Revoir tout le programme de l'année</a></p></article>\n";
 
+  // Options du select multiple d'accès
+  $select_protection = '
+          <option value="0">Accès public</option>
+          <option value="6">Utilisateurs identifiés</option>
+          <option value="1">Invités</option>
+          <option value="2">Élèves</option>
+          <option value="3">Colleurs</option>
+          <option value="4">Administratifs</option>
+          <option value="5">Professeurs</option>
+          <option value="32">Fonction désactivée</option>';
+  $p = $matiere['protection'];
+  if ( ( $p == 0 ) || ( $p == 32 ) )
+    $sel_protection = str_replace("\"$p\"","\"$p\" selected",$select_protection);
+  else  {
+    $sel_protection = str_replace('"6"','"6" selected',$select_protection);
+    for ( $a=1; $a<6; $a++ )
+      if ( ( ($p-1)>>($a-1) & 1 ) == 0 )
+        $sel_protection = str_replace("\"$a\"","\"$a\" selected",$sel_protection);
+  }
   // Aide et formulaire d'ajout
 ?>
 
-  <form id="form-ajoute">
-    <textarea name="texte" class="edithtml" rows="10" cols="100" data-placeholder="Texte du programme de colles (obligatoire)"></textarea>
-    <p class="ligne"><label for="cache">Ne pas diffuser sur la partie publique&nbsp;: </label><input type="checkbox" id="cache" name="cache" value="1"></p>
-    <input type="hidden" name="id" value="">
-    <input type="hidden" name="table" value="colles">
+  <form id="form-ajoutecolle" data-action="ajout-colle">
+    <textarea name="texte" class="edithtml" rows="10" cols="100" placeholder="Texte du programme de colles (obligatoire)"></textarea>
+    <p class="ligne"><label for="cache">Ne pas diffuser sur la partie publique&nbsp;: </label><input type="checkbox" name="cache" value="1"></p>
+  </form>
+  
+  <form id="form-prefs" data-action="prefsmatiere">
+    <h3 class="edition">Réglages des programmes de colles en <?php echo $matiere['nom']; ?></h3>
+    <p class="ligne"><label for="colles_protection">Accès&nbsp;: </label>
+      <select name="colles_protection[]" multiple><?php echo $sel_protection; ?>
+      </select>
+    </p>
+    <p>Si vous désactivez ici cette fonction, vous ne pourrez plus utiliser cette page. Les programmes de colles déjà saisis ne seront pas supprimés, le choix est réversible. Vous pourrez réactiver cette fonction à la <a href="matieres">gestion des matières</a>.</p>
+    <p>Pour modifier les semaines correspondant à un programme de colle, il faut vous rendre à la <a href="planning">gestion du planning annuel</a>.</p>
+    <p>Pour modifier les utilisateurs concernés par cette matière, il faut vous rendre à la <a href="utilisateurs-matieres">gestion des associations utilisateurs-matières</a>.</p>
+    <input type="hidden" name="id" value="<?php echo $mid; ?>">
   </form>
   
   <div id="aide-page">
     <h3>Aide et explications</h3>
-    <p>Il est possible ici d'ajouter et de modifier des programmes de colles. On peut précédemment sélectionner une semaine ou effectuer une recherche de texte.</p>
-    <p>Chaque programme de colles est associé à une semaine du planning annuel. Il est impossible de déplacer un programme de colles.</p>
+    <p>Il est possible ici d'ajouter et de modifier des programmes de colles. On peut également les consulter, sélectionner une semaine ou effectuer une recherche de texte.</p>
+    <p>Chaque programme de colles est associé à une semaine du planning annuel. Il est impossible de déplacer un programme de colles. Pour modifier les semaines correspondant à un programme de colle, il faut vous rendre à la <a href="planning">gestion du planning annuel</a>.</p>
     <p>Si une semaine de colles ne contient pas de programme, il est possible d'en rajouter un en cliquant sur le bouton <span class="icon-ajoute"></span>.</p>
     <p>Les textes modifiables sont dans les zones indiquées par des pointillés. Ils sont modifiables en cliquant sur les boutons <span class="icon-edite"></span> et en validant avec le bouton <span class="icon-ok"></span> qui apparaît alors.</p>
-    <h4>Autres réglages</h4>
-    <p>Vous ne pouvez modifier les programmes de colles et leur possibilité d'accès que pour les matières qui sont associées à votre compte. Tout cela est modifiable dans la page de gestion des <a href="matieres">Matières</a>.</p>
-    <p>Vous pouvez modifier les semaines avec ou sans colle et les vacances scolaires sur la page du <a href="planning">Planning annuel</a>.</p>
+    <h4>Gestion des accès</h4>
+    <p>L'accès aux programmes de colles peut être protégé en cliquant sur le bouton des préférences <span class="icon-prefs"></span>. Trois choix sont possibles&nbsp;:</p>
+    <ul>
+      <li><em>Accès public</em>&nbsp;: programme accessible de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: programme accessible uniquement par les utilisateurs identifiés, en fonction de leur type de compte et des matières auxquelles ils sont associés. Un cadenas <span class="icon-lock"></span> est alors affiché dans le titre de la page. Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Fonction désactivée</em>&nbsp;: les programmes de colles pour cette matière sont complètement indisponibles. La fonction n'apparaît plus dans le menu, y compris pour vous.</li>
+    </ul>
+    <p>Si vous désactivez ici cette fonction, vous ne pourrez plus utiliser cette page. Les programmes de colles ne seront pas supprimés, le choix est réversible. Vous pourrez réactiver cette fonction à la <a href="matieres">gestion des matières</a>.</p>
+    <p>Le lien de la page dans le menu est visible avant identification. Il disparaît après identification pour les utilisateurs n'ayant pas accès à la page.</p>
+    <h4>Suppression de tous les programmes de colles</h4>
+    <p>L'ensemble des programmes de colles est supprimable en un clic à la <a href="matieres">gestion des matières</a>.</p>
   </div>
 
-  <div id="aide-colle">
+  <div id="aide-prefs">
+    <h3>Aide et explications</h3>
+    <p>Ce formulaire permet de modifier l'accès aux programmes de colles en <?php echo $matiere['nom']; ?>. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>. Trois choix sont possibles&nbsp;:</p>
+    <ul>
+      <li><em>Accès public</em>&nbsp;: programme accessible de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: programme accessible uniquement par les utilisateurs identifiés, en fonction de leur type de compte et des matières auxquelles ils sont associés. Un cadenas <span class="icon-lock"></span> est alors affiché dans le titre de la page. Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Fonction désactivée</em>&nbsp;: les programmes de colles pour cette matière sont complètement indisponibles. La fonction n'apparaît plus dans le menu, y compris pour vous.</li>
+    </ul>
+    <p>Si vous désactivez ici cette fonction, vous ne pourrez plus utiliser cette page. Les programmes de colles ne seront pas supprimés, le choix est réversible. Vous pourrez réactiver cette fonction à la <a href="matieres">gestion des matières</a>.</p>
+    <h4>Suppression de tous les programmes de colles</h4>
+    <p>L'ensemble des programmes de colles n'est pas supprimable ici, mais en un clic à la <a href="matieres">gestion des matières</a>.</p>
+  </div>
+
+  <div id="aide-colles">
     <h3>Aide et explications</h3>
     <p>Le texte de chaque programme de colle existant est modifiable, en cliquant sur le bouton <span class="icon-edite"></span>. Si aucun programme de colle n'existe pour la semaine concerné, il est possible d'en créer un en cliquant sur le bouton <span class="icon-ajoute"></span>. Chaque contenu éditable est indiqué par une bordure en pointillés.</p>
     <p>Une fois le texte édité, il est validé en appuyant sur le bouton <span class="icon-ok"></span>. Au contraire, un clic sur le bouton <span class="icon-annule"></span> annule l'édition.</p>
@@ -325,7 +355,7 @@
     <p>Le texte doit être formaté en HTML&nbsp;: par exemple, chaque bloc de texte doit être encadré par &lt;p&gt; et &lt;/p&gt;. Il peut donc contenir des liens vers les autres pages du site, vers des documents du site, vers le web... Des boutons sont fournis pour aider au formattage (ainsi qu'une aide).</p>
   </div>
 
-  <div id="aide-ajoute">
+  <div id="aide-ajoutecolle">
     <h3>Aide et explications</h3>
     <p>Ce formulaire permet de créer un nouveau programme de colles. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-annule"></span>.</p>
     <p>Le texte doit être formaté en HTML&nbsp;: par exemple, chaque bloc de texte doit être encadré par &lt;p&gt; et &lt;/p&gt;. Il peut donc contenir des liens vers les autres pages du site, vers des documents du site, vers le web... Des boutons sont fournis pour aider au formattage (ainsi qu'une aide).</p>
@@ -336,7 +366,5 @@
   <p id="log"></p>
 <?php
 }
-
-$mysqli->close();
 fin($edition,$mathjax);
 ?>
diff -urN cahier-de-prepa6.2.0/config.php cahier-de-prepa8.0.1/config.php
--- cahier-de-prepa6.2.0/config.php	2015-09-20 22:00:18.811329075 +0200
+++ cahier-de-prepa8.0.1/config.php	2018-10-14 21:23:43.171112433 +0200
@@ -2,12 +2,9 @@
 // Fichier de configuration de Cahier de Prépa
 
 // Adresse web
-// Ne pas mettre le protocole ("http://" ou "https://") ni de "/" final
+// Ne pas mettre le protocole ("https://") ni de "/" final
 $site = '';
 
-// Forçage du https
-$https = true;
-
 // Accès à la base de données MySQL
 /* $serveur est le nom du serveur. localhost est la valeur par défaut.
  * $base est le nom de la base de données utilisée. 12 caractères maximum.
@@ -23,4 +20,10 @@
 // redirigées l'ensemble des erreurs
 $mailadmin = 'admin@cahier-de-prepa.fr';
 
+// Adresse d'envoi des mails
+// Utilisée pour envoyer les mails en évitant les erreurs de mauvais
+// serveur SMTP (mettre le même nom de domaine que le serveur web)
+// Les adresses réelles des expéditeurs sont utilisées si vide
+$mailenvoi = 'ne-pas-repondre@cahier-de-prepa.fr';
+
 ?>
diff -urN cahier-de-prepa6.2.0/connexion.php cahier-de-prepa8.0.1/connexion.php
--- cahier-de-prepa6.2.0/connexion.php	2017-08-25 00:47:53.657017642 +0200
+++ cahier-de-prepa8.0.1/connexion.php	1970-01-01 01:00:00.000000000 +0100
@@ -1,461 +0,0 @@
-<?php
-// Sécurité
-define('OK',1);
-// Configuration
-include('config.php');
-// Fonctions
-include('fonctions.php');
-
-// Titre : on récupère celui de la première page
-$mysqli = connectsql();
-$resultat = $mysqli->query('SELECT titre FROM pages WHERE id = 1');
-$r = $resultat->fetch_assoc();
-$resultat->free();
-$titre = "${r['titre']}";
-
-/////////////////////////////////////////////////////////////////////
-// Gestion de la demande : oubli d'identifiant, création de compte //
-/////////////////////////////////////////////////////////////////////
-
-// Oubli d'identifiant
-if ( isset($_REQUEST['oublimdp']) && isset($_REQUEST['mail']) && strlen($mail = str_replace('ø','@',$_REQUEST['mail'])) )  {
-
-  // Recherche de l'adresse électronique dans la base de données
-  $resultat = $mysqli->query('SELECT id, login, mail, mdp FROM utilisateurs WHERE LENGTH(mail)');
-  while ( $r = $resultat->fetch_assoc() )
-    if ( $r['mail'] == $mail )  {
-      $utilisateur = $r;
-      break;
-    }
-  $resultat->free();
-  if ( !isset($utilisateur) )  {
-    $mysqli->close();
-    exit('{"etat":"nok_","message":"L\'adresse électronique donnée est inconnue. Si vous venez de demander la création de votre compte, le mot de passe n\'est pas modifiable tant que les professeurs de la classe n\'ont pas validé l\'inscription. Vous recevrez un courriel quand cela sera effectif.<p class=\"warning\"><a href=\".\">Retourner au Cahier de Prépa</a></p>"}');
-  }
-
-  // Retour de courriel : variable $_REQUEST['p'] 
-  if ( isset($_REQUEST['p']) )  {
-    // Si variable $_REQUEST['p'] à une mauvaise valeur, on l'efface : le
-    // formulaire affiché sera celui de demande d'adresse électronique
-    if ( ( $_REQUEST['p'] != sha1($site.date('Y-m-d-H').$utilisateur['mdp']) ) && ( $_REQUEST['p'] != sha1($site.date('Y-m-d-H',time()+900).$utilisateur['mdp']) ) )
-      unset($_REQUEST['p']);
-
-    // Traitement du formulaire
-    elseif ( isset($_REQUEST['mdp1']) && strlen($newmdp = $_REQUEST['mdp1']) )  {
-      if ( $newmdp != $_REQUEST['mdp2'] )  {
-        $mysqli->close();
-        exit('{"etat":"nok_","message":"Les deux mots de passe donnés ne sont pas identiques."}');
-      }
-      // Écriture du nouveau mot de passe
-      $mysqli->close();
-      $mysqli = connectsql(true);
-      $message = ( requete('utilisateurs',"UPDATE utilisateurs SET mdp = '".sha1($newmdp)."' WHERE id = ${utilisateur['id']}",$mysqli) ) ? '{"etat":"ok_","message":"Votre mot de passe a bien été modifié.</p><p class=\"warning\"><a href=\".\">Retourner au Cahier de Prépa</a></p>"}' : '{"etat":"nok_","message":"Votre mot de passe n\'a pas pu être modifié suite à une erreur technique."}';
-      $mysqli->close();
-      exit($message);
-    }
-  }
-
-  // Envoi de courriel
-  else  {
-    // On ajoute 15 minutes au temps utilisé :
-    // de xh00 à xh45, on a jusqu'à (x+1)h, de xh45 à (x+1)h on a jusqu'à (x+2)h
-    $t = time() + 900;
-    $lien = ( $https ? 'https' : 'http' )."://$site/connexion?oublimdp&mail=".str_replace('@','ø',$mail).'&p='.sha1($site.date('Y-m-d-H',$t).$utilisateur['mdp']);
-    $s = strstr($site.'/','/',true);
-    if ( !isset($mailadmin) )  $mailadmin = 'admin@cahier-de-prepa.fr';
-    mail($mail,'=?UTF-8?B?'.base64_encode('[Cahier de Prépa] Changement de mot de passe').'?=',
-'Bonjour
-
-Vous avez rempli une demande de modification de votre mot de passe sur le Cahier de Prépa <http'.($https?'s':'')."://$site>, correspondant à l'identifiant ${utilisateur['login']}.
-
-Si cette demande ne vient pas de vous ou si vous avez retrouvé votre mot de passe, merci d'ignorer simplement ce courriel.
-
-Sinon, veuillez cliquer ci-dessous pour vous rendre à la page qui vous permettra de modifier votre mot de passe :
-    $lien
-Ce lien est valable jusqu'à ".date('G\h00',$t+3600).'.
-
-Cordialement,
--- 
-Cahier de Prépa
-','From: =?UTF-8?B?'.base64_encode('Cahier de Prépa')."?= <$mailadmin>\r\nContent-type: text/plain; charset=UTF-8","-f$mailadmin");
-    $mysqli->close();
-    exit('{"etat":"ok_","message":"Un courriel vient de vous être envoyé à l\'adresse <code>'.$mail.'</code>.<br>Si vous ne voyez rien, pensez à regarder dans les courriels marqués comme spam. Certains serveurs retardent jusqu\'à 10 minutes l\'arrivée des messages, normalement la première fois uniquement.<br>Le courriel qui vous a été envoyé contiendra un lien, valable jusqu\'à '.date('G\h00',$t+3600).', vous permettant de modifier votre mot de passe.<p class=\"warning\"><a href=\".\">Retourner au Cahier de Prépa</a></p>"}');
-  }
-}
-
-// Création de compte
-elseif ( isset($_REQUEST['creationcompte']) )  {
-  
-  // Vérification de la possibilité de rajouter des comptes
-  // Si un utilisateur au mot de passe vide a été inséré, c'est le réglage pour
-  // arrêter la création de compte
-  $resultat = $mysqli->query('SELECT id FROM utilisateurs WHERE LENGTH(mdp) = 0');
-  if ( $creationinterdite = $resultat->num_rows )
-    $resultat->free();
-  elseif ( isset($_REQUEST['mail']) && strlen($mail = str_replace('ø','@',strtolower(trim($_REQUEST['mail'])))) )  {
-    // Vérifications
-    if ( !filter_var($mail,FILTER_VALIDATE_EMAIL) )  {
-      $mysqli->close();
-      exit('{"etat":"nok_","message":"L\'adresse saisie n\'est pas une adresse électronique valide.<br>Vous avez dû faire une faute de frappe."}');
-    }
-    if ( in_array($domaine = strstr($mail,'@'),array('@gmail.fr','@laposte.fr')) )  {
-      $mysqli->close();
-      exit('{"etat":"nok_","message":"L\'adresse saisie ne pourra pas fonctionner&nbsp;: le domaine <code>'.$domaine.'</code> ne reçoit pas de courriels."}');
-    }
-    // Recherche du mail dans la base de données
-    $resultat = $mysqli->query('SELECT GROUP_CONCAT(mail) AS mails FROM utilisateurs WHERE LENGTH(mail)');
-    $r = $resultat->fetch_assoc();
-    $resultat->free();
-    if ( in_array($mail,$mails = explode(',',$r['mails'])) || in_array("@$mail",$mails) )  {
-      $mysqli->close();
-      exit('{"etat":"nok_","message":"Un compte avec cette adresse électronique existe déjà. Si vous venez de demander la création de votre compte, le compte n\'est pas modifiable tant que les professeurs de la classe n\'ont pas validé l\'inscription. Vous recevrez un courriel quand cela sera effectif. Si ce compte a déjà été validé par les professeurs, vous pouvez <a href=\"?oublimdp\">changer le mot de passe</a>.<p class=\"warning\"><a href=\".\">Retourner au Cahier de Prépa</a></p>"}');
-    }
-
-    // Retour de mail : variable $_REQUEST['p'] 
-    if ( isset($_REQUEST['p']) )  {
-      // Si variable $_REQUEST['p'] à une mauvaise valeur, on l'efface : le
-      // formulaire affiché sera celui de demande d'adresse mail
-      if ( ( $_REQUEST['p'] != sha1($site.$mdp.date('Y-m-d-H').$mail) ) && ( $_REQUEST['p'] != sha1($site.$mdp.date('Y-m-d-H',time()+900).$mail) ) )
-        unset($_REQUEST['p']);
-
-      // Traitement du formulaire
-      // Un compte temporaire non validé par l'équipe pédagogique contient une
-      // adresse mail commençant par @ (pour interdire le changement de mdp)
-      // et un mot de passe commençant par * (pour interdire la connexion).
-      elseif ( isset($_REQUEST['nom']) )  {
-        if ( !strlen($nom = trim($_REQUEST['nom'])) )
-          $message = '{"etat":"nok_","message":"Le nom est obligatoire."}';
-        elseif ( !isset($_REQUEST['prenom']) || !strlen($prenom = trim($_REQUEST['prenom'])) )
-          $message = '{"etat":"nok_","message":"Le prénom est obligatoire."}';
-        elseif ( !isset($_REQUEST['mdp1']) || !strlen($newmdp = $_REQUEST['mdp1']) )
-          $message = '{"etat":"nok_","message":"Le mot de passe est obligatoire."}';
-        elseif ( $newmdp != $_REQUEST['mdp2'] )
-          $message = '{"etat":"nok_","message":"Les deux mots de passe donnés ne sont pas identiques."}';
-        else  {
-          // Nettoyage des données envoyées
-          $prenom = mb_convert_case(strip_tags($mysqli->real_escape_string($prenom)),MB_CASE_TITLE,'UTF-8');
-          $nom = mb_convert_case(strip_tags($mysqli->real_escape_string($nom)),MB_CASE_TITLE,'UTF-8');
-          $mail = $mysqli->real_escape_string($mail);
-          $newmdp = sha1($newmdp);
-          // Login automatique
-          $login = mb_strtolower(mb_substr($prenom,0,1,'UTF-8').str_replace(' ','_',$nom),'UTF-8');
-          $resultat = $mysqli->query('SELECT GROUP_CONCAT(login) AS logins FROM utilisateurs');
-          $r = $resultat->fetch_assoc();
-          $resultat->free();
-          if ( in_array($login,explode(',',$r['logins'])) )
-            $message = '{"etat":"nok_","message":"Un compte avec le même identifiant existe déjà. Merci de vous connecter avec l\'adresse électronique correspondante."}';
-          else  {
-            // Récupération des matières
-            $resultat = $mysqli->query('SELECT GROUP_CONCAT(id) AS matieres FROM matieres');
-            $r = $resultat->fetch_row();
-            $resultat->free();
-            // Si "colleur" choisi, autorisation vaut 3. Sinon, 2 (élève)
-            if ( $_REQUEST['autorisation'] == 2 )  {
-              $autorisation = 3;
-              // Vérification de la matière
-              $matiere = ( isset($_REQUEST['matiere']) && in_array($_REQUEST['matiere'],explode(',',$r['0'])) ) ? "0,${_REQUEST['matiere']}" : 0;
-              $mailexp = "$prenom $nom";
-              $mailcopy = 1;
-            }
-            else  {
-              $autorisation = 2;
-              // De base : toutes matières
-              $matiere = "0,${r[0]}";
-              $mailexp = '';
-              $mailcopy = 0;
-            }
-            // Écriture du nouveau compte
-            $mysqli->close();
-            $mysqli = connectsql(true);
-            $message = ( requete('utilisateurs',"INSERT INTO utilisateurs SET login = '$login', prenom = '$prenom', nom = '$nom', mail = '@$mail',
-                       mdp = '*$newmdp', autorisation = $autorisation, matieres = '$matiere', timeout = 900, mailexp = '$mailexp', mailcopy = $mailcopy",$mysqli) )
-                       ? '{"etat":"ok_","message":"Votre demande d\'inscription est terminée. Elle est maintenant en attente de validation par les professeurs de la classe. Vous recevrez un courriel lorsque votre inscription sera validée.</p><p class=\"warning\"><a href=\".\">Retourner au Cahier de Prépa</a></p>"}'
-                       : '{"etat":"nok_","message":"Votre demande d\'inscription n\'a pas pu être enregistrée suite à une erreur technique."}';
-          }
-        }
-        $mysqli->close();
-        exit($message);
-      }
-    }
-    
-    // Envoi de mail
-    else  {
-      // On ajoute 15 minutes au temps utilisé : de xh00 à xh45,
-      // on a jusqu'à (x+1)h, de xh45 à (x+1)h on a jusqu'à (x+2)h
-      $t = time() + 900;
-      $lien = ( $https ? 'https' : 'http' )."://$site/connexion?creationcompte&mail=".str_replace('@','ø',$mail).'&p='.sha1($site.$mdp.date('Y-m-d-H',$t).$mail);
-      if ( !isset($mailadmin) )  $mailadmin = 'admin@cahier-de-prepa.fr';
-      mail($mail,'=?UTF-8?B?'.base64_encode('[Cahier de Prépa] Création de compte').'?=',
-'Bonjour
-
-Vous venez de donner cette adresse pour une demande de création de compte sur le Cahier de Prépa <http'.($https?'s':'')."://$site>.
-
-Si cette demande ne vient pas de vous, merci d'ignorer simplement ce courriel.
-
-Sinon, veuillez cliquer ci-dessous pour vous rendre à la page qui vous permettra de terminer votre inscription :
-   $lien
-Ce lien est valable jusqu'à ".date('G\h00',$t+3600).'.
-
-Cordialement,
--- 
-Cahier de Prépa
-','From: =?UTF-8?B?'.base64_encode('Cahier de Prépa')."?= <$mailadmin>\r\nContent-type: text/plain; charset=UTF-8","-f$mailadmin");
-      $mysqli->close();
-      exit('{"etat":"ok_","message":"Un courriel vient de vous être envoyé à l\'adresse <code>'.$mail.'</code>.<br>Si vous ne voyez rien, pensez à regarder dans les courriels marqués comme spam. Vérifiez bien qu\'il n\'y a pas d\'erreur dans cette adresse, car vous ne pourrez pas continuer votre inscription si elle est fausse. Certains serveurs retardent jusqu\'à 10 minutes l\'arrivée des messages, la première fois uniquement.<br>Le courriel qui vous a été envoyé contiendra un lien, valable jusqu\'à '.date('G\h00',$t+3600).', vous permettant de terminer votre inscription.<p class=\"warning\"><a href=\".\">Retourner au Cahier de Prépa</a></p>"}');
-    }
-  }
-}
-
-// Réponse à une invitation
-elseif ( isset($_REQUEST['reponseinvitation']) && isset($_REQUEST['mail']) && strlen($mail = str_replace('ø','@',$_REQUEST['mail'])) && isset($_REQUEST['p']) && ( $_REQUEST['p'] == sha1($site.$mdp.$mail) ) )  {
-  // Recherche de l'adresse électronique dans la base de données
-  $resultat = $mysqli->query('SELECT id, login, mail, LENGTH(mdp) AS lmdp FROM utilisateurs WHERE LENGTH(mail)');
-  while ( $r = $resultat->fetch_assoc() )
-    if ( $r['mail'] == $mail )  {
-      $utilisateur = $r;
-      break;
-    }
-  $resultat->free();
-  if ( !isset($utilisateur) )  {
-    debut($mysqli,$titre,'L\'adresse électronique donnée est inconnue. Le compte pour lequel vous avez été invité a peut-être été supprimé.',false);
-    $mysqli->close();
-    fin();
-  }
-  if ( $r['lmdp'] == 40 )  {
-    debut($mysqli,$titre,'Le mot de passe a déjà été défini. Si vous souhaitez le modifier, vous pouvez utiliser le formulaire de <a href="?oublimdp">mot de passe oublié</a>.',false);
-    $mysqli->close();
-    fin();
-  }
-  
-  // Modification du mot de passe
-  if ( isset($_REQUEST['mdp1']) && strlen($newmdp = $_REQUEST['mdp1']) )  {
-    if ( $newmdp != $_REQUEST['mdp2'] )  {
-      $mysqli->close();
-      exit('{"etat":"nok_","message":"Les deux mots de passe donnés ne sont pas identiques."}');
-    }
-    // Écriture du nouveau mot de passe
-    $mysqli->close();
-    $mysqli = connectsql(true);
-    $message = ( requete('utilisateurs',"UPDATE utilisateurs SET mdp = '".sha1($newmdp)."' WHERE id = ${utilisateur['id']}",$mysqli) ) ? '{"etat":"ok_","message":"Votre mot de passe a bien été modifié.</p><p class=\"warning\"><a href=\".\">Retourner au Cahier de Prépa</a></p>"}' : '{"etat":"nok_","message":"Votre mot de passe n\'a pas pu être modifié suite à une erreur technique."}';
-    $mysqli->close();
-    exit($message);
-  }
-}
-
-////////////
-/// HTML ///
-////////////
-debut($mysqli,$titre,'',false);
-if ( isset($_REQUEST['oublimdp']) )  {
-  if ( !isset($_REQUEST['p']) )
-    echo <<<FIN
-
-  <article>
-    <a class="icon-ok" title="Valider"></a>
-    <form onsubmit="$('a.icon-ok').click(); return false;">
-      <h3>Mot de passe oublié</h3>
-      <p>Si vous avez oublié votre mot de passe, vous pouvez le récupérer en entrant votre adresse électronique ci-dessous. Vous recevrez un courriel contenant un lien temporaire permettant de modifier votre mot de passe.</p>
-      <input class="ligne" type="text" name="mail" data-placeholder="Adresse électronique">
-    </form>
-  </article>
-
-FIN;
-  else
-    echo <<<FIN
-
-  <article>
-    <a class="icon-ok" title="Valider"></a>
-    <form>
-      <h3>Mot de passe oublié</h3>
-      <p>Veuillez entrer deux fois votre nouveau mot de passe&nbsp;:</p>
-      <input class="ligne" type="password" name="mdp1" data-placeholder="Mot de passe">
-      <input class="ligne" type="password" name="mdp2" data-placeholder="Confirmation">
-      <input type="hidden" name="p" value="${_REQUEST['p']}">
-      <input type="hidden" name="mail" value="${utilisateur['mail']}">
-    </form>
-  </article>
-
-FIN;
-?>
-
-  <script type="text/javascript">
-$( function() {
-  // Placeholders
-  $('input[data-placeholder]').placeholder();
-  // Envoi
-  $('a.icon-ok').on("click",function () {
-    $.ajax({url: 'connexion.php', method: "post", data: 'oublimdp=&'+$('form').serialize(), dataType: 'json'})
-    .done( function(data) {
-      if ( data['etat'] == 'ok_' ) {
-        $('p').html(data['message']).removeClass('warning');
-        $('a.icon-ok, input').remove();
-      }
-      else if ( data['etat'] == 'nok_' )
-        $('p').html(data['message']).addClass('warning');
-    });
-  });
-  // Envoi par appui sur Entrée
-  $('input[name!="mail"]').on('keypress',function (e) {
-    if ( e.which == 13 )
-      $('a.icon-ok').click();
-  });
-  // Focus avec du retard pour éviter un bug d'affichage du placeholder
-  setTimeout(function() { $('input:first').focus() },500);
-});
-  </script>
-
-<?php
-}
-
-elseif ( isset($_REQUEST['creationcompte']) )  {
-  if ( $creationinterdite )
-    echo '<h2>La demande de création de compte n\'est pas autorisée sur ce Cahier de Prépa.</h2><p style="text-align:center;"><a href=".">Retour à la page d\'accueil</a></p>';
-  elseif ( !isset($_REQUEST['p']) )
-    echo <<<FIN
-
-  <article>
-    <a class="icon-ok" title="Valider"></a>
-    <form onsubmit="$('a.icon-ok').click(); return false;">
-      <h3>Création de compte</h3>
-      <p>Vous pouvez demander ici une création de compte sur ce Cahier de Prépa, si vous êtes élève ou colleur. Vous devez tout d'abord fournir une adresse électronique valide. Vous recevrez un courriel contenant un lien temporaire permettant de continuer votre inscription.</p>
-      <p>Il est par ailleurs conseillé de consulter cette adresse régulièrement, puisque les professeurs et colleurs de la classe pourront vous envoyer des courriels sur cette adresse via Cahier de Prépa. Cette adresse ne sera divulguée à personne d'autre que les professeurs et colleurs de la classe.</p>
-      <p>À la fin de votre inscription, la demande sera mise en attente de validation par les professeurs de la classe.</p>
-      <input class="ligne" type="text" name="mail" data-placeholder="Adresse électronique">
-    </form>
-  </article>
-
-FIN;
-  else  {
-    // Récupération des matières
-    $resultat = $mysqli->query('SELECT id, nom FROM matieres');
-    $select_matieres = '';
-    while ( $r = $resultat->fetch_assoc() )
-      $select_matieres .= "        <option value=\"${r['id']}\">${r['nom']}</option>\n";
-    $resultat->free();
-    echo <<<FIN
-
-  <article>
-    <a class="icon-ok" title="Valider"></a>
-    <form>
-      <h3>Création de compte</h3>
-      <p>Vous pouvez demander ici une création de compte sur ce Cahier de Prépa, si vous êtes élève ou colleur. Une fois votre demande remplie, l'inscription sera mise en attente de validation par les professeurs de la classe. Vous recevrez un courriel si votre inscription est validée.</p>
-      <p>Le mot de passe vous est complètement personnel et ne sera divulgué à personne. Il est chiffré avant son stockage dans la base de données. La bonne pratique est de ne pas écrire un simple mot du dictionnaire mais une suite de lettres, de chiffres et de signes de ponctuation qui n'ont de sens que pour vous. Tous les caractères de votre clavier, y compris l'espace, sont autorisés.</p>
-      <input class="ligne" type="text" id="prenom" name="prenom" size="50" data-placeholder="Prénom">
-      <input class="ligne" type="text" id="nom" name="nom" size="50"" data-placeholder="Nom">
-      <input class="ligne" type="password" name="mdp1" data-placeholder="Mot de passe">
-      <input class="ligne" type="password" name="mdp2" data-placeholder="Confirmation">
-      <p class="ligne"><label for="autorisation">Élève ou colleur&nbsp;:</label>
-        <select name="autorisation" id="autorisation">
-          <option value="1">Élève</option>
-          <option value="2">Colleur</option>
-        </select>
-      </p>
-      <p class="ligne"><label for="matiere">Matière&nbsp;:</label>
-        <select name="matiere" id="matiere">
-$select_matieres
-        </select>
-      </p>
-      <input type="hidden" name="p" value="${_REQUEST['p']}">
-      <input type="hidden" name="mail" value="$mail">
-    </form>
-  </article>
-
-FIN;
-  }
-?>
-
-  <script type="text/javascript">
-$( function() {
-  // Placeholders
-  $('input[data-placeholder]').placeholder();
-  // Envoi
-  $('a.icon-ok').on("click",function () {
-    $.ajax({url: 'connexion.php', method: "post", data: 'creationcompte=&'+$('form').serialize(), dataType: 'json'})
-    .done( function(data) {
-      if ( data['etat'] == 'ok_' )  {
-        $('p:first').html(data['message']).removeClass('warning');
-        $('a.icon-ok, p+p, .ligne').remove();
-      }
-      else if ( data['etat'] == 'nok_' )  {
-        $('p:first').html(data['message']).addClass('warning');
-        $('p').not(':first').not('.ligne').remove();
-      }
-    });
-  });
-  // Envoi par appui sur Entrée
-  $('input[name!="mail"],select').on('keypress',function (e) {
-    if ( e.which == 13 )
-      $('a.icon-ok').click();
-  });
-  // Focus avec du retard pour éviter un bug d'affichage du placeholder
-  setTimeout(function() { $('input:first').focus() },500);
-  // Pour la matière associée (colleurs seulement)
-  $('#autorisation').on("change keyup", function() { 
-    switch ( $(this).val() )  {
-      case '1' : $('#matiere').parent().hide(); break;
-      case '2' : $('#matiere').parent().show();
-    }
-  });
-  $('#matiere').parent().hide();
-});
-  </script>
-
-<?php
-}
-
-elseif ( isset($_REQUEST['reponseinvitation']) )  {
-  if ( isset($utilisateur['login']) )  {
-?>
-
-  <article>
-    <a class="icon-ok" title="Valider"></a>
-    <form>
-      <h3>Création de compte</h3>
-      <p>L'équipe pédagogique vous a créé un compte ici. L'identifiant associé à ce compte est</p>
-      <p class="warning"><?php echo $utilisateur['login']; ?></p>
-      <p>Il ne vous reste plus qu'à définir votre mot de passe. Le mot de passe vous est complètement personnel et ne sera divulgué à personne. Il est chiffré avant son stockage dans la base de données. La bonne pratique est de ne pas écrire un simple mot du dictionnaire mais une suite de lettres, de chiffres et de signes de ponctuation qui n'ont de sens que pour vous. Tous les caractères de votre clavier, y compris l'espace, sont autorisés.</p>
-      <input class="ligne" type="password" name="mdp1" data-placeholder="Mot de passe">
-      <input class="ligne" type="password" name="mdp2" data-placeholder="Confirmation">
-      <input type="hidden" name="p" value="<?php echo $_REQUEST['p']; ?>">
-      <input type="hidden" name="mail" value="<?php echo $mail; ?>">
-    </form>
-  </article>
-  
-  <script type="text/javascript">
-$( function() {
-  // Placeholders
-  $('input').placeholder();
-  // Envoi
-  $('a.icon-ok').on("click",function () {
-    $.ajax({url: 'connexion.php', method: "post", data: 'reponseinvitation=&'+$('form').serialize(), dataType: 'json'})
-    .done( function(data) {
-      if ( data['etat'] == 'ok_' )  {
-        $('form').html('<p>'+data['message']+'</p>');
-      }
-      else if ( data['etat'] == 'nok_' )  {
-        $('p:first').html(data['message']).addClass('warning');
-        $('p').not(':first').not('.ligne').remove();
-      }
-    });
-  });
-  // Envoi par appui sur Entrée
-  $('input').on('keypress',function (e) {
-    if ( e.which == 13 )
-      $('a.icon-ok').click();
-  });
-  // Focus avec du retard pour éviter un bug d'affichage du placeholder
-  setTimeout(function() { $('input:first').focus() },500);
-});
-  </script>
-<?php
-  }
-  else
-    echo '<h2>Les paramètres d\'accès à cette page ne sont pas corrects.</h2>';
-}
-
-else
-  echo '<h2>Aucune action n\'a été effectuée.</h2>';
-
-$mysqli->close();
-fin();
-?>
diff -urN cahier-de-prepa6.2.0/css/icones1810.min.css cahier-de-prepa8.0.1/css/icones1810.min.css
--- cahier-de-prepa6.2.0/css/icones1810.min.css	1970-01-01 01:00:00.000000000 +0100
+++ cahier-de-prepa8.0.1/css/icones1810.min.css	2018-10-14 22:22:51.240384586 +0200
@@ -0,0 +1 @@
+@font-face{font-family:'icomoon';src:url('../fonts/icomoon.eot?13');src:url('../fonts/icomoon.eot?#iefix13') format('embedded-opentype'),url('../fonts/icomoon.ttf?13') format('truetype'),url('../fonts/icomoon.woff?13') format('woff'),url('../fonts/icomoon.svg?13#icomoon') format('svg');font-weight:normal;font-style:normal}[class^="icon-"]{font-family:'icomoon'!important;color:black;speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}a[class^="icon-"]{cursor:pointer;text-decoration:none}span.icon-minilock{position:relative!important;left:-0.3em;top:.1em;color:#777;margin-right:-0.5em}.icon-montre:before{content:"\e600"}.icon-cache:before{content:"\e601"}.icon-aide:before{content:"\e602"}.icon-ajoute:before,.icon-ajoutecolle:before{content:"\e603"}.icon-supprime:before{content:"\e604"}.icon-annule:before{content:"\e605"}.icon-ok:before{content:"\e606"}.icon-prefs:before{content:"\e607"}.icon-monte:before{content:"\e608"}.icon-descend:before{content:"\e609"}.icon-ferme:before{content:"\e60a"}.icon-epingle:before{content:"\e60b"}.icon-par1:before{content:"\e60c"}.icon-par2:before{content:"\e60d"}.icon-par3:before{content:"\e60e"}.icon-gras:before{content:"\e60f"}.icon-italique:before{content:"\e610"}.icon-souligne:before{content:"\e611"}.icon-omega:before{content:"\e612"}.icon-sigma:before{content:"\e613"}.icon-exp:before{content:"\e614"}.icon-ind:before{content:"\e615"}.icon-ol:before{content:"\e616"}.icon-ul:before{content:"\e617"}.icon-lien1:before{content:"\e618"}.icon-lien2:before{content:"\e619"}.icon-retour:before{content:"\e61a"}.icon-source:before{content:"\e61b"}.icon-nosource:before{content:"\e61c"}.icon-tex:before{content:"\e61d"}.icon-titres:before{content:"\e61e"}.icon-edite:before,.icon-editedoc:before,.icon-editerep:before{content:"\e61f"}.icon-precedent:before{content:"\e620"}.icon-suivant:before{content:"\e621"}.icon-recherche:before{content:"\e622"}.icon-voirtout:before{content:"\e623";color:#000}.icon-accueil:before{content:"\e624"}.icon-imprime:before{content:"\e625"}.icon-connexion:before{content:"\e626"}.icon-deconnexion:before{content:"\e627"}.icon-mail:before{content:"\e628"}.icon-menu:before{content:"\e629"}.icon-cocher:before{content:"\e62a"}.icon-decocher:before{content:"\e62b"}.icon-rep:before{content:"\e62c"}.icon-rep-open:before{content:"\e62d"}.icon-download:before{content:"\e62e"}.icon-minilock:before{content:"\e62f"}.icon-alphaasc:before{content:"\e630"}.icon-alphadesc:before{content:"\e631"}.icon-chronoasc:before{content:"\e632"}.icon-chronodesc:before{content:"\e633"}.icon-ajouterep:before{content:"\e634"}.icon-ajoutedoc:before{content:"\e635"}.icon-doc:before{content:"\e636"}.icon-doc-pdf:before{content:"\e637"}.icon-doc-doc:before{content:"\e638"}.icon-doc-xls:before{content:"\e639"}.icon-doc-ppt:before{content:"\e63a"}.icon-doc-jpg:before{content:"\e63b"}.icon-doc-zip:before{content:"\e63c"}.icon-doc-mp3:before{content:"\e63d"}.icon-doc-mp4:before{content:"\e63e"}.icon-doc-pyt:before{content:"\e63f"}.icon-rss:before{content:"\e640"}.icon-infos:before{content:"\e641"}.icon-colles:before{content:"\e642"}.icon-recent:before{content:"\e643"}.icon-lock1:before{content:"\e644"}.icon-lock2:before{content:"\e645"}.icon-lock3:before{content:"\e646"}.icon-lock4:before{content:"\e647"}.icon-lock5:before{content:"\e648"}.icon-agenda:before{content:"\e649"}.icon-ajout-colle:before{content:"\e64a"}.icon-messages:before{content:"\e64b"}.icon-lock:before{content:"\e64c"}.icon-locktotal:before{content:"\e64d"}.icon-editemultiple:before{content:"\e64e"}.icon-plie:before{content:"\e64f"}.icon-deplie:before{content:"\e650"}.icon-mailenvoi:before{content:"\e651"}.icon-nok:before{content:"\e652"}.icon-active:before{content:"\e653"}.icon-desactive:before{content:"\e654"}.icon-supprutilisateur:before{content:"\e655"}.icon-validutilisateur:before{content:"\e656"}.icon-lecture:before{content:"\e657"}.icon-nolecture:before{content:"\e658";
\ No newline at end of file
diff -urN cahier-de-prepa6.2.0/css/icones.css cahier-de-prepa8.0.1/css/icones.css
--- cahier-de-prepa6.2.0/css/icones.css	2016-07-25 23:24:21.557674925 +0200
+++ cahier-de-prepa8.0.1/css/icones.css	2018-10-09 01:12:05.346269747 +0200
@@ -1,30 +1,34 @@
 @font-face {
-	font-family: 'icomoon';
-	src:url('../fonts/icomoon.eot?13');
-	src:url('../fonts/icomoon.eot?#iefix13') format('embedded-opentype'),
-		url('../fonts/icomoon.ttf?13') format('truetype'),
-		url('../fonts/icomoon.woff?13') format('woff'),
-		url('../fonts/icomoon.svg?13#icomoon') format('svg');
-	font-weight: normal;
-	font-style: normal;
+  font-family: 'icomoon';
+  src:url('../fonts/icomoon.eot?13');
+  src:url('../fonts/icomoon.eot?#iefix13') format('embedded-opentype'),
+    url('../fonts/icomoon.ttf?13') format('truetype'),
+    url('../fonts/icomoon.woff?13') format('woff'),
+    url('../fonts/icomoon.svg?13#icomoon') format('svg');
+  font-weight: normal;
+  font-style: normal;
 }
 
-[class^="icon-"], [class*=" icon-"] {
-	font-family: 'icomoon';
+[class^="icon-"] {
+  font-family: 'icomoon' !important;
   color: black;
-	/* Better Font Rendering =========== */
-	-webkit-font-smoothing: antialiased;
-	-moz-osx-font-smoothing: grayscale;
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+
+  /* Better Font Rendering =========== */
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
 }
+
 a[class^="icon-"] {
   cursor: pointer;
-  float: right;
-  margin-left: 5px;
-  padding-top: 0.2em;
   text-decoration: none;
-  font-weight: 500;
 }
-span.icon-lock {
+span.icon-minilock {
     position: relative !important;
     left: -0.3em;
     top: 0.1em;
@@ -32,227 +36,224 @@
     margin-right: -0.5em;
 }
 .icon-montre:before {
-	content: "\e600";
+  content: "\e600";
 }
 .icon-cache:before {
-	content: "\e601";
+  content: "\e601";
 }
 .icon-aide:before {
-	content: "\e602";
+  content: "\e602";
 }
-.icon-ajoute:before {
-	content: "\e603";
+.icon-ajoute:before, .icon-ajoutecolle:before {
+  content: "\e603";
 }
 .icon-supprime:before {
-	content: "\e604";
+  content: "\e604";
 }
 .icon-annule:before {
-	content: "\e605";
+  content: "\e605";
 }
 .icon-ok:before {
-	content: "\e606";
+  content: "\e606";
 }
 .icon-prefs:before {
-	content: "\e607";
+  content: "\e607";
 }
 .icon-monte:before {
-	content: "\e608";
+  content: "\e608";
 }
 .icon-descend:before {
-	content: "\e609";
+  content: "\e609";
 }
 .icon-ferme:before {
-	content: "\e60a";
+  content: "\e60a";
 }
 .icon-epingle:before {
-	content: "\e60b";
+  content: "\e60b";
 }
 .icon-par1:before {
-	content: "\e60c";
+  content: "\e60c";
 }
 .icon-par2:before {
-	content: "\e60d";
+  content: "\e60d";
 }
 .icon-par3:before {
-	content: "\e60e";
+  content: "\e60e";
 }
 .icon-gras:before {
-	content: "\e60f";
+  content: "\e60f";
 }
 .icon-italique:before {
-	content: "\e610";
+  content: "\e610";
 }
 .icon-souligne:before {
-	content: "\e611";
+  content: "\e611";
 }
 .icon-omega:before {
-	content: "\e612";
+  content: "\e612";
 }
 .icon-sigma:before {
-	content: "\e613";
+  content: "\e613";
 }
 .icon-exp:before {
-	content: "\e614";
+  content: "\e614";
 }
 .icon-ind:before {
-	content: "\e615";
+  content: "\e615";
 }
 .icon-ol:before {
-	content: "\e616";
+  content: "\e616";
 }
 .icon-ul:before {
-	content: "\e617";
+  content: "\e617";
 }
 .icon-lien1:before {
-	content: "\e618";
+  content: "\e618";
 }
 .icon-lien2:before {
-	content: "\e619";
+  content: "\e619";
 }
 .icon-retour:before {
-	content: "\e61a";
+  content: "\e61a";
 }
 .icon-source:before {
-	content: "\e61b";
+  content: "\e61b";
 }
 .icon-nosource:before {
-	content: "\e61c";
+  content: "\e61c";
 }
 .icon-tex:before {
-	content: "\e61d";
+  content: "\e61d";
 }
 .icon-titres:before {
-	content: "\e61e";
+  content: "\e61e";
 }
-.icon-edite:before {
-	content: "\e61f";
+.icon-edite:before, .icon-editedoc:before, .icon-editerep:before {
+  content: "\e61f";
 }
 .icon-precedent:before {
-	content: "\e620";
+  content: "\e620";
 }
 .icon-suivant:before {
-	content: "\e621";
+  content: "\e621";
 }
 .icon-recherche:before {
-	content: "\e622";
+  content: "\e622";
 }
 .icon-voirtout:before {
-	content: "\e623";
+  content: "\e623";
+  color: #000;
 }
 .icon-accueil:before {
-	content: "\e624";
+  content: "\e624";
 }
 .icon-imprime:before {
-	content: "\e625";
+  content: "\e625";
 }
 .icon-connexion:before {
-	content: "\e626";
+  content: "\e626";
 }
 .icon-deconnexion:before {
-	content: "\e627";
+  content: "\e627";
 }
 .icon-mail:before {
-	content: "\e628";
+  content: "\e628";
 }
 .icon-menu:before {
-	content: "\e629";
+  content: "\e629";
 }
 .icon-cocher:before {
-	content: "\e62a";
+  content: "\e62a";
 }
 .icon-decocher:before {
-	content: "\e62b";
+  content: "\e62b";
 }
 .icon-rep:before {
-	content: "\e62c";
+  content: "\e62c";
 }
 .icon-rep-open:before {
-	content: "\e62d";
+  content: "\e62d";
 }
 .icon-download:before {
-	content: "\e62e";
+  content: "\e62e";
 }
-.icon-lock:before {
-	content: "\e62f";
+.icon-minilock:before {
+  content: "\e62f";
 }
 .icon-alphaasc:before {
-	content: "\e630";
+  content: "\e630";
 }
 .icon-alphadesc:before {
-	content: "\e631";
+  content: "\e631";
 }
 .icon-chronoasc:before {
-	content: "\e632";
+  content: "\e632";
 }
 .icon-chronodesc:before {
-	content: "\e633";
+  content: "\e633";
 }
 .icon-ajouterep:before {
-	content: "\e634";
+  content: "\e634";
 }
 .icon-ajoutedoc:before {
-	content: "\e635";
+  content: "\e635";
 }
 .icon-doc:before {
-	content: "\e636";
-}
-.icon-doc-txt:before {
-	content: "\e636";
+  content: "\e636";
 }
 .icon-doc-pdf:before {
-	content: "\e637";
+  content: "\e637";
 }
 .icon-doc-doc:before {
-	content: "\e638";
+  content: "\e638";
 }
 .icon-doc-xls:before {
-	content: "\e639";
+  content: "\e639";
 }
 .icon-doc-ppt:before {
-	content: "\e63a";
+  content: "\e63a";
 }
 .icon-doc-jpg:before {
-	content: "\e63b";
+  content: "\e63b";
 }
 .icon-doc-zip:before {
-	content: "\e63c";
+  content: "\e63c";
 }
 .icon-doc-mp3:before {
-	content: "\e63d";
+  content: "\e63d";
 }
 .icon-doc-mp4:before {
-	content: "\e63e";
+  content: "\e63e";
 }
 .icon-doc-pyt:before {
-	content: "\e63f";
+  content: "\e63f";
 }
 .icon-rss:before {
-	content: "\e640";
+  content: "\e640";
 }
 .icon-infos:before {
-	content: "\e641";
+  content: "\e641";
 }
 .icon-colles:before {
-	content: "\e642";
+  content: "\e642";
 }
 .icon-recent:before {
-	content: "\e643";
+  content: "\e643";
 }
 .icon-lock1:before {
-	content: "\e644";
+  content: "\e644";
 }
 .icon-lock2:before {
-	content: "\e645";
+  content: "\e645";
 }
 .icon-lock3:before {
-	content: "\e646";
+  content: "\e646";
 }
 .icon-lock4:before {
-	content: "\e647";
+  content: "\e647";
 }
 .icon-lock5:before {
-	content: "\e648";
-  color: #C00;
+  content: "\e648";
 }
 .icon-agenda:before {
   content: "\e649";
@@ -260,3 +261,44 @@
 .icon-ajout-colle:before {
   content: "\e64a";
 }
+.icon-messages:before {
+  content: "\e64b";
+}
+.icon-lock:before {
+  content: "\e64c";
+}
+.icon-locktotal:before {
+  content: "\e64d";
+}
+.icon-editemultiple:before {
+  content: "\e64e";
+}
+.icon-plie:before {
+  content: "\e64f";
+}
+.icon-deplie:before {
+  content: "\e650";
+}
+.icon-mailenvoi:before {
+  content: "\e651";
+}
+.icon-nok:before {
+  content: "\e652";
+}
+.icon-active:before {
+  content: "\e653";
+}
+.icon-desactive:before {
+  content: "\e654";
+}
+.icon-supprutilisateur:before {
+  content: "\e655";
+}
+.icon-validutilisateur:before {
+  content: "\e656";
+}
+.icon-lecture:before {
+  content: "\e657";
+}
+.icon-nolecture:before {
+  content: "\e658";
diff -urN cahier-de-prepa6.2.0/css/icones.min.css cahier-de-prepa8.0.1/css/icones.min.css
--- cahier-de-prepa6.2.0/css/icones.min.css	2016-08-30 18:10:42.106670849 +0200
+++ cahier-de-prepa8.0.1/css/icones.min.css	1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-@font-face{font-family:'icomoon';src:url('../fonts/icomoon.eot?13');src:url('../fonts/icomoon.eot?#iefix13') format('embedded-opentype'),url('../fonts/icomoon.ttf?13') format('truetype'),url('../fonts/icomoon.woff?13') format('woff'),url('../fonts/icomoon.svg?13#icomoon') format('svg');font-weight:normal;font-style:normal}[class^="icon-"],[class*=" icon-"]{font-family:'icomoon';color:black;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}a[class^="icon-"]{cursor:pointer;float:right;margin-left:5px;padding-top:.2em;text-decoration:none;font-weight:500}span.icon-lock{position:relative!important;left:-0.3em;top:.1em;color:#777;margin-right:-0.5em}.icon-montre:before{content:"\e600"}.icon-cache:before{content:"\e601"}.icon-aide:before{content:"\e602"}.icon-ajoute:before{content:"\e603"}.icon-supprime:before{content:"\e604"}.icon-annule:before{content:"\e605"}.icon-ok:before{content:"\e606"}.icon-prefs:before{content:"\e607"}.icon-monte:before{content:"\e608"}.icon-descend:before{content:"\e609"}.icon-ferme:before{content:"\e60a"}.icon-epingle:before{content:"\e60b"}.icon-par1:before{content:"\e60c"}.icon-par2:before{content:"\e60d"}.icon-par3:before{content:"\e60e"}.icon-gras:before{content:"\e60f"}.icon-italique:before{content:"\e610"}.icon-souligne:before{content:"\e611"}.icon-omega:before{content:"\e612"}.icon-sigma:before{content:"\e613"}.icon-exp:before{content:"\e614"}.icon-ind:before{content:"\e615"}.icon-ol:before{content:"\e616"}.icon-ul:before{content:"\e617"}.icon-lien1:before{content:"\e618"}.icon-lien2:before{content:"\e619"}.icon-retour:before{content:"\e61a"}.icon-source:before{content:"\e61b"}.icon-nosource:before{content:"\e61c"}.icon-tex:before{content:"\e61d"}.icon-titres:before{content:"\e61e"}.icon-edite:before{content:"\e61f"}.icon-precedent:before{content:"\e620"}.icon-suivant:before{content:"\e621"}.icon-recherche:before{content:"\e622"}.icon-voirtout:before{content:"\e623"}.icon-accueil:before{content:"\e624"}.icon-imprime:before{content:"\e625"}.icon-connexion:before{content:"\e626"}.icon-deconnexion:before{content:"\e627"}.icon-mail:before{content:"\e628"}.icon-menu:before{content:"\e629"}.icon-cocher:before{content:"\e62a"}.icon-decocher:before{content:"\e62b"}.icon-rep:before{content:"\e62c"}.icon-rep-open:before{content:"\e62d"}.icon-download:before{content:"\e62e"}.icon-lock:before{content:"\e62f"}.icon-alphaasc:before{content:"\e630"}.icon-alphadesc:before{content:"\e631"}.icon-chronoasc:before{content:"\e632"}.icon-chronodesc:before{content:"\e633"}.icon-ajouterep:before{content:"\e634"}.icon-ajoutedoc:before{content:"\e635"}.icon-doc:before{content:"\e636"}.icon-doc-txt:before{content:"\e636"}.icon-doc-pdf:before{content:"\e637"}.icon-doc-doc:before{content:"\e638"}.icon-doc-xls:before{content:"\e639"}.icon-doc-ppt:before{content:"\e63a"}.icon-doc-jpg:before{content:"\e63b"}.icon-doc-zip:before{content:"\e63c"}.icon-doc-mp3:before{content:"\e63d"}.icon-doc-mp4:before{content:"\e63e"}.icon-doc-pyt:before{content:"\e63f"}.icon-rss:before{content:"\e640"}.icon-infos:before{content:"\e641"}.icon-colles:before{content:"\e642"}.icon-recent:before{content:"\e643"}.icon-lock1:before{content:"\e644"}.icon-lock2:before{content:"\e645"}.icon-lock3:before{content:"\e646"}.icon-lock4:before{content:"\e647"}.icon-lock5:before{content:"\e648";color:#C00}.icon-agenda:before{content:"\e649"}.icon-ajout-colle:before{content:"\e64a"}
\ No newline at end of file
diff -urN cahier-de-prepa6.2.0/css/style1810.min.css cahier-de-prepa8.0.1/css/style1810.min.css
--- cahier-de-prepa6.2.0/css/style1810.min.css	1970-01-01 01:00:00.000000000 +0100
+++ cahier-de-prepa8.0.1/css/style1810.min.css	2018-10-14 22:22:51.476387398 +0200
@@ -0,0 +1 @@
+*{font-size:1em;margin:0;padding:0}html,body{height:100%}body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;position:relative;width:100%;margin:0 auto;min-width:320px;max-width:1500px;background-color:#f6f6f6}h1{font-size:2.2em;text-align:center;padding:1em 2em}h2{font-size:1.8em;margin:1em 0 .8em}h3{font-size:1.5em;margin:1em 0 .5em}h2+h3{padding-left:1%}h4{font-size:1.3em;margin:.5em 0 .2em;padding-left:2.5%}h5{font-size:1.1em;margin-top:.2em;padding-left:4%}h6{font-size:1em;margin-top:.2em;padding-left:5.5%}ul,ol{margin:.5em 0;padding:0 2% 0 6%}p{padding:0 2%}p+p{margin-top:.5em}img{border:0;max-width:100%}div,p,section,article{text-align:justify}section>:first-child,article>:first-child,article>h2:first-of-type,article>h3:first-of-type,#fenetre h3:first-of-type{margin-top:0}article>:last-child,#fenetre>:last-child{margin-bottom:0}article{margin-bottom:1em;padding:1em 2%;background-color:#cdd5e4}#fenetre{position:fixed;left:50%;z-index:15;padding:1em 2%;overflow:auto;background-color:#e7eefe;opacity:.97;box-shadow:.5em .5em .5em #777}#fenetre_fond{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;opacity:.2;z-index:14}article>a[class^="icon-"],#fenetre>a[class^="icon-"]{float:right;margin-left:.3em}#fenetre hr{margin:1.5em 0}footer{text-align:center;width:90%;padding:1em 5%;clear:both;position:fixed;left:0;bottom:0;z-index:20;border-top:1px solid black;background-color:#DDD;opacity:.97}@media screen and (min-width:800px){#colonne{width:280px;float:left;margin:0 30px 3em}nav{padding:1em 20px .7em}.icon-menu,.icon-recent{display:none}#recent{margin-top:1.5em;padding:1em 20px}section{position:relative;margin:0 30px 0 340px;padding-bottom:3em}#icones{position:absolute;top:-4em;right:0}header+section{width:96%;margin:0 auto;max-width:1500px;text-align:center}footer{font-size:.8em}#fenetre{top:10%;width:70%;margin-left:-37%;max-height:80%}}@media screen and (max-width:800px){h1{font-size:1.8em;padding:.3em 3em}h2{font-size:1.65em}#colonne{display:none}#colonne.visible{display:block}nav.visible,#recent.visible{display:block;position:fixed;z-index:10;top:3.3em;left:.5em;padding:1em 20px .7em;width:80%;min-width:200px;max-width:280px;max-height:70%;overflow:auto;box-shadow:.5em .5em .5em #777}.icon-menu,.icon-recent{position:fixed;z-index:10;top:.8em;left:.5em;font-size:1.2em;cursor:pointer}.icon-recent{left:2.5em}section{position:relative;width:96%;margin:0 auto;padding-bottom:3em}#icones{position:absolute;top:-2.2em;right:0}header+section{text-align:center}footer{font-size:.6em}#fenetre{top:4%;width:92%;margin-left:-48%;max-height:92%}}nav,#recent{background-color:#99b3e5}nav a[class^="icon-"]{display:inline-block;margin:0 .3em;color:#001030}nav a{display:block;margin-bottom:.2em;text-decoration:none;color:#002877}nav a:hover{color:#CDF}nav h3{font-size:1.2em;margin:.5em 3% .1em;padding-top:.3em;color:#001030;border-top:1px solid #001030}nav hr{margin:.5em 3% .5em;color:#001030;border-top:1px solid #001030;border-bottom:0}nav a.menurep{padding-left:3%;font-size:.9em}#actuel{font-style:italic}nav h3 span{font-weight:500;font-size:.83em;margin-right:.2em;color:#001030!important}#recent a{display:block;margin-bottom:.4em;text-decoration:none;color:#002877;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#recent a span{color:#002877}#recent h3{font-size:1.2em;margin:0 3% .5em;color:#001030}.pdf{height:0;width:100%;overflow:hidden;position:relative}.portrait{padding-bottom:138%}.paysage{padding-bottom:74%}.hauteur50{padding-bottom:50%}.pdf object{position:absolute}.warning{text-align:center;width:50%;margin:1em auto;padding:.5em 3%}.annonce{margin:1em 3%;padding:.5em 4%}.note{margin:.5em 2%;padding:0 4%}.warning,.annonce{color:#ba0d1f;border:2px solid #ba0d1f}.note{color:#ba0d1f}.oubli{font-size:.8em;text-align:center}.oubli a{text-decoration:none;color:#333}.oubli label{font-weight:700}.oubli input{margin-left:1em;vertical-align:text-top}p.titrecdt{text-align:right;text-decoration:underline}p.titrecdt.edition{text-align:left;text-decoration:none;padding-right:1%}.topbarre{height:1.5em;background-color:#DDD;border:1px solid #BBB;width:auto;border-radius:4px;margin-bottom:1.5em}h1 span{font-size:70%;vertical-align:7%;margin-left:.4em}#recherchecdt a,#recherchecolle a,#rechercheagenda a{margin-right:.4em;vertical-align:middle}.topbarre select#semaines{margin-left:.8em;width:8.5em}.topbarre select#seances{margin-left:.8em;width:11em}.topbarre input{position:absolute;top:.25em;margin:0 .8em 0 1em;padding-left:1.8em;width:-moz-available}.topbarre input,.topbarre select{font-size:.9em;height:1.25em;vertical-align:bottom}.topbarre .icon-recherche{position:relative;left:1.9em;font-size:.7em;cursor:pointer}@media screen and (max-width:980px) and (min-width:800px),screen and (max-width:600px){#recherchecdt input{display:none;top:1.8em;padding-left:.1em;right:2em;margin-left:2em!important}#recherchecdt span{left:1.3em}}@media screen and (max-width:450px){#recherchecdt a,#recherchecdt select{margin-left:.3em!important}#recherchecdt input{margin-left:.3em}#recherchecdt select{width:6em!important}#recherchecdt span{left:.3em}}@media screen and (max-width:400px){#recherchecolle a,#recherchecolle select{margin-left:.3em!important}#recherchecolle input{margin-left:0!important}#recherchecolle select{width:6em}#recherchecolle span{left:.3em}}@media screen and (max-width:350px){#recherchecolle input{display:none;top:1.8em;padding-left:.1em;right:2em;margin-left:2em!important}}#parentsdoc{margin-bottom:1.5em;padding:.3em 2% 0;line-height:1.3em}#parentsdoc span{position:static;cursor:auto}.rep,.doc{margin:0 2%;padding:0;border-bottom:1px dotted #BBB;line-height:1.3em}.repcontenu,.docdonnees{float:right;font-size:.8em;padding-left:.5em;line-height:1.625em}#parentsdoc>a[class^="icon-"],.rep>a[class^="icon-"],.doc>a[class^="icon-"]{float:right;margin-left:.3em;line-height:1.3em}#parentsdoc a,.rep a,.doc a{text-decoration:none;color:black}#parentsdoc .nom,.rep .nom,.doc .nom{font-weight:700;margin-left:.5em}.edition{display:inline;text-align:left;padding-right:3em}h3.edition.editable{padding-right:1%}.edition+p,.edition ~ form{margin-top:.75em}article.cache{background-color:#AAA;opacity:.6}#log{position:fixed;top:3%;left:3%;padding:.3em 2%;z-index:20;margin-right:3%}.ok{background-color:#EFE;color:#090;border:1px solid #090}.nok{background-color:#FEE;color:#D00;border:1px solid #D00}.ok span{color:#090}.nok span{color:#D00}#log span{cursor:pointer;position:relative;right:-0.8em;top:.1em}[id^="aide-"],[id^="form-"]{display:none}#fenetre [name="titre"]{margin:2em 0 1em}#fenetre [name="titre"]+*{display:inline}form.titrecdt{padding:.2em 0 .5em;margin-top:.2em}.confirmation{text-align:center}#fenetre.usergrp .ligne{border-bottom:1px dotted #BBB}#fenetre.usergrp input{margin-top:.2em}.protection{font-size:.8em;color:#999}input,select,textarea{box-sizing:border-box;-webkit-box-sizing:border-box;border:1px solid;border-radius:2px;padding:0 .3em}#fichier{border:0;font-size:.8em;height:1.9em}p.ligne label{font-weight:700}p.ligne input,p.ligne select,p.ligne code{width:65%;float:right;margin-left:.2em;font-size:.8em;height:1.6em}p.ligne input[type="checkbox"],p.ligne input[type="radio"]{width:1em}input.ligne{width:96%;margin:.5em 2%;height:1.6em;font-size:.8em;display:block}p.ligne+*{clear:right}.gestion_protection{margin:0!important}.gestion_protection p{margin:0}.gestion_protection input{vertical-align:sub}.supprmultiple{margin-top:.3em;margin-bottom:0!important}table{width:100%;margin:1em 0;border-collapse:collapse;border:medium solid #999}table td{padding:.15em .5%}table th{padding:.15em 3%}table td,table th{border:thin solid #AAA}.centre,#planning,#notes{text-align:center}.centre th{padding:.15em 0}tr[data-id]:hover,#planning tbody tr:hover,#fenetre tbody tr:not(.categorie):hover{background-color:#EFF}#selmult{width:auto;min-width:70%;margin:2em auto;padding-bottom:2em}#selmult td{border:0;padding-left:5%;font-weight:700}#selmult .element td:first-child{padding-left:10%;font-weight:500}#selmult td+td{vertical-align:sub;text-align:center;width:5em;padding:.1em}.editable,.titrecdt.edition,form.titrecdt{border:1px dotted #BBB;position:relative}.editable a[class^="icon-"],p.titrecdt a[class^="icon-"],.maildest a[class^="icon-"]{padding-left:.3em}.avecform{border:none!important}h3.editable a[class^="icon-"],h3 span.editable a[class^="icon-"]{font-weight:500;font-size:.67em;padding-top:.1em}h3.avecform span{font-weight:500;font-size:.6em;margin-top:.5em;width:80%;overflow:hidden;white-space:nowrap}h3.avecform input{width:50%}textarea,[contenteditable=true].ligne{width:96%;margin:.5em 2%}textarea+div{min-height:6em;border:1px dotted #CCC;background-color:#FFF}.boutons{clear:right;background-color:#DDD;border:1px solid #BBB;width:auto;padding:0;margin-bottom:0;border-radius:4px}.boutons button{cursor:default;background-color:transparent;border-top:0;border-left:none;border-bottom:1px solid #BBB;border-right:1px solid #BBB;height:1.5em;font-size:100%;width:2em}.boutons button+button{margin-left:-0.2em}p.boutons.ligne{width:96%;margin:0 2%}[contenteditable=true]:empty:before{content:attr(placeholder);color:#999;font-size:.8em;display:block}div.editable a[class^="icon-"]{position:absolute;right:3px;top:0}div.editable a.icon-annule{top:1.5em}div.edithtml a.icon-ok{top:1.6em}div.edithtml a.icon-annule{top:3.1em}.icon-mailenvoi{font-size:2em}#notes th,#notes td{padding-left:0;padding-right:0}#notes .icones{text-align:left;padding:.15em .5em;width:2.3em}.notes td+td{width:6em;text-align:center}.notes td+td select{width:6em;font-size:.8em;margin-bottom:.2em}td.pasnote{font-style:italic;text-align:center}.collsel{font-weight:700}.collnosel,.dejanote{color:#AAA}#recherchenote{padding:0 1em}#tableaunotes td+td{text-align:right}.sel{background-color:#EFF}#utilisateurs tr,#selmult tr{border-top:0;border-left:none;border-right:0;border-bottom:1px dotted #BBB}#utilisateurs th,#utilisateurs td,#utilisateurs-matieres th,#utilisateurs-matieres td{border:1px dotted #BBB}#fenetre #utilisateurs th,#fenetre #utilisateurs td{border:0}#fenetre #utilisateurs th+th,#fenetre #utilisateurs td+td{text-align:center;width:5em;padding:.1em}#utilisateurs input,#utilisateurs-matieres input,#selmult input{vertical-align:sub}.categorie th+th,#utilisateurs-matieres td+td{text-align:center;padding:0}#utilisateurs .icones{width:1em;white-space:nowrap;padding:.15em .3em;font-weight:normal;text-align:right}#utilisateurs .icones *{font-weight:normal;vertical-align:middle;padding:0;margin:0}#utilisateurs .icones input{width:1em}#utilisateurs .icones a{display:inline-block;width:1em}.categorie th{border:none!important}.categorie span{cursor:pointer}th.semaines,th.matieres,th.colleurs{vertical-align:bottom;padding:1em 0;text-align:center}th.semaines{width:1.7em}th.semaines span,th.matieres span,th.colleurs span{display:inline-block;writing-mode:vertical-lr;-webkit-writing-mode:vertical-lr;transform:rotate(180deg);-webkit-transform:rotate(180deg)}@media print{body{font-size:90%;font-family:Serif}#colonne,#recherchecolle,#recherchecdt,#rechercheagenda,[id^="aide-"],[id^="form-"],footer,a[class^="icon-"]{display:none}.editable,.titrecdt.edition,form.titrecdt{border:0}h1{font-size:1.7em}h2{font-size:1.5em;margin:.7em 0}h3{font-size:1.35em;margin:.6em 0}h4{font-size:1.2em;margin:.4em 0 .2em}article{border:1px solid #999}}#calendrier{margin-top:1em}#calendrier table{table-layout:fixed}#semaine,.semaine-bg,.evenements{margin:0}#semaine{font-weight:900;text-align:center}#semaine th{overflow:hidden;text-overflow:clip}.semaine-bg{border-top:0;position:absolute;z-index:1}.autremois{background-color:#e7eefe;color:#002877}#aujourdhui{background-color:#99b3e5}.evenements{position:relative;z-index:2;border-top:0;border-bottom:0}.evenements thead{border-bottom:1px solid #999}.evenements th{padding:.15em .5%;text-align:right}.evenements td{padding:2px 3px 1px;border:none!important}.modifevnmt{padding:1px 3px;border-radius:5px;white-space:nowrap;overflow:hidden;font-size:.8em;cursor:pointer}.evnmt_suivi{border-top-right-radius:0;border-bottom-right-radius:0;margin-right:-2px}.evnmt_suite{border-top-left-radius:0;border-bottom-left-radius:0;margin-left:-3px}#load{display:none;margin:0;padding:0;position:fixed;top:0;left:0;width:100%;height:100%;z-index:30;text-align:center}#load img{position:absolute;top:50%}
\ No newline at end of file
diff -urN cahier-de-prepa6.2.0/css/style.css cahier-de-prepa8.0.1/css/style.css
--- cahier-de-prepa6.2.0/css/style.css	2016-09-09 11:08:48.337403096 +0200
+++ cahier-de-prepa8.0.1/css/style.css	2018-10-14 15:08:06.438640850 +0200
@@ -11,57 +11,82 @@
  * #AAA : articles cachés
  * #EFE/#090 : bandeau ok vert (fond/texte)
  * #FEE/#D00 : bandeau non-ok rouge (fond/texte)
- * #EFF : hover sur les tableaux (#planning,#utilisateurs,#notes,.usergrp,.usermat)
+ * #EFF : hover sur les tableaux (#planning,.notes,.destinataires,.usergrp,.usermat)
 */
 
 /* Police et taille globales */
-html,body { height: 100%; }
-body { font-size: 100%; font-family:Arial, Helvetica, sans-serif; position: relative; width:100%; margin: 0 auto; max-width: 1500px; background-color: #F6F6F6; }
-h1 { font-size: 2.2em; text-align: center; margin: 0; padding: 1em 2em; }
-h2 { font-size: 1.8em; margin: 1em 0 0.8em; padding: 0; }
-h3 { font-size: 1.5em; margin: 1em 0 0.5em; padding: 0 1% 0; }
-h4 { font-size: 1.3em; margin: 0.5em 0 0.2em; padding: 0 2.5% 0; }
-h5 { font-size: 1.1em; margin: 0.2em 0 0; padding: 0 4% 0; }
-h6 { font-size: 1em; margin: 0.2em 0 0; padding: 0 5.5% 0; }
-ul { margin: 0.5em 0; padding: 0 2% 0 6%; }
-p { margin: 0; padding: 0 2%; }
+* { font-size: 1em; margin: 0; padding: 0; }
+html, body { height: 100%; }
+body { font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
+       position: relative; width: 100%; margin: 0 auto; min-width: 320px; max-width: 1500px; background-color: #F6F6F6; }
+h1 { font-size: 2.2em; text-align: center; padding: 1em 2em; }
+h2 { font-size: 1.8em; margin: 1em 0 0.8em; }
+h3 { font-size: 1.5em; margin: 1em 0 0.5em; }
+h2 + h3 { padding-left: 1%; }
+h4 { font-size: 1.3em; margin: 0.5em 0 0.2em; padding-left: 2.5%; }
+h5 { font-size: 1.1em; margin-top: 0.2em; padding-left: 4%; }
+h6 { font-size: 1em; margin-top: 0.2em; padding-left: 5.5%; }
+ul, ol { margin: 0.5em 0; padding: 0 2% 0 6%; }
+p { padding: 0 2%; }
 p + p { margin-top: 0.5em; }
-div, form { margin: 0; padding: 0; }
 img { border: none; max-width: 100%; }
-div,p,section,article { text-align: justify; }
-article > *:first-child { margin-top: 0; }
-article > *:last-child { margin-bottom: 0; }
-section > h2:first-child { margin-top: 0; }
+div, p, section, article { text-align: justify; }
 
-/* Blocs pricipaux */
+/* Blocs principaux, icônes */
+section > :first-child, article > :first-child, article > h2:first-of-type, article > h3:first-of-type, #fenetre h3:first-of-type { margin-top: 0; }
+article > :last-child, #fenetre > :last-child { margin-bottom: 0; }
+article { margin-bottom: 1em; padding: 1em 2%; background-color: #CDD5E4; }
+#fenetre { position: fixed; left: 50%; z-index: 15; padding: 1em 2%; overflow: auto;
+           background-color: #E7EEFE; opacity: 0.97; box-shadow: 0.5em 0.5em 0.5em #777; }
+#fenetre_fond { position: fixed; top: 0; left: 0; width: 100%; height: 100%;
+                background-color: #000; opacity: 0.2; z-index: 14; }
+article > a[class^="icon-"], #fenetre > a[class^="icon-"] { float: right; margin-left: 0.3em; }
+#fenetre hr { margin: 1.5em 0; }
+footer { text-align: center; width: 90%; padding: 1em 5%; clear: both; position: fixed; left: 0; bottom: 0; z-index: 20;
+         border-top: 1px solid black; background-color: #DDD; opacity: 0.97; }
+
+/* Blocs principaux, affichage différentiés selon la taille de l'écran */
 @media screen and (min-width: 800px) {
   #colonne { width: 280px; float: left; margin: 0 30px 3em; }
-  nav { margin: 0; padding: 1em 20px 0.7em; }
+  nav { padding: 1em 20px 0.7em; }
   .icon-menu, .icon-recent { display: none; }
   #recent { margin-top: 1.5em; padding: 1em 20px; }
-  section { position: relative; margin: 0 30px 0 340px; padding: 0 0 3em;  }
+  section { position: relative; margin: 0 30px 0 340px; padding-bottom: 3em;  }
+  #icones { position: absolute; top: -4em; right: 0; }
   header + section { width: 96%; margin: 0 auto; max-width: 1500px; text-align: center;}
   footer { font-size: 0.8em; }
+  #fenetre { top: 10%; width: 70%; margin-left: -37%; max-height: 80%; }
 }
 @media screen and (max-width: 800px) {
   h1 { font-size: 1.8em; padding: 0.3em 3em; }
   h2 { font-size: 1.65em; }
-  #colonne, nav, #recent { display: none; }
+  #colonne { display: none; }
   #colonne.visible { display: block; }
   nav.visible, #recent.visible { display: block; position: fixed; z-index: 10; top: 3.3em; left: 0.5em; padding: 1em 20px 0.7em;
-                                 width: 80%; min-width: 200px; max-width: 280px; max-height: 70%; overflow: auto;
-                                 box-shadow: 0.5em 0.5em 0.5em #777; -moz-box-shadow: 0.5em 0.5em 0.5em #777; -webkit-box-shadow: 0.5em 0.5em 0.5em #777; }
+                                 width: 80%; min-width: 200px; max-width: 280px; max-height: 70%; overflow: auto; box-shadow: 0.5em 0.5em 0.5em #777; }
   .icon-menu, .icon-recent { position: fixed; z-index: 10; top: 0.8em; left: 0.5em; font-size: 1.2em; cursor: pointer; }
   .icon-recent { left: 2.5em; }
-  section { position: relative; width:96%; margin: 0 2%; padding: 0 0 3em; }
+  section { position: relative; width:96%; margin: 0 auto; padding-bottom: 3em; }
+  #icones { position: absolute; top: -2.2em; right: 0; }
   header + section { text-align: center; }
   footer { font-size: 0.6em; }
+  #fenetre { top: 4%; width: 92%; margin-left: -48%; max-height: 92%; }
 }
-article { margin: 1em 0; padding: 1em 2%; background-color: #CDD5E4; }
-article:first-child, .general + article { margin-top: 0 !important; }
-footer { text-align: center; width: 90%; padding: 1em 5%; clear: both; position: fixed; left: 0; bottom: 0; z-index: 15;
-         border-top: 1px solid black; background-color: #DDD; opacity: 0.97; filter: alpha(opacity=97); }
 
+/* Dans le menu et dans les informations récentes*/
+nav, #recent { background-color: #99B3E5; }
+nav a[class^="icon-"] { display: inline-block; margin: 0 0.3em; color: #001030; }
+nav a { display: block; margin-bottom: 0.2em; text-decoration: none; color: #002877; }
+nav a:hover { color: #CDF; }
+nav h3 { font-size: 1.2em; margin: 0.5em 3% 0.1em; padding-top: 0.3em; color: #001030; border-top: 1px solid #001030; }
+nav hr { margin: 0.5em 3% 0.5em; color: #001030; border-top: 1px solid #001030; border-bottom: none; }
+nav a.menurep { padding-left: 3%; font-size: 0.9em; }
+#actuel { font-style: italic; }
+nav h3 span { font-weight: 500; font-size: 0.83em; margin-right: 0.2em; color: #001030 !important; }
+#recent a { display: block; margin-bottom: 0.4em; text-decoration: none; color: #002877; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+#recent a span { color: #002877; }
+#recent h3 { font-size: 1.2em; margin: 0 3% 0.5em; color: #001030; }
+ 
 /* PDF et JPG */
 .pdf { height: 0; width: 100%; overflow: hidden; position: relative; }
 .portrait { padding-bottom: 138%; }
@@ -77,18 +102,20 @@
 .note { color: #BA0D1F; }
 .oubli { font-size: 0.8em; text-align: center; }
 .oubli a { text-decoration: none; color: #333; }
+.oubli label { font-weight: 700; }
+.oubli input { margin-left: 1em; vertical-align: text-top; }
 p.titrecdt { text-align: right; text-decoration: underline; }
-.titrecdt.edition { text-align: left; text-decoration: none; padding-right: 1%; }
-.topbarre { height: 1.5em; background-color: #DDD; border: 1px solid #BBB; width: auto; padding: 0; margin-bottom: 0; border-radius: 4px; }
+p.titrecdt.edition { text-align: left; text-decoration: none; padding-right: 1%; }
+.topbarre { height: 1.5em; background-color: #DDD; border: 1px solid #BBB; width: auto; border-radius: 4px; margin-bottom: 1.5em; }
 h1 span { font-size: 70%; vertical-align: 7%; margin-left: 0.4em; } /* indication de protection en mode édition */
 
 /* Menu de recherche, programme de colle et cahier de texte */
-#recherchecdt a, #recherchecolle a, #rechercheagenda a { float: none !important; margin-left: 0.5em; }
-#rechercheagenda a { vertical-align: sub; }
-.topbarre select#semaines { margin-left: 1.3em; width: 8.5em; height: 1.7em; }
-.topbarre select#seances { margin-left: 1.3em; width: 11em; height: 1.7em; }
-.topbarre input { position: absolute; margin:0 0.8em 0 1.5em; padding-left: 1.8em; width: -moz-available; height: 1.7em; }
-.topbarre span { position: relative; left: 1.9em; cursor: pointer; top: 0.1em; }
+#recherchecdt a, #recherchecolle a, #rechercheagenda a { margin-right: 0.4em; vertical-align: middle; }
+.topbarre select#semaines { margin-left: 0.8em; width: 8.5em; }
+.topbarre select#seances { margin-left: 0.8em; width: 11em; }
+.topbarre input { position: absolute; top: 0.25em; margin:0 0.8em 0 1em; padding-left: 1.8em; width: -moz-available; }
+.topbarre input, .topbarre select { font-size: 0.9em; height: 1.25em; vertical-align: bottom; }
+.topbarre .icon-recherche { position: relative; left: 1.9em; font-size: 0.7em; cursor: pointer; }
 /* Disparition de la case de recherche */
 @media screen and (max-width: 980px) and (min-width: 800px), screen and (max-width: 600px) {
   #recherchecdt input { display: none; top: 1.8em; padding-left: 0.1em; right: 2em; margin-left: 2em !important; }
@@ -110,151 +137,139 @@
   #recherchecolle input { display: none; top: 1.8em; padding-left: 0.1em; right: 2em; margin-left: 2em !important; }
 }
 
-/* Spécifique webkit */
+/* Spécifique webkit
 @media screen and (-webkit-min-device-pixel-ratio:0) { 
   .topbarre { height: 1.4em; vertical-align: bottom; }
   #recherchecolle input { width: 40%; }
   .topbarre span { top: 0.2em; }
   #recherchecdt a, #recherchecolle a { vertical-align: middle; }
-/*  .topbarre select { height: 1.7em; }*/
-}
+}*/
 
 /* Documents */
-#parentsdoc { margin-bottom: 1.5em; padding: 0.3em 2% 0; }
-#parentsdoc * { padding-top: 0; }
+#parentsdoc { margin-bottom: 1.5em; padding: 0.3em 2% 0; line-height: 1.3em; }
 #parentsdoc span { position: static; cursor: auto; }
-.rep, .doc { padding: 0; margin-left: 2%; margin-right: 2%; border-bottom:  1px dotted #BBB; }
-.repcontenu, .docdonnees { float: right; font-size: 0.8em; padding-top: 0.2em; padding-left: 0.5em; }
+.rep, .doc { margin: 0 2%; padding: 0; border-bottom:  1px dotted #BBB; line-height: 1.3em; }
+.repcontenu, .docdonnees { float: right; font-size: 0.8em; padding-left: 0.5em; line-height: 1.625em; }
+#parentsdoc > a[class^="icon-"], .rep > a[class^="icon-"], .doc > a[class^="icon-"] { float: right; margin-left: 0.3em; line-height: 1.3em; }
 #parentsdoc a, .rep a, .doc a { text-decoration: none; color: black; }
 #parentsdoc .nom, .rep .nom, .doc .nom { font-weight: 700; margin-left: 0.5em; }
 
 /* Édition : positionnements globaux */
-.general { position: absolute; top: -4.1em; right: 1em; }
-.general + .general { right: 2.2em; }
-.general + .general + .general { right: 3.4em; }
-.general + .general + .general + .general { right: 4.6em; }
 .edition { display: inline; text-align: left; padding-right: 3em; }
 h3.edition.editable { padding-right: 1%; }
-.edition ~ div, .edition + p, .edition ~ form { margin-top: 0.75em; }
-article.cache { background-color: #AAA; opacity: 0.6; filter: alpha(opacity=60); }
+.edition + p, .edition ~ form { margin-top: 0.75em; }
+article.cache { background-color: #AAA; opacity: 0.6; }
 #log { position: fixed; top:3%; left: 3%; padding: 0.3em 2%; z-index: 20; margin-right: 3%; }
 .ok { background-color: #EFE; color: #090; border: 1px solid #090; }
 .nok { background-color: #FEE; color: #D00; border: 1px solid #D00; }
 .ok span { color: #090; }
 .nok span { color: #D00; }
 #log span { cursor: pointer; position: relative; right: -0.8em; top: 0.1em;}
-#fenetre_fond { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: #000; opacity: 0.2; filter: alpha(opacity=20); z-index: 12; }
-#fenetre { position: fixed; left: 50%; z-index: 14; padding: 0 2%; overflow: auto; background-color: #E7EEFE; opacity: 0.97; filter: alpha(opacity=97);
-           box-shadow: 0.5em 0.5em 0.5em #777; -webkit-box-shadow: 0.5em 0.5em 0.5em #777; }
-#fenetre > *:last-child { margin-bottom: 1em; }
-@media screen and (min-width: 800px) {
-  #fenetre { top: 10%; width: 70%; margin-left: -37%; max-height: 80%; }
-}
-@media screen and (max-width: 800px) {
-  .general { top: -2.2em; }
-  #fenetre { top: 4%; width: 92%; margin-left: -48%; max-height: 92%; }
-}
-#fenetre a[class^="icon-"] { margin-top: 1.5em; }
-#fenetre hr { margin: 1.5em 0; }
-#epingle { margin-top: 1.5em; }
-#epingle h3 { padding-left: 0; }
 [id^="aide-"],[id^="form-"] { display: none; }
 
 /*  Édition : spécial */
 #fenetre [name="titre"] { margin: 2em 0 1em; } /* pour popup titres */
 #fenetre [name="titre"]+* { display: inline; } 
 form.titrecdt { padding: 0.2em 0 0.5em; margin-top: 0.2em; } /* pour édition des propriétés des éléments des cahiers de texte */
-.suppression { text-align: center; }
-#fenetre.usermat h3 { margin-bottom: 0; }
-#fenetre.usermat a.icon-ajoute, #fenetre a.icon-supprime { margin-top: 0.8em; }
-#fenetre.usermat .ligne, #fenetre.usergrp .ligne { border-bottom: 1px dotted #BBB; }
-#fenetre.usermat input, #fenetre.usergrp input { margin-top: 0.2em; }
-#fenetre th+th, #fenetre td+td { text-align: center; width: 2em; padding: 0.15em; } /* Formulaire pour choisir les destinataires de mail */
-#fenetre th a { float: none; } /* Formulaire pour choisir les destinataires de mail */
-#fenetre th { border: none !important; }
-#fenetre td { border-top: none !important; border-left: none !important; border-right: none !important; border-bottom: 1px dotted #BBB !important; }
-#planning { text-align: center; }
-.labelchecked { color: #999; }
-#planning tr:hover, #utilisateurs tr:hover, #notes tr:hover, .usergrp .ligne:hover, .usermat .ligne:hover { background-color: #EFF; }
- 
+.confirmation { text-align: center; }
+#fenetre.usergrp .ligne { border-bottom: 1px dotted #BBB; }
+#fenetre.usergrp input { margin-top: 0.2em; }
+.protection { font-size: 0.8em; color: #999; } /* texte indiquant la protection des icônes */
+
 /* Édition : formulaires */
 input, select, textarea { box-sizing: border-box; -webkit-box-sizing: border-box; border: 1px solid; border-radius: 2px; padding: 0 0.3em; }
 #fichier { border: none; font-size: 0.8em; height: 1.9em; }
-p.ligne { padding: 0; clear: right; }
-.ligne label { font-weight: 700; }
-.ligne input, .ligne select, .ligne code { width: 65%; float: right; margin-top: -0.1em; margin-left: 0.2em; line-height: 1em; height: 1.5em; box-sizing: border-box; }
-.ligne input[type="checkbox"], .ligne input[type="radio"] { width: 1em; margin-top: 0.2em; }
-input.ligne { width: 100%;  margin-bottom: 0.5em; line-height: 1em; height: 1.5em; display: block; }
+p.ligne label { font-weight: 700; }
+p.ligne input, p.ligne select, p.ligne code { width: 65%; float: right; margin-left: 0.2em; font-size: 0.8em; height: 1.6em; }
+p.ligne input[type="checkbox"], p.ligne input[type="radio"] { width: 1em; }
+input.ligne { width: 96%;  margin: 0.5em 2%; height: 1.6em; font-size: 0.8em; display: block; }
+p.ligne + * { clear: right; }
+.gestion_protection { margin: 0 !important; }
+.gestion_protection p { margin: 0; }
+.gestion_protection input { vertical-align: sub; }
 .supprmultiple { margin-top: 0.3em; margin-bottom: 0 !important; }
-.usermat { margin-top: 0.5em; padding: 0; clear: right; }
-.usermat a { padding: 0 !important; }
 
+/* Tableaux */
 table { width: 100%; margin: 1em 0; border-collapse: collapse; border: medium solid #999; }
 table td { padding: 0.15em 0.5%; }
 table th { padding: 0.15em 3%; }
-table td, table th { border: thin solid #AAA !important; }
-table td .icon-edite { float: left !important; margin: 0 0.2em 0 0.1em; }
+table td, table th { border: thin solid #AAA; }
+.centre, #planning, #notes { text-align: center; }
+.centre th { padding: 0.15em 0; }
+tr[data-id]:hover, #planning tbody tr:hover, #fenetre tbody tr:not(.categorie):hover { background-color: #EFF; }
+#selmult { width: auto; min-width: 70%; margin: 2em auto; padding-bottom: 2em; }
+#selmult td { border: none; padding-left: 5%; font-weight: 700; }
+#selmult .element td:first-child { padding-left: 10%; font-weight: 500; }
+#selmult td+td {vertical-align: sub; text-align: center; width: 5em; padding: 0.1em; }
 
 /* Édition : champs éditables */
-div.placeholder:before { content: attr(data-placeholder); color: #AAA; margin-left: 4px; z-index: 12;  }
-span.placeholder { color: #AAA; position: absolute; margin: 0.25em 2.5% 0 5px; z-index: 12; font-size: 0.8em; }
-p.ligne span.placeholder { left: 35%; margin-top: 0.1em; margin-left: 1em; }
-#fenetre span.placeholder, #fenetre div.placeholder:before { z-index: 15;  }
-.editable, .titrecdt.edition, form.titrecdt, .editabledest { border: 1px dotted #BBB; position: relative; }
-.editable a[class^="icon-"], p.titrecdt a[class^="icon-"], .editabledest a[class^="icon-"] { float: none; }
-.avecform { padding: 0; border: none !important; }
+.editable, .titrecdt.edition, form.titrecdt { border: 1px dotted #BBB; position: relative; }
+.editable a[class^="icon-"], p.titrecdt a[class^="icon-"], .maildest a[class^="icon-"] { padding-left: 0.3em; }
+.avecform { border: none !important; }
 h3.editable a[class^="icon-"], h3 span.editable a[class^="icon-"] { font-weight: 500; font-size: 0.67em; padding-top: 0.1em;}
 h3.avecform span { font-weight: 500; font-size: 0.6em; margin-top: 0.5em; width: 80%; overflow: hidden; white-space:nowrap; }
 h3.avecform input { width: 50%; }
-textarea { width: 100%;  margin: 0 0 0.2em; }
+textarea, [contenteditable=true].ligne { width: 96%; margin: 0.5em 2%; }
 textarea + div { min-height: 6em; border: 1px dotted #CCC; background-color: #FFF; }
 .boutons { clear: right; background-color: #DDD; border: 1px solid #BBB; width: auto; padding: 0; margin-bottom: 0; border-radius: 4px; }
 .boutons button { cursor: default; background-color: transparent;
                   border-top: none; border-left: none; border-bottom: 1px solid #BBB; border-right: 1px solid #BBB;
                   height: 1.5em; font-size: 100%; width: 2em; }
 .boutons button + button { margin-left: -0.2em; }
+p.boutons.ligne { width: 96%; margin: 0 2%; }
+[contenteditable=true]:empty:before { content: attr(placeholder); color: #999; font-size: 0.8em; display: block; }
 div.editable a[class^="icon-"] { position: absolute; right: 3px; top: 0; }
 div.editable a.icon-annule { top: 1.5em; }
 div.edithtml a.icon-ok { top: 1.6em; }
 div.edithtml a.icon-annule { top: 3.1em; }
 
-/* Dans le menu et dans les informations récentes*/
-nav, #recent { background-color: #99B3E5; }
-nav a[class^="icon-"] { float: none !important; display: inline !important; margin: 0.5em 3% 1em; color: #001030; }
-nav a { display: block; margin-bottom: 0.2em; padding: 0; text-decoration: none; color: #002877; }
-nav a:hover { color: #CDF; }
-nav h3 { font-size: 1.2em; margin: 0.5em 3% 0.1em; padding-top: 0.3em; color: #001030; border-top: 1px solid #001030; }
-nav hr { margin: 0.5em 3% 0.5em; color: #001030; border-top: 1px solid #001030; border-bottom: none; }
-nav a.menurep { padding-left: 3%; font-size: 0.9em; }
-#actuel { font-style: italic; }
-nav h3 span { font-weight: 500; font-size: 0.83em; margin-right: 0.2em; color: #001030 !important; }
-#recent a { display: block; margin-bottom: 0.4em; padding: 0; text-decoration: none; color: #002877; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
-#recent a span { color: #002877; }
-#recent h3 { font-size: 1.2em; margin: 0 3% 0.5em; color: #001030; }
- 
-/* Tableau de notes */
-th.semaines { vertical-align: bottom; padding: 1.6em 0; text-align: center; }
-td.semaines { padding-bottom: 1em !important; vertical-align: bottom; text-align: center; padding: 0.2em 5px;}
-td.semaines span { display: block; font-weight: 700; margin: 0; padding: 0; width: 1.2em; 
-  transform: rotate(-90deg); -webkit-transform: rotate(-90deg);	-moz-transform: rotate(-90deg); -ms-transform: rotate(-90deg); -o-transform: rotate(-90deg);
-  filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); white-space: nowrap; zoom: 1; }
+/* Mails */
+.icon-mailenvoi { font-size: 2em; }
+
+/* Tableau de notes de colles personnel */
+#notes th, #notes td { padding-left: 0; padding-right: 0; }
+#notes .icones { text-align: left; padding: 0.15em 0.5em; width: 2.3em; }
+.notes td+td { width: 6em; text-align: center; }
+.notes td+td select { width: 6em; font-size: 0.8em; margin-bottom: 0.2em; }
+
+/* Tableau de notes global */
 td.pasnote { font-style: italic; text-align: center; }
 .collsel { font-weight: 700; }
 .collnosel, .dejanote { color: #AAA; }
 #recherchenote { padding: 0px 1em; }
-#notes td+td { text-align: right;}
+#tableaunotes td+td { text-align: right;}
+/* th.semaines réglés comme th.matieres et th.colleurs */
+
+/* Tableau d'utilisateurs : mail, groupes, utilisateurs, utilisateurs-matieres */
+.sel { background-color: #EFF; }
+#utilisateurs tr, #selmult tr { border-top: none; border-left: none; border-right: none; border-bottom: 1px dotted #BBB; }
+#utilisateurs th, #utilisateurs td, #utilisateurs-matieres th, #utilisateurs-matieres td { border: 1px dotted #BBB; }
+#fenetre #utilisateurs th, #fenetre #utilisateurs td { border: none; }
+#fenetre #utilisateurs th+th, #fenetre #utilisateurs td+td { text-align: center; width: 5em; padding: 0.1em; }
+#utilisateurs input, #utilisateurs-matieres input, #selmult input { vertical-align: sub; }
+.categorie th+th, #utilisateurs-matieres td+td { text-align: center; padding: 0; }
+#utilisateurs .icones { width: 1em; white-space: nowrap; padding: 0.15em 0.3em; font-weight: normal; text-align: right; }
+#utilisateurs .icones * { font-weight: normal; vertical-align: middle; padding: 0; margin: 0; }
+#utilisateurs .icones input { width: 1em; }
+#utilisateurs .icones a { display: inline-block; width: 1em; }
+.categorie th { border: none !important; }
+.categorie span { cursor:pointer; }
+th.semaines, th.matieres, th.colleurs { vertical-align: bottom; padding: 1em 0; text-align: center; }
+th.semaines { width: 1.7em; }
+th.semaines span, th.matieres span, th.colleurs span { display:inline-block;
+  writing-mode: vertical-lr; -webkit-writing-mode: vertical-lr;
+  transform: rotate(180deg); -webkit-transform: rotate(180deg);
+}
 
 /* Pour l'impression */
 @media print {
   body { font-size: 90%; font-family:Serif; }
   #colonne, #recherchecolle, #recherchecdt, #rechercheagenda, [id^="aide-"], [id^="form-"], footer, a[class^="icon-"] { display: none; }
-  .editable, .titrecdt.edition, form.titrecdt, .editabledest { border: none; }
-  h1 { font-size: 1.7em; text-align: center; margin: 0; padding: 0 0 1em; }
-  h2 { font-size: 1.5em; margin: 0.7em 0; padding: 0; }
-  h3 { font-size: 1.35em; margin: 0.6em 0; padding: 0 1% 0; }
-  h4 { font-size: 1.2em; margin: 0.4em 0 0.2em; padding: 0 2.5% 0; }
-  h5 { font-size: 1.1em; margin: 0.2em 0 0; padding: 0 4% 0; }
-  h6 { font-size: 1em; margin: 0.2em 0 0; padding: 0 5.5% 0; }
+  .editable, .titrecdt.edition, form.titrecdt { border: none; }
+  h1 { font-size: 1.7em; }
+  h2 { font-size: 1.5em; margin: 0.7em 0; }
+  h3 { font-size: 1.35em; margin: 0.6em 0; }
+  h4 { font-size: 1.2em; margin: 0.4em 0 0.2em; }
   article { border: 1px solid #999; }
 }
 
@@ -271,6 +286,10 @@
 .evenements thead { border-bottom: 1px solid #999; }
 .evenements th { padding: 0.15em 0.5%; text-align: right; }
 .evenements td { padding: 2px 3px 1px; border: none !important; }
-.evnmt { padding: 1px 3px; border-radius: 5px; white-space: nowrap; overflow: hidden; font-size: 0.8em; cursor: pointer; }
+.modifevnmt { padding: 1px 3px; border-radius: 5px; white-space: nowrap; overflow: hidden; font-size: 0.8em; cursor: pointer; }
 .evnmt_suivi { border-top-right-radius: 0px; border-bottom-right-radius: 0px; margin-right: -2px; }
 .evnmt_suite { border-top-left-radius: 0px; border-bottom-left-radius: 0px; margin-left: -3px; }
+
+/* Chargement pour les envoi ajax */
+#load { display: none; margin: 0; padding: 0; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 30; text-align: center;}
+#load img { position: absolute; top: 50%; }
diff -urN cahier-de-prepa6.2.0/css/style.min.css cahier-de-prepa8.0.1/css/style.min.css
--- cahier-de-prepa6.2.0/css/style.min.css	2016-09-09 11:11:06.778955925 +0200
+++ cahier-de-prepa8.0.1/css/style.min.css	1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-html,body{height:100%}body{font-size:100%;font-family:Arial,Helvetica,sans-serif;position:relative;width:100%;margin:0 auto;max-width:1500px;background-color:#f6f6f6}h1{font-size:2.2em;text-align:center;margin:0;padding:1em 2em}h2{font-size:1.8em;margin:1em 0 .8em;padding:0}h3{font-size:1.5em;margin:1em 0 .5em;padding:0 1% 0}h4{font-size:1.3em;margin:.5em 0 .2em;padding:0 2.5% 0}h5{font-size:1.1em;margin:.2em 0 0;padding:0 4% 0}h6{font-size:1em;margin:.2em 0 0;padding:0 5.5% 0}ul{margin:.5em 0;padding:0 2% 0 6%}p{margin:0;padding:0 2%}p+p{margin-top:.5em}div,form{margin:0;padding:0}img{border:0;max-width:100%}div,p,section,article{text-align:justify}article>*:first-child{margin-top:0}article>*:last-child{margin-bottom:0}section>h2:first-child{margin-top:0}@media screen and (min-width:800px){#colonne{width:280px;float:left;margin:0 30px 3em}nav{margin:0;padding:1em 20px .7em}.icon-menu,.icon-recent{display:none}#recent{margin-top:1.5em;padding:1em 20px}section{position:relative;margin:0 30px 0 340px;padding:0 0 3em}header+section{width:96%;margin:0 auto;max-width:1500px;text-align:center}footer{font-size:.8em}}@media screen and (max-width:800px){h1{font-size:1.8em;padding:.3em 3em}h2{font-size:1.65em}#colonne,nav,#recent{display:none}#colonne.visible{display:block}nav.visible,#recent.visible{display:block;position:fixed;z-index:10;top:3.3em;left:.5em;padding:1em 20px .7em;width:80%;min-width:200px;max-width:280px;max-height:70%;overflow:auto;box-shadow:.5em .5em .5em #777;-moz-box-shadow:.5em .5em .5em #777;-webkit-box-shadow:.5em .5em .5em #777}.icon-menu,.icon-recent{position:fixed;z-index:10;top:.8em;left:.5em;font-size:1.2em;cursor:pointer}.icon-recent{left:2.5em}section{position:relative;width:96%;margin:0 2%;padding:0 0 3em}header+section{text-align:center}footer{font-size:.6em}}article{margin:1em 0;padding:1em 2%;background-color:#cdd5e4}article:first-child,.general+article{margin-top:0!important}footer{text-align:center;width:90%;padding:1em 5%;clear:both;position:fixed;left:0;bottom:0;z-index:15;border-top:1px solid black;background-color:#DDD;opacity:.97;filter:alpha(opacity=97)}.pdf{height:0;width:100%;overflow:hidden;position:relative}.portrait{padding-bottom:138%}.paysage{padding-bottom:74%}.hauteur50{padding-bottom:50%}.pdf object{position:absolute}.warning{text-align:center;width:50%;margin:1em auto;padding:.5em 3%}.annonce{margin:1em 3%;padding:.5em 4%}.note{margin:.5em 2%;padding:0 4%}.warning,.annonce{color:#ba0d1f;border:2px solid #ba0d1f}.note{color:#ba0d1f}.oubli{font-size:.8em;text-align:center}.oubli a{text-decoration:none;color:#333}p.titrecdt{text-align:right;text-decoration:underline}.titrecdt.edition{text-align:left;text-decoration:none;padding-right:1%}.topbarre{height:1.5em;background-color:#DDD;border:1px solid #BBB;width:auto;padding:0;margin-bottom:0;border-radius:4px}h1 span{font-size:70%;vertical-align:7%;margin-left:.4em}#recherchecdt a,#recherchecolle a,#rechercheagenda a{float:none!important;margin-left:.5em}#rechercheagenda a{vertical-align:sub}.topbarre select#semaines{margin-left:1.3em;width:8.5em;height:1.7em}.topbarre select#seances{margin-left:1.3em;width:11em;height:1.7em}.topbarre input{position:absolute;margin:0 .8em 0 1.5em;padding-left:1.8em;width:-moz-available;height:1.7em}.topbarre span{position:relative;left:1.9em;cursor:pointer;top:.1em}@media screen and (max-width:980px) and (min-width:800px),screen and (max-width:600px){#recherchecdt input{display:none;top:1.8em;padding-left:.1em;right:2em;margin-left:2em!important}#recherchecdt span{left:1.3em}}@media screen and (max-width:450px){#recherchecdt a,#recherchecdt select{margin-left:.3em!important}#recherchecdt input{margin-left:.3em}#recherchecdt select{width:6em!important}#recherchecdt span{left:.3em}}@media screen and (max-width:400px){#recherchecolle a,#recherchecolle select{margin-left:.3em!important}#recherchecolle input{margin-left:0!important}#recherchecolle select{width:6em}#recherchecolle span{left:.3em}}@media screen and (max-width:350px){#recherchecolle input{display:none;top:1.8em;padding-left:.1em;right:2em;margin-left:2em!important}}@media screen and (-webkit-min-device-pixel-ratio:0){.topbarre{height:1.4em;vertical-align:bottom}#recherchecolle input{width:40%}.topbarre span{top:.2em}#recherchecdt a,#recherchecolle a{vertical-align:middle}}#parentsdoc{margin-bottom:1.5em;padding:.3em 2% 0}#parentsdoc *{padding-top:0}#parentsdoc span{position:static;cursor:auto}.rep,.doc{padding:0;margin-left:2%;margin-right:2%;border-bottom:1px dotted #BBB}.repcontenu,.docdonnees{float:right;font-size:.8em;padding-top:.2em;padding-left:.5em}#parentsdoc a,.rep a,.doc a{text-decoration:none;color:black}#parentsdoc .nom,.rep .nom,.doc .nom{font-weight:700;margin-left:.5em}.general{position:absolute;top:-4.1em;right:1em}.general+.general{right:2.2em}.general+.general+.general{right:3.4em}.general+.general+.general+.general{right:4.6em}.edition{display:inline;text-align:left;padding-right:3em}h3.edition.editable{padding-right:1%}.edition ~ div,.edition+p,.edition ~ form{margin-top:.75em}article.cache{background-color:#AAA;opacity:.6;filter:alpha(opacity=60)}#log{position:fixed;top:3%;left:3%;padding:.3em 2%;z-index:20;margin-right:3%}.ok{background-color:#EFE;color:#090;border:1px solid #090}.nok{background-color:#FEE;color:#D00;border:1px solid #D00}.ok span{color:#090}.nok span{color:#D00}#log span{cursor:pointer;position:relative;right:-0.8em;top:.1em}#fenetre_fond{position:fixed;top:0;left:0;width:100%;height:100%;background-color:#000;opacity:.2;filter:alpha(opacity=20);z-index:12}#fenetre{position:fixed;left:50%;z-index:14;padding:0 2%;overflow:auto;background-color:#e7eefe;opacity:.97;filter:alpha(opacity=97);box-shadow:.5em .5em .5em #777;-webkit-box-shadow:.5em .5em .5em #777}#fenetre>*:last-child{margin-bottom:1em}@media screen and (min-width:800px){#fenetre{top:10%;width:70%;margin-left:-37%;max-height:80%}}@media screen and (max-width:800px){.general{top:-2.2em}#fenetre{top:4%;width:92%;margin-left:-48%;max-height:92%}}#fenetre a[class^="icon-"]{margin-top:1.5em}#fenetre hr{margin:1.5em 0}#epingle{margin-top:1.5em}#epingle h3{padding-left:0}[id^="aide-"],[id^="form-"]{display:none}#fenetre [name="titre"]{margin:2em 0 1em}#fenetre [name="titre"]+*{display:inline}form.titrecdt{padding:.2em 0 .5em;margin-top:.2em}.suppression{text-align:center}#fenetre.usermat h3{margin-bottom:0}#fenetre.usermat a.icon-ajoute,#fenetre a.icon-supprime{margin-top:.8em}#fenetre.usermat .ligne,#fenetre.usergrp .ligne{border-bottom:1px dotted #BBB}#fenetre.usermat input,#fenetre.usergrp input{margin-top:.2em}#fenetre th+th,#fenetre td+td{text-align:center;width:2em;padding:.15em}#fenetre th a{float:none}#fenetre th{border:none!important}#fenetre td{border-top:none!important;border-left:none!important;border-right:none!important;border-bottom:1px dotted #BBB!important}#planning{text-align:center}.labelchecked{color:#999}#planning tr:hover,#utilisateurs tr:hover,#notes tr:hover,.usergrp .ligne:hover,.usermat .ligne:hover{background-color:#EFF}input,select,textarea{box-sizing:border-box;-webkit-box-sizing:border-box;border:1px solid;border-radius:2px;padding:0 .3em}#fichier{border:0;font-size:.8em;height:1.9em}p.ligne{padding:0;clear:right}.ligne label{font-weight:700}.ligne input,.ligne select,.ligne code{width:65%;float:right;margin-top:-0.1em;margin-left:.2em;line-height:1em;height:1.5em;box-sizing:border-box}.ligne input[type="checkbox"],.ligne input[type="radio"]{width:1em;margin-top:.2em}input.ligne{width:100%;margin-bottom:.5em;line-height:1em;height:1.5em;display:block}.supprmultiple{margin-top:.3em;margin-bottom:0!important}.usermat{margin-top:.5em;padding:0;clear:right}.usermat a{padding:0!important}table{width:100%;margin:1em 0;border-collapse:collapse;border:medium solid #999}table td{padding:.15em .5%}table th{padding:.15em 3%}table td,table th{border:thin solid #AAA!important}table td .icon-edite{float:left!important;margin:0 .2em 0 .1em}div.placeholder:before{content:attr(data-placeholder);color:#AAA;margin-left:4px;z-index:12}span.placeholder{color:#AAA;position:absolute;margin:.25em 2.5% 0 5px;z-index:12;font-size:.8em}p.ligne span.placeholder{left:35%;margin-top:.1em;margin-left:1em}#fenetre span.placeholder,#fenetre div.placeholder:before{z-index:15}.editable,.titrecdt.edition,form.titrecdt,.editabledest{border:1px dotted #BBB;position:relative}.editable a[class^="icon-"],p.titrecdt a[class^="icon-"],.editabledest a[class^="icon-"]{float:none}.avecform{padding:0;border:none!important}h3.editable a[class^="icon-"],h3 span.editable a[class^="icon-"]{font-weight:500;font-size:.67em;padding-top:.1em}h3.avecform span{font-weight:500;font-size:.6em;margin-top:.5em;width:80%;overflow:hidden;white-space:nowrap}h3.avecform input{width:50%}textarea{width:100%;margin:0 0 .2em}textarea+div{min-height:6em;border:1px dotted #CCC;background-color:#FFF}.boutons{clear:right;background-color:#DDD;border:1px solid #BBB;width:auto;padding:0;margin-bottom:0;border-radius:4px}.boutons button{cursor:default;background-color:transparent;border-top:0;border-left:none;border-bottom:1px solid #BBB;border-right:1px solid #BBB;height:1.5em;font-size:100%;width:2em}.boutons button+button{margin-left:-0.2em}div.editable a[class^="icon-"]{position:absolute;right:3px;top:0}div.editable a.icon-annule{top:1.5em}div.edithtml a.icon-ok{top:1.6em}div.edithtml a.icon-annule{top:3.1em}nav,#recent{background-color:#99b3e5}nav a[class^="icon-"]{float:none!important;display:inline!important;margin:.5em 3% 1em;color:#001030}nav a{display:block;margin-bottom:.2em;padding:0;text-decoration:none;color:#002877}nav a:hover{color:#CDF}nav h3{font-size:1.2em;margin:.5em 3% .1em;padding-top:.3em;color:#001030;border-top:1px solid #001030}nav hr{margin:.5em 3% .5em;color:#001030;border-top:1px solid #001030;border-bottom:0}nav a.menurep{padding-left:3%;font-size:.9em}#actuel{font-style:italic}nav h3 span{font-weight:500;font-size:.83em;margin-right:.2em;color:#001030!important}#recent a{display:block;margin-bottom:.4em;padding:0;text-decoration:none;color:#002877;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}#recent a span{color:#002877}#recent h3{font-size:1.2em;margin:0 3% .5em;color:#001030}th.semaines{vertical-align:bottom;padding:1.6em 0;text-align:center}td.semaines{padding-bottom:1em!important;vertical-align:bottom;text-align:center;padding:.2em 5px}td.semaines span{display:block;font-weight:700;margin:0;padding:0;width:1.2em;transform:rotate(-90deg);-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);white-space:nowrap;zoom:1}td.pasnote{font-style:italic;text-align:center}.collsel{font-weight:700}.collnosel,.dejanote{color:#AAA}#recherchenote{padding:0 1em}#notes td+td{text-align:right}@media print{body{font-size:90%;font-family:Serif}#colonne,#recherchecolle,#recherchecdt,#rechercheagenda,[id^="aide-"],[id^="form-"],footer,a[class^="icon-"]{display:none}.editable,.titrecdt.edition,form.titrecdt,.editabledest{border:0}h1{font-size:1.7em;text-align:center;margin:0;padding:0 0 1em}h2{font-size:1.5em;margin:.7em 0;padding:0}h3{font-size:1.35em;margin:.6em 0;padding:0 1% 0}h4{font-size:1.2em;margin:.4em 0 .2em;padding:0 2.5% 0}h5{font-size:1.1em;margin:.2em 0 0;padding:0 4% 0}h6{font-size:1em;margin:.2em 0 0;padding:0 5.5% 0}article{border:1px solid #999}}#calendrier{margin-top:1em}#calendrier table{table-layout:fixed}#semaine,.semaine-bg,.evenements{margin:0}#semaine{font-weight:900;text-align:center}#semaine th{overflow:hidden;text-overflow:clip}.semaine-bg{border-top:0;position:absolute;z-index:1}.autremois{background-color:#e7eefe;color:#002877}#aujourdhui{background-color:#99b3e5}.evenements{position:relative;z-index:2;border-top:0;border-bottom:0}.evenements thead{border-bottom:1px solid #999}.evenements th{padding:.15em .5%;text-align:right}.evenements td{padding:2px 3px 1px;border:none!important}.evnmt{padding:1px 3px;border-radius:5px;white-space:nowrap;overflow:hidden;font-size:.8em;cursor:pointer}.evnmt_suivi{border-top-right-radius:0;border-bottom-right-radius:0;margin-right:-2px}.evnmt_suite{border-top-left-radius:0;border-bottom-left-radius:0;margin-left:-3px}
\ No newline at end of file
diff -urN cahier-de-prepa6.2.0/def_sql.php cahier-de-prepa8.0.1/def_sql.php
--- cahier-de-prepa6.2.0/def_sql.php	2017-08-25 03:55:12.296954564 +0200
+++ cahier-de-prepa8.0.1/def_sql.php	2018-10-17 00:49:36.193538372 +0200
@@ -22,11 +22,16 @@
 FLUSH PRIVILEGES;
 USE `$base`;
 
-CREATE TABLE `prefs` (
-  `nom` varchar(50) NOT NULL,
-  `val` tinyint(2) unsigned NOT NULL
-) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-
+-- colles, cdt, docs : 0 si vide, 1 si présent
+-- notes : 0 si vide, 1 si présent, 2 si désactivées
+-- *_protection : valeur numérique de gestion de la protection. Si nul, autorisation à tous, sans
+--    nécessité de connexion identifiée. Si entre 1 et 32, conversion de la valeur binaire PACEI
+--    (profs,administration,colleurs,élèves,invités) à laquelle on a ajouté 1. Chaque 0 correspond
+--    aux accès autorisés, chaque 1 correspond aux protections (accès interdit pour ce type de compte).
+--    Exemple : 10->PACEI=9=01001->autorisé pour P,C,E et interdit pour A et I.
+--    Le code 32 (interdit pour tous) correspond aux fonctions désactivées et aux documents/répertoires
+--    non visibles : plus d'affichage dans le menu, plus d'accès.
+-- dureecolle : durée pour un élève, en minutes
 CREATE TABLE `matieres` (
   `id` tinyint(2) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   `ordre` tinyint(2) unsigned NOT NULL,
@@ -38,12 +43,26 @@
   `notes` tinyint(1) unsigned NOT NULL,
   `colles_protection` tinyint(1) unsigned NOT NULL,
   `cdt_protection` tinyint(1) unsigned NOT NULL,
+  `docs_protection` tinyint(1) unsigned NOT NULL,
+  `dureecolle`tinyint(2) unsigned NOT NULL,
   KEY `colles` (`colles`),
   KEY `cdt` (`cdt`),
   KEY `docs` (`docs`),
   KEY `notes` (`notes`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
+-- autorisation : type d'utilisateur (1:invité, 2:élève, 3:colleur, 4:administration, 5:professeur)
+-- mdp : stockage du mot de passe sur 40 caractères
+--       * si commence par un ? : invitation non répondue (mot de passe non défini)
+--       * si commence par un * : compte demandé en attente de validation
+--       * si commence par un ! : compte suspendu
+-- mailexp : nom d'expédition des courriels
+-- mailcopie : si par défaut envoi personnel d'une copie de ses courriels
+-- mailenvoi : si on peut envoyer des courriels ; doit être cohérent avec mail
+-- mailliste : si on apparait dans la liste des destinataires possibles
+--             de courriels pour les élèves ; doit être cohérent avec mail
+-- permconn : token d'identification légère, par cookie
+-- lastconn : horodatage de la connexion actuelle
 CREATE TABLE `utilisateurs` (
   `id` smallint(3) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
   `login` varchar(50) NOT NULL UNIQUE,
@@ -55,7 +74,11 @@
   `matieres` varchar(30) NOT NULL,
   `timeout` smallint(4) UNSIGNED NOT NULL,
   `mailexp` varchar(50) NOT NULL,
-  `mailcopy` tinyint(1) UNSIGNED NOT NULL
+  `mailcopie` tinyint(1) UNSIGNED NOT NULL,
+  `mailenvoi` tinyint(1) UNSIGNED NOT NULL,
+  `mailliste` tinyint(1) UNSIGNED NOT NULL,
+  `permconn` varchar(10) NOT NULL,
+  `lastconn` datetime NOT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
   
 CREATE TABLE `pages` (
@@ -76,6 +99,7 @@
   `cache` tinyint(1) unsigned NOT NULL,
   `titre` text NOT NULL,
   `texte` text NOT NULL,
+  `protection` tinyint(1) unsigned NOT NULL,
   KEY `ordre` (`ordre`,`page`),
   KEY `cache` (`cache`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
@@ -112,6 +136,7 @@
   `texte` text NOT NULL,
   `demigroupe` tinyint(1) unsigned NOT NULL,
   `cache` tinyint(1) unsigned NOT NULL,
+  `protection` tinyint(1) unsigned NOT NULL,
   KEY `matiere` (`matiere`),
   KEY `semaine` (`semaine`),
   KEY `type` (`type`),
@@ -126,7 +151,6 @@
   `cle` varchar(20) NOT NULL,
   `deb_fin_pour` tinyint(1) unsigned NOT NULL,
   `nb` tinyint(2) unsigned NOT NULL,
-  `nb_v` tinyint(2) unsigned NOT NULL,
   KEY `matiere` (`matiere`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
@@ -140,12 +164,14 @@
   `h_fin` time NOT NULL,
   `type` tinyint(3) unsigned NOT NULL,
   `demigroupe` tinyint(1) unsigned NOT NULL,
+  `protection` tinyint(1) unsigned NOT NULL,
+  `template` text NOT NULL,
   KEY `matiere` (`matiere`)
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
 CREATE TABLE `reps` (
-  `id` tinyint(3) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
-  `parent` tinyint(3) unsigned NOT NULL,
+  `id` smallint(3) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  `parent` smallint(3) unsigned NOT NULL,
   `parents` varchar(50) NOT NULL,
   `matiere` tinyint(2) unsigned NOT NULL,
   `nom` varchar(100) NOT NULL,
@@ -161,7 +187,7 @@
 
 CREATE TABLE `docs` (
   `id` smallint(3) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
-  `parent` tinyint(3) unsigned NOT NULL,
+  `parent` smallint(3) unsigned NOT NULL,
   `parents` varchar(50) NOT NULL,
   `matiere` tinyint(2) unsigned NOT NULL,
   `nom` varchar(100) NOT NULL,
@@ -190,18 +216,37 @@
 CREATE TABLE notes (
   `id` smallint(5) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
   `semaine` tinyint(2) unsigned NOT NULL,
-  `eleve` tinyint(2) unsigned NOT NULL,
-  `colleur` tinyint(2) unsigned NOT NULL,
+  `heure` smallint(3) unsigned NOT NULL,
+  `eleve` smallint(3) unsigned NOT NULL,
+  `colleur` smallint(3) unsigned NOT NULL,
+  `matiere` tinyint(2) unsigned NOT NULL,
+  `note` varchar(4) NOT NULL,
+  KEY `semaine` (`semaine`),
+  KEY `heure` (`heure`),
+  KEY `eleve` (`eleve`),
+  KEY `colleur` (`colleur`),
+  KEY `matiere` (`matiere`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+
+CREATE TABLE heurescolles (
+  `id` smallint(5) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  `colleur` smallint(3) unsigned NOT NULL,
   `matiere` tinyint(2) unsigned NOT NULL,
-  `note` varchar(4) NOT NULL
+  `jour` date NOT NULL,
+  `heure` time NOT NULL,
+  `duree` smallint(3) unsigned NOT NULL,
+  `releve` date NOT NULL,
+  KEY `colleur` (`colleur`),
+  KEY `matiere` (`matiere`)
 ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
 
 CREATE TABLE groupes (
   `id` tinyint(2) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
-  `nom` varchar(10) NOT NULL,
-  `nom_nat` varchar(30) NOT NULL,
-  `mailnotes` tinyint(1) UNSIGNED NOT NULL,
-  `eleves` varchar(250) NOT NULL
+  `nom` varchar(50) NOT NULL,
+  `nom_nat` varchar(50) NOT NULL,
+  `mails` tinyint(1) UNSIGNED NOT NULL,
+  `notes` tinyint(1) UNSIGNED NOT NULL,
+  `utilisateurs` varchar(250) NOT NULL
 ) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
 
 CREATE TABLE `agenda` (
@@ -223,24 +268,30 @@
   `couleur` varchar(6) NOT NULL
 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
+CREATE TABLE `prefs` (
+  `nom` varchar(50) NOT NULL,
+  `val` tinyint(2) unsigned NOT NULL
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
 INSERT INTO prefs (nom,val)
   VALUES ('creation_compte',1),
          ('nb_agenda_index',10),
          ('protection_globale',0),
-         ('protection_agenda',0);
+         ('protection_agenda',0),
+         ('envoi_mail_defaut',0);
 
-INSERT INTO utilisateurs (id,login,prenom,nom,mail,mdp,autorisation,matieres,timeout,mailexp,mailcopy)
-  VALUES (1, '$login', '$prenom', '$nom', '$mail', '*', 4, '0,1', 900, '$prenom $nom', 1);
+INSERT INTO utilisateurs (id,login,prenom,nom,mail,mdp,autorisation,matieres,timeout,mailexp,mailcopie,mailenvoi,mailliste)
+  VALUES (1, '$login', '$prenom', '$nom', '$mail', '?', 5, '0,1', 900, '$prenom $nom', 1, 1, 1);
 
-INSERT INTO matieres (id,ordre,cle,nom,colles,cdt,docs,notes,colles_protection,cdt_protection)
-  VALUES (1, 1, '$cle_matiere', '$nom_matiere', 0, 0, 0, 0, 0, 0);
+INSERT INTO matieres (id,ordre,cle,nom)
+  VALUES (1, 1, '$cle_matiere', '$nom_matiere');
 
-INSERT INTO reps (id,parent,parents,matiere,nom,nbrep,nbrep_v,nbdoc,nbdoc_v,protection,menu)
-  VALUES (1, 0, '0', 0, 'Général', 0, 0, 0, 0, 0, 0),
-         (2, 0, '0', 1, '$nom_matiere', 0, 0, 0, 0, 0, 0);
+INSERT INTO reps (id,parents,matiere,nom)
+  VALUES (1, '0', 0, 'Général'),
+         (2, '0', 1, '$nom_matiere');
 
-INSERT INTO pages (ordre,cle,nom,titre,bandeau,protection)
-  VALUES (1, 'accueil', 'Accueil', '$titre', 'Dernières informations importantes', 0);
+INSERT INTO pages (ordre,cle,nom,titre,bandeau)
+  VALUES (1, 'accueil', 'Accueil', '$titre', 'Dernières informations importantes');
 
 INSERT INTO `cdt-types` (matiere,ordre,cle,titre,deb_fin_pour)
   VALUES (1, 1, 'cours', 'Cours', 1),
@@ -262,38 +313,38 @@
          (7, 8, 'Jour férié', 'fer', 'CC3333'),
          (8, 9, 'Vacances', 'vac', '66CC33');
 
-INSERT INTO semaines (debut) VALUES ('2017-09-04'),('2017-09-11'),('2017-09-18'),('2017-09-25'),
-  ('2017-10-02'),('2017-10-09'),('2017-10-16'),('2017-10-23'),('2017-10-30'),('2017-11-06'),('2017-11-13'),('2017-11-20'),('2017-11-27'),
-  ('2017-12-04'),('2017-12-11'),('2017-12-18'),('2017-12-25'),('2018-01-01'),('2018-01-08'),('2018-01-15'),('2018-01-22'),
-  ('2018-01-29'),('2018-02-05'),('2018-02-12'),('2018-02-19'),('2018-02-26'),('2018-03-05'),('2018-03-12'),('2018-03-19'),('2018-03-26'),
-  ('2018-04-03'),('2018-04-09'),('2018-04-16'),('2018-04-23'),('2018-04-30'),('2018-05-07'),('2018-05-14'),('2018-05-22'),('2018-05-28'),
-  ('2018-06-04'),('2018-06-11'),('2018-06-18'),('2018-06-25'),('2018-07-02');
+INSERT INTO semaines (debut) VALUES ('2018-09-03'),('2018-09-10'),('2018-09-17'),('2018-09-24'),
+  ('2018-10-01'),('2018-10-08'),('2018-10-15'),('2018-10-22'),('2018-10-29'),('2018-11-05'),('2018-11-12'),('2018-11-19'),('2018-11-26'),
+  ('2018-12-03'),('2018-12-10'),('2018-12-17'),('2018-12-24'),('2018-12-31'),('2019-01-07'),('2019-01-14'),('2019-01-21'),
+  ('2019-01-28'),('2019-02-04'),('2019-02-11'),('2019-02-18'),('2019-02-25'),('2019-03-04'),('2019-03-11'),('2019-03-18'),('2019-03-25'),
+  ('2019-04-02'),('2019-04-08'),('2019-04-15'),('2019-04-22'),('2019-04-29'),('2019-05-06'),('2019-05-13'),('2019-05-21'),('2019-05-27'),
+  ('2019-06-03'),('2019-06-10'),('2019-06-17'),('2019-06-24'),('2019-07-01');
 UPDATE semaines SET colle = 1;
 
 INSERT INTO agenda (id,matiere,debut,fin,type,texte)
   VALUES 
-         (1, 0, '2017-09-04 00:00:00', '2017-09-04 00:00:00', 1, '<div class="annonce">C''est la rentrée ! Bon courage pour cette nouvelle année&nbsp;!</div>'),
-         (2, 0, '2017-08-15 00:00:00', '2017-08-15 00:00:00', 7, '<p>Assomption</p>'),
-         (3, 0, '2017-11-01 00:00:00', '2017-11-01 00:00:00', 7, '<p>Toussaint</p>'),
-         (4, 0, '2017-11-11 00:00:00', '2017-11-11 00:00:00', 7, '<p>Armistice 1918</p>'),
-         (5, 0, '2017-12-25 00:00:00', '2017-12-25 00:00:00', 7, '<p>Noël</p>'),
-         (6, 0, '2018-01-01 00:00:00', '2018-01-01 00:00:00', 7, '<p>Jour de l''an</p>'),
-         (7, 0, '2018-04-02 00:00:00', '2018-04-02 00:00:00', 7, '<p>Pâques</p>'),
-         (8, 0, '2018-05-01 00:00:00', '2018-05-01 00:00:00', 7, '<p>Fête du travail</p>'),
-         (9, 0, '2018-05-08 00:00:00', '2018-05-08 00:00:00', 7, '<p>Armistice 1945</p>'),
-         (10, 0, '2018-05-10 00:00:00', '2018-05-10 00:00:00', 7, '<p>Ascension</p>'),
-         (11, 0, '2018-05-21 00:00:00', '2018-05-21 00:00:00', 7, '<p>Pentecôte</p>'),
-         (12, 0, '2018-07-14 00:00:00', '2018-07-14 00:00:00', 7, '<p>Fête Nationale</p>'),
-         (13, 0, '2017-07-06 00:00:00', '2017-09-03 00:00:00', 8, '<p>Vacances d''été</p>'),
-         (14, 0, '2017-10-22 00:00:00', '2017-11-05 00:00:00', 8, '<p>Vacances de la Toussaint</p>'),
-         (15, 0, '2017-12-24 00:00:00', '2018-01-07 00:00:00', 8, '<p>Vacances de Noël</p>'),
-         (16, 0, '2018-02-11 00:00:00', '2018-02-25 00:00:00', 8, '<p>Vacances d''hiver, zone A</p>'),
-         (17, 0, '2018-02-25 00:00:00', '2018-03-11 00:00:00', 8, '<p>Vacances d''hiver, zone B</p>'),
-         (18, 0, '2018-02-18 00:00:00', '2018-03-04 00:00:00', 8, '<p>Vacances d''hiver, zone C</p>'),
-         (19, 0, '2018-04-08 00:00:00', '2018-04-22 00:00:00', 8, '<p>Vacances de printemps, zone A</p>'),
-         (20, 0, '2018-04-22 00:00:00', '2018-05-06 00:00:00', 8, '<p>Vacances de printemps, zone B</p>'),
-         (21, 0, '2018-04-15 00:00:00', '2018-04-29 00:00:00', 8, '<p>Vacances de printemps, zone C</p>'),
-         (22, 0, '2018-07-09 00:00:00', '2018-08-31 00:00:00', 8, '<p>Vacances d''été</p>');
+         (1, 0, '2018-09-03 00:00:00', '2018-09-03 00:00:00', 3, '<div class="annonce">C''est la rentrée ! Bon courage pour cette nouvelle année&nbsp;!</div>'),
+         (2, 0, '2018-08-15 00:00:00', '2018-08-15 00:00:00', 7, '<p>Assomption</p>'),
+         (3, 0, '2018-11-01 00:00:00', '2018-11-01 00:00:00', 7, '<p>Toussaint</p>'),
+         (4, 0, '2018-11-11 00:00:00', '2018-11-11 00:00:00', 7, '<p>Armistice 1918</p>'),
+         (5, 0, '2018-12-25 00:00:00', '2018-12-25 00:00:00', 7, '<p>Noël</p>'),
+         (6, 0, '2019-01-01 00:00:00', '2019-01-01 00:00:00', 7, '<p>Jour de l''an</p>'),
+         (7, 0, '2019-04-22 00:00:00', '2019-04-22 00:00:00', 7, '<p>Lundi de Pâques</p>'),
+         (8, 0, '2019-05-01 00:00:00', '2019-05-01 00:00:00', 7, '<p>Fête du travail</p>'),
+         (9, 0, '2019-05-08 00:00:00', '2019-05-08 00:00:00', 7, '<p>Armistice 1945</p>'),
+         (10, 0, '2019-05-30 00:00:00', '2019-05-30 00:00:00', 7, '<p>Jeudi de l''Ascension</p>'),
+         (11, 0, '2019-06-10 00:00:00', '2019-06-10 00:00:00', 7, '<p>Lundi de Pentecôte</p>'),
+         (12, 0, '2019-07-14 00:00:00', '2019-07-14 00:00:00', 7, '<p>Fête Nationale</p>'),
+         (13, 0, '2018-07-08 00:00:00', '2018-09-02 00:00:00', 8, '<p>Vacances d''été</p>'),
+         (14, 0, '2018-10-21 00:00:00', '2018-11-04 00:00:00', 8, '<p>Vacances de la Toussaint</p>'),
+         (15, 0, '2018-12-23 00:00:00', '2019-01-06 00:00:00', 8, '<p>Vacances de Noël</p>'),
+         (16, 0, '2019-02-17 00:00:00', '2019-03-03 00:00:00', 8, '<p>Vacances d''hiver, zone A</p>'),
+         (17, 0, '2019-02-10 00:00:00', '2019-02-24 00:00:00', 8, '<p>Vacances d''hiver, zone B</p>'),
+         (18, 0, '2019-02-24 00:00:00', '2019-03-10 00:00:00', 8, '<p>Vacances d''hiver, zone C</p>'),
+         (19, 0, '2019-04-14 00:00:00', '2019-04-28 00:00:00', 8, '<p>Vacances de printemps, zone A</p>'),
+         (20, 0, '2019-04-07 00:00:00', '2019-04-22 00:00:00', 8, '<p>Vacances de printemps, zone B</p>'),
+         (21, 0, '2019-04-21 00:00:00', '2019-05-05 00:00:00', 8, '<p>Vacances de printemps, zone C</p>'),
+         (22, 0, '2019-07-07 00:00:00', '2019-09-01 00:00:00', 8, '<p>Vacances d''été</p>');
 
 FIN;
 ?>
diff -urN cahier-de-prepa6.2.0/docs.php cahier-de-prepa8.0.1/docs.php
--- cahier-de-prepa6.2.0/docs.php	2015-10-27 12:48:33.019597305 +0100
+++ cahier-de-prepa8.0.1/docs.php	2018-10-15 17:50:49.820945409 +0200
@@ -11,10 +11,11 @@
 //////////////////////////////////////////////////////
 
 // Ordre d'affichage des documents (à supprimer avant d'analyser la requête)
-$ordre = '';
+$ordre = 'ORDER BY nom_nat ASC';
+$ordrerep = 'ORDER BY nom ASC';
 if ( isset($_REQUEST['ordre']) )  {
   switch ($_REQUEST['ordre'])  {
-    case 'alpha-inv':  $ordre = 'ORDER BY nom_nat DESC'; break;
+    case 'alpha-inv':  $ordre = 'ORDER BY nom_nat DESC'; $ordrerep = 'ORDER BY nom DESC'; break;
     case 'chrono':     $ordre = 'ORDER BY docs.upload ASC'; break;
     case 'chrono-inv': $ordre = 'ORDER BY docs.upload DESC'; break;
   }
@@ -24,22 +25,21 @@
 // Récupération des données du répertoire demandé
 $mysqli = connectsql();
 // Requête non nulle : soit un numéro de répertoire, soit une clé de matière
-// Les répertoires "non visibles" (protection 5), sauf pour les professeurs
-// associés à la matière, ne sont pas accessibles.
-$restriction = ( $autorisation == 4 ) ? "AND ( r.protection < 5 OR FIND_IN_SET(r.matiere,'${_SESSION['matieres']}') )" : 'AND r.protection < 5';
+// Les répertoires "non visibles" (protection 32), sauf pour les professeurs
+// associés à la matière, ne sont pas accessibles (il ne faut pas que l'on
+// obtienne "accès non autorisé" mais "mauvais paramètre").
+$requete = 'SELECT r.id, r.nom, r.parent, r.parents, r.menu, r.protection, IFNULL(m.id,0) AS mid, m.cle, m.nom AS mat
+            FROM reps AS r LEFT JOIN matieres AS m ON r.matiere = m.id';
+$restriction = ( $autorisation == 5 ) ? "AND ( r.protection != 32 OR FIND_IN_SET(r.matiere,'${_SESSION['matieres']}') )" : 'AND r.protection != 32';
 if ( isset($_REQUEST['rep']) && ctype_digit($rid = $_REQUEST['rep']) )  {
-  $resultat = $mysqli->query("SELECT r.nom, r.parent, r.parents, r.nbrep_v+r.nbdoc_v AS nbv, r.nbrep+r.nbdoc AS nb, r.menu, r.protection, IFNULL(m.id,0) AS mid, m.cle, m.nom AS mat
-                              FROM reps AS r LEFT JOIN matieres AS m ON r.matiere = m.id
-                              WHERE r.id = $rid $restriction");
+  $resultat = $mysqli->query("$requete WHERE r.id = $rid $restriction");
   if ( $resultat->num_rows )  {
     $rep = $resultat->fetch_assoc();
     $resultat->free();
   }
 }
 elseif ( !empty($_REQUEST) )  {
-  $resultat = $mysqli->query("SELECT r.id, r.nom, r.parent, r.parents, r.nbrep_v+r.nbdoc_v AS nbv, r.nbrep+r.nbdoc AS nb, r.menu, r.protection, IFNULL(m.id,0) AS mid, m.cle, m.nom AS mat
-                              FROM reps AS r LEFT JOIN matieres AS m ON r.matiere = m.id
-                              WHERE r.parent = 0 $restriction");
+  $resultat = $mysqli->query("$requete WHERE r.parent = 0 $restriction");
   if ( $resultat->num_rows )  {
     while ( $r = $resultat->fetch_assoc() )
       if ( isset($_REQUEST[$r['cle']]) )  {
@@ -52,13 +52,10 @@
 }
 // Pas d'argument : répertoire "Général" (peut être non visible)
 else  {
-  $resultat = $mysqli->query("SELECT r.id, r.nom, r.parent, r.parents, r.nbrep_v+r.nbdoc_v AS nbv, r.nbrep+r.nbdoc AS nb, r.menu, r.protection, IFNULL(m.id,0) AS mid, m.cle, m.nom AS mat
-                              FROM reps AS r LEFT JOIN matieres AS m ON r.matiere = m.id
-                              WHERE r.id = 1 $restriction");
+  $resultat = $mysqli->query("$requete WHERE r.id = 1 $restriction");
   if ( $resultat->num_rows )  {
-    $r = $resultat->fetch_assoc();
-    $rep = $r;
-    $rid = $r['id'];
+    $rep = $resultat->fetch_assoc();
+    $rid = $rep['id'];
     $resultat->free();
   }
 }
@@ -72,30 +69,17 @@
 /////////////////////////////
 // Vérification de l'accès //
 /////////////////////////////
-// Interdiction de voir les répertoires de protection élevée ou non associés à
-// ses propres matières si on est invité/élève/colleur
-// Toutes les matières sont visibles pour les professeurs (édition: cf plus bas)
-if ( $rep['protection'] && ( $autorisation > 0 ) && ( $autorisation < 4 )
-  && ( ( $rep['protection'] > $autorisation ) || ( !in_array($rep['mid'],explode(',',$_SESSION['matieres'])) ) ) )  {
-  debut($mysqli,($rep['mat']) ? "Documents à télécharger - ${rep['mat']}" : 'Documents à télécharger','Vous n\'avez pas accès à cette page.',$autorisation,($rep['mat']) ? "docs?${rep['cle']}" : 'docs');
-  $mysqli->close();
-  fin();
-}
-// Connexion nécessaire si protection
-if ( $rep['protection'] && !$autorisation )  {
-  $titre = ($rep['mat']) ? "Documents à télécharger - ${rep['mat']}" : 'Documents à télécharger';
-  $actuel = ($rep['mat']) ? "docs?${rep['cle']}" : 'docs';
-  include('login.php');
-}
-// Mode édition pour les professeurs sur leurs propres matières uniquement
-$edition = ( ( $autorisation == 4 ) && in_array($rep['mid'],explode(',',$_SESSION['matieres'])) );
+$edition = acces($rep['protection'],$rep['mid'],($rep['mat']) ? "Documents à télécharger - ${rep['mat']}" : 'Documents à télécharger',($rep['mat']) ? "docs?${rep['cle']}" : 'docs',$mysqli);
 
 ////////////
 /// HTML ///
 ////////////
-debut($mysqli,($rep['mat']) ? "Documents à télécharger - ${rep['mat']}" : 'Documents à télécharger',$message,$autorisation,($rep['mat']) ? "docs?${rep['cle']}" : 'docs');
+if ( $edition && $rep['protection'] )
+  $icone = ( $rep['protection'] == 32 ) ? '<span class="icon-locktotal"></span>' : '<span class="icon-lock"></span>';
+else  $icone = '';
+debut($mysqli,($rep['mat']) ? "Documents à télécharger - ${rep['mat']}$icone" : 'Documents à télécharger',$message,$autorisation,($rep['mat']) ? "docs?${rep['cle']}" : 'docs');
 
-// Répertoires parents
+// Répertoires parents (pas de vérification de protection a priori)
 $resultat = $mysqli->query("SELECT GROUP_CONCAT(CONCAT('<a href=\"docs?rep=',id,'\">',nom,'</a>') SEPARATOR '&nbsp;/&nbsp;')
                             FROM reps WHERE FIND_IN_SET(id,'${rep['parents']},$rid')");
 $r = $resultat->fetch_row();
@@ -130,55 +114,74 @@
 FIN;
 
   // Affichage du répertoire et de son contenu
-  if ( $rep['nbv'] )  {
-
-    // Sous-répertoires
-    $resultat = $mysqli->query("SELECT id, nom, nbrep_v AS nbrep, nbdoc_v AS nbdoc, IF(protection>$autorisation,1,0) AS protection FROM reps WHERE parent = $rid AND protection < 5");
-    if ( $resultat->num_rows )  {
-      while ( $r = $resultat->fetch_assoc() )
-        // Affichage du contenu : pas de lien si connecté et protection trop élevée
-        if ( $autorisation && $r['protection'] )
-          echo "\n  <p class=\"rep\"><span class=\"icon-rep\"></span><span class=\"icon-lock\"></span><span class=\"nom\">${r['nom']}</span></p>\n";
-        // Lien sinon, avec cadenas si non connecté mais protection
-        else  {
-          $protection = ( $r['protection'] ) ? '<span class="icon-lock"></span>' : '';
-          $contenu = ( $r['nbrep']+$r['nbdoc'] ) ? str_replace(array('0 répertoire,',', 0 document'),'',"(${r['nbrep']} répertoire".( ( $r['nbrep'] > 1 ) ? 's' : '' ).", ${r['nbdoc']} document".( ( $r['nbdoc'] > 1 ) ? 's' : '' ).'&nbsp;)')
-                                                 : '(vide)';
-          echo "\n  <p class=\"rep\"><span class=\"repcontenu\">$contenu</span> <a href=\"?rep=${r['id']}\"><span class=\"icon-rep\"></span>$protection<span class=\"nom\">${r['nom']}</span></a></p>\n";
-        }
-      $resultat->free();
-    }
+  // Sous-répertoires
+  $resultat = $mysqli->query('SELECT id, nom, IF('.requete_protection($autorisation).",0,1) AS protection
+                              FROM reps WHERE parent = $rid AND protection != 32 $ordrerep");
+  if ( $nr = $resultat->num_rows )  {
+    while ( $r = $resultat->fetch_assoc() )
+      // Si protégé, pas de détails et lien que si utilisateur non connecté
+      if ( $r['protection'] == 1 )  {
+        if ( $autorisation )
+          echo "\n  <p class=\"rep\"><span class=\"icon-rep\"></span><span class=\"icon-minilock\"></span><span class=\"nom\">${r['nom']}</span></p>\n";
+        else
+          echo "\n  <p class=\"rep\"><a href=\"?rep=${r['id']}\"><span class=\"icon-rep\"></span><span class=\"icon-minilock\"></span><span class=\"nom\">${r['nom']}</span></a></p>\n";
+      }
+      else  {
+        $resultatbis = $mysqli->query("SELECT id FROM reps WHERE parent = ${r['id']} AND protection != 32");
+        $contenu = ( ($n = $resultatbis->num_rows) > 0 ) ? "$n répertoire".( ($n>1) ? 's' : '' ) : '';
+        $resultatbis->free();
+        $resultatbis = $mysqli->query("SELECT id FROM docs WHERE parent = ${r['id']} AND protection != 32");
+        $contenu .= ( ($n = $resultatbis->num_rows) > 0 ) ? ", $n document".( ($n>1) ? 's' : '' ) : '';
+        $resultatbis->free();
+        if ( strlen($contenu) == 0 )
+          $contenu = 'vide';
+        elseif ( $contenu[0] == ',' )
+          $contenu = substr($contenu,2);
+        echo "\n  <p class=\"rep\"><span class=\"repcontenu\">($contenu)</span> <a href=\"?rep=${r['id']}\"><span class=\"icon-rep\"></span><span class=\"nom\">${r['nom']}</span></a></p>\n";
+      }
+    $resultat->free();
+  }
 
-    // Documents
-    $resultat = $mysqli->query("SELECT id, nom, taille, DATE_FORMAT(upload,'%d/%m/%Y') AS upload, LOWER(ext) AS ext, IF(protection>$autorisation,1,0) AS protection
-                                FROM docs WHERE parent = $rid AND protection < 5 $ordre");
-    if ( $resultat->num_rows )  {
-      while ( $r = $resultat->fetch_assoc() )  {
-        $icone = isset($icones[$ext = substr($r['ext'],1)]) ? $icones[$ext] : '';
-        if ( $autorisation && $r['protection'] )
-          echo "\n  <p class=\"doc\"><span class=\"docdonnees\">($ext, ${r['upload']}, ${r['taille']})</span> <span class=\"icon-doc$icone\"></span><span class=\"icon-lock\"></span><span class=\"nom\">${r['nom']}</span></p>\n";
-        else  {
-          $protection = ( $r['protection'] ) ? '<span class="icon-lock"></span>' : '';
-          echo "\n  <p class=\"doc\"><span class=\"docdonnees\">($ext, ${r['upload']}, ${r['taille']})</span> <a href=\"download?id=${r['id']}\"><span class=\"icon-doc$icone\"></span>$protection<span class=\"nom\">${r['nom']}</span></a></p>\n";
-        }
+  // Documents
+  $resultat = $mysqli->query('SELECT id, nom, taille, DATE_FORMAT(upload,\'%d/%m/%Y\') AS upload, LOWER(ext) AS ext, IF('.requete_protection($autorisation).",0,1) AS protection
+                              FROM docs WHERE parent = $rid AND protection != 32 $ordre");
+  if ( $nd = $resultat->num_rows )  {
+    while ( $r = $resultat->fetch_assoc() )  {
+      $icone = isset($icones[$ext = substr($r['ext'],1)]) ? $icones[$ext] : '';
+      // Si protégé, pas de détails et lien que si utilisateur non connecté
+      if ( $r['protection'] == 1 )  {
+        if ( $autorisation )
+          echo "\n  <p class=\"doc\"><span class=\"icon-doc$icone\"></span><span class=\"icon-minilock\"></span><span class=\"nom\">${r['nom']}</span></p>\n";
+        else
+          echo "\n  <p class=\"doc\"><a href=\"download?id=${r['id']}\"><span class=\"icon-doc$icone\"></span><span class=\"icon-minilock\"></span><span class=\"nom\">${r['nom']}</span></a></p>\n";
       }
-      $resultat->free();
+      else
+        echo "\n  <p class=\"doc\"><span class=\"docdonnees\">($ext, ${r['upload']}, ${r['taille']})</span> <a href=\"download?id=${r['id']}\"><span class=\"icon-doc$icone\"></span><span class=\"nom\">${r['nom']}</span></a></p>\n";
     }
+    $resultat->free();
   }
-  else
+  // Répertoire vide
+  if ( $nr+$nd == 0 )
     echo "\n  <h2>Ce répertoire est vide.</h2>\n";
 }
+
 // Affichage professeur éditeur
 else  {
-  $protection = ( $rep['protection'] ) ? "<span class=\"icon-lock${rep['protection']}\"></span>" : '';
+  switch ( $rep['protection'] )  {
+    case 0:  $protection = ''; break;
+    case 32: $protection = '<span class="icon-locktotal"></span>'; break;
+    default: $protection = '<span class="icon-lock"></span>';
+  }
   echo <<< FIN
 
-  <a class="icon-aide general" data-id="page" title="Aide pour les modifications des répertoires et documents"></a>
+  <div id="icones">
+    <a class="icon-aide" title="Aide pour les modifications des répertoires et documents"></a>
+  </div>
 
   <p id="parentsdoc" class="topbarre" data-id="reps|$rid" data-donnees="${rep['parent']}|${rep['menu']}|${rep['protection']}">
-    <a class="icon-edite" title="Modifier ce répertoire"></a>
-    <a class="icon-ajoutedoc" title="Ajouter un document dans ce répertoire"></a>
-    <a class="icon-ajouterep" title="Ajouter un sous-répertoire"></a>
+    <a class="icon-editerep formulaire" title="Modifier ce répertoire"></a>
+    <a class="icon-ajoutedoc formulaire" title="Ajouter un document dans ce répertoire"></a>
+    <a class="icon-ajouterep formulaire" title="Ajouter un sous-répertoire"></a>
     <a class="icon-chronodesc ordre" title="Classer les documents par ordre chronologique inversé"></a>
     <a class="icon-chronoasc ordre" title="Classer les documents par ordre chronologique"></a>
     <a class="icon-alphadesc ordre" title="Classer les documents par ordre alphabétique inversé"></a>
@@ -189,42 +192,56 @@
 FIN;
 
   // Affichage du répertoire et de son contenu
-  if ( $rep['nb'] )  {
-
-    // Sous-répertoires
-    $resultat = $mysqli->query("SELECT id, nom, nbrep, nbdoc, parent, menu, protection FROM reps WHERE parent = $rid");
-    if ( $resultat->num_rows )  {
-      while ( $r = $resultat->fetch_assoc() )  {
-        $protection = ( $r['protection'] ) ? "<span class=\"icon-lock${r['protection']}\"></span>" : '';
-        $contenu = ( $r['nbrep']+$r['nbdoc'] ) ? str_replace(array('0 répertoire,',', 0 document'),'',"(${r['nbrep']} répertoire".( ( $r['nbrep'] > 1 ) ? 's' : '' ).", ${r['nbdoc']} document".( ( $r['nbdoc'] > 1 ) ? 's' : '' ).'&nbsp;)')
-                                               : '(vide)';
-        // data-donnees : parent|menu|protection
-        echo <<<FIN
+  // Sous-répertoires
+  $resultat = $mysqli->query("SELECT id, nom, parent, menu, protection FROM reps WHERE parent = $rid $ordrerep");
+  if ( $nr = $resultat->num_rows )  {
+    while ( $r = $resultat->fetch_assoc() )  {
+      switch ( $r['protection'] )  {
+        case 0:  $protection = ''; break;
+        case 32: $protection = '<span class="icon-locktotal"></span>'; break;
+        default: $protection = '<span class="icon-minilock"></span>';
+      }
+      $resultatbis = $mysqli->query("SELECT id FROM reps WHERE parent = ${r['id']}");
+      $contenu = ( ($n = $resultatbis->num_rows) > 0 ) ? "$n répertoire".( ($n>1) ? 's' : '' ) : '';
+      $resultatbis->free();
+      $resultatbis = $mysqli->query("SELECT id FROM docs WHERE parent = ${r['id']}");
+      $contenu .= ( ($n = $resultatbis->num_rows) > 0 ) ? ", $n document".( ($n>1) ? 's' : '' ) : '';
+      $resultatbis->free();
+      if ( strlen($contenu) == 0 )
+        $contenu = 'vide';
+      elseif ( $contenu[0] == ',' )
+        $contenu = substr($contenu,2);
+      // data-donnees : parent|menu|protection
+      echo <<<FIN
 
   <p class="rep" data-id="reps|${r['id']}" data-donnees="${r['parent']}|${r['menu']}|${r['protection']}">
-    <a class="icon-edite" title="Modifier ce répertoire"></a>
-    <a class="icon-ajoutedoc" title="Ajouter un document dans ce répertoire"></a>
+    <a class="icon-editerep formulaire" title="Modifier ce répertoire"></a>
+    <a class="icon-ajoutedoc formulaire" title="Ajouter un document dans ce répertoire"></a>
     <a class="icon-supprime" title="Supprimer ce répertoire et son contenu"></a>
-    <span class="repcontenu">$contenu</span> 
+    <span class="repcontenu">($contenu)</span> 
     <a href="?rep=${r['id']}"><span class="icon-rep"></span></a>$protection<span class="nom editable" data-id="reps|nom|${r['id']}">${r['nom']}</span>
   </p>
 
 FIN;
-      }
-      $resultat->free();
     }
+    $resultat->free();
+  }
 
-    // Documents
-    $resultat = $mysqli->query("SELECT id, nom, taille, DATE_FORMAT(upload,'%d/%m/%Y') AS upload, LOWER(ext) AS ext, protection
-                                FROM docs WHERE parent = $rid $ordre");
-    if ( $resultat->num_rows )  {
-      while ( $r = $resultat->fetch_assoc() )  {
-        $icone = isset($icones[$ext = substr($r['ext'],1)]) ? $icones[$ext] : '';
-        $protection = ( $r['protection'] ) ? "<span class=\"icon-lock${r['protection']}\"></span>" : '';
-        echo <<<FIN
+  // Documents
+  $resultat = $mysqli->query("SELECT id, nom, taille, DATE_FORMAT(upload,'%d/%m/%Y') AS upload, LOWER(ext) AS ext, protection
+                              FROM docs WHERE parent = $rid $ordre");
+  if ( $nd = $resultat->num_rows )  {
+    while ( $r = $resultat->fetch_assoc() )  {
+      $icone = isset($icones[$ext = substr($r['ext'],1)]) ? $icones[$ext] : '';
+      switch ( $r['protection'] )  {
+        case 0:  $protection = ''; break;
+        case 32: $protection = '<span class="icon-locktotal"></span>'; break;
+        default: $protection = '<span class="icon-minilock"></span>';
+      }
+      echo <<<FIN
 
   <p class="doc" data-id="docs|${r['id']}" data-protection="${r['protection']}">
-    <a class="icon-edite" title="Modifier ce document"></a>
+    <a class="icon-editedoc formulaire" title="Modifier ce document"></a>
     <a class="icon-download" href="download?id=${r['id']}&amp;dl" title="Télécharger ce document"></a>
     <a class="icon-supprime" title="Supprimer ce document"></a>
     <span class="docdonnees">($ext, ${r['upload']}, ${r['taille']})</span>
@@ -232,11 +249,11 @@
   </p>
 
 FIN;
-      }
-      $resultat->free();
     }
+    $resultat->free();
   }
-  else
+  // Répertoire vide
+  if ( $nr+$nd == 0 )
     echo "\n  <h2>Ce répertoire est vide.</h2>\n";
 
   // Select sur les répertoires (pour les déplacements)
@@ -264,204 +281,180 @@
     $taille = substr($taille,0,-1)*1024;
   $taille = ( $taille < 1048576 ) ? intval($taille/1024).'&nbsp;ko' : intval($taille/1048576).'&nbsp;Mo';
 
+  // Options du select multiple d'accès
+  $select_protection = '
+          <option value="0">Accès public</option>
+          <option value="6">Utilisateurs identifiés</option>
+          <option value="1">Invités</option>
+          <option value="2">Élèves</option>
+          <option value="3">Colleurs</option>
+          <option value="4">Administratifs</option>
+          <option value="5">Professeurs</option>
+          <option value="32">Répertoire invisible</option>';
+  // Protection des nouveaux répertoires = protection globale du répertoire
+  $p = $rep['protection'];
+  if ( ( $p == 0 ) || ( $p == 32 ) )
+    $sel_protection = str_replace("\"$p\"","\"$p\" selected",$select_protection);
+  else  {
+    $sel_protection = str_replace('"6"','"6" selected',$select_protection);
+    for ( $a=1; $a<6; $a++ )
+      if ( ( ($p-1)>>($a-1) & 1 ) == 0 )
+        $sel_protection = str_replace("\"$a\"","\"$a\" selected",$sel_protection);
+  }
   // Aide et formulaire d'ajout
 ?>
 
-  <form id="form-repertoire">
+  <form id="form-editerep" data-action="reps">
     <h3 class="edition">Modifier le répertoire <em></em></h3>
-    <p class="ligne"><label for="nom">Nom&nbsp;: </label><input type="text" id="nom" name="nom" value="" size="50"></p>
+    <p class="ligne"><label for="nom">Nom&nbsp;: </label><input type="text"name="nom" value="" size="50"></p>
     <p class="ligne"><label for="parent">Déplacer&nbsp;: </label>
-      <select id="parent" name="parent">
+      <select name="parent">
         <option value="0">Ne pas déplacer</option><?php echo $select_reps; ?>
       </select>
     </p>
-    <p class="ligne"><label for="menu">Affichage du répertoire dans le menu&nbsp;: </label><input type="checkbox" id="menu" name="menu" value="1"></p>
+    <p class="ligne"><label for="menu">Affichage du répertoire dans le menu&nbsp;: </label><input type="checkbox" name="menu" value="1"></p>
     <p class="ligne"><label for="protection">Accès&nbsp;: </label>
-      <select id="protection" name="protection">
-        <option value="0">Visible de tous</option>
-        <option value="1">Visible pour les connectés</option>
-        <option value="2">Visible pour les élèves, colleurs, profs</option>
-        <option value="3">Visible pour les colleurs et les profs</option>
-        <option value="4">Visible pour les profs uniquement</option>
-        <option value="5">Non visible</option>
+      <select name="protection[]" multiple><?php echo $select_protection; ?>
       </select>
     </p>
-    <p class="ligne"><label for="propagation">Propager le choix ci-dessus à chaque document/sous-répertoire&nbsp;: </label><input type="checkbox" id="propagation" name="propagation" value="1"></p>
-    <input type="hidden" name="id" value="">
-    <input type="hidden" name="table" value="reps">
+    <input type="button" class="ligne" value="Propager le choix d'accès à chaque document/sous-répertoire">
+    <input type="button" class="ligne" value="Vider ce répertoire">
   </form>
 
-  <form id="form-ajouterep">
+  <form id="form-ajouterep" data-action="ajout-rep">
     <h3 class="edition">Ajouter un répertoire</h3>
-    <p class="ligne"><label for="nom">Nom&nbsp;: </label><input type="text" id="nom" name="nom" value="" size="50"></p>
-    <p class="ligne"><label for="menu">Affichage du répertoire dans le menu&nbsp;: </label><input type="checkbox" id="menu" name="menu" value="1"></p>
+    <p class="ligne"><label for="nom">Nom&nbsp;: </label><input type="text" name="nom" value="" size="50"></p>
+    <p class="ligne"><label for="menu">Affichage du répertoire dans le menu&nbsp;: </label><input type="checkbox" name="menu" value="1"></p>
     <p class="ligne"><label for="protection">Accès&nbsp;: </label>
-      <select id="protection" name="protection">
-        <option value="0">Visible de tous</option>
-        <option value="1">Visible pour les connectés</option>
-        <option value="2">Visible pour les élèves, colleurs, profs</option>
-        <option value="3">Visible pour les colleurs et les profs</option>
-        <option value="4">Visible pour les profs uniquement</option>
-        <option value="5">Non visible</option>
+      <select name="protection[]" multiple><?php echo $sel_protection; ?>
       </select>
     </p>
-    <input type="hidden" name="parent" value="<?php echo $rid; ?>">
-    <input type="hidden" name="table" value="reps">
   </form>
 
-  <form id="form-document">
+  <form id="form-editedoc" data-action="docs">
     <h3 class="edition">Modifier le document <em></em></h3>
-    <p class="ligne"><label for="nom">Nom&nbsp;: </label><input type="text" id="nom" name="nom" value="" size="50"></p>
-    <p class="ligne"><label for="fichier">Mettre à jour&nbsp;: </label><input type="file" id="fichier" name="fichier"></p>
+    <p class="ligne"><label for="nom">Nom&nbsp;: </label><input type="text" name="nom" value="" size="50"></p>
+    <p class="ligne"><label for="fichier">Mettre à jour&nbsp;: </label><input type="file" name="fichier"></p>
     <p class="ligne"><label for="parent">Déplacer&nbsp;: </label>
-      <select id="parent" name="parent">
+      <select name="parent">
         <option value="0">Ne pas déplacer</option><?php echo str_replace("\"$rid\"","\"$rid\" disabled",$select_reps); ?>
       </select>
     </p>
     <p class="ligne"><label for="protection">Accès&nbsp;: </label>
-      <select id="protection" name="protection">
-        <option value="0">Visible de tous</option>
-        <option value="1">Visible pour les connectés</option>
-        <option value="2">Visible pour les élèves, colleurs, profs</option>
-        <option value="3">Visible pour les colleurs et les profs</option>
-        <option value="4">Visible pour les profs uniquement</option>
-        <option value="5">Non visible</option>
+      <select name="protection[]" multiple><?php echo str_replace('Répertoire','Document',$select_protection); ?>
       </select>
     </p>
-    <input type="hidden" name="id" value="">
-    <input type="hidden" name="table" value="docs">
   </form>
 
-  <form id="form-ajoutedoc">
+  <form id="form-ajoutedoc" data-action="ajout-doc">
     <h3 class="edition">Ajouter un document</h3>
     <p>Ce document sera envoyé dans le répertoire <em></em>.</p>
-    <p class="ligne"><label for="fichier">Fichier&nbsp;: </label><input type="file" id="fichier" name="fichier"></p>
-    <p class="ligne"><label for="nom">Nom à afficher&nbsp;: </label><input type="text" id="nom" name="nom" value="" size="50"></p>
+    <p class="ligne"><label for="fichier">Fichier&nbsp;: </label><input type="file" name="fichier"></p>
+    <p class="ligne"><label for="nom">Nom à afficher&nbsp;: </label><input type="text" name="nom" value="" size="50"></p>
     <p class="ligne"><label for="protection">Accès&nbsp;: </label>
-      <select id="protection" name="protection">
-        <option value="0">Visible de tous</option>
-        <option value="1">Visible pour les connectés</option>
-        <option value="2">Visible pour les élèves, colleurs, profs</option>
-        <option value="3">Visible pour les colleurs et les profs</option>
-        <option value="4">Visible pour les profs uniquement</option>
-        <option value="5">Non visible</option>
+      <select name="protection[]" multiple><?php echo str_replace('Répertoire','Document',$select_protection); ?>
       </select>
     </p>
-    <input type="hidden" name="parent" value="">
-    <input type="hidden" name="table" value="docs">
   </form>
 
   <div id="aide-page">
     <h3>Aide et explications</h3>
-    <p>Il est possible ici de modifier le contenu des répertoires et les propriétés des répertoires et des documents présents. Vous n'avez la possibilité de modifier que les documents &laquo;&nbsp;toutes matières&nbsp;&raquo; et les documents des matières qui vous sont associées. Pour modifier les matières qui vous sont associées, il faut aller sur la page de <a href="matieres">gestion des matières</a>.</p>
+    <p>Il est possible ici de modifier le contenu des répertoires et les propriétés des répertoires et des documents présents. Vous avez la possibilité de modifier les documents &laquo;&nbsp;toutes matières&nbsp;&raquo; ainsi que dans les matières qui vous sont associées. Le réglage de ces matières s'effectue à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</p>
     <p>Les noms des répertoires et documents contenus dans le répertoire affiché sur cette page sont modifiables directement, en cliquant sur le bouton <span class="icon-edite"></span> situé dans la case encadrée de pointillés.</p>
-    <p>Comme sur la partie visible par les visiteurs non connectés en tant que professeurs, les répertoires sont indiqués par l'icône <span class="icon-rep"></span> (cliquer dessus affiche le répertoire correspondant), les documents par l'icône correspondant à leur type (<span class="icon-doc-pdf"></span> pour les <code>pdf</code>, <span class="icon-doc-doc"></span> pour les textes <code>doc</code> ou <code>odt</code>...). Le contenu des répertoires et les principales propriétés des documents sont indiqués à droite.</p>
+    <p>Les répertoires sont indiqués par l'icône <span class="icon-rep"></span>, cliquer dessus affiche le répertoire correspondant. Les documents sont indiqués par l'icône correspondant à leur type (<span class="icon-doc-pdf"></span> pour les <code>pdf</code>, <span class="icon-doc-doc"></span> pour les textes <code>doc</code> ou <code>odt</code>...). Le contenu des répertoires et les principales propriétés des documents sont indiqués à droite.</p>
     <p>La taille des fichiers envoyés est limitée à <?php echo $taille; ?>.</p>
     <h4>Actions possibles</h4>
     <p>Les différentes actions possibles sont&nbsp;:</p>
     <ul>
       <li><span class="icon-ajouterep"></span>&nbsp;: ajouter un répertoire à l'intérieur du répertoire affiché sur cette page. Une aide sera disponible sur le formulaire qui s'affichera.</li>
       <li><span class="icon-ajoutedoc"></span>&nbsp;: ajouter un document à l'intérieur du répertoire choisi. Une aide sera disponible sur le formulaire qui s'affichera.</li>
-      <li><span class="icon-edite"></span>&nbsp;: modifier les propriétés du répertoire ou du document choisi. Il est notamment possible de modifier individuellement l'<em>accès</em> à chaque répertoire/document (explications ci-dessous). Une aide sera disponible sur le formulaire qui s'affichera.</li>
+      <li><span class="icon-edite"></span>&nbsp;: modifier les propriétés du répertoire ou du document choisi. Il est notamment possible de modifier individuellement l'accès à chaque répertoire/document (explications ci-dessous) ou de vider un répertoire. Une aide sera disponible sur le formulaire qui s'affichera.</li>
       <li><span class="icon-supprime"></span>&nbsp;: supprimer le répertoire ou document choisi (une confirmation sera demandée). Supprimer un répertoire supprime automatiquement tout son contenu (sous-répertoires et documents).</li>
       <li><span class="icon-download"></span>&nbsp;: télécharger un document pour le voir.</li>
     </ul>
     <p>Les liens vers les répertoires et les documents sont garantis&nbsp;: aucune modification (changement de nom, mise à jour de document, déplacement...) réalisée sur les répertoires ou les documents ne peut modifier ces liens. Si vous souhaitez mettre à jour un document, surtout ne le supprimez pas pour le recréer&nbsp;: cela changerait le lien, les liens existants ne seraient plus valables. Modifiez plutôt le document.</p>
-    <h4>Accès</h4>
-    <p>L'<em>accès</em> à chaque répertoire et chaque document peut être choisi parmi six possibilités. Le choix réalisé pour les répertoires/documents existants est directement visible sur cette page (uniquement lorsque vous êtes connecté en tant que professeur) avec un cadenas portant un numéro. Les choix possibles sont&nbsp;:</p>
+    <h4>Gestion de l'accès</h4>
+    <p>L'accès à chaque répertoire et chaque document peut être protégé. Trois choix sont possibles&nbsp;:</p>
     <ul>
-      <li><em>Visible de tous</em> (pas de marquage)&nbsp;: accessible de tout visiteur, sans identification.</li>
-      <li><em>Visible pour les connectés</em> (<span class="icon-lock1"></span>)&nbsp;: accessible de tout utilisateur mais uniquement après identification (utilisateurs de type invité, élève, colleur ou professeur). Pour les répertoires/documents associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les élèves, colleurs, professeurs</em> (<span class="icon-lock2"></span>)&nbsp;: accessible uniquement par les utilisateurs de type élève, colleur ou professeur. Pour les répertoires/documents associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les colleurs, professeurs</em> (<span class="icon-lock3"></span>)&nbsp;: accessible uniquement par les utilisateurs de type colleur ou professeur. Pour les répertoires/documents associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les professeurs</em> (<span class="icon-lock4"></span>)&nbsp;: accessible uniquement par les utilisateurs de type professeur. Pour les répertoires/documents associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Non visible</em> (<span class="icon-lock5"></span>)&nbsp;: entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont le répertoire peut dépendre). La différence avec le choix précédent est que le répertoire/document n'apparaît plus du tout sur le site pour ceux qui ne le voient pas.</li>
+      <li><em>Accès public</em>&nbsp;: ressource accessible de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: ressource accessible uniquement par les utilisateurs identifiés, en fonction de leur type de compte. Un cadenas &nbsp;<span class="icon-minilock">&nbsp;</span> est alors affiché à côté de l'icône de la ressource. Pour les répertoires/documents associés à une matière, seuls les utilisateurs associés à cette matière peuvent y accéder. Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Répertoire/Document invisible</em>&nbsp;: ressource entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont la ressource peut dépendre). Un cadenas <span class="icon-locktotal"></span> est alors affiché à côté de l'icône de la ressource.</li>
     </ul>
-    <p>Les répertoires/documents protégés (sauf <em>Non visible</em>) apparaissent même s'ils ne sont pas accessibles, marqués d'un petit cadenas. Le contenu des répertoires tient compte de cela&nbsp;: les répertoires/documents non visibles ne sont pas décomptés.</p>
     <h4>Liens dans le menu</h4>
     <p>Le lien dans le menu vers le répertoire racine de chaque matière est généré automatiquement. Il ne s'affiche que si ce répertoire est non vide&nbsp;:</p>
     <ul>
-      <li>pour les utilisateurs non connectés, si des documents éventuellement protégés sont présents (les répertoires et documents protégés apparaissent avec un petit cadenas).</li>
-      <li>pour les utilisateurs connectés, si des documents sont présents et si le répertoire racine est accessible (<em>accès</em> pas trop élevé).</li>
+      <li>pour les visiteurs non identifiés, si des documents éventuellement protégés sont présents (les répertoires et documents protégés apparaissent avec un cadenas &nbsp;<span class="icon-minilock">&nbsp;</span>).</li>
+      <li>pour les utilisateurs identifiés, si des documents sont présents et si le répertoire racine est accessible.</li>
     </ul>
     <p>Il est possible de rajouter des liens dans le menu vers des sous-répertoires. C'est une des propriétés que vous pouvez modifier pour chaque répertoire en cliquant sur le bouton <span class="icon-edite"></span> correspondant.</p>
   </div>
 
-  <div id="aide-repertoire">
+  <div id="aide-editerep">
     <h3>Aide et explications</h3>
-    <p>Ce formulaire permet de modifier les propriétés du répertoire choisi. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
+    <p>Ce formulaire permet de modifier les propriétés du répertoire concerné. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
     <p>Les répertoires racines des matières sont très peu modifiables&nbsp;: seul l'<em>accès</em> peut être modifié. Pour les autres répertoires, il est possible de les renommer, de les déplacer à l'intérieur d'un autre répertoire ou d'afficher un lien supplémentaire dans le menu.</p>
     <p>Le <em>nom</em> du répertoire peut comporter des espaces, des accents... Vous pouvez donc l'écrire en français&nbsp;! Ce nom est aussi modifiable directement en cliquant sur le bouton <span class="icon-edite"></span> dans la case entourée de pointillés.</p>
-    <p>Vous pouvez <em>déplacer</em> le répertoire dans un autre répertoire, qui peut éventuellement appartenir à une autre matière si elle vous est associée. La liste des répertoires où le déplacement est possible est dans le menu déroulant. L'ensemble du contenu du répertoire déplacé est bien sûr automatiquement déplacé.</p>
-    <p>La case à cocher <em>Affichage du répertoire dans le menu</em> permet d'afficher un lien direct dans le menu vers la page correspondant au répertoire. Ce lien sera situé en-dessous du lien <em>Documents à télécharger</em> qui permet d'arriver sur cette page. Sa visibilité dépend du choix de l'<em>accès</em> explicité ci-dessous.</p>
-    <p>L'<em>accès</em> à ce répertoire peut être choisi parmi six possibilités, visibles directement lorsque vous êtes connecté en tant que professeur&nbsp;:</p>
+    <p>Vous pouvez <em>déplacer</em> le répertoire dans un autre répertoire, qui peut éventuellement appartenir à une autre matière si elle vous est associée. Le menu déroulant contient la liste des répertoires où le déplacement est possible. L'ensemble du contenu du répertoire déplacé est bien sûr automatiquement déplacé. L'accès n'est pas modifié.</p>
+    <p>La case à cocher <em>Affichage du répertoire dans le menu</em> permet d'afficher un lien direct dans le menu vers la page correspondant au répertoire. Ce lien sera situé en-dessous du lien <em>Documents à télécharger</em> qui permet d'arriver sur cette page. Sa visibilité pour les utilisateurs dépend du choix de l'<em>accès</em> explicité ci-dessous.</p>
+    <h4>Boutons d'action</h4>
+    <p>Le bouton <em>Propager le choix d'accès à chaque document/sous-répertoire</em> permet de copier l'accès du répertoire à l'ensemble de son contenu. Attention&nbsp;: si vous venez de modifier la valeur dans la case <em>accès</em>, vous devez valider votre modification avant de <em>propager</em> le réglage.</p>
+    <p>Le bouton <em>Vider ce répertoire</em> permet de supprimer l'ensemble du contenu du répertoire&nbsp;: sous-répertoires et documents. Une confirmation est demandée.</p>
+    <h4>Gestion de l'accès</h4>
+    <p>L'accès à ce répertoire peut être protégé. Trois choix sont possibles&nbsp;:</p>
     <ul>
-      <li><em>Visible de tous</em> (pas de marquage)&nbsp;: accessible de tout visiteur, sans identification.</li>
-      <li><em>Visible pour les connectés</em> (<span class="icon-lock1"></span>)&nbsp;: accessible de tout utilisateur mais uniquement après identification (utilisateurs de type invité, élève, colleur ou professeur). Pour les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les élèves, colleurs, professeurs</em> (<span class="icon-lock2"></span>)&nbsp;: accessible uniquement par les utilisateurs de type élève, colleur ou professeur. Pour les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les colleurs, professeurs</em> (<span class="icon-lock3"></span>)&nbsp;: accessible uniquement par les utilisateurs de type colleur ou professeur. Pour les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les professeurs</em> (<span class="icon-lock4"></span>)&nbsp;: accessible uniquement par les utilisateurs de type professeur. Pour les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Non visible</em> (<span class="icon-lock5"></span>)&nbsp;: entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont le répertoire peut dépendre). La différence avec le choix précédent est que le répertoire n'apparaît plus du tout sur le site pour ceux qui ne le voient pas.</li>
+      <li><em>Accès public</em>&nbsp;: répertoire et son contenu accessibles de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: répertoire et son contenu accessibles uniquement par les utilisateurs identifiés, en fonction de leur type de compte. Un cadenas &nbsp;<span class="icon-minilock">&nbsp;</span> est alors affiché à côté de l'icône du répertoire. Pour les répertoires associés à une matière, seuls les utilisateurs associés à cette matière peuvent y accéder. Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Répertoire invisible</em>&nbsp;: répertoire entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont le répertoire peut dépendre). Un cadenas <span class="icon-locktotal"></span> est alors affiché à côté de l'icône du répertoire.</li>
     </ul>
-    <p>Hormis le dernier choix, le répertoire apparaît pour tout visiteur (identifié ou non), éventuellement avec un petit cadenas gris indiquant qu'il est protégé. Avec le choix <em>Non visible</em>, le répertoire n'est plus du tout visible sans être connecté en tant que professeur.</p>
-    <p>La case à cocher <em>Propager le choix ci-dessus à chaque document/sous-répertoire</em> permet de faire suivre le choix précédent à l'ensemble du contenu du répertoire. Si elle est décochée, aucune modification supplémentaire n'a lieu. Si elle est cochée, une action sera effectuée même si le choix d'<em>accès</em> n'a pas été modifié.</p>
-    <p>Les comptes utilisateurs sont à définir ou modifier sur la page de <a href="utilisateurs">gestion des utilisateurs</a>. Les associations avec les matières sont modifiables sur la page de <a href="matieres">gestion des matières</a>.</p>
+    <p>À moins d'être <em>invisible</em>, le répertoire apparaît pour tout visiteur non identifié comme utilisateur identifié, éventuellement avec un cadenas &nbsp;<span class="icon-minilock">&nbsp;</span> indiquant qu'il est protégé. Un répertoire <em>invisible</em> n'est plus du tout visible sans être connecté en tant que professeur, éventuellement de la matière concernée.</p>
+    <p>Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</p>
   </div>
 
   <div id="aide-ajouterep">
     <h3>Aide et explications</h3>
-    <p>Ce formulaire permet de créer un nouveau répertoire au sein du répertoire dont le contenu est affiché sur cette page. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
+    <p>Ce formulaire permet de créer un nouveau répertoire au sein du répertoire <em><?php echo $rep['nom']; ?></em>. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
     <p>Le <em>nom</em> du répertoire peut comporter des espaces, des accents... Vous pouvez donc l'écrire en français&nbsp;!</p>
     <p>La case à cocher <em>Affichage du répertoire dans le menu</em> permet d'afficher un lien direct dans le menu vers la page correspondant au répertoire. Ce lien sera situé en-dessous du lien <em>Documents à télécharger</em> qui permet d'arriver sur cette page. Sa visibilité dépend du choix de l'<em>accès</em> explicité ci-dessous.</p>
-    <p>L'<em>accès</em> à ce nouveau répertoire peut être choisi parmi six possibilités&nbsp;:</p>
+    <h4>Gestion de l'accès</h4>
+    <p>L'accès à ce répertoire peut être protégé. Trois choix sont possibles&nbsp;:</p>
     <ul>
-      <li><em>Visible de tous</em>&nbsp;: accessible de tout visiteur, sans identification.</li>
-      <li><em>Visible pour les connectés</em>&nbsp;: accessible de tout utilisateur mais uniquement après identification (utilisateurs de type invité, élève, colleur ou professeur). Pour les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les élèves, colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type élève, colleur ou professeur. Pour les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type colleur ou professeur. Pour les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type professeur. Pour les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Non visible</em>&nbsp;: entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont le répertoire peut dépendre). La différence avec le choix précédent est que le répertoire n'apparaît plus du tout sur le site pour ceux qui ne le voient pas.</li>
+      <li><em>Accès public</em>&nbsp;: répertoire et son contenu accessibles de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: répertoire et son contenu accessibles uniquement par les utilisateurs identifiés, en fonction de leur type de compte. Un cadenas &nbsp;<span class="icon-minilock">&nbsp;</span> est alors affiché à côté de l'icône du répertoire. Pour les répertoires associés à une matière, seuls les utilisateurs associés à cette matière peuvent y accéder. Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Répertoire invisible</em>&nbsp;: répertoire entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont le répertoire peut dépendre). Un cadenas <span class="icon-locktotal"></span> est alors affiché à côté de l'icône du répertoire.</li>
     </ul>
-    <p>Hormis le dernier choix, le répertoire apparaîtra pour tout visiteur (identifié ou non), éventuellement avec un petit cadenas gris indiquant qu'il est protégé. Avec le choix <em>Non visible</em>, le répertoire n'est plus du tout visible sans être connecté en tant que professeur.</p>
-    <p>Les comptes utilisateurs sont à définir ou modifier sur la page de <a href="utilisateurs">gestion des utilisateurs</a>. Les associations avec les matières sont modifiables sur la page de <a href="matieres">gestion des matières</a>.</p>
+    <p>À moins d'être <em>invisible</em>, le répertoire apparaît pour tout visiteur non identifié comme utilisateur identifié, éventuellement avec un cadenas &nbsp;<span class="icon-minilock">&nbsp;</span> indiquant qu'il est protégé. Un répertoire <em>invisible</em> n'est plus du tout visible sans être connecté en tant que professeur, éventuellement de la matière concernée.</p>
   </div>
   
-  <div id="aide-document">
+  <div id="aide-editedoc">
     <h3>Aide et explications</h3>
-    <p>Ce formulaire permet de modifier les propriétés du document choisi. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
-    <p>Le <em>nom</em> du document peut comporter des espaces, des accents... Vous pouvez donc l'écrire en français&nbsp;! Ce nom est aussi modifiable directement en cliquant sur le bouton <span class="icon-edite"></span> dans la case entourée de pointillés. Il ne doit pas comporter l'extension, qui est ajoutée lorsque l'on le télécharge.</p>
+    <p>Ce formulaire permet de modifier les propriétés du document concerné. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
+    <p>Le <em>nom</em> du document peut comporter des espaces, des accents... Vous pouvez donc l'écrire en français&nbsp;! Ce nom est aussi modifiable directement en cliquant sur le bouton <span class="icon-edite"></span> dans la case entourée de pointillés. Il ne doit pas comporter l'extension, qui est ajoutée lorsqu'on le télécharge.</p>
     <p>Vous pouvez <em>mettre à jour</em> le document en envoyant un nouveau fichier. C'est bien mieux que de supprimer/recréer le document, car les liens existants restent valables. Une fois le formulaire validé, le document apparaîtra à nouveau tout en haut des informations récentes, comme s'il venait d'être envoyé. La date et la taille du document seront modifiées.</p>
     <p>La taille maximale du fichier envoyé est <?php echo $taille; ?>. Le fichier envoyé doit être de même extension que le fichier originel.</p>
     <p>Vous pouvez <em>déplacer</em> le document dans un autre répertoire, qui peut éventuellement appartenir à une autre matière si elle vous est associée. La liste des répertoires où le déplacement est possible est dans le menu déroulant.</p>
-    <p>L'<em>accès</em> à ce document peut être choisi parmi six possibilités, visibles directement lorsque vous êtes connecté en tant que professeur&nbsp;:</p>
+    <h4>Gestion de l'accès</h4>
+    <p>L'accès à ce document peut être protégé. Trois choix sont possibles&nbsp;:</p>
     <ul>
-      <li><em>Visible de tous</em> (pas de marquage)&nbsp;: accessible de tout visiteur, sans identification.</li>
-      <li><em>Visible pour les connectés</em> (<span class="icon-lock1"></span>)&nbsp;: accessible de tout utilisateur mais uniquement après identification (utilisateurs de type invité, élève, colleur ou professeur). Dans les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les élèves, colleurs, professeurs</em> (<span class="icon-lock2"></span>)&nbsp;: accessible uniquement par les utilisateurs de type élève, colleur ou professeur. Dans les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les colleurs, professeurs</em> (<span class="icon-lock3"></span>)&nbsp;: accessible uniquement par les utilisateurs de type colleur ou professeur. Dans les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les professeurs</em> (<span class="icon-lock4"></span>)&nbsp;: accessible uniquement par les utilisateurs de type professeur. Dans les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Non visible</em> (<span class="icon-lock5"></span>)&nbsp;: entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la bonne matière). La différence avec le choix précédent est que le document n'apparaît plus du tout sur le site pour ceux qui ne le voient pas, notamment dans le décompte du répertoire le contenant.</li>
+      <li><em>Accès public</em>&nbsp;: document téléchargeable de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: document téléchargeable uniquement par les utilisateurs identifiés, en fonction de leur type de compte, simplement visible pour les autres. Un cadenas &nbsp;<span class="icon-minilock">&nbsp;</span> est alors affiché à côté de l'icône du document. Pour les documents associés à une matière, seuls les utilisateurs associés à cette matière peuvent y accéder. Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Document invisible</em>&nbsp;: document entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont le document peut dépendre). Un cadenas <span class="icon-locktotal"></span> est alors affiché à côté de l'icône du document.</li>
     </ul>
-    <p>Hormis le dernier choix, le document apparaît pour tout visiteur (identifié ou non), éventuellement avec un petit cadenas gris indiquant qu'il est protégé. Avec le choix <em>Non visible</em>, le document n'est plus du tout visible sans être connecté en tant que professeur.</p>
-    <p>Les comptes utilisateurs sont à définir ou modifier sur la page de <a href="utilisateurs">gestion des utilisateurs</a>. Les associations avec les matières sont modifiables sur la page de <a href="matieres">gestion des matières</a>.</p>
+    <p>À moins d'être <em>invisible</em>, le document est listé pour tout visiteur non identifié comme utilisateur identifié, éventuellement avec un cadenas &nbsp;<span class="icon-minilock">&nbsp;</span> indiquant qu'il est protégé. Un document <em>invisible</em> n'est plus du tout visible sans être connecté en tant que professeur, éventuellement de la matière concernée.</p>
   </div>
 
   <div id="aide-ajoutedoc">
     <h3>Aide et explications</h3>
-    <p>Ce formulaire permet d'ajouter un document dans le répertoire choisi. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
+    <p>Ce formulaire permet d'ajouter un document dans le répertoire concerné. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
     <p>La taille maximale du <em>fichier</em> envoyé est <?php echo $taille; ?>.</p>
     <p>Le <em>nom à afficher</em> du document peut comporter des espaces, des accents... Vous pouvez donc l'écrire en français&nbsp;!</p>
-    <p>L'<em>accès</em> à ce document peut être choisi parmi six possibilités, visibles directement lorsque vous êtes connecté en tant que professeur&nbsp;:</p>
+    <h4>Gestion de l'accès</h4>
+    <p>L'accès à ce document peut être protégé. Trois choix sont possibles&nbsp;:</p>
     <ul>
-      <li><em>Visible de tous</em> (pas de marquage)&nbsp;: accessible de tout visiteur, sans identification.</li>
-      <li><em>Visible pour les connectés</em> (<span class="icon-lock1"></span>)&nbsp;: accessible de tout utilisateur mais uniquement après identification (utilisateurs de type invité, élève, colleur ou professeur). Dans les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les élèves, colleurs, professeurs</em> (<span class="icon-lock2"></span>)&nbsp;: accessible uniquement par les utilisateurs de type élève, colleur ou professeur. Dans les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les colleurs, professeurs</em> (<span class="icon-lock3"></span>)&nbsp;: accessible uniquement par les utilisateurs de type colleur ou professeur. Dans les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les professeurs</em> (<span class="icon-lock4"></span>)&nbsp;: accessible uniquement par les utilisateurs de type professeur. Dans les répertoires associés à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Non visible</em> (<span class="icon-lock5"></span>)&nbsp;: entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la bonne matière). La différence avec le choix précédent est que le document n'apparaît plus du tout sur le site pour ceux qui ne le voient pas, notamment dans le décompte du répertoire le contenant.</li>
+      <li><em>Accès public</em>&nbsp;: document téléchargeable de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: document téléchargeable uniquement par les utilisateurs identifiés, en fonction de leur type de compte, simplement visible pour les autres. Un cadenas &nbsp;<span class="icon-minilock">&nbsp;</span> est alors affiché à côté de l'icône du document. Pour les documents associés à une matière, seuls les utilisateurs associés à cette matière peuvent y accéder. Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Document invisible</em>&nbsp;: document entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont le document peut dépendre). Un cadenas <span class="icon-locktotal"></span> est alors affiché à côté de l'icône du document.</li>
     </ul>
-    <p>Hormis le dernier choix, le document apparaît pour tout visiteur (identifié ou non), éventuellement avec un petit cadenas gris indiquant qu'il est protégé. Avec le choix <em>Non visible</em>, le document n'est plus du tout visible sans être connecté en tant que professeur.</p>
-    <p>Les comptes utilisateurs sont à définir ou modifier sur la page de <a href="utilisateurs">gestion des utilisateurs</a>. Les associations avec les matières sont modifiables sur la page de <a href="matieres">gestion des matières</a>.</p>
+    <p>À moins d'être <em>invisible</em>, le document est listé pour tout visiteur non identifié comme utilisateur identifié, éventuellement avec un cadenas &nbsp;<span class="icon-minilock">&nbsp;</span> indiquant qu'il est protégé. Un document <em>invisible</em> n'est plus du tout visible sans être connecté en tant que professeur, éventuellement de la matière concernée.</p>
   </div>
   
   <p id="log"></p>
diff -urN cahier-de-prepa6.2.0/download.php cahier-de-prepa8.0.1/download.php
--- cahier-de-prepa6.2.0/download.php	2015-09-19 15:24:25.012335512 +0200
+++ cahier-de-prepa8.0.1/download.php	2018-09-02 01:04:54.919209106 +0200
@@ -14,11 +14,11 @@
 if ( !isset($_REQUEST['id']) || !ctype_digit($id = $_REQUEST['id']) )
   exit('Mauvais paramètre d\'accès à cette page.');
 $mysqli = connectsql();
-// Les répertoires "non visibles" (protection 5), sauf pour les professeurs
+// Les documents "non visibles" (protection 32), sauf pour les professeurs
 // associés à la matière, ne sont pas accessibles.
-$resultat = $mysqli->query("SELECT d.id, d.parents, d.nom, d.lien, d.ext, d.matiere, d.protection, m.cle
+$resultat = $mysqli->query("SELECT d.id, d.parents, d.nom, d.lien, d.ext, d.protection, d.matiere AS mid, m.nom AS mat, m.cle
                             FROM docs AS d LEFT JOIN matieres AS m ON d.matiere = m.id
-                            WHERE d.id = $id ".(( $autorisation == 4 ) ? "AND ( d.protection < 5 OR FIND_IN_SET(d.matiere,'${_SESSION['matieres']}') )" : 'AND d.protection < 5'));
+                            WHERE d.id = $id ".(( $autorisation == 5 ) ? "AND ( d.protection != 32 OR FIND_IN_SET(d.matiere,'${_SESSION['matieres']}') )" : 'AND d.protection != 32'));
 if ( $resultat->num_rows )  {
   $f = $resultat->fetch_assoc();
   $resultat->free();
@@ -32,22 +32,7 @@
 /////////////////////////////
 // Vérification de l'accès //
 /////////////////////////////
-// Interdiction de voir les documents de protection élevée ou non associés à ses
-// propres matières si on est invité/élève/colleur
-// Tous les documents sont visibles par les professeurs
-if ( $f['protection'] && ( $autorisation > 0 ) && ( $autorisation < 4 )
-  && ( ( $f['protection'] > $autorisation ) || ( !in_array($f['matiere'],explode(',',$_SESSION['matieres'])) ) ) )  {
-  debut($mysqli,"Documents à télécharger - ${f['nom']}",'Vous n\'avez pas accès à ce document.',$autorisation,( strlen($f['cle']) ) ? "docs?${f['cle']}" : 'docs');
-  $mysqli->close();
-  fin();
-}
-// Connexion nécessaire si protection
-if ( $f['protection'] && !$autorisation )  {
-  $titre = "Documents à télécharger - ${f['nom']}";
-  $actuel = ( strlen($f['cle']) ) ? "docs?${f['cle']}" : 'docs';
-  include('login.php');
-}
-$mysqli->close();
+acces($f['protection'],$f['mid'],$f['mat'] ? "Documents à télécharger - ${f['mat']}" : 'Documents à télécharger',$f['mat'] ? "docs?${f['cle']}" : 'docs',$mysqli);
 
 // Définition du type de fichier et du type d'attachement (entête HTML à envoyer)
 $attachment = 'attachment';
diff -urN cahier-de-prepa6.2.0/fonctions.php cahier-de-prepa8.0.1/fonctions.php
--- cahier-de-prepa6.2.0/fonctions.php	2016-12-24 17:26:37.180825823 +0100
+++ cahier-de-prepa8.0.1/fonctions.php	2018-10-17 01:03:08.182943306 +0200
@@ -2,53 +2,223 @@
 // Sécurité : ne doit pas être exécuté s'il n'est pas lancé par un script autorisé
 if ( !defined('OK') )  exit();
 
-// HTTPS obligatoire dès le début si $https est true
-if ( $https && !isset($_SERVER['HTTPS']) )  {
-  header("Location: https://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}");
-  exit();
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 //////////////////////////// Gestion de la session /////////////////////////////
 ////////////////////////////////////////////////////////////////////////////////
-// Connexion et déconnexion sont gérées dans ajax.php
+
+// Fonction de connexion à la base MySQL
+function connectsql($ecriture=false)  {
+  $mysqli = new mysqli($GLOBALS['serveur'],( $ecriture ) ? $GLOBALS['base'].'-adm' : $GLOBALS['base'], $GLOBALS['mdp'], $GLOBALS['base']);
+  $mysqli->set_charset('utf8');
+  return $mysqli;
+}
+
+// Fonction d'écriture des connexion
+// $connexion = 1 pour connexion normale, 2 pour connexion light par cookie,
+//              3 pour déconnexion, 4 pour reconnexion light, 5 pour
+//              reconnexion normale
+function logconnect($connexion,$login)  {
+  if ( is_dir('sauvegarde') && is_executable('sauvegarde') && is_writable('sauvegarde') )  {
+    $mois = date('Y-m');
+    if ( !file_exists("sauvegarde/connexion.$mois.php") )  {
+      $fichier = fopen("sauvegarde/connexion.$mois.php",'wb');
+      fwrite($fichier, "<?php exit(); ?>\n\n");
+    }
+    else
+      $fichier = fopen("sauvegarde/connexion.$mois.php",'ab');
+    switch ( $connexion )  {
+      case 1: fwrite($fichier, 'Le '.date('d/m/Y à H:i:s').", connexion de $login\n"); break;
+      case 2: fwrite($fichier, 'Le '.date('d/m/Y à H:i:s').", connexion light par cookie de $login\n"); break;
+      case 3: fwrite($fichier, 'Le '.date('d/m/Y à H:i:s').", déconnexion de $login\n"); break;
+      case 4: fwrite($fichier, 'Le '.date('d/m/Y à H:i:s').", reconnexion light de $login\n"); break;
+      case 5: fwrite($fichier, 'Le '.date('d/m/Y à H:i:s').", reconnexion normale de $login\n"); break;
+    }
+    fclose($fichier);
+  }
+}
+
+// Fonction de remplissage de la variable $_SESSION
+// Le paramètre $light est true si la connexion est obtenue par cookie,
+// et ne sert donc qu'à la lecture
+// Cette fonction sert dans ajax.php et fonctions.php
+function enregistre_session($r,$light=true)  {
+  // Interdiction de garder son identifiant de session
+  session_regenerate_id(true);
+  $_SESSION = array();
+  // Interdiction de pouvoir se connecter aux autres site sur le même serveur
+  $_SESSION[md5($GLOBALS['site'])] = true;
+  // Pour vérification aux connexions ultérieures
+  $_SESSION['client'] = $_SERVER['HTTP_USER_AGENT'];
+  $_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
+  $_SESSION['login'] = $r['login'];
+  $_SESSION['id'] = $r['id'];
+  $_SESSION['permconn'] = $r['permconn'];
+  $_SESSION['lastconn'] = $r['lastconn'];
+  // Mise à jour de dernière connexion
+  $mysqli = connectsql(true);
+  $mysqli->query("UPDATE utilisateurs SET lastconn = NOW() WHERE id = ${r['id']}");
+  // Autorisations
+  $_SESSION['light'] = $light;
+  $_SESSION['autorisation'] = $r['autorisation'];
+  $_SESSION['mailenvoi'] = $r['mailenvoi'];
+  $_SESSION['matieres'] = $r['matieres'];
+  // Temps de session
+  $_SESSION['timeout'] = $r['timeout'];
+  $_SESSION['time'] = ( $_SESSION['timeout'] == 0 ) ? time()+900 : time()+$_SESSION['timeout'];
+  // Écriture de la connexion dans le fichier de log
+  logconnect(1+$light,$r['login']);
+  // Pour sécurisation des requêtes AJAX
+  $_SESSION['csrf-token'] = ( isset($_REQUEST['csrf-token']) ) ? $_REQUEST['csrf-token'] : bin2hex(random_bytes(32));
+}
+
+// Fonction de vérification de la qualité light ou non de la connexion
+// Renvoie true si la connexion est complète, false si elle est light.
+// Les connexions permanentes le sont par connexion par cookie, sans taper
+// son mot de passe systématiquement. Pour les modifications, il faut se
+// connecter à nouveau. Cette fonction n'est utilisée que dans ajax.php.
+function connexionlight()  {
+  if ( $_SESSION['light'] )
+    exit('{"etat":"mdp"}');
+  else
+    return true;
+}
+
+// Création de session
 session_name(md5($site));
-session_set_cookie_params(0,strchr($site,'/').'/',str_replace(strchr($site,'/'),'',$site));
+session_set_cookie_params(0,strchr($site,'/').'/',str_replace(strchr($site,'/'),'',$site),true);
 session_start();
-$autorisation = 0;
+// Niveau d'autorisation
+//  * 0 = non connecté
+//  * 1 = compte invité
+//  * 2 = élève
+//  * 3 = colleur
+//  * 4 = administration
+//  * 5 = professeur
+// Gestion des protections : voir la fonction acces ci-dessous
 $message = '';
 // Gestion des utilisateurs connectés
 if ( isset($_SESSION[md5("$site")]) )  {
+  // Passage en connexion light si timeout mais connexion permanente
+  if ( ( $_SESSION['time'] < time() ) && !$_SESSION['light'] && $_SESSION['permconn'] )  {
+    $_SESSION['light'] = true;
+    // Écriture de la connexion dans le fichier de log
+    logconnect(4,$_SESSION['login']);
+    // Mise à jour du timeout : une session est conservée moins d'un jour sur le serveur
+    $_SESSION['timeout'] = 86400;
+    $_SESSION['time'] = time()+86400;
+  }
+  // Passage en connexion normale si reconnexion sur une connexion light
+  elseif ( $_SESSION['light'] && isset($_REQUEST['motdepasse']) )  {
+    $mysqli = connectsql();
+    // Récupération des logins/mdp dans la base MySQL et comparaison
+    $resultat = $mysqli->query('SELECT timeout FROM utilisateurs WHERE mdp = \''.sha1($mdp.$_REQUEST['motdepasse']).'\' OR mdp = \''.sha1($_REQUEST['motdepasse'])."' AND id = ${_SESSION['id']}");
+    $mysqli->close();
+    if ( $resultat->num_rows )  {
+      $r = $resultat->fetch_row();
+      $resultat->free();
+      $_SESSION['light'] = false;
+      // Écriture de la connexion dans le fichier de log
+      logconnect(5,$_SESSION['login']);
+      // Mise à jour du timeout
+      $_SESSION['timeout'] = $r[0];
+      $_SESSION['time'] = ( $_SESSION['timeout'] == 0 ) ? time()+900 : time()+$_SESSION['timeout'];
+      // Si paramètre "connexion", reconnexion sans autre demande (par login.php),
+      // suivie d'un rechargement immédiat : terminaison et $_SESSION['message']
+      if ( isset($_REQUEST['connexion']) )
+        exit($_SESSION['message'] = '{"etat":"ok","message":"Connexion réussie"}');
+    }
+    else
+      exit('{"etat":"'.(isset($_REQUEST['connexion'])?'':'mdp').'nok","message":"Mauvais mot de passe"}');
+  }
   // Déconnexion automatique
-  if ( ( $_SESSION['time'] < time() ) || ( $_SESSION['client'] != $_SERVER['HTTP_USER_AGENT'] ) || ( $_SESSION['ip'] != $_SERVER['REMOTE_ADDR'] ) )  {
-    $message = 'Vous devez vous connecter à nouveau, suite à une longue durée d\'inactivité.';
+  elseif ( ( $_SESSION['time'] < time() ) || ( $_SESSION['client'] != $_SERVER['HTTP_USER_AGENT'] ) || ( $_SESSION['ip'] != $_SERVER['REMOTE_ADDR'] ) )  {
+    // Écriture de la déconnexion dans le fichier de log
+    logconnect(3,$_SESSION['login']);
     // Suppression du cookie et des données de session
     $_SESSION = array();
     setcookie(session_name(),'',time()-3600);
+    setcookie('loginpermanent','',time()-3600);
     session_regenerate_id(true);
+    // Si déconnexion demandée, pas la peine d'aller plus loin (venant de ajax.php)
+    if ( isset($_REQUEST['action']) && ( $_REQUEST['action'] == 'deconnexion' ) )
+      exit($_SESSION['message'] = '{"etat":"ok","message":"Déconnexion réussie"}');
+    $message = 'Vous devez vous connecter à nouveau, suite à une longue durée d\'inactivité.';
+    $_SESSION['autorisation'] = 0;
   }
   // Tout est ok : session valide pendant timeout
-  else  {
-    $_SESSION['time'] = time()+$_SESSION['timeout']+14400*!$_SESSION['timeout'];
-    $autorisation = $_SESSION['autorisation'];
+  else
+    $_SESSION['time'] = ( $_SESSION['timeout'] == 0 ) ? time()+900 : time()+$_SESSION['timeout'];
+}
+else  {
+  // Pas de connexion a priori
+  $_SESSION['autorisation'] = 0;
+  // Connexion complète (login et mdp, script ajax.php demandé)
+  if ( isset($_REQUEST['motdepasse']) && isset($_REQUEST['login']) )  {
+    $mysqli = connectsql();
+    // Récupération des logins/mdp dans la base MySQL et comparaison
+    $resultat = $mysqli->query('SELECT id, login, mdp, autorisation, matieres, timeout, mailenvoi, permconn, lastconn FROM utilisateurs WHERE mdp = \''.sha1($mdp.$_REQUEST['motdepasse']).'\' OR mdp = \''.sha1($_REQUEST['motdepasse']).'\'');
+    $mysqli->close();
+    while ( $r = $resultat->fetch_assoc() )
+      if ( $r['login'] == $_REQUEST['login'] )  {
+        enregistre_session($r,false);
+        // Modification interne du stockage du mot de passe -- mesure temporaire
+        if ( $r['mdp'] == sha1($_REQUEST['motdepasse']) )  {
+          $mysqli = connectsql(true);
+          $mysqli->query('UPDATE utilisateurs SET mdp = \''.sha1($mdp.$_REQUEST['motdepasse'])."' WHERE id = ${r['id']}");
+          $mysqli->close();
+        }
+        // Écriture du cookie pour connexion light
+        if ( $_SESSION['permconn'] )
+          setcookie('loginpermanent',$r['permconn'],time()+31536000);
+        break;
+      }
+    $resultat->free();
+    if ( $_SESSION['autorisation'] == 0 )  {
+      if ( filter_var($_REQUEST['login'],FILTER_VALIDATE_EMAIL) )
+        exit('{"etat":"nok","message":"Mauvais couple identifiant/mot de passe. L\'identifiant n\'est pas une adresse électronique."}');
+      exit('{"etat":"nok","message":"Mauvais couple identifiant/mot de passe"}');
+    }
+    // Si paramètre "connexion", connexion initiale (bouton "connexion" ou login.php),
+    // suivie d'un rechargement immédiat : terminaison et $_SESSION['message']
+    if ( isset($_REQUEST['connexion']) )  {
+      if ( isset($_REQUEST['permconn']) )  {
+        // Token de connexion automatique
+        $permconn = '';
+        if ( !strlen($permconn) && isset($_REQUEST['permconn']) )
+          for ( $i = 0; $i < 10; $i++ )
+            $permconn .= '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'[random_int(0,61)];
+        $mysqli = connectsql(true);
+        $mysqli->query("UPDATE utilisateurs SET permconn = '$permconn' WHERE id = ${r['id']}");
+        $mysqli->close();
+        $_SESSION['permconn'] = $permconn;
+        setcookie('loginpermanent',$permconn,time()+31536000);
+      }
+      exit($_SESSION['message'] = '{"etat":"ok","message":"Connexion réussie"}');
+    }
+  }
+  // Connexion light automatique par cookie
+  elseif ( isset($_COOKIE['loginpermanent']) && preg_match('/^\w{10}$/',$_COOKIE['loginpermanent']) )  {
+    $mysqli = connectsql();
+    // Récupération des logins/mdp dans la base MySQL et comparaison
+    $resultat = $mysqli->query("SELECT id, login, autorisation, matieres, timeout, mailenvoi, permconn, lastconn FROM utilisateurs WHERE mdp > '0' AND permconn = '${_COOKIE['loginpermanent']}'");
+    $mysqli->close();
+    if ( $resultat->num_rows )  {
+      enregistre_session($resultat->fetch_assoc(),true);
+      $_SESSION['timeout'] = 86400;
+      $_SESSION['time'] = time()+86400;
+      $resultat->free();
+    }
+    // Suppression du cookie s'il ne correspond pas à un compte
+    else 
+      setcookie('loginpermanent','',time()-3600);
   }
 }
+$autorisation = $_SESSION['autorisation'];
 
 ////////////////////////////////////////////////////////////////////////////////
-/////////////////// Fonctions utilisées dans Cahier de Prépa ///////////////////
+////////////////////// Mise à jour de la base de données ///////////////////////
 ////////////////////////////////////////////////////////////////////////////////
 
-/////////////
-/// MySQL ///
-/////////////
-
-// Connexion à la base MySQL
-function connectsql($ecriture=false)  {
-  $mysqli = new mysqli($GLOBALS['serveur'],( $ecriture ) ? $GLOBALS['base'].'-adm' : $GLOBALS['base'], $GLOBALS['mdp'], $GLOBALS['base']);
-  $mysqli->set_charset('utf8');
-  return $mysqli;
-}
-
 // Fonction d'envoi de requêtes MySQL et d'enregistrement
 //  * sauvegarde une fois par mois la table complète
 //  * enregistre la requête
@@ -89,11 +259,12 @@
     // Éxécution de la requête
     $resultat = $mysqli->query($requete);
     // Sauvegarde systématique de la requête
+    $insert = ( $mysqli->insert_id ) ? ' (identifiant '.$mysqli->insert_id.')' : '';
     if ( isset($_SESSION['login'])  )
-      fwrite($fichier, "\n-- Requête de ${_SESSION['login']} (${_SESSION['ip']}) le $heure\n$requete; -- ".$mysqli->affected_rows." ligne(s) affectée(s)\n");
+      fwrite($fichier, "\n-- Requête de ${_SESSION['login']} (${_SESSION['ip']}) le $heure\n$requete; -- ".$mysqli->affected_rows." ligne(s) affectée(s)$insert\n");
     else  {
       $login = ( isset($GLOBALS['utilisateur']) ) ? $GLOBALS['utilisateur']['login'] : $GLOBALS['login'];
-      fwrite($fichier, "\n-- Requête de $login (${_SERVER['REMOTE_ADDR']}) le $heure\n$requete; -- ".$mysqli->affected_rows." ligne(s) affectée(s)\n");
+      fwrite($fichier, "\n-- Requête de $login (${_SERVER['REMOTE_ADDR']}) le $heure\n$requete; -- ".$mysqli->affected_rows." ligne(s) affectée(s)$insert\n");
     }
     fclose($fichier);
   }
@@ -150,11 +321,15 @@
           $requete = "titre = '<span class=\"icon-infos\"></span> ${prop['titre']}'";
         // Modification de texte : mise en valeur dans les flux RSS
         elseif ( isset($prop['texte']) )
-          $requete = "heure = NOW(), texte = '${prop['texte']}', titre = CONCAT(titre,' (mise à jour)')";
+          $requete = "heure = NOW(), texte = '${prop['texte']}', titre = CONCAT(REPLACE(titre,' (mise à jour)',''),' (mise à jour)')";
+        // Modification de la protection : pas de changement de date de l'info récente
+        elseif ( isset($prop['protection']) )
+          $requete = "protection = ${prop['protection']}";
       break;
       case 2:
         // Modification de texte uniquement
-        $requete = "heure = NOW(), texte = '${prop['texte']}', titre = CONCAT(titre,' (mise à jour)')";
+        $requete = "heure = NOW(), texte = '${prop['texte']}', titre = CONCAT(REPLACE(titre,' (mise à jour)',''),' (mise à jour)')";
+      break;
       break;
       case 3:
         // Nouveau nom de document : mise à jour du titre et du texte seulement
@@ -166,6 +341,9 @@
           $requete = "texte = REPLACE(texte,SUBSTRING(titre,36),'${prop['chemin']}'), titre = CONCAT(SUBSTRING(titre,1,35),'${prop['chemin']}'), matiere = '${prop['matiere']}'";
         elseif ( isset($prop['maj']) )
           $requete = 'heure = NOW(), texte = REPLACE(texte,\'<p>Nouveau \',\'<p>Mise à jour du \')';
+        // Modification de la protection : pas de changement de date de l'info récente
+        elseif ( isset($prop['protection']) )
+          $requete = "protection = ${prop['protection']}";
       break;
     }
     requete('recents',"UPDATE recents SET $requete WHERE id=".($type*1000+$id),$mysqli);
@@ -211,7 +389,7 @@
   $resultat->free();
   $titre = $r[0];
   $d = date(DATE_RSS);
-  $site = ( $GLOBALS['https'] ) ? "https://${GLOBALS['site']}" : "http://${GLOBALS['site']}";
+  $site = "https://${GLOBALS['site']}";
   $preambule = <<<FIN
 <?xml version="1.0" encoding="UTF-8"?>
 <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
@@ -242,7 +420,7 @@
   // Génération pour les différentes combinaisons autorisation-matières
   foreach ( $combinaisons as $c )  {
     list($autorisation,$matieres) = explode('|',$c);
-    $requete = ( $autorisation ) ? "FIND_IN_SET(matiere,'$matieres') AND protection <= $autorisation" : 'protection = 0';
+    $requete = ( $autorisation ) ? "FIND_IN_SET(matiere,'$matieres') AND (".requete_protection($autorisation).')' : 'protection = 0';
     $resultat = $mysqli->query("SELECT UNIX_TIMESTAMP(heure) AS heure, titre, lien, texte, protection FROM recents
                                 WHERE $requete AND DATEDIFF(NOW(),heure) < 60");
     $rss = '';
@@ -280,17 +458,119 @@
   fclose($fichierlog);
 }
 
-////////////
-/// HTML ///
-////////////
+// Fonction PHP pour le stockage dans la base MySQL de l'ordre "naturel" (1,2,10,11 et non 1,10,11,2)
+// Remplace tout nombre par un nombre égal mais écrit sur 10 chiffres, complété par des zéros à gauche
+// Utilisé pour les documents et les groupes
+function zpad($s) {
+  return preg_replace_callback('/(\d+)/', function($m){
+    return(str_pad($m[1],10,'0',STR_PAD_LEFT)); }
+  , $s);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+////////////////////////// Gestion des autorisations ///////////////////////////
+////////////////////////////////////////////////////////////////////////////////
+
+// Fonction d'accès en lecture ou en écriture
+// Retourne true si accès en mode édition (professeur de la matière)
+// Retourne faux si accès en mode lecture uniquement (autre utilisateur autorisé)
+// Affiche une page de connexion si besoin (et arrête l'exécution)
+// Affiche une page d'interdiction et arrête l'exécution sinon
+// $protection est une valeur numérique.
+// * si $protection = 0, accès autorisé sans connexion identifiée.
+// * si 0 < $protection < 33, $protection est la représentation décimale de la
+// valeur binaire PACEI (types d'utilisateurs : professeurs, administration,
+// colleurs, élèves, invités) à laquelle on a ajouté 1.
+// Chaque 0 correspond aux accès autorisés, chaque 1 correspond aux protections
+// (accès interdit pour ce type de compte).
+// Exemple : 10 -> PACEI=9=01001 -> autorisé pour P,C,E et interdit pour A et I.
+// Le code 32 (interdit pour tous) correspond aux docs/reps/... non visibles.
+// * si $protection = 32, page non affichée dans le menu, non visible sauf pour
+// les professeurs associés à la matière
+// $matiere est la matière associée à la page à afficher
+// $titre est le titre de la page affichée si accès refusé ou connexion demandée
+// $actuel est le lien du menu si accès refusé ou connexion demandée
+function acces($protection,$matiere,$titre,$actuel,$mysqli)  {
+  $autorisation = $_SESSION['autorisation'];
+  // Mode édition
+  if ( ( $autorisation == 5 ) && in_array($matiere,explode(',',$_SESSION['matieres'])) )
+    return !isset($_REQUEST['mode_lecture']);
+  if ( $protection == 0 )
+    return false; // accès autorisé sans condition
+  // Si page protégée et utilisateur non connecté : connexion demandée
+  // login.php contient $mysqli->close() et fin()
+  if ( $autorisation == 0 )
+    include('login.php');
+  // À partir d'ici, on a un utilisateur connecté
+  // On affiche seulement si :
+  // * pour les professeurs, tout contenu autorisé d'une autre matière
+  // * pour les autres, uniquement les contenus à la fois autorisés et de
+  // matière associée
+  if ( ( $autorisation == 5 ) && ( $protection < 17 ) || in_array($matiere,explode(',',$_SESSION['matieres'])) && ( ( ($protection-1)>>($autorisation-1) & 1 ) == 0 ) )
+    return false;
+  // Accès non autorisé
+  debut($mysqli,$titre,'Vous n\'avez pas accès à cette page.',$autorisation,$actuel);
+  $mysqli->close();
+  fin();
+}
+// Version simplifiée pour le test d'éléments seuls, matière déjà vérifiée
+// Retourne true si accès autorisé, false si non
+function accestest($protection,$autorisation)  {
+  if ( ( $protection == 0 ) || ( $autorisation == 5 ) )  return true;
+  if ( $autorisation == 0 )  return false;
+  if ( ( ($protection-1)>>($autorisation-1) & 1 ) == 0 )  return true;
+  return false;
+}
+// Définition de la chaîne de protection mysql pour récupérer les éléments,
+// matière déjà vérifiée, sans mode édition. 
+// Pour index.php, agenda.php, cdt.php, docs.php
+function requete_protection($autorisation)  {
+  return $autorisation ? "( protection = 0 ) OR ( ( (protection-1)>>($autorisation-1) & 1 ) = 0 )" : 'protection = 0';
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////// Affichage HTML ////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
 
 // En-têtes HTML et début de page
 function debut($mysqli,$titre,$message,$autorisation,$actuel=false,$matiere=false,$css=false)  {
   // Menu seulement si $actuel non vide
   if ( $actuel )  {
+    
+    // Requête partielles de récupération
+    // Non connecté : on voit tout ce qui est non vide et non désactivé
+    // Invité/Élèves/Colleurs/Administration : on voit ce qui est non vide et
+    // autorisé (matière associée et protection correcte)
+    // Prof : on voit tout ce qui est non vide et autorisé pour les matières non
+    // associées, toutes les pages et reps pour les matières associées, tout ce
+    // qui n'est pas désactivé (colles,cdt,docs) pour les matières associées
+    switch ( $autorisation )  {
+      case 5:
+        $requete_matieres = '';
+        $requete_pages = "( protection < 17 ) OR FIND_IN_SET(mat,'${_SESSION['matieres']}')";
+        $requete_reps = "( protection < 17 ) OR FIND_IN_SET(matiere,'${_SESSION['matieres']}')";
+        $requete_collesdocscdt = "( X_protection < 17 ) OR ( ( X_protection < 32 ) AND FIND_IN_SET(m.id,'${_SESSION['matieres']}') )";
+        $requete_notes = "(notes < 2 AND FIND_IN_SET(m.id,'${_SESSION['matieres']}'))";
+        $requete_rss = "( protection < 17 ) OR FIND_IN_SET(matiere,'${_SESSION['matieres']}')";
+        break;
+      case 0:
+        $requete_matieres = '';
+        $requete_pages = $requete_reps = "protection < 32";
+        $requete_collesdocscdt = "X_protection < 32";
+        $requete_notes = '0';
+        $requete_rss = 'protection = 0';
+        break;
+      default:
+        $requete_matieres = "WHERE FIND_IN_SET(m.id,'${_SESSION['matieres']}')";
+        $requete_pages = $requete_reps = "( protection = 0 ) OR ( ( (protection-1)>>($autorisation-1) & 1 ) = 0 )";
+        $requete_collesdocscdt = "( X_protection = 0 ) OR ( ( (X_protection-1)>>($autorisation-1) & 1 ) = 0 )";
+        $requete_notes = 'IF(notes = 1,1,0)';
+        $requete_rss = "FIND_IN_SET(matiere,'${_SESSION['matieres']}') AND ( ( protection = 0 ) OR ( ( (protection-1)>>($autorisation-1) & 1 ) = 0 ) )";
+    }
+    
     // Récupération et affichage des pages
     $menu = "<a class=\"icon-menu\" title=\"Afficher le menu\"></a>\n<div id=\"colonne\">\n  <nav>\n";
-    $resultat = $mysqli->query('SELECT cle, nom FROM pages WHERE mat = 0 AND (id = 1 OR '. ( $autorisation ? "LEAST(protection,4) <= $autorisation" : 'protection < 5' ) .')');
+    $resultat = $mysqli->query("SELECT cle, nom FROM pages WHERE mat = 0 AND (id = 1 OR $requete_pages) ORDER BY ordre");
     $r = $resultat->fetch_assoc();
     // Icônes principales (accueil, agenda, impression, rss)
     $menu .= <<<FIN
@@ -300,61 +580,47 @@
     <a class="icon-rss" href="rss" title="Voir les flux RSS disponibles"></a>
 
 FIN;
-    // Préférences (élèves, colleurs, professeurs)
+    // Préférences (tous types de comptes sauf invité)
     if ( $autorisation > 1 )
       $menu .= "    <a class=\"icon-prefs\" href=\"prefs\" title=\"Gérer ses préférences\"></a>\n";
-    // Mail (colleurs, professeurs)
-    if ( $autorisation > 2 )
-      $menu .= "    <a class=\"icon-mail\" href=\"mail\" title=\"Envoyer un mail\"></a>\n";
+    // Envoi de courriel
+    if ( ( $autorisation > 1 ) && $_SESSION['mailenvoi'] )
+      $menu .= "    <a class=\"icon-mail\" href=\"mail\" title=\"Envoyer un courriel\"></a>\n";
     // Connexion/Déconnexion
-    $menu .= ( $autorisation ) ? "    <a class=\"icon-deconnexion\" title=\"Se déconnecter\"></a>\n    <hr>\n" : "    <a class=\"icon-connexion\" title=\"Se connecter\"></a>\n    <hr>\n";
+    $menu .= $autorisation ? "    <a class=\"icon-deconnexion\" title=\"Se déconnecter\"></a>\n    <hr>\n" : "    <a class=\"icon-connexion\" title=\"Se connecter\"></a>\n    <hr>\n";
+    // Relève des colles, compte administratif uniquement
+    if ( $autorisation == 4 )
+      $menu .= "    <a href=\"relevenotes\">Relève des notes</a>\n";
     // Autres pages
     while ( $r = $resultat->fetch_assoc() )
       $menu .= "    <a href=\".?${r['cle']}\">${r['nom']}</a>\n";
     $resultat->free();
     // Page de téléchargement
     $menu .= "    <a href=\"docs\">Documents à télécharger</a>\n";
-    $resultat = $mysqli->query('SELECT id, nom FROM reps WHERE matiere = 0 AND menu = 1 AND '. ( $autorisation ? "LEAST(protection,4) <= $autorisation" : 'protection < 5' ));
+    $resultat = $mysqli->query("SELECT id, nom FROM reps WHERE matiere = 0 AND menu = 1 AND ( $requete_reps )");
     if ( $resultat->num_rows )  {
       while ( $r = $resultat->fetch_assoc() )
-        $menu .= "      <a class=\"menurep\" href=\"docs?rep=${r['id']}\">${r['nom']}</a>\n";        
+        $menu .= "      <a class=\"menurep\" href=\"docs?rep=${r['id']}\">${r['nom']}</a>\n";
       $resultat->free();
     }
     // Récupération et affichage des matières
-    // Non connecté : on voit tout ce qui est non vide
-    // Prof : on voit tout ce qui est non vide + on liste les matières associées
-    // Invité/Élèves/Colleurs : on voit ce qui est non vide et autorisé
-    if ( !$autorisation )
-      $resultat = $mysqli->query("SELECT m.id, m.cle, m.nom, m.colles, m.cdt, m.docs, 0 AS notes, GROUP_CONCAT(CONCAT(m.cle,'/',p.cle) SEPARATOR '//') AS pcle, GROUP_CONCAT(p.nom SEPARATOR '//') AS pnom
-                                  FROM matieres AS m LEFT JOIN (SELECT * FROM pages WHERE protection < 5) AS p ON p.mat = m.id
-                                  WHERE m.colles+m.cdt+m.docs OR p.nom IS NOT NULL GROUP BY m.id ORDER BY m.ordre, p.ordre");
-    elseif ( $autorisation == 4 )
-      $resultat = $mysqli->query("SELECT m.id, m.cle, m.nom, m.colles, m.cdt, m.docs, m.notes, GROUP_CONCAT(CONCAT(m.cle,'/',p.cle) SEPARATOR '//') AS pcle, GROUP_CONCAT(p.nom SEPARATOR '//') AS pnom
-                                  FROM matieres AS m LEFT JOIN (SELECT * FROM pages WHERE protection < 5 OR FIND_IN_SET(mat,'${_SESSION['matieres']}')) AS p ON p.mat = m.id
-                                  WHERE m.colles+m.cdt+m.docs+m.notes OR p.nom IS NOT NULL OR FIND_IN_SET(m.id,'${_SESSION['matieres']}') GROUP BY m.id ORDER BY m.ordre, p.ordre");
-    else  {
-      if ( $autorisation == 1 )
-        $notes = '0';
-      elseif ( $autorisation == 2 )
-        $notes = 'notes';
-      elseif ( $autorisation == 3 )
-        $notes = '2';
-      $resultat = $mysqli->query("SELECT m.id, m.cle, m.nom, (colles AND colles_protection<=$autorisation ) AS colles,
-                                  (cdt AND cdt_protection<=$autorisation ) AS cdt, (docs AND r.protection<=$autorisation ) AS docs,
-                                  $notes AS notes, GROUP_CONCAT(CONCAT(m.cle,'/',p.cle) SEPARATOR '//') AS pcle, GROUP_CONCAT(p.nom SEPARATOR '//') AS pnom
-                                  FROM matieres AS m LEFT JOIN (SELECT * FROM pages WHERE protection <=$autorisation ) AS p ON p.mat = m.id LEFT JOIN reps AS r ON r.matiere = m.id AND r.parent = 0
-                                  WHERE FIND_IN_SET(m.id,'${_SESSION['matieres']}') AND ( $notes OR (docs AND r.protection<=$autorisation ) OR (colles AND colles_protection<=$autorisation ) OR (cdt AND cdt_protection<=$autorisation ) OR p.nom IS NOT NULL )
-                                  GROUP BY m.id ORDER BY m.ordre, p.ordre");
-    }
+    $resultat = $mysqli->query('SELECT * FROM (
+                                  SELECT m.id, m.cle, m.nom,
+                                  ( colles = 1 AND ('.str_replace('X','colles',$requete_collesdocscdt).') ) AS colles,
+                                  (   docs = 1 AND ('.str_replace('X',  'docs',$requete_collesdocscdt).') ) AS docs,
+                                  (    cdt = 1 AND ('.str_replace('X',   'cdt',$requete_collesdocscdt).') ) AS cdt,
+                                  '.$requete_notes.' AS notes,
+                                  GROUP_CONCAT(CONCAT(m.cle,"/",p.cle) SEPARATOR "//") AS pcle, GROUP_CONCAT(p.nom SEPARATOR "//") AS pnom
+                                  FROM matieres AS m LEFT JOIN (
+                                    SELECT * FROM pages WHERE '.$requete_pages.'
+                                  ) AS p ON p.mat = m.id
+                                  '.$requete_matieres.'
+                                  GROUP BY m.id ORDER BY m.ordre, p.ordre
+                                ) AS t WHERE colles + docs + cdt + notes > 0 OR pnom IS NOT NULL');
     if ( $resultat->num_rows )  {
       while ( $r = $resultat->fetch_assoc() )  {
-        $edition = ( $autorisation == 4 ) && ( in_array($r['id'],explode(',',$_SESSION['matieres'])) );
-        if ( $edition )  {
-          $menu .= "    <h3><span class=\"icon-edite\"></span>${r['nom']}</h3>\n";
-          $r['colles'] = $r['cdt'] = $r['docs'] = $r['notes'] = 1;
-        }
-        else
-          $menu .= "    <h3>${r['nom']}</h3>\n";
+        $edition = ( $autorisation == 5 ) && ( in_array($r['id'],explode(',',$_SESSION['matieres'])) );
+        $menu .= "    <h3>${r['nom']}</h3>\n";
         if ( !is_null($r['pcle']) )  {
           $pcle = explode('//',$r['pcle']);
           $pnom = explode('//',$r['pnom']);
@@ -366,34 +632,34 @@
         }
         if ( $r['colles'] )
           $menu .= "    <a href=\"colles?${r['cle']}\">Programme de colles</a>\n";
-        if ( $r['cdt'] )  {
-          $menu .= "    <a href=\"cdt?${r['cle']}\">Cahier de texte</a>\n";
-          if ( $edition && ( substr($actuel,0,3) == 'cdt' ) && ( substr($actuel,-strlen($r['cle'])-1) == "?${r['cle']}" ) )
-            $menu .= "      <a class=\"menurep\" href=\"cdt-seances?${r['cle']}\">Types de séances</a>\n      <a class=\"menurep\" href=\"cdt-raccourcis?${r['cle']}\">Raccourcis de séances</a>\n";
-        }
         if ( $r['docs'] )  {
           $menu .= "    <a href=\"docs?${r['cle']}\">Documents à télécharger</a>\n";
-          $resultat_doc = $mysqli->query("SELECT id, nom FROM reps WHERE matiere = ${r['id']} AND menu = 1 AND ". ( $autorisation ? "LEAST(protection,4) <= $autorisation" : 'protection < 5' ));
+          $resultat_doc = $mysqli->query("SELECT id, nom FROM reps WHERE matiere = ${r['id']} AND menu = 1 AND ( $requete_reps )");
           if ( $resultat_doc->num_rows )  {
             while ( $d = $resultat_doc->fetch_assoc() )
               $menu .= "    <a class=\"menurep\" href=\"docs?rep=${d['id']}\">${d['nom']}</a>\n";        
             $resultat_doc->free();
           }
         }
-        // Pour les colleurs, $r['notes'] > 1 dans leurs matières associées
+        if ( $r['cdt'] )  {
+          $menu .= "    <a href=\"cdt?${r['cle']}\">Cahier de texte</a>\n";
+          if ( $edition && ( substr($actuel,0,3) == 'cdt' ) && ( substr($actuel,-strlen($r['cle'])-1) == "?${r['cle']}" ) )
+            $menu .= "      <a class=\"menurep\" href=\"cdt-seances?${r['cle']}\">Types de séances</a>\n      <a class=\"menurep\" href=\"cdt-raccourcis?${r['cle']}\">Raccourcis de séances</a>\n";
+        }
         if ( $r['notes'] )
-          $menu .= ( $r['notes'] == 1 ) ? "    <a href=\"notes?${r['cle']}\">Notes</a>\n" : "    <a href=\"notes?${r['cle']}\">Notes&nbsp;<span class=\"icon-edite\"></span></a>\n";
+          $menu .= "    <a href=\"notes?${r['cle']}\">Notes</a>\n";
       }
       $resultat->free();
     }
-    // Liens d'édition
-    if ( $autorisation == 4 )
-    $menu .= <<<FIN
+    // Liens d'édition, professeurs seulement
+    if ( $autorisation == 5 )
+      $menu .= <<<FIN
     <h3>Gestion du site</h3>
     <a href="pages">Les pages</a>
-    <a href="utilisateurs">Les utilisateurs</a>
-    <a href="groupes">Les groupes d'élèves</a>
     <a href="matieres">Les matières</a>
+    <a href="utilisateurs">Les utilisateurs</a>
+    <a href="utilisateurs-matieres">Les associations utilisateurs-matières</a>
+    <a href="groupes">Les groupes</a>
     <a href="planning">Le planning annuel</a>
 
 FIN;
@@ -402,7 +668,9 @@
 
     // Affichage des informations récentes
     $recent = '';
-    $resultat = $mysqli->query('SELECT titre, lien FROM recents WHERE DATEDIFF(NOW(),heure) < 10'.( ($autorisation) ? " AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')" : '' ).' LIMIT 20');
+    //~ $resultat = $mysqli->query('SELECT titre, lien FROM recents WHERE TRUE OR DATEDIFF(NOW(),heure) < 10'.( ($autorisation) ? " AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')" : '' ).' LIMIT 20');
+    //~ $resultat = $mysqli->query('SELECT titre, lien FROM recents WHERE TRUE OR DATEDIFF(NOW(),heure) < 10 AND '.( ($autorisation) ? "( $requete_protection ) AND FIND_IN_SET(matiere,'${_SESSION['matieres']}')" : ' protection = 0' ).' LIMIT 20');
+    $resultat = $mysqli->query("SELECT titre, lien FROM recents WHERE TRUE OR DATEDIFF(NOW(),heure) < 10 AND $requete_rss LIMIT 20");
     if ( $resultat->num_rows )  {
       $menu = "<a class=\"icon-recent\" title=\"Afficher les informations récentes\"></a>\n$menu";
       while ( $r = $resultat->fetch_assoc() )
@@ -422,13 +690,12 @@
     $recent .= '</div>';
     
   // RSS
-  $rss = sha1($GLOBALS['base'].'00');
   $rss = ( $autorisation ) ? sha1("?!${GLOBALS['base']}$autorisation|${_SESSION['matieres']}") : sha1("?!${GLOBALS['base']}0|toutes");
   // Message si non vide
   if ( strlen($message) )
     $message = "  <div class=\"warning\">$message</div>\n";
-  elseif ( $autorisation == 4 )  {
-    $resultat = $mysqli->query('SELECT id FROM utilisateurs WHERE LENGTH(mdp)=41');
+  elseif ( ( $autorisation == 5 ) && ( basename($_SERVER['PHP_SELF']) == 'index.php' ) )  {
+    $resultat = $mysqli->query('SELECT id FROM utilisateurs WHERE mdp LIKE "*%"');
     if ( $n = $resultat->num_rows )  {
       $resultat->free();
       $n = ( $n > 1 ) ? "$n comptes" : "1 compte";
@@ -451,14 +718,13 @@
   <title>$head</title>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0">
-  <link rel="stylesheet" href="css/style.min.css">
-  <link rel="stylesheet" href="css/icones.min.css">$css
+  <link rel="stylesheet" href="css/style1810.min.css">
+  <link rel="stylesheet" href="css/icones1810.min.css">$css
   <script type="text/javascript" src="js/jquery.min.js"></script>
   <link rel="alternate" type="application/rss+xml" title="Flux RSS" href="documents/rss/$rss/rss.xml">
-  <!--[if lte IE 8]><script src="js/html5shiv.min.js"></script><![endif]--> 
-  <!--[if lte IE 8]><script src="js/respond.min.js"></script><![endif]--> 
 </head>
 <body$token$matiere>
+
 <header><h1>$titre</h1></header>
 
 $menu
@@ -476,7 +742,7 @@
 <script type="text/javascript" src="/MathJax/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
 <script type="text/x-mathjax-config">MathJax.Hub.Config({tex2jax:{inlineMath:[["$","$"],["\\\\(","\\\\)"]]}});</script>' : '';
   // Édition possible si $edition est true
-  $js = ( $edition ) ? '<script type="text/javascript" src="js/edition.min.js"></script>' : '<script type="text/javascript" src="js/fonctions.min.js"></script>';
+  $js = ( $edition ) ? '<script type="text/javascript" src="js/edition.min18.js"></script>' : '<script type="text/javascript" src="js/fonctions.min18.js"></script>';
   // Affichage de message si $_SESSION['message']
   if ( isset($_SESSION['message']) )  {
     $m = json_decode($_SESSION['message'],true);
@@ -489,14 +755,16 @@
 
 </section>
 
-<footer>Ce site est réalisé par le logiciel <a href="http://cahier-de-prepa.fr">Cahier de prépa</a>, publié sous <a href="Licence_CeCILL_V2-fr.html">licence libre</a>.</footer>
+<footer>Ce site est réalisé par le logiciel <a href="http://cahier-de-prepa.fr">Cahier de prépa</a>, publié sous <a href="Licence_CeCILL_V2-fr.html">licence libre</a>.
+</footer>
+<div id="load"><img src="js/ajax-loader.gif"></div>
 
 $js$mathjax$m
 
 </body>
 </html>
 FIN;
-exit();
+  exit();
 }
 
 // Affichage des semaines
Binary files cahier-de-prepa6.2.0/fonts/icomoon.eot and cahier-de-prepa8.0.1/fonts/icomoon.eot differ
diff -urN cahier-de-prepa6.2.0/fonts/icomoon.svg cahier-de-prepa8.0.1/fonts/icomoon.svg
--- cahier-de-prepa6.2.0/fonts/icomoon.svg	2016-07-25 23:25:24.406289625 +0200
+++ cahier-de-prepa8.0.1/fonts/icomoon.svg	2018-10-09 01:19:20.059315070 +0200
@@ -14,7 +14,7 @@
 <glyph unicode="&#xe604;" glyph-name="supprime" d="M0 544v-192c0-17.672 14.328-32 32-32h960c17.672 0 32 14.328 32 32v192c0 17.672-14.328 32-32 32h-960c-17.672 0-32-14.328-32-32z" />
 <glyph unicode="&#xe605;" glyph-name="annule" d="M761.862-64c113.726 206.032 132.888 520.306-313.862 509.824v-253.824l-384 384 384 384v-248.372c534.962 13.942 594.57-472.214 313.862-775.628z" />
 <glyph unicode="&#xe606;" glyph-name="ok" d="M864 832l-480-480-224 224-160-160 384-384 640 640z" />
-<glyph unicode="&#xe607;" glyph-name="prefs" d="M1002.934 142.124l-460.552 394.76c21.448 40.298 33.618 86.282 33.618 135.116 0 159.058-128.942 288-288 288-29.094 0-57.172-4.332-83.646-12.354l166.39-166.39c24.89-24.89 24.89-65.62 0-90.51l-101.49-101.49c-24.89-24.89-65.62-24.89-90.51 0l-166.39 166.39c-8.022-26.474-12.354-54.552-12.354-83.646 0-159.058 128.942-288 288-288 48.834 0 94.818 12.17 135.116 33.62l394.76-460.552c22.908-26.724 62.016-28.226 86.904-3.338l101.492 101.492c24.888 24.888 23.386 63.994-3.338 86.902z" />
+<glyph unicode="&#xe607;" glyph-name="prefs" d="M896 512h16c26.4 0 48 21.6 48 48v160c0 26.4-21.6 48-48 48h-16v192h-128v-192h-16c-26.4 0-48-21.6-48-48v-160c0-26.4 21.6-48 48-48h16v-576h128v576zM768 704h128v-128h-128v128zM592 128c26.4 0 48 21.6 48 48v160c0 26.4-21.6 48-48 48h-16v576h-128v-576h-16c-26.4 0-48-21.6-48-48v-160c0-26.4 21.6-48 48-48h16v-192h128v192h16zM448 320h128v-128h-128v128zM272 512c26.4 0 48 21.6 48 48v160c0 26.4-21.6 48-48 48h-16v192h-128v-192h-16c-26.4 0-48-21.6-48-48v-160c0-26.4 21.6-48 48-48h16v-576h128v576h16zM128 704h128v-128h-128v128z" />
 <glyph unicode="&#xe608;" glyph-name="monte" d="M762.453 512v-436.907h72.818v436.907h182.045l-218.453 218.453-218.453-218.453zM34.275 876.089h109.227v-72.818h-109.227v72.818zM179.911 876.089h109.227v-72.818h-109.227v72.818zM325.547 876.089h72.818v-109.227h-72.818v109.227zM34.275 621.227h72.818v-109.227h-72.818v109.227zM143.502 584.818h109.227v-72.818h-109.227v72.818zM289.138 584.818h109.227v-72.818h-109.227v72.818zM34.275 766.862h72.818v-109.227h-72.818v109.227zM325.547 730.453h72.818v-109.227h-72.818v109.227zM325.547 293.547v-218.453h-218.453v218.453h218.453zM398.365 366.365h-364.089v-364.089h364.089v364.089z" />
 <glyph unicode="&#xe609;" glyph-name="descend" d="M835.271 293.547v436.907h-72.818v-436.907h-182.045l218.453-218.453 218.453 218.453zM325.547 803.271v-218.453h-218.453v218.453h218.453zM398.365 876.089h-364.089v-364.089h364.089v364.089zM34.275 366.365h109.227v-72.818h-109.227v72.818zM179.911 366.365h109.227v-72.818h-109.227v72.818zM325.547 366.365h72.818v-109.227h-72.818v109.227zM34.275 111.502h72.818v-109.227h-72.818v109.227zM143.502 75.093h109.227v-72.818h-109.227v72.818zM289.138 75.093h109.227v-72.818h-109.227v72.818zM34.275 257.138h72.818v-109.227h-72.818v109.227zM325.547 220.729h72.818v-109.227h-72.818v109.227z" />
 <glyph unicode="&#xe60a;" glyph-name="ferme" d="M512 960c-282.77 0-512-229.23-512-512s229.23-512 512-512 512 229.23 512 512-229.23 512-512 512zM512 32c-229.75 0-416 186.25-416 416s186.25 416 416 416 416-186.25 416-416-186.25-416-416-416zM672 704l-160-160-160 160-96-96 160-160-160-160 96-96 160 160 160-160 96 96-160 160 160 160z" />
@@ -49,12 +49,12 @@
 <glyph unicode="&#xe627;" glyph-name="deconnexion" d="M768 320v128h-320v128h320v128l192-192zM704 384v-256h-320v-192l-384 192v832h704v-320h-64v256h-512l256-128v-576h256v192z" />
 <glyph unicode="&#xe628;" glyph-name="mail" d="M936.77 812.089h-849.54c-33.511 0-60.682-27.17-60.682-60.682v-606.815c0-33.511 27.17-60.682 60.682-60.682h849.54c33.511 0 60.682 27.17 60.682 60.682v606.815c0 33.511-27.17 60.682-60.682 60.682zM852.918 690.726l-340.918-232.49-340.918 232.49h681.836zM876.089 235.615c0-16.755-13.57-30.34-30.34-30.34h-667.497c-16.771 0-30.34 13.585-30.34 30.34v325.408l333.57-225.926c9.423-5.645 19.972-8.46 30.519-8.46s21.097 2.814 30.519 8.46l333.57 225.926v-325.408z" />
 <glyph unicode="&#xe629;" glyph-name="menu" d="M0.854 962.48h1023.825v-279.787h-1023.825zM0.854 589.429h1023.825v-279.787h-1023.825zM0.854 216.381h1023.825v-279.787h-1023.825z" />
-<glyph unicode="&#xe62a;" glyph-name="cocher" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM448 165.49l-237.254 237.256 90.51 90.508 146.744-146.744 306.746 306.746 90.508-90.51-397.254-397.256z" />
-<glyph unicode="&#xe62b;" glyph-name="decocher" d="M896 960h-768c-70.4 0-128-57.6-128-128v-768c0-70.4 57.6-128 128-128h768c70.4 0 128 57.6 128 128v768c0 70.4-57.6 128-128 128zM896 64h-768v768h768v-768z" />
+<glyph unicode="&#xe62a;" glyph-name="cocher" d="M831.815 552.631v-423.844h-635.687v635.687h435.844l104 105.948h-539.844c-58.271 0-105.948-47.677-105.948-105.948v-635.687c0-58.271 47.677-105.948 105.948-105.948h635.687c58.271 0 105.948 47.677 105.948 105.948v529.844zM864.246 887.659l-276.497-282.355-129.032 131.766-92.166-94.118 221.198-225.884 368.663 376.473z" />
+<glyph unicode="&#xe62b;" glyph-name="decocher" d="M827.576 341.376v-211.268h-634.537v634.537h209.268c0 0-31.793 31.007-45.907 45.272-8.429 8.519-7.401 15.556 2.405 26.015 6.946 7.409 35.502 34.469 35.502 34.469h-201.268c-58.166 0-105.756-47.59-105.756-105.756v-634.537c0-58.166 47.59-105.756 105.756-105.756h634.537c58.166 0 105.756 46.505 105.756 104.671v202.353c0 0-25.618-26.344-35.731-37.169s-17.175-10.114-22.988-4.075c-11.737 12.193-47.037 51.244-47.037 51.244zM971.54 502.677c-0.002 0.002-0.004 0.004-0.006 0.005l-155.322 155.325 155.322 155.325c0.002 0.002 0.004 0.003 0.006 0.005 1.672 1.673 2.881 3.627 3.656 5.708 2.123 5.688 0.912 12.341-3.662 16.915l-73.373 73.373c-4.574 4.573-11.225 5.783-16.914 3.66-2.080-0.775-4.035-1.984-5.709-3.655 0-0.002-0.002-0.003-0.004-0.005l-155.324-155.326-155.324 155.325c-0.002 0.002-0.003 0.003-0.005 0.005-1.673 1.671-3.627 2.88-5.707 3.655-5.69 2.124-12.341 0.913-16.915-3.66l-73.374-73.374c-4.574-4.574-5.784-11.226-3.661-16.914 0.776-2.080 1.985-4.036 3.656-5.708 0.002-0.001 0.003-0.003 0.005-0.005l155.325-155.324-155.325-155.326c-0.001-0.002-0.003-0.003-0.004-0.005-1.671-1.673-2.88-3.627-3.657-5.707-2.124-5.688-0.913-12.341 3.661-16.915l73.374-73.373c4.575-4.574 11.226-5.784 16.915-3.661 2.080 0.776 4.035 1.985 5.708 3.656 0.001 0.002 0.003 0.003 0.005 0.005l155.324 155.325 155.324-155.325c0.002-0.001 0.004-0.003 0.006-0.004 1.674-1.672 3.627-2.881 5.707-3.657 5.689-2.123 12.342-0.913 16.914 3.661l73.373 73.374c4.574 4.574 5.785 11.227 3.662 16.915-0.776 2.080-1.985 4.034-3.657 5.707z" />
 <glyph unicode="&#xe62c;" glyph-name="rep" d="M448 832l128-128h448v-704h-1024v832z" />
 <glyph unicode="&#xe62d;" glyph-name="rep-open" d="M832 0l192 512h-832l-192-512zM128 576l-128-576v832h288l128-128h416v-128z" />
 <glyph unicode="&#xe62e;" glyph-name="download" d="M896 448h-160l-224-224-224 224h-160l-128-256v-64h1024v64l-128 256zM0 64h1024v-64h-1024v64zM576 640v256h-128v-256h-224l288-288 288 288h-224z" />
-<glyph unicode="&#xe62f;" glyph-name="lock" horiz-adv-x="512" d="M411.798 353.763h-10.864v130.355c0 71.879-58.477 130.355-130.355 130.355h-86.903c-71.879 0-130.355-58.477-130.355-130.355v-130.355h-10.864c-17.923 0-32.589-14.665-32.589-32.589v-325.889c0-17.923 14.664-32.589 32.589-32.589h369.34c17.923 0 32.589 14.665 32.589 32.589v325.889c0 17.923-14.665 32.589-32.589 32.589zM140.225 484.118c0 23.959 19.492 43.451 43.451 43.451h86.903c23.959 0 43.451-19.492 43.451-43.451v-130.355h-173.807v130.355z" />
+<glyph unicode="&#xe62f;" glyph-name="minilock" horiz-adv-x="512" d="M411.798 353.763h-10.864v130.355c0 71.879-58.477 130.355-130.355 130.355h-86.903c-71.879 0-130.355-58.477-130.355-130.355v-130.355h-10.864c-17.923 0-32.589-14.665-32.589-32.589v-325.889c0-17.923 14.664-32.589 32.589-32.589h369.34c17.923 0 32.589 14.665 32.589 32.589v325.889c0 17.923-14.665 32.589-32.589 32.589zM140.225 484.118c0 23.959 19.492 43.451 43.451 43.451h86.903c23.959 0 43.451-19.492 43.451-43.451v-130.355h-173.807v130.355z" />
 <glyph unicode="&#xe630;" glyph-name="alphaasc" d="M320 192v768h-128v-768h-160l224-224 224 224h-160zM928-64h-256c-11.8 0-22.644 6.496-28.214 16.9-5.566 10.404-4.958 23.030 1.59 32.85l222.832 334.25h-196.208c-17.672 0-32 14.328-32 32s14.328 32 32 32h256c11.8 0 22.644-6.496 28.214-16.9 5.566-10.404 4.958-23.030-1.59-32.85l-222.83-334.25h196.206c17.672 0 32-14.328 32-32s-14.328-32-32-32zM1020.622 558.314l-192.002 384c-5.42 10.842-16.502 17.69-28.622 17.69-12.122 0-23.202-6.848-28.624-17.69l-191.996-384c-7.904-15.806-1.496-35.030 14.31-42.932 4.594-2.296 9.476-3.386 14.288-3.386 11.736 0 23.040 6.484 28.644 17.698l55.156 110.31h216.446l55.156-110.31c7.902-15.806 27.124-22.21 42.932-14.31 15.808 7.902 22.216 27.124 14.312 42.93zM723.778 704.004l76.22 152.446 76.224-152.446h-152.444z" />
 <glyph unicode="&#xe631;" glyph-name="alphadesc" d="M320 192v768h-128v-768h-160l224-224 224 224h-160zM928 512h-256c-11.8 0-22.644 6.496-28.214 16.9-5.566 10.406-4.958 23.030 1.59 32.85l222.832 334.25h-196.208c-17.672 0-32 14.328-32 32s14.328 32 32 32h256c11.8 0 22.644-6.496 28.214-16.9 5.566-10.406 4.958-23.030-1.59-32.85l-222.83-334.25h196.206c17.672 0 32-14.328 32-32s-14.328-32-32-32zM1020.622-17.69l-192.002 384c-5.42 10.842-16.502 17.69-28.622 17.69-12.122 0-23.202-6.848-28.624-17.69l-191.996-384c-7.904-15.806-1.496-35.030 14.31-42.932 4.594-2.296 9.476-3.386 14.288-3.386 11.736 0 23.040 6.484 28.644 17.698l55.158 110.31h216.446l55.156-110.31c7.902-15.806 27.124-22.21 42.932-14.31 15.806 7.902 22.214 27.124 14.31 42.93zM723.778 128l76.22 152.446 76.226-152.446h-152.446z" />
 <glyph unicode="&#xe632;" glyph-name="chronoasc" d="M320 192v768h-128v-768h-160l224-224 224 224h-160zM802.919 506.985l-126.446 126.448v169.506h76.8v-137.694l103.954-103.952zM714.873 956.539c-169.662 0-307.2-137.538-307.2-307.2s137.538-307.2 307.2-307.2c169.662 0 307.2 137.538 307.2 307.2s-137.538 307.2-307.2 307.2zM714.873 418.939c-127.247 0-230.4 103.153-230.4 230.4s103.153 230.4 230.4 230.4c127.247 0 230.4-103.153 230.4-230.4s-103.153-230.4-230.4-230.4zM886.139 178.11h-126.72v126.72c0 6.362-5.158 11.52-11.52 11.52h-69.12c-6.362 0-11.52-5.158-11.52-11.52v-126.72h-126.72c-6.362 0-11.52-5.158-11.52-11.52v-69.12c0-6.362 5.158-11.52 11.52-11.52h126.72v-126.72c0-6.362 5.158-11.52 11.52-11.52h69.12c6.362 0 11.52 5.158 11.52 11.52v126.72h126.72c6.362 0 11.52 5.158 11.52 11.52v69.12c0 6.362-5.158 11.52-11.52 11.52z" />
@@ -82,4 +82,18 @@
 <glyph unicode="&#xe648;" glyph-name="lock5" d="M443.75 960c-112.928 0-204.813-91.885-204.813-204.813v-204.813h-17.063c-28.16 0-51.188-23.027-51.188-51.188v-512c0-28.16 23.028-51.188 51.188-51.188h580.25c28.16 0 51.188 23.028 51.188 51.188v512c0 28.16-23.028 51.188-51.188 51.188h-17.063v204.813c0 112.928-91.885 204.813-204.813 204.813h-136.5zM443.75 823.437h136.5c37.644 0 68.25-30.608 68.25-68.25v-204.813h-273.063v204.813c0 37.644 30.671 68.25 68.313 68.25zM398.063 500.187h269.063v-77.5h-190.75c-0.507-8.614-1.299-18.479-2.313-29.625-0.507-10.64-1.237-21.541-2.25-32.688-0.507-11.147-1.299-21.805-2.313-31.938s-1.987-18.72-3-25.813c74.986-4.054 130.221-21.288 165.688-51.688 35.973-29.894 53.937-70.939 53.938-123.125 0-23.813-4.081-45.858-12.188-66.125s-20.468-37.744-37.188-52.438c-16.72-14.693-37.792-26.324-63.125-34.938-25.334-8.107-54.929-12.187-88.875-12.188-13.68 0-27.626 1.036-41.813 3.063s-27.889 4.523-41.063 7.563c-12.667 2.533-24.054 5.335-34.188 8.375s-17.746 5.842-22.813 8.375l17.5 77.5c10.64-5.067 25.802-10.608 45.563-16.688 20.267-5.573 44.87-8.375 73.75-8.375 19.76 0 36.751 2.009 50.938 6.063 14.186 4.56 25.574 10.65 34.188 18.25 9.12 7.6 15.696 16.186 19.75 25.813 4.053 10.133 6.062 20.791 6.063 31.938 0 16.72-3.289 31.701-9.875 44.875s-18.218 24.317-34.938 33.438c-16.214 9.12-38.259 15.94-66.125 20.5-27.36 4.56-62.072 6.812-104.125 6.813 5.573 48.64 9.897 94.801 12.938 138.375 3.040 44.080 5.536 88.107 7.563 132.188z" />
 <glyph unicode="&#xe649;" glyph-name="agenda" d="M320 576h128v-128h-128zM512 576h128v-128h-128zM704 576h128v-128h-128zM128 192h128v-128h-128zM320 192h128v-128h-128zM512 192h128v-128h-128zM320 384h128v-128h-128zM512 384h128v-128h-128zM704 384h128v-128h-128zM128 384h128v-128h-128zM832 960v-64h-128v64h-448v-64h-128v64h-128v-1024h960v1024h-128zM896 0h-832v704h832v-704z" />
 <glyph unicode="&#xe64a;" glyph-name="ajout-colle" d="M992 576h-352v352c0 17.672-14.328 32-32 32h-192c-17.672 0-32-14.328-32-32v-352h-352c-17.672 0-32-14.328-32-32v-192c0-17.672 14.328-32 32-32h352v-352c0-17.672 14.328-32 32-32h192c17.672 0 32 14.328 32 32v352h352c17.672 0 32 14.328 32 32v192c0 17.672-14.328 32-32 32zM958.454 190.145c0 0-24.773 46.503-83.81 47.377-36.65 0.543-102.27-30.681-102.19-112.544 0.084-86.010 69.942-113.203 102.686-112.398 27.915 0.686 61.171 12.84 84.039 48.86 0.229-22.411-0.509-46.731 0.108-69.597-23.462-14.295-50.084-34.075-104.461-31.33-85.844 4.334-149.030 79.244-149.945 165.399-1.114 104.9 86.806 157.013 130.992 162.992 61.646 8.341 102.132-15.219 122.256-31.709 0.274-38.882 0.323-67.049 0.323-67.049z" />
+<glyph unicode="&#xe64b;" glyph-name="messages" horiz-adv-x="1152" d="M480 832c-50.666 0-99.582-7.95-145.386-23.628-42.924-14.694-81.114-35.436-113.502-61.646-60.044-48.59-93.112-110.802-93.112-175.174 0-35.99 10.066-70.948 29.92-103.898 20.686-34.34 51.898-65.794 90.26-90.958 30.44-19.968 50.936-51.952 56.362-87.95 0.902-5.99 1.63-12.006 2.18-18.032 2.722 2.52 5.424 5.114 8.114 7.794 24.138 24.040 56.688 37.312 90.322 37.312 5.348 0 10.718-0.336 16.094-1.018 19.36-2.452 39.124-3.696 58.748-3.696 50.666 0 99.58 7.948 145.384 23.628 42.926 14.692 81.116 35.434 113.504 61.644 60.046 48.59 93.112 110.802 93.112 175.174s-33.066 126.582-93.112 175.174c-32.388 26.212-70.578 46.952-113.504 61.646-45.804 15.678-94.718 23.628-145.384 23.628zM480 960v0c265.096 0 480-173.914 480-388.448s-214.904-388.448-480-388.448c-25.458 0-50.446 1.62-74.834 4.71-103.106-102.694-222.172-121.108-341.166-123.814v25.134c64.252 31.354 116 88.466 116 153.734 0 9.106-0.712 18.048-2.030 26.794-108.558 71.214-177.97 179.988-177.97 301.89 0 214.534 214.904 388.448 480 388.448zM996 89.314c0-55.942 36.314-104.898 92-131.772v-21.542c-103.126 2.318-197.786 18.102-287.142 106.126-21.14-2.65-42.794-4.040-64.858-4.040-95.47 0-183.408 25.758-253.614 69.040 144.674 0.506 281.26 46.854 384.834 130.672 52.208 42.252 93.394 91.826 122.414 147.348 30.766 58.866 46.366 121.582 46.366 186.406 0 10.448-0.45 20.836-1.258 31.168 72.57-59.934 117.258-141.622 117.258-231.676 0-104.488-60.158-197.722-154.24-258.764-1.142-7.496-1.76-15.16-1.76-22.966z" />
+<glyph unicode="&#xe64c;" glyph-name="lock" d="M802.133 550.396h-17.067v204.8c0 112.928-91.872 204.8-204.8 204.8h-136.533c-112.928 0-204.8-91.872-204.8-204.8v-204.8h-17.067c-28.16 0-51.2-23.040-51.2-51.2v-511.999c0-28.16 23.040-51.2 51.2-51.2h580.266c28.16 0 51.2 23.040 51.2 51.2v511.999c0 28.16-23.040 51.2-51.2 51.2zM375.467 755.195c0 37.643 30.624 68.267 68.267 68.267h136.533c37.643 0 68.267-30.624 68.267-68.267v-204.8h-273.066v204.8z" />
+<glyph unicode="&#xe64d;" glyph-name="locktotal" d="M443.73 959.996c-112.928 0-204.797-91.873-204.797-204.801v-204.801h-17.070c-28.16 0-51.199-23.039-51.199-51.199v-512c0-28.16 23.039-51.199 51.199-51.199h580.266c28.16 0 51.199 23.039 51.199 51.199v512c0 28.159-23.037 51.197-51.195 51.199h-17.066v204.801c0 112.928-91.873 204.801-204.801 204.801h-136.535zM443.73 823.461h136.535c37.644 0 68.266-30.624 68.266-68.266v-204.801h-273.066v204.801c0 37.644 30.624 68.266 68.266 68.266zM468.648 500.219l98.047-0.012 0.406-183.074c0 0-1.107-31.94-8.133-106.848-3.334-35.552-8.242-72.605-8.242-72.605l-65.363 0.023c0 0-5.242 35.191-8.898 72.656-7.194 73.722-8.359 107.48-8.359 107.48l0.543 182.379zM517.496 85c0.001 0 0.003 0 0.004 0 33.687 0 60.996-27.306 61-60.992v0l-61-0.008h61c0-33.689-27.311-61-61-61v0c-33.688 0-60.998 27.308-61 60.996v0c0 0.001 0 0.003 0 0.004 0 33.688 27.308 60.998 60.996 61v0z" />
+<glyph unicode="&#xe64e;" glyph-name="editemultiple" d="M864 960c88.364 0 160-71.634 160-160 0-36.020-11.91-69.258-32-96l-64-64-224 224 64 64c26.742 20.090 59.978 32 96 32zM64 224l-64-288 288 64 592 592-224 224-592-592zM715.578 596.422l-448-448-55.156 55.156 448 448 55.156-55.156zM944.814 322.508h-266.847c-24.461 0-44.475-20.014-44.475-44.475v-266.847c0-24.461 20.014-44.475 44.475-44.475h266.847c24.461 0 44.475 20.014 44.475 44.475v266.847c0 24.461-20.014 44.475-44.475 44.475zM789.153 46.45l-82.436 82.436 31.448 31.448 50.987-50.987 106.581 106.581 31.448-31.448-138.029-138.030z" />
+<glyph unicode="&#xe64f;" glyph-name="plie" d="M192 832l640-384-640-384z" />
+<glyph unicode="&#xe650;" glyph-name="deplie" d="M896 768l-384-640-384 640z" />
+<glyph unicode="&#xe651;" glyph-name="mailenvoi" d="M676.492 846.799c-90.915 0.283-185.555-6.515-268.293-46.484-24.502-12.035-45.499-27.879-65.255-46.963-14.314-17.746-27.432-37.053-32.001-59.338-12.987-48.33 5.401-98.475 33.969-137.396 21.042-27.609 46.976-53.198 75.537-73.1 8.291-9.673 16.518-9.686 2.47 3.519-28.78 35.785-50.875 83.78-37.505 130.735 6.129 17.822 16.229 32.511 31.361 44.655 26.584 23.498 62.119 34.629 96.34 42.864 53.562 12.261 108.689 13.459 163.376 13.144 0.151-20.822-0.385-64.435 0.33-85.246 97.622 40.596 223.369 108.5 320.381 150.563-96.929 43.986-223.634 105.626-320.997 148.621-0.324-28.524 0.286-57.035 0.286-85.574zM75.9 630.639c-0.3-0.202-1.9-0.605-3.7-1.009-21.5-3.734-41.8-25.027-45-46.925-0.4-3.027-0.7-135.226-0.6-293.662l0.1-288.112 2.1-6.559c4.3-13.321 13-24.522 24.7-31.99 4.5-2.927 8.3-4.743 11-5.348 0.6-0.101 1.8-0.605 2.7-1.11s3.4-1.211 5.5-1.615c2.1-0.505 200.2-0.807 440.3-0.807h436.5l7.4 2.422c18 6.055 31.7 19.477 37.8 37.137l2.7 7.871v581.27l-2.2 7.064c-6 19.477-21.2 34.21-41.1 39.76-7.5 2.119-8.5 2.119-238.3 2.119-126.9 0-230.8-0.202-230.8-0.505 0-0.202-1.1-4.44-2.5-9.183-3.5-12.009-4-16.853-3.9-33.201 0.1-15.541 2.2-27.247 7-39.559 3.2-8.174 10.2-20.385 14.8-26.036l3.4-4.037 173.3-0.202c95.4 0 173.9-0.202 174.4-0.202 0.6-0.101-2.1-2.119-6-4.541-3.8-2.422-7.5-4.945-8.2-5.55s-1.5-1.11-1.8-1.11c-1 0-17-10.495-17.3-11.302-0.2-0.505-0.8-0.807-1.3-0.807s-13.6-8.073-29.2-17.963c-15.5-9.89-46-29.265-67.7-43.091-40.3-25.531-55.7-35.32-69-43.898-4.1-2.624-16.5-10.495-27.5-17.458s-20.5-13.119-21.2-13.724c-0.7-0.605-1.4-1.11-1.7-1.11-0.8 0-17.1-10.596-17.4-11.302-0.2-0.505-0.8-0.807-1.3-0.807-1.1 0-17.2-10.192-18.1-11.605-0.4-0.505-0.8-0.605-0.8-0.101s-0.8 0.202-1.7-0.605c-2.8-2.422-48.3-31.082-49.4-31.082-0.5 0-3.3 1.514-6.2 3.431-2.8 1.917-36.5 23.311-74.7 47.531s-72.9 46.32-77 48.944c-6.4 4.138-71.5 45.513-127.5 81.035-21.2 13.422-33.3 21.091-43.3 27.651-4.5 2.927-8.7 5.348-9.2 5.348-0.6 0-1 0.605-1 1.312 0 0.908 11.1 1.211 52.2 1.11 28.8-0.101 52.6 0.101 52.9 0.202 0.4 0.202-3.4 4.339-8.3 9.183-21.5 21.192-32.6 42.082-41.3 77.503-1.5 6.156-2.9 12.009-3.1 13.119s-0.9 4.44-1.5 7.367l-1 5.248h-73.3c-40.3 0-73.5-0.202-73.7-0.505zM192.1 369.572c23.9-15.036 87.1-54.898 140.4-88.603 53.4-33.706 108.6-68.622 122.9-77.604 14.2-8.981 27.5-17.156 29.6-18.266 10.8-5.55 29.9-7.569 41.2-4.44 2.9 0.807 5.8 1.615 6.3 1.816 4.6 1.11 21.4 11.202 65 39.155 2.2 1.413 4.4 2.523 4.9 2.624 0.5 0 1.2 0.404 1.5 0.908 0.6 0.908 31.4 20.486 33.5 21.394 0.6 0.202 1.5 0.807 2.1 1.312 2.2 1.917 13.6 8.881 14.5 8.881 0.4 0 1 0.303 1.2 0.807 0.2 0.404 11.1 7.468 24.3 15.743s27 17.055 30.7 19.477c3.7 2.422 7.1 4.339 7.7 4.339 0.5 0 1.1 0.303 1.3 0.807 0.2 0.404 17.2 11.403 37.8 24.32 20.6 13.018 38.3 24.32 39.3 25.128 0.9 0.807 1.7 1.11 1.7 0.605s0.4-0.404 0.8 0.202c0.7 1.009 32.9 21.596 33.8 21.596 0.3 0 1 0.505 1.7 1.009 2.3 2.018 13.8 9.082 14.7 9.082 0.4 0 1 0.303 1.2 0.807 0.3 0.605 22.8 15.238 25 16.146 0.5 0.202 0.7-69.53 0.6-154.904l-0.3-155.207-2.4-4.541c-2.4-4.743-7.8-10.394-12.1-12.614-7.5-3.936 0.2-3.835-350-3.835l-338.5 0.101-5.7 2.119c-6.6 2.523-13.4 8.679-16.3 14.734-2 3.936-2 6.963-2.3 159.244-0.1 85.273-0.1 155.106 0.2 155.106 0.2 0 19.9-12.312 43.7-27.449z" />
+<glyph unicode="&#xe652;" glyph-name="nok" d="M1014.662 137.34c-0.004 0.004-0.008 0.008-0.012 0.010l-310.644 310.65 310.644 310.65c0.004 0.004 0.008 0.006 0.012 0.010 3.344 3.346 5.762 7.254 7.312 11.416 4.246 11.376 1.824 24.682-7.324 33.83l-146.746 146.746c-9.148 9.146-22.45 11.566-33.828 7.32-4.16-1.55-8.070-3.968-11.418-7.31 0-0.004-0.004-0.006-0.008-0.010l-310.648-310.652-310.648 310.65c-0.004 0.004-0.006 0.006-0.010 0.010-3.346 3.342-7.254 5.76-11.414 7.31-11.38 4.248-24.682 1.826-33.83-7.32l-146.748-146.748c-9.148-9.148-11.568-22.452-7.322-33.828 1.552-4.16 3.97-8.072 7.312-11.416 0.004-0.002 0.006-0.006 0.010-0.010l310.65-310.648-310.65-310.652c-0.002-0.004-0.006-0.006-0.008-0.010-3.342-3.346-5.76-7.254-7.314-11.414-4.248-11.376-1.826-24.682 7.322-33.83l146.748-146.746c9.15-9.148 22.452-11.568 33.83-7.322 4.16 1.552 8.070 3.97 11.416 7.312 0.002 0.004 0.006 0.006 0.010 0.010l310.648 310.65 310.648-310.65c0.004-0.002 0.008-0.006 0.012-0.008 3.348-3.344 7.254-5.762 11.414-7.314 11.378-4.246 24.684-1.826 33.828 7.322l146.746 146.748c9.148 9.148 11.57 22.454 7.324 33.83-1.552 4.16-3.97 8.068-7.314 11.414z" />
+<glyph unicode="&#xe653;" glyph-name="active" d="M563.090 266.543v58.034c77.516 43.683 140.741 152.585 140.741 261.505 0 174.89 0 316.666-211.111 316.666s-211.111-141.776-211.111-316.666c0-108.92 63.225-217.822 140.741-261.505v-58.034c-238.711-19.514-422.222-136.805-422.222-278.609h623.613c0 70.902-1.153 157.045-1.232 265.384-15.509 7.682-47.509 12.682-59.419 13.225zM715.841 261.148v86.759h68.505l0.001 42c0 18.887 15.365 34.252 34.252 34.252h68.506c18.887 0 34.252-15.365 34.252-34.252v-128.759h-137.012zM715.84 261.148h-8.564c-14.129 0-25.69-11.561-25.69-25.69v-256.899c0-14.129 11.561-25.69 25.69-25.69h291.151c14.129 0 25.69 11.561 25.69 25.69v256.899c0 14.129-11.561 25.69-25.69 25.69h-8.564v128.759c0 56.661-46.096 102.759-102.759 102.759h-68.506c-56.661 0-102.759-46.096-102.759-102.759zM784.345 261.148v128.759z" />
+<glyph unicode="&#xe654;" glyph-name="desactive" d="M563.090 266.543v58.034c77.516 43.683 140.741 152.585 140.741 261.505 0 174.89 0 316.666-211.111 316.666s-211.111-141.776-211.111-316.666c0-108.92 63.225-217.822 140.741-261.505v-58.034c-238.711-19.514-422.222-136.805-422.222-278.609h623.613c0 70.902-1.153 157.045-1.232 265.384-15.509 7.682-47.509 12.682-59.419 13.225zM998.428 261.148h-8.564v102.759c0 56.661-46.096 102.759-102.759 102.759h-68.506c-56.661 0-102.759-46.096-102.759-102.759v-102.759h-8.564c-14.129 0-25.69-11.561-25.69-25.69v-256.899c0-14.129 11.561-25.69 25.69-25.69h291.151c14.129 0 25.69 11.561 25.69 25.69v256.899c0 14.129-11.561 25.69-25.69 25.69zM784.347 363.906c0 18.887 15.365 34.252 34.252 34.252h68.506c18.887 0 34.252-15.365 34.252-34.252v-102.759h-137.012v102.759z" />
+<glyph unicode="&#xe655;" glyph-name="supprutilisateur" d="M563.090 266.543v58.034c77.516 43.683 140.741 152.585 140.741 261.505 0 174.89 0 316.666-211.111 316.666s-211.111-141.776-211.111-316.666c0-108.92 63.225-217.822 140.741-261.505v-58.034c-238.711-19.514-422.222-136.805-422.222-278.609l583.949-4.219c-63.823 57.711-106.553 179.321-33.459 267.293 2.844 3.934 7.477 10.066 12.472 15.535zM811.275 357.437c-115.493 0-209.119-93.626-209.119-209.119s93.626-209.119 209.119-209.119c115.492 0 209.119 93.626 209.119 209.119s-93.626 209.119-209.119 209.119zM946.612 103.93h-268.681v88.776h268.681z" />
+<glyph unicode="&#xe656;" glyph-name="valideutilisateur" d="M563.090 266.543v58.034c77.516 43.683 140.741 152.585 140.741 261.505 0 174.89 0 316.666-211.111 316.666s-211.111-141.776-211.111-316.666c0-108.92 63.225-217.822 140.741-261.505v-58.034c-238.711-19.514-422.222-136.805-422.222-278.609l595.956 0.384c-36.514 39.594-55.251 59.727-94.968 98.653 49.259 51.199 106.855 110.76 143.402 154.746-22.556 14.982-61.726 25.122-81.428 24.827zM955.207 268.179l-197.727-197.727-92.273 92.273-65.909-65.909 158.182-158.182 263.637 263.637z" />
+<glyph unicode="&#xe657;" glyph-name="lecture" d="M864 960h-768c-52.8 0-96-43.2-96-96v-832c0-52.8 43.2-96 96-96h768c52.8 0 96 43.2 96 96v832c0 52.8-43.2 96-96 96zM832 64h-704v768h704zM256 624h448v-64h-448zM256 496h448v-64h-448zM256 368h448v-64h-448zM256 752h448v-64h-448zM252.773 235.747h264v-64h-264z" />
+<glyph unicode="&#xe658;" glyph-name="nolecture" d="M222.654-64h641.346c52.8 0 96 43.2 96 96v665.218l-128-141.521v-491.697h-483.654zM128 285.494v546.506h563.031l120.969 128h-716c-52.8 0-96-43.2-96-96v-711.425zM256 624h448v-64h-448zM256 496h448v-64h-448zM256 368h448v-64h-448zM256 752h448v-64h-448zM252.773 239.747h264v-64h-264zM993.909 929.806c-19.541 19.659-51.221 19.659-70.762 0l-900.658-906.169c-19.541-19.661-19.541-51.534 0-71.195 9.77-9.832 22.575-14.746 35.38-14.746s25.61 4.915 35.382 14.744l900.658 906.169c19.539 19.661 19.539 51.536 0 71.197z" />
 </font></defs></svg>
\ No newline at end of file
Binary files cahier-de-prepa6.2.0/fonts/icomoon.ttf and cahier-de-prepa8.0.1/fonts/icomoon.ttf differ
Binary files cahier-de-prepa6.2.0/fonts/icomoon.woff and cahier-de-prepa8.0.1/fonts/icomoon.woff differ
diff -urN cahier-de-prepa6.2.0/gestioncompte.php cahier-de-prepa8.0.1/gestioncompte.php
--- cahier-de-prepa6.2.0/gestioncompte.php	1970-01-01 01:00:00.000000000 +0100
+++ cahier-de-prepa8.0.1/gestioncompte.php	2018-10-16 22:38:26.652841918 +0200
@@ -0,0 +1,409 @@
+<?php
+// Sécurité
+define('OK',1);
+// Configuration
+include('config.php');
+// Fonctions
+include('fonctions.php');
+
+// Titre : on récupère celui de la première page
+$mysqli = connectsql();
+$resultat = $mysqli->query('SELECT titre FROM pages WHERE id = 1');
+$mysqli->close();
+$r = $resultat->fetch_row();
+$resultat->free();
+$titre = $r[0];
+
+/////////////////////////////////////
+// Récupération du type de demande //
+/////////////////////////////////////
+
+// Action
+if ( isset($_REQUEST['oublimdp']) )
+  // 4 étapes :
+  // oublimdp (get) -> affichage du formulaire mail
+  // oublimdp&mail (ajax) -> envoi du mail de vérification
+  // oublimdp&mail&p (retour mail, get) -> affichage du formulaire des mdp
+  // oublimdp&mail&p&mdp1&mdp2 (ajax) -> ok
+  // p est le sha1 de $site.$mdp.date('Y-m-d-H').$mail
+  $action = 'oublimdp';
+elseif ( isset($_REQUEST['creation']) ) {
+  // 4 étapes :
+  // creation (get) -> affichage du formulaire mail
+  // creation&mail (ajax) -> envoi du mail de vérification
+  // creation&mail&p (retour mail, get) -> affichage du formulaire des mdp
+  // creation&mail&p&prenom&nom&mdp1&mdp2&autorisation (ajax) -> ok
+  // $creation_compte réglée dans config.php : true si création autorisée
+  // p est le sha1 de $site.$mdp.date('Y-m-d-H').$mail
+  $action = 'creation';
+  // Vérification de l'ouverture des créations de compte
+  $mysqli = connectsql();
+  $resultat = $mysqli->query('SELECT val FROM prefs WHERE nom = "creation_compte"');
+  $r = $resultat->fetch_row();
+  $resultat->free();
+  if ( $r[0] == 0 )  {
+    debut($mysqli,$titre,'',false);
+    echo '<h2>La demande de création de compte n\'est pas autorisée sur ce Cahier de Prépa.</h2><p style="text-align:center;"><a href=".">Retour à la page d\'accueil</a></p>';
+    $mysqli->close();
+    fin();
+  }
+  $mysqli->close();
+}
+elseif ( isset($_REQUEST['invitation']) )
+  // 2 étapes :
+  // invitation&mail&p (retour mail généré dans ajax.php) -> formulaire
+  // invitation&mail&p&mdp1&mdp2 (ajax) -> ok
+  // p est le sha1 de $site.$mdp.$mail (validité permanente)
+  $action = 'invitation';
+else  {
+  // Aucune action
+  $mysqli = connectsql();
+  debut($mysqli,$titre,'',false);
+  echo '<h2>Aucune action n\'a été effectuée.</h2><p style="text-align:center;"><a href=".">Retour à la page d\'accueil</a></p>';
+  $mysqli->close();
+  fin();
+}
+
+///////////////////////////////////////////////////////////////////
+// Réponses successives : seulement si $_REQUEST['mail'] définie //
+///////////////////////////////////////////////////////////////////
+
+if ( isset($_REQUEST['mail']) )  {
+  
+  if ( !strlen($mail = str_replace('__','@',strtolower(trim($_REQUEST['mail'])))) || !filter_var($mail,FILTER_VALIDATE_EMAIL) )  {
+    if ( $_SERVER['REQUEST_METHOD'] == 'GET' )  {
+      $mysqli = connectsql();
+      debut($mysqli,$titre,'',false);
+      $mysqli->close();
+      echo '<h2>Les données que vous venez de saisir sont incomplètes ou erronées.</h2><p>Si vous avez cliqué sur un lien depuis un webmail, il est très probable que ce lien ait été coupé. Vous devez copier l\'adresse et la coller dans la barre d\'adresse de votre navigateur.</p><p style="text-align:center;"><a href=".">Retour à la page d\'accueil</a></p>';
+      fin();
+    }
+    exit('{"etat":"nok_","message":"L\'adresse saisie n\'est pas une adresse électronique valide.<br>Vous avez dû faire une faute de frappe."}');
+  }
+  if ( isset($_REQUEST['p']) )  {
+    $p = $_REQUEST['p'];
+    // Pour invitation, $p est le hash de $site.$mdp.$mail
+    // Pour oublimdp et creation, $site.$mdp.date('Y-m-d-H').$mail
+    if ( !( ( $action == 'invitation' ) && ( $p == sha1($site.$mdp.$mail) ) || ( $p == sha1($site.$mdp.date('Y-m-d-H').$mail) ) || ( $p == sha1($site.$mdp.date('Y-m-d-H',time()+900).$mail) ) ) )  {
+      if ( $_SERVER['REQUEST_METHOD'] == 'GET' )  {
+        $mysqli = connectsql();
+        debut($mysqli,$titre,'',false);
+        $mysqli->close();
+        echo '<h2>Les données que vous venez de saisir sont incomplètes ou erronées.</h2><p>Si vous avez cliqué sur un lien depuis un webmail, il est très probable que ce lien ait été coupé. Vous devez copier l\'adresse et la coller dans la barre d\'adresse de votre navigateur.</p><p style="text-align:center;"><a href=".">Retour à la page d\'accueil</a></p>';
+        fin();
+      }
+      exit('{"etat":"nok_","message":"Les paramètres d\'identification ne sont plus corrects, vous avez dû attendre trop longtemps. Veuillez recommencer la procédure."}');
+    }
+  }
+  
+  // À partir d'ici, $mail est défini et est valide.
+  // Si $p est défini, il est correct. (si non défini, mail à envoyer)
+  switch ( $action )  {
+    // Oubli d'identifiant
+    case 'oublimdp': {
+
+      // Recherche de l'adresse électronique dans la base de données
+      // Les comptes non encore validés et suspendus ont un mdp de 41 caractères.
+      $mysqli = connectsql();
+      $resultat = $mysqli->query('SELECT id, login, mail FROM utilisateurs WHERE mdp > \'\' AND mail > \'\'');
+      $mysqli->close();
+      while ( $r = $resultat->fetch_assoc() )
+        if ( $r['mail'] == $mail )  {
+          $utilisateur = $r;
+          break;
+        }
+      $resultat->free();
+      if ( !isset($utilisateur) )
+        exit('{"etat":"nok_","message":"L\'adresse électronique donnée est inconnue. Si vous venez de demander la création de votre compte, le mot de passe n\'est pas modifiable tant que les professeurs de la classe n\'ont pas validé l\'inscription. Vous recevrez un courriel quand cela sera effectif.<p class=\"warning\"><a href=\".\">Retourner au Cahier de Prépa</a></p>"}');
+
+      // Envoi de courriel
+      if ( !isset($p) )  {
+        // On ajoute 15 minutes au temps utilisé : de xh00 à xh45,
+        // on a jusqu'à (x+1)h, de xh45 à (x+1)h on a jusqu'à (x+2)h
+        $t = time() + 900;
+        $lien = 'https://'.$site.'/gestioncompte?oublimdp&mail='.str_replace('@','__',$mail).'&p='.sha1($site.$mdp.date('Y-m-d-H',$t).$mail);
+        mail($mail,'=?UTF-8?B?'.base64_encode('[Cahier de Prépa] Changement de mot de passe').'?=',
+"Bonjour
+
+Vous avez rempli une demande de modification de votre mot de passe sur le Cahier de Prépa <https://$site>, correspondant à l'identifiant ${utilisateur['login']}.
+
+Si cette demande ne vient pas de vous ou si vous avez retrouvé votre mot de passe, merci d'ignorer simplement ce courriel.
+
+Sinon, veuillez cliquer ci-dessous pour vous rendre à la page qui vous permettra de modifier votre mot de passe :
+    $lien
+Ce lien est valable jusqu'à ".date('G\h00',$t+3600).'. Si ce lien ne s\'ouvre pas correctement, il a peut-être été coupé lors du clic : dans ce cas, essayez à nouveau en copiant-collant le lien.
+
+Cordialement,
+-- 
+Cahier de Prépa
+','From: =?UTF-8?B?'.base64_encode('Cahier de Prépa')."?= <$mailadmin>\r\nContent-type: text/plain; charset=UTF-8","-f$mailadmin");
+        exit('{"etat":"ok_","message":"Un courriel vient de vous être envoyé à l\'adresse <code>'.$mail.'</code>.<br>Si vous ne voyez rien, pensez à regarder dans les courriels marqués comme spam. Certains serveurs retardent jusqu\'à 10 minutes l\'arrivée des messages, normalement la première fois uniquement.<br>Le courriel qui vous a été envoyé contiendra un lien, valable jusqu\'à '.date('G\h00',$t+3600).', vous permettant de modifier votre mot de passe.<p class=\"warning\"><a href=\".\">Retourner au Cahier de Prépa</a></p>"}');
+      }
+
+      // Retour de courriel : utilisation d'un passe
+      if ( isset($p) && isset($_REQUEST['mdp1']) && strlen($newmdp = $_REQUEST['mdp1']) )  {
+        if ( $newmdp != $_REQUEST['mdp2'] )
+          exit('{"etat":"nok_","message":"Les deux mots de passe donnés ne sont pas identiques."}');
+        // Token de connexion automatique
+        $permconn = '';
+        for ( $i = 0; $i < 10; $i++ )
+          $permconn .= '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'[random_int(0,61)];
+        // Écriture du nouveau mot de passe
+        $mysqli = connectsql(true);
+        if( requete('utilisateurs',"UPDATE utilisateurs SET mdp = '".sha1($mdp.$newmdp)."', permconn = IF(permconn > '','$permconn','') WHERE id = ${utilisateur['id']}",$mysqli) )
+          exit('{"etat":"ok_","message":"Votre mot de passe a bien été modifié.</p><p class=\"warning\"><a href=\".\">Retourner au Cahier de Prépa</a></p>"}');
+        exit('{"etat":"nok_","message":"Votre mot de passe n\'a pas pu être modifié suite à une erreur technique."}' );
+      }
+      break;
+
+    }
+    case 'creation': {
+
+      // Impossibilités classiques
+      if ( in_array($domaine = strstr($mail,'@'),array('@gmail.fr','@laposte.fr')) )
+        exit('{"etat":"nok_","message":"L\'adresse saisie ne pourra pas fonctionner&nbsp;: le domaine <code>'.$domaine.'</code> ne reçoit pas de courriels."}');
+      // Recherche du mail dans la base de données
+      $mysqli = connectsql();
+      $resultat = $mysqli->query('SELECT id, login, mail FROM utilisateurs');
+      $mysqli->close();
+      while ( $r = $resultat->fetch_assoc() )
+        if ( $r['mail'] == $mail )  {
+          $resultat->free();
+          exit('{"etat":"nok_","message":"Un compte avec cette adresse électronique existe déjà. Si vous venez de demander la création de votre compte, le compte n\'est pas modifiable tant que les professeurs de la classe n\'ont pas validé l\'inscription. Vous recevrez un courriel quand cela sera effectif. Si ce compte a déjà été validé par les professeurs, vous pouvez <a href=\"?oublimdp\">changer le mot de passe</a>.<p class=\"warning\"><a href=\".\">Retourner au Cahier de Prépa</a></p>"}');
+        }
+      $resultat->free();
+
+      // Envoi de courriel
+      if ( !isset($p) )  {
+        // On ajoute 15 minutes au temps utilisé : de xh00 à xh45,
+        // on a jusqu'à (x+1)h, de xh45 à (x+1)h on a jusqu'à (x+2)h
+        $t = time() + 900;
+        $lien = 'https://'.$site.'/gestioncompte?creation&mail='.str_replace('@','__',$mail).'&p='.sha1($site.$mdp.date('Y-m-d-H',$t).$mail);
+        mail($mail,'=?UTF-8?B?'.base64_encode('[Cahier de Prépa] Création de compte').'?=',
+"Bonjour
+
+Vous venez de donner cette adresse pour une demande de création de compte sur le Cahier de Prépa <https://$site>.
+
+Si cette demande ne vient pas de vous, merci d'ignorer simplement ce courriel.
+
+Sinon, veuillez cliquer ci-dessous pour vous rendre à la page qui vous permettra de terminer votre inscription :
+   $lien
+Ce lien est valable jusqu'à ".date('G\h00',$t+3600).'. Si ce lien ne s\'ouvre pas correctement, il a peut-être été coupé lors du clic : dans ce cas, essayez à nouveau en copiant-collant le lien. 
+
+Cordialement,
+-- 
+Cahier de Prépa
+','From: =?UTF-8?B?'.base64_encode('Cahier de Prépa')."?= <$mailadmin>\r\nContent-type: text/plain; charset=UTF-8","-f$mailadmin");
+        exit('{"etat":"ok_","message":"Un courriel vient de vous être envoyé à l\'adresse <code>'.$mail.'</code>.<br>Si vous ne voyez rien, pensez à regarder dans les courriels marqués comme spam. Vérifiez bien qu\'il n\'y a pas d\'erreur dans cette adresse, car vous ne pourrez pas continuer votre inscription si elle est fausse. Certains serveurs retardent jusqu\'à 10 minutes l\'arrivée des messages, la première fois uniquement.<br>Le courriel qui vous a été envoyé contiendra un lien, valable jusqu\'à '.date('G\h00',$t+3600).', vous permettant de terminer votre inscription.<p class=\"warning\"><a href=\".\">Retourner au Cahier de Prépa</a></p>"}');
+      }
+      
+      // Retour de courriel : utilisation d'un passe
+      if ( isset($p) && isset($_REQUEST['nom']) )  {
+        // Spécifications pour les manipulations de caractères sur 2 octets (accents)
+        mb_internal_encoding('UTF-8');
+        if ( !strlen($nom = trim($_REQUEST['nom'])) )
+          exit('{"etat":"nok_","message":"Le nom est obligatoire."}');
+        if ( !isset($_REQUEST['prenom']) || !strlen($prenom = trim($_REQUEST['prenom'])) )
+          exit('{"etat":"nok_","message":"Le prénom est obligatoire."}');
+        if ( !isset($_REQUEST['mdp1']) || !strlen($newmdp = $_REQUEST['mdp1']) )
+          exit('{"etat":"nok_","message":"Le mot de passe est obligatoire."}');
+        if ( $newmdp != $_REQUEST['mdp2'] )
+          exit('{"etat":"nok_","message":"Les deux mots de passe donnés ne sont pas identiques."}');
+        // Nettoyage des données envoyées
+        $mysqli = connectsql();
+        $prenom = mb_convert_case(strip_tags($mysqli->real_escape_string($prenom)),MB_CASE_TITLE);
+        $nom = mb_convert_case(strip_tags($mysqli->real_escape_string($nom)),MB_CASE_TITLE);
+        $mail = $mysqli->real_escape_string($mail);
+        $newmdp = sha1($mdp.$newmdp);
+        // Login déterminé automatiquement
+        $login = mb_strtolower(mb_substr($prenom,0,1).str_replace(' ','_',$nom));
+        $resultat = $mysqli->query('SELECT GROUP_CONCAT(login) AS logins FROM utilisateurs');
+        $mysqli->close();
+        $r = $resultat->fetch_assoc();
+        $resultat->free();
+        if ( in_array($login,explode(',',$r['logins'])) )
+          exit('{"etat":"nok_","message":"Un compte avec le même identifiant existe déjà. Merci de vous connecter avec l\'adresse électronique correspondante."}');
+        // Écriture du nouveau compte
+        $mysqli = connectsql(true);
+        if( requete('utilisateurs',"INSERT INTO utilisateurs SET login = '$login', prenom = '$prenom', nom = '$nom', mail = '$mail', mdp = '*$newmdp', autorisation = 2, matieres = CONCAT('0,',(SELECT GROUP_CONCAT(id) AS matieres FROM matieres)), timeout = 3600, mailexp = '$prenom $nom', mailcopie = 1, mailenvoi = 0, permconn = ''",$mysqli) )
+          exit('{"etat":"ok_","message":"Votre demande d\'inscription est terminée. Elle est maintenant en attente de validation par les professeurs de la classe. Vous recevrez un courriel lorsque votre inscription sera validée.</p><p class=\"warning\"><a href=\".\">Retourner au Cahier de Prépa</a></p>"}');
+        exit('{"etat":"nok_","message":"Votre demande d\'inscription n\'a pas pu être enregistrée suite à une erreur technique."}');
+      }
+      break;
+
+    }
+    case 'invitation': {
+      
+      // Recherche de l'adresse électronique dans la base de données
+      // Les comptes non encore validés ont un mot de passe égal à '?'.
+      $mysqli = connectsql();
+      $resultat = $mysqli->query('SELECT id, login, mail FROM utilisateurs WHERE mdp = \'?\'');
+      $mysqli->close();
+      while ( $r = $resultat->fetch_assoc() )
+        if ( $r['mail'] == $mail )  {
+          $utilisateur = $r;
+          break;
+        }
+      $resultat->free();
+      if ( !isset($utilisateur) )  {
+        $mysqli = connectsql();
+        debut($mysqli,$titre,'L\'adresse électronique donnée est inconnue. Il est probable que vous ayez déjà répondu à l\'invitation envoyée ou que le compte pour lequel vous avez été invité ait été supprimé.',false);
+        $mysqli->close();
+        fin();
+      }
+  
+      // Modification du mot de passe
+      if ( isset($_REQUEST['mdp1']) && strlen($newmdp = $_REQUEST['mdp1']) )  {
+        if ( $newmdp != $_REQUEST['mdp2'] )
+          exit('{"etat":"nok_","message":"Les deux mots de passe donnés ne sont pas identiques."}');
+        // Token de connexion automatique
+        $permconn = '';
+        for ( $i = 0; $i < 10; $i++ )
+          $permconn .= '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'[random_int(0,61)];
+        // Écriture du nouveau mot de passe
+        $mysqli = connectsql(true);
+        if( requete('utilisateurs',"UPDATE utilisateurs SET mdp = '".sha1($mdp.$newmdp)."', permconn = '$permconn' WHERE id = ${utilisateur['id']}",$mysqli) )
+          exit('{"etat":"ok_","message":"Votre mot de passe a bien été modifié. Votre compte est opérationnel.</p><p class=\"warning\"><a href=\".\">Retourner au Cahier de Prépa</a></p>"}');
+        exit('{"etat":"nok_","message":"Votre mot de passe n\'a pas pu être modifié suite à une erreur technique."}');
+      }
+    }
+  }
+}
+
+////////////
+/// HTML ///
+////////////
+$mysqli = connectsql();
+debut($mysqli,$titre,'',false);
+$mysqli->close();
+switch ( $action )  {
+  case 'oublimdp': {
+    if ( !isset($mail) )
+      echo <<<FIN
+
+  <article>
+    <a class="icon-ferme" onclick="history.back()" title="Revenir à la page précédente"></a>
+    <a class="icon-ok" title="Valider"></a>
+    <h3>Mot de passe oublié</h3>
+    <form onsubmit="return false;">
+      <p>Si vous avez oublié votre mot de passe, vous pouvez le récupérer en entrant votre adresse électronique ci-dessous. Vous recevrez un courriel contenant un lien temporaire permettant de modifier votre mot de passe.</p>
+      <input class="ligne" type="email" name="mail" autofocus placeholder="Adresse électronique">
+    </form>
+  </article>
+
+FIN;
+    else
+      echo <<<FIN
+
+  <article>
+    <a class="icon-ok" title="Valider"></a>
+    <h3>Mot de passe oublié</h3>
+    <form>
+      <p>Veuillez entrer deux fois votre nouveau mot de passe&nbsp;:</p>
+      <input class="ligne" type="password" name="mdp1" autofocus placeholder="Mot de passe">
+      <input class="ligne" type="password" name="mdp2" placeholder="Confirmation">
+      <input type="hidden" name="p" value="$p">
+      <input type="hidden" name="mail" value="$mail">
+    </form>
+  </article>
+
+FIN;
+    break;
+  }
+  case 'creation': {
+    if ( !isset($mail) )
+      echo <<<FIN
+
+  <article>
+    <a class="icon-ferme" onclick="history.back()" title="Revenir à la page précédente"></a>
+    <a class="icon-ok" title="Valider"></a>
+    <h3>Création de compte</h3>
+    <form onsubmit="return false;">
+      <p>Vous pouvez demander ici une création de compte sur ce Cahier de Prépa, si vous êtes élève dans cette classe. Vous devez tout d'abord fournir une adresse électronique valide. Vous recevrez un courriel contenant un lien temporaire permettant de continuer votre inscription.</p>
+      <p>Il est conseillé de fournir une adresse électronique régulièrement consultée&nbsp;: elle pourra servir aux professeurs et aux colleurs pour vous contacter.</p>
+      <p>À la fin de votre inscription, la demande sera mise en attente de validation par les professeurs de la classe.</p>
+      <p>Aucune donnée personnelle n'est enregistrée à cette étape.</p>
+      <input class="ligne" type="email" name="mail" autofocus placeholder="Adresse électronique">
+    </form>
+  </article>
+
+FIN;
+    else
+      echo <<<FIN
+
+  <article>
+    <a class="icon-ok" title="Valider"></a>
+    <h3>Création de compte</h3>
+    <form>
+      <p>Vous pouvez demander ici une création de compte sur ce Cahier de Prépa, si vous êtes élève de cette classe. Une fois votre demande remplie, l'inscription sera mise en attente de validation par les professeurs de la classe. Vous recevrez un courriel dès que votre inscription sera validée.</p>
+      <p>Le mot de passe vous est complètement personnel et ne sera divulgué à personne. Il est chiffré avant son stockage dans la base de données. La bonne pratique est de ne pas écrire un simple mot du dictionnaire mais une suite de lettres, de chiffres et/ou de signes de ponctuation qui n'ont de sens que pour vous. Tous les caractères de votre clavier, y compris l'espace, sont autorisés.</p>
+      <p>Seules les données permettant le fonctionnement de ce site sont stockées&nbsp;: nom, prénom, adresse électronique. Aucune de ces données ne sera partagée avec une autre entité. En demandant la création de votre compte, vous autorisez l'administrateur du site à stocker ces informations. Vous pourrez supprimer votre compte à tout moment.</p>
+      <input class="ligne" type="text" name="prenom" size="50" autofocus placeholder="Prénom">
+      <input class="ligne" type="text" name="nom" size="50"" placeholder="Nom">
+      <input class="ligne" type="password" name="mdp1" placeholder="Mot de passe">
+      <input class="ligne" type="password" name="mdp2" placeholder="Confirmation">
+      <input type="hidden" name="p" value="$p">
+      <input type="hidden" name="mail" value="$mail">
+    </form>
+  </article>
+
+FIN;
+    break;
+  }
+  case 'invitation': {
+    if ( isset($utilisateur['login']) )
+      echo <<<FIN
+
+  <article>
+    <a class="icon-ok" title="Valider"></a>
+    <h3>Création de compte</h3>
+    <form>
+      <p>L'équipe pédagogique vous a créé un compte ici. L'identifiant associé à ce compte est</p>
+      <p class="warning">${utilisateur['login']}</p>
+      <p>Il ne vous reste plus qu'à définir votre mot de passe. Le mot de passe vous est complètement personnel et ne sera divulgué à personne. Il est chiffré avant son stockage dans la base de données. La bonne pratique est de ne pas écrire un simple mot du dictionnaire mais une suite de lettres, de chiffres et de signes de ponctuation qui n'ont de sens que pour vous. Tous les caractères de votre clavier, y compris l'espace, sont autorisés.</p>
+      <p>Seules les données permettant le fonctionnement de ce site sont stockées&nbsp;: nom, prénom, adresse électronique. Aucune de ces données ne sera partagée avec une autre entité. En demandant la création de votre compte, vous autorisez l'administrateur du site à stocker ces informations. Vous pourrez supprimer votre compte à tout moment.</p>
+      <input class="ligne" type="password" name="mdp1" autofocus placeholder="Mot de passe">
+      <input class="ligne" type="password" name="mdp2" placeholder="Confirmation">
+      <input type="hidden" name="p" value="$p">
+      <input type="hidden" name="mail" value="$mail">
+    </form>
+  </article>
+
+FIN;
+    else
+      echo '<h2>Les paramètres d\'accès à cette page ne sont pas corrects.</h2>';
+    break;
+  }
+  default:
+    echo '<h2>Aucune action n\'a été effectuée.</h2>';
+}
+?>
+  
+  <script type="text/javascript">
+$( function() {
+  // Envoi
+  $('a.icon-ok').on('click',function () {
+    $.ajax({url: 'gestioncompte.php', method: "post", data: '<?php echo $action;?>=&'+$('form').serialize(), dataType: 'json'})
+    .done( function(data) {
+      if ( data['etat'] == 'ok_' ) {
+        $('form').html('<p>'+data['message']+'</p>');
+        $('a.icon-ok, input').remove();
+      }
+      else if ( data['etat'] == 'nok_' )  {
+        $('p:first').html(data['message']).addClass('warning');
+        $('p').not(':first').not('.ligne').remove();
+      }
+    });
+  });
+  // Envoi par appui sur Entrée
+  $('input,select').on('keypress',function (e) {
+    if ( e.which == 13 ) {
+      $('a.icon-ok').click();
+      return false;
+    }
+  });
+});
+  </script>
+<?php
+fin();
+?>
diff -urN cahier-de-prepa6.2.0/groupes.php cahier-de-prepa8.0.1/groupes.php
--- cahier-de-prepa6.2.0/groupes.php	2015-10-27 14:26:00.370565981 +0100
+++ cahier-de-prepa8.0.1/groupes.php	2018-10-13 00:14:13.766307094 +0200
@@ -10,62 +10,81 @@
 // Autorisation //
 //////////////////
 
-// Accès aux professeurs connectés uniquement
+// Accès aux professeurs connectés uniquement. Redirection pour les autres.
+if ( $autorisation < 5 )  {
+  header("Location: https://$site");
+  exit();
+}
 $mysqli = connectsql();
-if ( !$autorisation )  {
-  $titre = 'Groupes';
-  $actuel = false;
+// Si connexion light : on doit répéter son mot de passe pour aller plus loin
+// login.php contient fin()
+if ( $_SESSION['light'] )  {
+  $titre = 'Modification des groupes';
+  $actuel = 'groupes';
   include('login.php');
 }
-elseif ( $autorisation < 4 )  {
-  debut($mysqli,'Groupes','Vous n\'avez pas accès à cette page.',$autorisation,' ');
-  $mysqli->close();
-  fin();
-}
 
-// Récupération des élèves et fabrication du formulaire de modification des utilisateurs
-$resultat = $mysqli->query('SELECT id, IF(LENGTH(nom),CONCAT(nom," ",prenom),CONCAT(login," (identifiant)")) AS nom FROM utilisateurs WHERE autorisation = 2 AND LENGTH(mdp)=40 ORDER BY nom');
-// Impossible d'aller plus loin si aucun élève existe
-if ( !$resultat->num_rows )  {
-  debut($mysqli,'Groupes','Aucun élève n\'existe dans la base de données. Vous devez commencer par renseigner leur existence, soit vous-même via la page <a href="utilisateurs">Utilisateurs</a>, soit en leur disant de demander individuellement un compte se connecter individuellement (icône de connexion puis <em>Créer un compte</em>)',4,'groupes');
-  $mysqli->close();
-  fin();
-}
-$eleves = array();
-$formulaire = '';
+// Récupération des utilisateurs et fabrication du formulaire de modification des utilisateurs
+$resultat = $mysqli->query('SELECT id, autorisation, IF(nom > \'\',CONCAT(nom,\' \',prenom),CONCAT(\'<em>\',login,\'</em>\')) AS nomcomplet,
+                            (mail=\'\') AS pasmail, (LEFT(mdp,1)=\'!\') AS desactive, (LEFT(mdp,1)=\'*\') AS demande, (mdp=\'?\') AS invitation
+                            FROM utilisateurs WHERE autorisation > 1 ORDER BY autorisation DESC, nom, prenom, login');
+$a = 0;
+$utilisateurs = array();
+$table = '';
 while ( $r = $resultat->fetch_assoc() )  {
-  $eleves[$r['id']] = $r['nom'];
-  $formulaire .= "    <p class=\"ligne\"><label for=\"u${r['id']}\">${r['nom']}</label> <input type=\"checkbox\" name=\"u${r['id']}\"></p>\n";
+  $utilisateurs[$r['id']] = $r['nomcomplet'];
+  if ( $a != $r['autorisation'] )  {
+    $a = $r['autorisation'];
+    switch ( $a )  {
+      case 2 : $t = 'Élèves'; break;
+      case 3 : $t = 'Colleurs'; break;
+      case 4 : $t = 'Administratifs'; break;
+      case 5 : $t = 'Professeurs'; break;
+    }
+    $table .= <<<FIN
+        <tr class="categorie">
+          <th>$t</th>
+          <th><a class="icon-cocher"></a></th>
+        </tr>
+
+FIN;
+  }
+  if ( $r['pasmail'] == 1 )         $r['nomcomplet'] .= ' (pas d\'adresse électronique)';
+  elseif ( $r['desactive'] == 1 )   $r['nomcomplet'] .= ' (compte désactivé)';
+  elseif ( $r['demande'] == 1 )     $r['nomcomplet'] .= ' (demande non répondue)';
+  elseif ( $r['invitation'] == 1 )  $r['nomcomplet'] .= ' (invitation non répondue)';
+  $table .= "        <tr><td>${r['nomcomplet']}</td><td><input type=\"checkbox\" id=\"u${r['id']}\"></td></tr>\n";
 }
 $resultat->free();
 
+
 //////////////
 //// HTML ////
 //////////////
-debut($mysqli,'Modication des groupes d\'élèves',$message,4,'groupes');
+debut($mysqli,'Modication des groupes d\'utilisateurs',$message,5,'groupes');
 echo <<<FIN
 
-  <a class="icon-aide general" data-id="page" title="Aide pour les modifications des groupes"></a>
-  <a class="icon-ajoute general" data-id="ajoute" title="Ajouter un groupe d'élève"></a>
+  <div id="icones">
+    <a class="icon-ajoute formulaire" title="Ajouter un groupe d'utilisateurs"></a>
+    <a class="icon-aide" title="Aide pour les modifications des groupes"></a>
+  </div>
 
 FIN;
-$select_mailnotes = '<option value="1">envoi de courriels</option><option value="2">notes de colles</option><option value="3">courriels et notes</option>';
 
-// Récupération
-$resultat = $mysqli->query('SELECT id, nom, mailnotes, eleves FROM groupes');
+// Récupération et affichage
+$resultat = $mysqli->query('SELECT id, nom, IF(mails,\' checked\',\'\') AS mails, IF(notes,\' checked\',\'\') AS notes, utilisateurs FROM groupes');
 if ( $resultat->num_rows )  {
   while ( $r = $resultat->fetch_assoc() )  {
-    $e =  implode(', ',array_intersect_key($eleves,array_flip(explode(',',$r['eleves']))));
-    $sel_mailnotes = str_replace("\"${r['mailnotes']}\"","\"${r['mailnotes']}\" selected",$select_mailnotes);
+    $u =  implode(', ',array_intersect_key($utilisateurs,array_flip(explode(',',$r['utilisateurs']))));
     echo <<<FIN
 
   <article data-id="groupes|${r['id']}">
+    <h3 class="edition">Groupe <span class="editable" data-id="groupes|nom|${r['id']}" data-placeholder="Nom du groupe (Ex: 1, A, LV2 Espagnol...)">${r['nom']}</span></h3>
+    <a class="icon-aide" title="Aide pour l'édition de ce groupe"></a>
     <a class="icon-supprime" title="Supprimer ce groupe"></a>
-    <h3 class="edition">Groupe <span class="editable" data-id="groupes|nom|${r['id']}">${r['nom']}</span></h3>
-    <p class="ligne"><label for="mailnotes${r['id']}">Utilisation&nbsp;: </label>
-      <select id="mailnotes${r['id']}" name="mailnotes">$sel_mailnotes</select>
-    </p>
-    <p class="ligne usergrp"><a class="icon-edite" data-uid="${r['eleves']}"></a><label>Élèves&nbsp;:</label> <span>$e</span></p>
+    <p class="usergrp"><strong>Utilisateurs&nbsp;:</strong> <span data-uids="${r['utilisateurs']}">$u</span></p>
+    <p class="ligne"><label for="mails${r['id']}">Groupe visible lors de l'envoi de courriel</label><input type="checkbox" id="mails${r['id']}"${r['mails']}></p>
+    <p class="ligne"><label for="notes${r['id']}">Groupe visible lors de la saisie des notes de colles</label><input type="checkbox" id="notes${r['id']}"${r['notes']}></p>
   </article>
 
 FIN;
@@ -79,51 +98,81 @@
 ?>
 
   <div id="form-utilisateurs">
-    <h3>Modifier les utilisateurs du groupe </h3>
-<?php echo $formulaire; ?>
+    <a class="icon-ok" title="Valider ces utilisateurs"></a>
+    <h3>Choix des utilisateurs du groupe </h3>
+    <form>
+    <table>
+      <tbody>
+<?php echo $table; ?>
+      </tbody>
+    </table>
+    </form>
   </div>
   
-  <form id="form-ajoute">
+  <form id="form-ajoute" data-action="ajout-groupe">
     <h3 class="edition">Ajouter un nouveau groupe</h3>
     <div>
-      <input type="input" class="ligne" name="nom" value="" size="50" data-placeholder="Nom du groupe (Ex: 1, A, LV2 Espagnol...)">
-      <p class="ligne"><label for="mailnotes">Utilisation&nbsp;: </label>
-        <select id="mailnotes" name="mailnotes"><?php echo str_replace("\"3\"","\"3\" selected",$select_mailnotes); ?></select>
-      </p>
-      <p class="ligne usergrp"><a class="icon-edite" data-uid=""></a><label>Élèves&nbsp;:</label> <span>[Aucun]</span></p>
-      <input type="hidden" name="eleves" value="">
-      <input type="hidden" name="table" value="groupes">
+      <input type="text" class="ligne" name="nom" value="" size="50" placeholder="Nom du groupe (Ex: 1, A, LV2 Espagnol...)">
+      <p class="usergrp"><strong>Utilisateurs&nbsp;:</strong> <span data-uids="">[Personne]</span></p>
+      <p class="ligne"><label for="mails">Groupe visible lors de l'envoi de courriel</label><input type="checkbox" name="mails" value="1"></p>
+      <p class="ligne"><label for="notes">Groupe visible lors de la saisie des notes de colles</label><input type="checkbox" name="notes" value="1"></p>
+      <input type="hidden" name="uids" value="">
     </div>
   </form>
 
   <div id="aide-page">
     <h3>Aide et explications</h3>
-    <p>Il est possible ici d'ajouter et de modifier des groupes d'élèves. Ces groupes peuvent être utilisés à deux endroits&nbsp;:</p>
+    <p>Il est possible ici d'ajouter et de modifier des groupes d'utilisateurs. Ces groupes peuvent être utilisés pour deux fonctionnalités&nbsp;:</p>
     <ul>
-      <li>l'envoi de courriels, par les professeurs et colleurs</li>
-      <li>la saisie de notes de colles, par les professeurs et colleurs</li>
+      <li>l'envoi de courriels&nbsp;: ils sont listés à la suite des expéditeurs possibles, pour les sélectionner plus facilement. Ils peuvent regrouper des utilisateurs de tout type.</li>
+      <li>la saisie de notes de colles&nbsp;: ils permettent une sélection rapide des élèves à noter. Ils sont logiquement censés regrouper des élèves uniquement.</li>
     </ul>
-    <p>Les groupes d'élèves ne se limitent pas obligatoirement qu'aux groupes de colles&nbsp;: il peut être notamment intéressant de créer les deux demi-groupes de la classe ou les groupes de LV2 par exemple, pour envoyer des courriels à la seule partie concernée de la classe.</p>
-    <p>Chaque élève peut participer à plusieurs groupes. Un groupe peut contenir autant d'élèves que possible, sans limitation sur les matières associées à chaque élève.</p>
-    <p>Chaque groupe existant peut être supprimé en cliquant sur le bouton <span class="icon-supprime"></span> (une confirmation sera demandée). Cela ne supprime pas les comptes des élèves du groupe.</p>
-    <h4>Préférences associées à chaque groupe</h4>
-    <p>Le nom du groupe est modifiable en cliquant sur le bouton <span class="icon-edite"></span> à côté. Il peut s'agir d'un simple numéro (1,2,3...) pour des groupes de colles, d'une lettre ou d'un mot pour des demi-groupes par exemple (A et B, impairs et pairs...), ou encore d'un nom plus long (&laquo;&nbsp;LV2 Espagnol&nbsp;&raquo;).</p>
-    <p>Le choix de l'<em>utilisation</em> permet de spécifier si le groupe sera visible uniquement pour l'envoi des courriels, uniquement pour la saisie des notes de colles, ou dans les deux cas. La modification est enregistrée immédiatement (pas de validation nécessaire).</p>
-    <p>La liste des élèves donnée est modifiable en cliquant sur le bouton <span class="icon-edite"></span> à côté. Une nouvelle fenêtre permet alors de cocher ou décocher les élèves, en cliquant sur les cases ou sur les noms des élèves. La modification est enregistrée immédiatement (pas de validation nécessaire). Un élève au minimum est obligatoire.</p>
+    <p>Les groupes pour l'envoi de courriel ne sont en réalité affichés que pour les utilisateurs colleur/administration/professeur. Les élèves qui peuvent éventuellement envoyer des courriels ne voient pas les groupes. Il est donc possible de faire des groupes «&nbsp;Colleurs de XXX&nbsp;» ou «&nbsp;Équipe pédagogique&nbsp;» par exemple.</p>
+    <p>Les groupes d'utilisateurs ne se limitent pas aux groupes de colles, il peut être notamment intéressant de créer les deux demi-groupes de la classe ou les groupes d'option par exemple, pour envoyer des courriels aux seuls élèves concernés le cas échéant.</p>
+    <p>Chaque utilisateur peut participer à plusieurs groupes. Chaque groupe peut contenir jusqu'à l'ensemble des utilisateurs, sans limite de type de compte ou de matière associée.</p>
+    <p>Un clic sur l'icône <span class="icon-ajoute"></span> permet d'ouvrir le formulaire permettant de créer un nouveau groupe.</p>
+    <h4>Modification des groupes</h4>
+    <p>Chaque groupe existant peut être supprimé en cliquant sur le bouton <span class="icon-supprime"></span> (une confirmation sera demandée). Cela ne supprime pas les comptes des utilisateurs du groupe.</p>
+    <p>Le nom et la liste des utilisateurs de chaque groupe existant sont indiqués par des zones en pointillés et peuvent être modifiés en cliquant sur le bouton <span class="icon-edite"></span> et en validant avec le bouton <span class="icon-ok"></span> qui apparaît alors.</p>
+    <p>Les cases à cocher pour définir l'utilisation des groupes sur les courriels ou les notes agissent immédiatement&nbsp;: cocher ou décocher active ou désactive l'utilisation, sans validation supplémentaire.</p>
     <h4>Ordre des groupes</h4>
     <p>Les groupes sont automatiquement classés par ordre alphanumérique.</p>
   </div>
   
   <div id="aide-ajoute">
     <h3>Aide et explications</h3>
-    <p>Ce formulaire permet de créer un nouveau groupe. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
-    <p>Les groupes d'élèves ne se limitent pas obligatoirement qu'aux groupes de colles&nbsp;: il peut être notamment intéressant de créer les deux demi-groupes de la classe ou les groupes de LV2 par exemple, pour envoyer des courriels à la seule partie concernée de la classe.</p>
-    <p>Chaque élève peut participer à plusieurs groupes. Un groupe peut contenir autant d'élèves que possible, sans limitation sur les matières associées à chaque élève.</p>
+    <p>Ce formulaire permet de créer un nouveau groupe d'utilisateurs. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
+    <p>Les groupes d'utilisateurs peuvent être utilisés pour deux fonctionnalités&nbsp;:</p>
+    <ul>
+      <li>l'envoi de courriels&nbsp;: ils sont listés à la suite des expéditeurs possibles, pour les sélectionner plus facilement. Ils peuvent regrouper des utilisateurs de tout type.</li>
+      <li>la saisie de notes de colles&nbsp;: ils permettent une sélection rapide des élèves à noter. Ils sont logiquement censés regrouper des élèves uniquement.</li>
+    </ul>
+    <p>Les groupes pour l'envoi de courriel ne sont en réalité affichés que pour les utilisateurs colleur/administration/professeur. Les élèves qui peuvent éventuellement envoyer des courriels ne voient pas les groupes. Il est donc possible de faire des groupes «&nbsp;Colleurs de XXX&nbsp;» ou «&nbsp;Équipe pédagogique&nbsp;» par exemple.</p>
+    <p>Les groupes d'utilisateurs ne se limitent pas aux groupes de colles, il peut être notamment intéressant de créer les deux demi-groupes de la classe ou les groupes d'option par exemple, pour envoyer des courriels aux seuls élèves concernés le cas échéant.</p>
     <p>Les groupes sont automatiquement classés par ordre alphanumérique.</p>
     <h4>Préférence du groupe</h4>
-    <p>Le <em>nom du groupe</em> est ce qui apparaîtra derrière la mention &laquo;&nbsp;Groupe&nbsp;&raquo;. Il peut s'agir d'un simple numéro (1,2,3...) pour des groupes de colles, d'une lettre ou d'un mot pour des demi-groupes par exemple (A et B, impairs et pairs...), ou encore d'un nom plus long (&laquo;&nbsp;LV2 Espagnol&nbsp;&raquo;).</p>
-    <p>Le choix de l'<em>utilisation</em> permet de spécifier si le groupe sera visible uniquement pour l'envoi des courriels, uniquement pour la saisie des notes de colles, ou dans les deux cas. La modification est enregistrée immédiatement (pas de validation nécessaire).</p>
-    <p>La liste des élèves est à définir en cliquant sur le bouton <span class="icon-edite"></span> à côté. Une nouvelle fenêtre permet alors de cocher ou décocher les élèves, en cliquant sur les cases ou sur les noms des élèves. Un élève au minimum est obligatoire.</p>
+    <p>Le <em>nom du groupe</em> est ce qui apparaîtra derrière la mention &laquo;&nbsp;Groupe&nbsp;&raquo;. Il peut s'agir d'un simple numéro (1,2,3...) pour des groupes de colles, d'une lettre ou d'un mot pour des demi-groupes par exemple (A et B, impairs et pairs...), ou encore d'un nom plus long (&laquo;&nbsp;Colleurs de Mathématiques&nbsp;&raquo;...).</p>
+    <p>La liste des <em>utilisateurs</em> est à définir en cliquant sur le bouton <span class="icon-edite"></span> à côté. Une nouvelle fenêtre permet alors de cocher ou décocher les utilisateurs, en cliquant sur les cases ou sur les noms des utilisateurs. L'icône <span class="icon-cocher"></span> permet de cocher tous les utilisateurs d'un même type. Un utilisateur au minimum est obligatoire.</p>
+    <p>Les deux cases à cocher <em>Groupe visible lors de l'envoi de courriel</em> et <em>Groupe visible lors de la saisie des notes de colles</em> permettent de choisir l'utilisation du groupe.</p>
+  </div>
+  
+  <div id="aide-groupes">
+    <h3>Aide et explications</h3>
+    <p>Le <em>nom du groupe</em> et la liste des <em>utilisateurs</em> sont modifiables séparément, en cliquant sur le bouton <span class="icon-edite"></span> correspondant. Chaque contenu éditable est indiqué par une bordure en pointillés.</p>
+    <p>Le <em>nom du groupe</em> est ce qui apparaîtra derrière la mention &laquo;&nbsp;Groupe&nbsp;&raquo;. Il peut s'agir d'un simple numéro (1,2,3...) pour des groupes de colles, d'une lettre ou d'un mot pour des demi-groupes par exemple (A et B, impairs et pairs...), ou encore d'un nom plus long (&laquo;&nbsp;Colleurs de Mathématiques&nbsp;&raquo;...).</p>
+    <p>La liste des <em>utilisateurs</em> est à définir en cliquant sur le bouton <span class="icon-edite"></span> à côté. Une nouvelle fenêtre permet alors de cocher ou décocher les utilisateurs, en cliquant sur les cases ou sur les noms des utilisateurs. L'icône <span class="icon-cocher"></span> permet de cocher tous les utilisateurs d'un même type. Un utilisateur au minimum est obligatoire.</p>
+    <p>Chaque groupe existant peut être supprimé en cliquant sur le bouton <span class="icon-supprime"></span> (une confirmation sera demandée). Cela ne supprime pas les comptes des utilisateurs du groupe.</p>
+    <h4>Utilisation du groupe</h4>
+    <p>Chaque groupe d'utilisateurs peut être utilisé pour deux fonctionnalités&nbsp;:</p>
+    <ul>
+      <li>l'envoi de courriels&nbsp;: ils sont listés à la suite des expéditeurs possibles, pour les sélectionner plus facilement. Ils peuvent regrouper des utilisateurs de tout type.</li>
+      <li>la saisie de notes de colles&nbsp;: ils permettent une sélection rapide des élèves à noter. Ils sont logiquement censés regrouper des élèves uniquement.</li>
+    </ul>
+    <p>Les groupes pour l'envoi de courriel ne sont en réalité affichés que pour les utilisateurs colleur/administration/professeur. Les élèves qui peuvent éventuellement envoyer des courriels ne voient pas les groupes. Il est donc possible de faire des groupes «&nbsp;Colleurs de XXX&nbsp;» ou «&nbsp;Équipe pédagogique&nbsp;» par exemple.</p>
+    <p>Les groupes d'utilisateurs ne se limitent pas aux groupes de colles, il peut être notamment intéressant de créer les deux demi-groupes de la classe ou les groupes d'option par exemple, pour envoyer des courriels aux seuls élèves concernés le cas échéant.</p>
+    <p>Les cases à cocher pour définir l'utilisation des groupes sur les courriels ou les notes agissent immédiatement&nbsp;: cocher ou décocher active ou désactive l'utilisation, sans validation supplémentaire.</p>
+    <h4>Ordre des groupes</h4>
+    <p>Les groupes sont automatiquement classés par ordre alphanumérique.</p>
   </div>
   
   <p id="log"></p>
diff -urN cahier-de-prepa6.2.0/index.php cahier-de-prepa8.0.1/index.php
--- cahier-de-prepa6.2.0/index.php	2016-09-02 10:27:19.508667976 +0200
+++ cahier-de-prepa8.0.1/index.php	2018-10-15 17:50:58.593037454 +0200
@@ -41,28 +41,15 @@
 /////////////////////////////
 // Vérification de l'accès //
 /////////////////////////////
-// Interdiction de voir les pages de protection élevée ou non associées à ses
-// propres matières si on est invité/élève/colleur
-// Toutes les pages sont visibles pour les professeurs (édition : cf plus bas)
-if ( $page['protection'] && ( $autorisation > 0 ) && ( $autorisation < 4 )
-  && ( ( $page['protection'] > $autorisation ) || ( !in_array($page['mat'],explode(',',$_SESSION['matieres'])) ) ) )  {
-  debut($mysqli,$page['titre'],'Vous n\'avez pas accès à cette page.',$autorisation,".?${page['cle']}");
-  $mysqli->close();
-  fin();
-}
-// Connexion nécessaire si protection
-if ( $page['protection'] && !$autorisation )  {
-  $titre = $page['titre'];
-  $actuel = ".?${page['cle']}";
-  include('login.php');
-}
-// Mode édition pour les professeurs sur leurs propres matières uniquement
-$edition = ( ( $autorisation == 4 ) && in_array($page['mat'],explode(',',$_SESSION['matieres'])) );
+$edition = acces($page['protection'],$page['mat'],$page['titre'],".?${page['cle']}",$mysqli);
 
 //////////////
 //// HTML ////
 //////////////
-debut($mysqli,$page['titre']. ( ( $edition && $page['protection'] ) ? "<span class=\"icon-lock${page['protection']}\"></span>" : '' ),$message,$autorisation,".?${page['cle']}",( $page['mat'] ) ? $page['mat'] : false);
+if ( $edition && $page['protection'] )
+  $icone = ( $page['protection'] == 32 ) ? '<span class="icon-locktotal"></span>' : '<span class="icon-lock"></span>';
+else  $icone = '';
+debut($mysqli,$page['titre'].$icone,$message,$autorisation,".?${page['cle']}",$page['mat']?:false);
 
 // MathJax désactivé par défaut
 $mathjax = false;
@@ -107,15 +94,17 @@
   }
 }
 
-// Affichage public sans édition
+// Affichage sans édition
 if ( !$edition )  {
-  // Affichage des informations diffusées
-  $resultat = $mysqli->query("SELECT IF(LENGTH(titre),CONCAT('<h3>',titre,'</h3>'),'') AS titre, texte FROM infos WHERE page = ${page['id']} AND cache = 0");
+  // Affichage des informations diffusées -- seulement les infos accessibles
+  // Fonction requete_protection définie dans fonctions.php
+  $resultat = $mysqli->query("SELECT IF(LENGTH(titre),CONCAT('<h3>',titre,'</h3>'),'') AS titre, texte
+                              FROM infos WHERE page = ${page['id']} AND cache = 0 AND ( ".requete_protection($autorisation).')');
   if ( $resultat->num_rows )  {
     if ( strlen($page['bandeau']) )
       echo "\n  <h2>${page['bandeau']}</h2>\n";
     while ( $r = $resultat->fetch_assoc() )  {
-      $mathjax = ( $mathjax ) ? true : strpos($r['texte'],'$')+strpos($r['texte'],'\\');
+      $mathjax = $mathjax ?: boolval(strpos($r['texte'],'$')+strpos($r['texte'],'\\'));
       echo <<<FIN
 
   <article>
@@ -135,19 +124,41 @@
 else  {
   echo <<<FIN
 
-  <a class="icon-aide general" data-id="page" title="Aide pour les modifications de cette page"></a>
-  <a class="icon-prefs general" data-id="prefs" title="Modifier les préférences de cette page"></a>
-  <a class="icon-ajoute general" data-id="ajoute" title="Ajouter une nouvelle information"></a>
+  <div id="icones">
+    <a class="icon-ajoute formulaire" title="Ajouter une nouvelle information"></a>
+    <a class="icon-prefs formulaire" title="Modifier les préférences de cette page"></a>
+    <a class="icon-aide" title="Aide pour les modifications de cette page"></a>
+  </div>
 
 FIN;
-  if ( strlen($page['bandeau']) )
-    echo "  <h2 class=\"edition\">${page['bandeau']}&nbsp;</h2>\n";
 
   // Affichage des informations diffusées
-  $resultat = $mysqli->query("SELECT id, ordre, cache, titre, texte FROM infos WHERE page = ${page['id']}");
+  $resultat = $mysqli->query("SELECT id, ordre, cache, titre, texte, protection FROM infos WHERE page = ${page['id']}");
   if ( $max = $resultat->num_rows )  {
+    if ( strlen($page['bandeau']) )
+      echo "  <h2 class=\"edition\">${page['bandeau']}&nbsp;</h2>\n";
     while ( $r = $resultat->fetch_assoc() )  {
-      $mathjax = ( $mathjax ) ? true : strpos($r['texte'],'$')+strpos($r['texte'],'\\');
+      $mathjax = $mathjax ?: boolval(strpos($r['texte'],'$')+strpos($r['texte'],'\\'));
+      $lock = $texte = '';
+      if ( $r['protection'] != $page['protection'] )  {
+        $p = $r['protection'];
+        if ( $p == 0 ) 
+          $texte = 'Cette information est visible de tous.';
+        elseif ( $p == 32 )  {
+          $texte = 'Cette information est invisible.';
+          $lock = '<span class="icon-locktotal" title="Information invisible"></span>';
+        }
+        else  {
+          $texte = array('invités','élèves','colleurs','administratifs','professeurs');
+        for ( $a=0; $a<5; $a++ )
+          if ( ( ($p-1)>>$a & 1 ) == 1 )
+            unset($texte[$a]);
+          $texte = 'Cette information est visible des comptes '.implode(', ',$texte).'.';
+          $lock = '<span class="icon-lock" title="La protection de cette information est différente de celle de la page."></span>';
+        }
+        $texte = "\n<p class=\"protection\">$texte</p>";
+      }
+      // Icônes de modification
       if ( $r['cache'] )  {
         $classe_cache = ' class="cache"';
         $visible = '<a class="icon-montre" title="Afficher l\'information sur la partie publique"></a>
@@ -160,18 +171,20 @@
       }
       $monte = ( $r['ordre'] == 1 ) ? ' style="display:none;"' : '';
       $descend = ( $r['ordre'] == $max ) ? ' style="display:none;"' : '';
+      // Affichage
       echo <<<FIN
 
   <article$classe_cache data-id="infos|${r['id']}">
-    <a class="icon-aide" data-id="information" title="Aide pour l'édition de cette information"></a>
+    $lock<h3 class="edition editable" data-id="infos|titre|${r['id']}" placeholder="Titre de l'information (non obligatoire)">${r['titre']}</h3>
+    <a class="icon-aide" title="Aide pour l'édition de cette information"></a>
+    <a class="icon-lock" title="Modifier la protection de l'information" data-val="${r['protection']}"></a> 
     $visible
     <a class="icon-monte"$monte title="Déplacer cette information vers le haut"></a>
     <a class="icon-descend"$descend title="Déplacer cette information vers le bas"></a>
     <a class="icon-supprime" title="Supprimer cette information"></a>
-    <h3 class="edition editable" data-id="infos|titre|${r['id']}" data-placeholder="Titre de l'information (non obligatoire)">${r['titre']}</h3>
-    <div class="editable edithtml" data-id="infos|texte|${r['id']}" data-placeholder="Texte de l'information (obligatoire)">
+    <div class="editable edithtml" data-id="infos|texte|${r['id']}" placeholder="Texte de l'information (obligatoire)">
 ${r['texte']}
-    </div>
+    </div>$texte
   </article>
 
 FIN;
@@ -181,44 +194,58 @@
   else
     echo "  <article><h2>Cette page est actuellement vide.</h2></article>\n\n";
 
-  // Select sur la protection
-  $select_protection = str_replace("\"${page['protection']}\"","\"${page['protection']}\" selected",'
-      <option value="0">Visible de tous</option>
-      <option value="1">Visible pour les connectés</option>
-      <option value="2">Visible pour les élèves, colleurs, profs</option>
-      <option value="3">Visible pour les colleurs et les profs</option>
-      <option value="4">Visible pour les profs uniquement</option>
-      <option value="5">Non visible</option>');
-
+  // Options du select multiple d'accès
+  $select_protection = '
+          <option value="0">Accès public</option>
+          <option value="6">Utilisateurs identifiés</option>
+          <option value="1">Invités</option>
+          <option value="2">Élèves</option>
+          <option value="3">Colleurs</option>
+          <option value="4">Administratifs</option>
+          <option value="5">Professeurs</option>
+          <option value="32">Information invisible</option>';
+  // Protection des nouvelles informations = protection globale de la page
+  $p = $page['protection'];
+  if ( ( $p == 0 ) || ( $p == 32 ) )
+    $sel_protection = str_replace("\"$p\"","\"$p\" selected",$select_protection);
+  else  {
+    $sel_protection = str_replace('"6"','"6" selected',$select_protection);
+    for ( $a=1; $a<6; $a++ )
+      if ( ( ($p-1)>>($a-1) & 1 ) == 0 )
+        $sel_protection = str_replace("\"$a\"","\"$a\" selected",$sel_protection);
+  }
   // Aide et formulaires d'ajout d'information, de préférences de page
 ?>
 
-  <form id="form-ajoute">
+  <form id="form-ajoute" data-action="ajout-info">
     <h3 class="edition">Ajouter une nouvelle information</h3>
     <div>
-      <input class="ligne" type="text" name="titre" size=50 data-placeholder="Titre de l'information (non obligatoire)">
-      <textarea name="texte" class="edithtml" rows="10" cols="100" data-placeholder="Texte de l'information (obligatoire)"></textarea>
+      <input class="ligne" type="text" name="titre" size=50 placeholder="Titre de l'information (non obligatoire)">
+      <textarea name="texte" class="edithtml ligne" rows="10" cols="100" placeholder="Texte de l'information (obligatoire)"></textarea>
       <p class="ligne"><label for="cache">Ne pas diffuser sur la partie publique&nbsp;: </label><input type="checkbox" id="cache" name="cache" value="1"></p>
     </div>
+    <p class="ligne"><label for="protection">Accès&nbsp;: </label>
+      <select name="protection[]" multiple><?php echo $sel_protection; ?>
+      </select>
+    </p>
     <input type="hidden" name="page" value="<?php echo $page['id']; ?>">
-    <input type="hidden" name="table" value="infos">
   </form>
 
-  <form id="form-prefs">
+  <form id="form-prefs" data-action="pages">
     <h3 class="edition">Modifier les préférences de la page</h3>
-    <p class="ligne"><label for="titre">Titre&nbsp;: </label><input type="text" id="titre" name="titre" value="<?php echo $page['titre']; ?>" size="80" data-placeholder="Titre de la page (obligatoire)"></p>
-    <p class="ligne"><label for="nom">Nom dans le menu&nbsp;: </label><input type="text" id="nom" name="nom" value="<?php echo $page['nom']; ?>" size="50" data-placeholder="Nom de la page dans le menu (obligatoire)"></p>
-    <p class="ligne"><label for="cle">Clé dans l'adresse&nbsp;: </label><input type="text" id="cle" name="cle" value="<?php echo ($p = strpos($page['cle'],'/'))?substr($page['cle'],$p+1):$page['cle']; ?>" size="30" data-placeholder="Mot-clé sans majuscule, sans accent, sans espace (obligatoire)"></p>
-    <p class="ligne"><label for="bandeau">Texte de début&nbsp;:</label></p>
-    <textarea id="bandeau" name="bandeau" rows="2" cols="100" data-placeholder="Texte apparaissant au début de la page (non obligatoire)"><?php echo $page['bandeau']; ?></textarea>
+    <p class="ligne"><label for="titre">Titre&nbsp;: </label><input type="text" name="titre" value="<?php echo $page['titre']; ?>" size="80" placeholder="Titre de la page (obligatoire)"></p>
+    <p class="ligne"><label for="nom">Nom dans le menu&nbsp;: </label><input type="text" name="nom" value="<?php echo $page['nom']; ?>" size="50" placeholder="Nom de la page dans le menu (obligatoire)"></p>
+    <p class="ligne"><label for="cle">Clé dans l'adresse&nbsp;: </label><input type="text" name="cle" value="<?php echo ($p = strpos($page['cle'],'/'))?substr($page['cle'],$p+1):$page['cle']; ?>" size="30" placeholder="Mot-clé sans majuscule, sans accent, sans espace (obligatoire)"></p>
     <p class="ligne"><label for="protection">Accès&nbsp;: </label>
-      <select id="protection" name="protection"><?php echo $select_protection; ?>
+      <select name="protection[]" multiple><?php echo str_replace('Information','Page',$sel_protection); ?>
       </select>
     </p>
+    <p class="ligne"><label for="propagation">Propager l'accès à chaque information de la page&nbsp;: </label><input type="checkbox" id="propagation" name="propagation" value="1"></p>
+    <p class="ligne"><label for="bandeau">Texte de début&nbsp;:</label></p>
+    <textarea name="bandeau" rows="2" cols="100" placeholder="Texte apparaissant au début de la page (non obligatoire)"><?php echo $page['bandeau']; ?></textarea>
     <input type="hidden" name="id" value="<?php echo $page['id']; ?>">
-    <input type="hidden" name="table" value="pages">
   </form>
-
+  
   <div id="aide-page">
     <h3>Aide et explications</h3>
     <p>Il est possible ici d'ajouter une information, de modifier les informations existantes ou de modifier les préférences de la page.</p>
@@ -227,38 +254,28 @@
     <p>Les deux boutons généraux permettent de&nbsp;:</p>
     <ul>
       <li><span class="icon-ajoute"></span>&nbsp;: ouvrir un formulaire pour ajouter une nouvelle information.</li>
-      <li><span class="icon-prefs"></span>&nbsp;: ouvrir un formulaire pour modifier les préférences de la page.</li>
-    </ul>
-    <h4>Préférences associées à cette page</h4>
-    <p>Concernant cette page, vous pouvez modifier&nbsp;:</p>
-    <ul>
-      <li>le <em>titre</em> qui sera affiché en haut de page et dans la barre de titre du navigateur. Par exemple, «&nbsp;À propos du TIPE&nbsp;».</li>
-      <li>le <em>nom dans le menu</em> qui est affiché dans le menu en tant que lien vers la page. Il est préférable qu'il rentre sur une ligne, il faut donc le choisir assez court. Par exemple, «&nbsp;Informations TIPE&nbsp;».</li>
-      <li>la <em>clé dans l'adresse</em> qui est un mot-clé utilisé uniquement dans l'adresse de la page. Par convention, il vaut mieux que ce soit un mot unique, court et sans majuscule. Par exemple, «&nbsp;tipe&nbsp;». La clé doit obligatoirement être unique (deux pages ne peuvent pas avoir la même clé).</li>
-      <li>l'<em>accès</em> à la page (voir ci-dessous).</li>
-      <li>le <em>texte de début</em> qui sera affiché au-dessus des informations de la page. Il s'agit d'une ou deux phrases maximum. Il n'est affiché que si la page contient des informations. Cette case peut être laissée vide.</li>
+      <li><span class="icon-prefs"></span>&nbsp;: ouvrir un formulaire pour modifier les préférences de la page pour modifier le titre, le nom dans le menu, l'accès à la page.</li>
     </ul>
-    <p>L'<em>accès</em> à chaque page peut être choisi parmi six possibilités&nbsp;:</p>
+    <h4>Création d'une autre page</h4>
+    <p>Il est tout à fait possible d'avoir d'autres pages que celle-ci, y compris une page spécifique à une matière. Vous pouvez pour cela aller à la <a href="pages">gestion des pages</a>.</p>
+    <h4>Gestion de l'accès</h4>
+    <p>L'accès à chaque page et chaque information peut être protégé. Trois choix sont possibles&nbsp;:</p>
     <ul>
-      <li><em>Visible de tous</em>&nbsp;: accessible de tout visiteur, sans identification.</li>
-      <li><em>Visible pour les connectés</em>&nbsp;: accessible de tout utilisateur mais uniquement après identification (utilisateurs de type invité, élève, colleur ou professeur). Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les élèves, colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type élève, colleur ou professeur. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type colleur ou professeur. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type professeur. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Non visible</em>&nbsp;: entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont la page peut dépendre). La différence avec le choix précédent est que cette page n'apparaît plus du tout dans le menu pour ceux qui ne la voient pas.</li>
+      <li><em>Accès public</em>&nbsp;: page accessible de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: page accessible uniquement par les utilisateurs identifiés, en fonction de leur type de compte. Un cadenas <span class="icon-lock"></span> est alors affiché dans le titre de la page. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière les voient affichées dans le menu et peuvent y accéder. Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Page invisible</em>&nbsp;: page entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont la page peut dépendre). Un cadenas <span class="icon-locktotal"></span> est alors affiché dans le titre de la page.</li>
     </ul>
-    <p>Hormis le dernier choix, le lien dans le menu apparaîtra pour tout visiteur (identifié ou non). Le niveau de protection est indiqué dans le titre par une icône de type <span class="icon-lock1"></span> où le numéro correspond au niveau de protection.</p>
-    <p>Les comptes utilisateurs sont à définir ou modifier sur la page de <a href="utilisateurs">gestion des utilisateurs</a>. Les associations avec les matières sont modifiables sur la page de <a href="matieres">gestion des matières</a>.</p>
-    <h4>Autres modifications de la page</h4>
-    <p>Il est aussi possible de modifier, sur la page de <a href="pages">gestion des pages</a>, la matière associée à la page. Une page sans matière apparaît en haut du menu, une page avec matière associée apparaît dans le menu au niveau de la matière. Elle n'est alors visible que par les utilisateurs associés à la matière, modifiable que par les professeurs associés à la matière. Pour modifier les matières qui vous sont associées, il faut aller sur la page de <a href="matieres">gestion des matières</a>.</p>
-    <p>Il est aussi possible de supprimer toutes les informations d'une page (sans supprimer la page elle-même), ou de supprimer la page elle-même, sur la page de <a href="pages">gestion des pages</a>.</p>
+    <p>Le lien de la page dans le menu est visible avant identification. Il disparaît après identification pour les utilisateurs n'ayant pas accès à la page.</p>
+    <h4>Protection des informations</h4>
+    <p>Chaque information peut être aussi protégée indépendamment de la page (les possibilités sont identiques à celles citées ci-dessus). L'information dont l'accès est différent de celui de la page ne sera simplement pas affichée pour les utilisateurs non autorisés. Elle est alors affichée ici avec un cadenas <span class="icon-lock"></span> devant son titre.</p>
   </div>
 
-  <div id="aide-information">
+  <div id="aide-infos">
     <h3>Aide et explications</h3>
     <p>Le titre et le texte de chaque information sont modifiables séparément, en cliquant sur le bouton <span class="icon-edite"></span> correspondant. Chaque contenu éditable est indiqué par une bordure en pointillés.</p>
     <p>Une fois le texte édité, il est validé en appuyant sur Entrée dans le cas des titres ou sur le bouton <span class="icon-ok"></span>. Au contraire, un clic sur le bouton <span class="icon-annule"></span> annule l'édition.</p>
     <p>Le titre d'une information peut rester vide.</p>
+    <p>Le texte doit être formaté en HTML&nbsp;: par exemple, chaque bloc de texte doit être encadré par &lt;p&gt; et &lt;/p&gt;. Il peut donc contenir des liens vers les autres pages du site, vers des documents du site, vers le web... Des boutons sont fournis pour aider au formattage (ainsi qu'une aide).</p>
     <p>D'autres modifications sont possibles à l'aide des boutons disponibles pour chaque information&nbsp;:</p>
     <ul>
       <li><span class="icon-supprime"></span>&nbsp;: supprimer l'information (une confirmation sera demandée)</li>
@@ -266,8 +283,17 @@
       <li><span class="icon-descend"></span>&nbsp;: descendre l'information d'un cran</li>
       <li><span class="icon-cache"></span>&nbsp;: rendre l'information non visible sur la partie publique. Cela peut être utile pour une information qui n'est plus valable mais que l'on souhaite réutiliser, ou pour une information rédigée en avance dans le but d'une publication ultérieure.</li>
       <li><span class="icon-montre"></span>&nbsp;: afficher l'information sur la partie publique</li>
+      <li><span class="icon-lock"></span>&nbsp;: gérer l'accès de l'information</li>
     </ul>
-    <p>Le texte doit être formaté en HTML&nbsp;: par exemple, chaque bloc de texte doit être encadré par &lt;p&gt; et &lt;/p&gt;. Il peut donc contenir des liens vers les autres pages du site, vers des documents du site, vers le web... Des boutons sont fournis pour aider au formattage (ainsi qu'une aide).</p>
+    <h4>Gestion des accès</h4>
+    <p>L'accès à chaque information peut être protégé. Trois choix sont possibles&nbsp;:</p>
+    <ul>
+      <li><em>Accès public</em>&nbsp;: information accessible de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: information accessible uniquement par les utilisateurs identifiés, en fonction de leur type de compte.</li>
+      <li><em>Information invisible</em>&nbsp;: information entièrement invisible pour les utilisateurs autres que les professeurs.</li>
+    </ul>
+    <p>Par défaut, l'accès de la page, réglable dans les préférences de la page par le bouton <span class="icon-prefs"></span>, est appliqué.</p>
+    <p>Si l'accès choisi pour une information est différent de celui de la page, l'information ne sera simplement pas affichée pour les utilisateurs non autorisés et affichée ici avec un cadenas <span class="icon-lock"></span> devant son titre. Le détail est affiché en gris sous le texte de l'information.</p>
   </div>
 
   <div id="aide-ajoute">
@@ -276,6 +302,7 @@
     <p>Le <em>titre</em> sera affiché, un peu plus gros, au-dessus de l'information. Ce doit être un simple texte. Il peut rester vide (il n'y aura alors pas de titre affiché).</p>
     <p>Le <em>texte</em> doit être formaté en HTML. Par exemple, chaque bloc de texte doit être encadré par &lt;p&gt; et &lt;/p&gt; (paragraphe). Il peut donc contenir des liens vers les autres pages du site, vers des documents du site, vers le web... Des boutons sont fournis pour aider au formattage.</p>
     <p>La case à cocher <em>Ne pas diffuser sur la partie publique</em> permet de cacher temporairement cette information, par exemple pour la diffuser ultérieurement. Si la case est cochée, l'information ne sera pas diffusée et pourra être affichée à l'aide du bouton <span class="icon-montre"></span>. Si la case est décochée, l'information sera immédiatement visible et pourra être rendue invisible à l'aide du bouton <span class="icon-cache"></span>.</p>
+    <p>L'accès est par défaut celui de la page, mais peut être modifié. Si l'accès choisi pour une information est différent de celui de la page, l'information ne sera simplement pas affichée pour les utilisateurs non autorisés et affichée ici avec un cadenas <span class="icon-lock"></span> devant son titre.</p>
     <p>L'information sera affichée en haut de page, mais pourra être déplacée ultérieurement à l'aide des boutons <span class="icon-descend"></span> et <span class="icon-monte"></span>.</p>
   </div>
 
@@ -289,18 +316,17 @@
       <li>le <em>texte de début</em>, qui sera affiché au-dessus des informations de la page. Il s'agit d'une ou deux phrases maximum. Il n'est affiché que si la page contient des informations. Cette case peut être laissée vide.</li>
       <li>l'<em>accès</em> à la page (voir ci-dessous).</li>
     </ul>
-    <p>Il est aussi possible de modifier sur la page de <a href="pages">gestion des pages</a> la matière associée à la page. Une page sans matière apparaît en haut du menu, une page avec matière associée apparaît dans le menu au niveau de la matière. Elle n'est alors visible que par les utilisateurs associés à la matière, modifiable que par les professeurs associés à la matière. Pour modifier les matières qui vous sont associées, il faut aller sur la page de <a href="matieres">gestion des matières</a>.</p>
-    <p>L'<em>accès</em> à cette page peut être choisi parmi six possibilités&nbsp;:</p>
+    <p>La case à cocher <em>Propager l'accès à chaque information</em> permet de modifier éventuellement l'accès individuel de chaque information. Les informations dont l'accès est différent de celui de la page ne sont simplement pas affichées pour les utilisateurs non autorisés et affichées ici avec un cadenas <span class="icon-lock"></span> devant leur titre.</p>
+    <h4>Gestion des accès</h4>
+    <p>L'accès à chaque page et chaque information peut être protégé. Trois choix sont possibles&nbsp;:</p>
     <ul>
-      <li><em>Visible de tous</em>&nbsp;: accessible de tout visiteur, sans identification.</li>
-      <li><em>Visible pour les connectés</em>&nbsp;: accessible de tout utilisateur mais uniquement après identification (utilisateurs de type invité, élève, colleur ou professeur). Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les élèves, colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type élève, colleur ou professeur. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type colleur ou professeur. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type professeur. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Non visible</em>&nbsp;: entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont la page peut dépendre). La différence avec le choix précédent est que cette page n'apparaît plus du tout dans le menu pour ceux qui ne la voient pas.</li>
+      <li><em>Accès public</em>&nbsp;: page accessible de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: page accessible uniquement par les utilisateurs identifiés, en fonction de leur type de compte. Un cadenas <span class="icon-lock"></span> est alors affiché dans le titre de la page. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière les voient affichées dans le menu et peuvent y accéder.</li>
+      <li><em>Page invisible</em>&nbsp;: page entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont la page peut dépendre). Un cadenas <span class="icon-locktotal"></span> est alors affiché dans le titre de la page.</li>
     </ul>
-    <p>Hormis le dernier choix, le lien dans le menu apparaîtra pour tout visiteur (identifié ou non). Le niveau de protection est indiqué dans le titre par une icône de type <span class="icon-lock1"></span> où le numéro correspond au niveau de protection.</p>
-    <p>Les comptes utilisateurs sont à définir ou modifier sur la page de <a href="utilisateurs">gestion des utilisateurs</a>. Les associations avec les matières sont modifiables sur la page de <a href="matieres">gestion des matières</a>.</p>
+    <p>Le lien de la page dans le menu est visible avant identification. Il disparaît après identification pour les utilisateurs n'ayant pas accès à la page.</p>
+    <h4>Autres modifications</h4>
+    <p>Il est aussi possible de modifier la matière associée à la page à la <a href="pages">gestion des pages</a>. Une page sans matière apparaît en haut du menu, une page avec matière associée apparaît dans le menu au niveau de la matière. Elle n'est alors visible que par les utilisateurs associés à la matière et modifiable que par les professeurs associés à la matière. Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</p>
   </div>
 
   <p id="log"></p>
diff -urN cahier-de-prepa6.2.0/installation.php cahier-de-prepa8.0.1/installation.php
--- cahier-de-prepa6.2.0/installation.php	2016-08-31 00:31:54.617596669 +0200
+++ cahier-de-prepa8.0.1/installation.php	2018-10-17 00:49:29.425459813 +0200
@@ -11,11 +11,9 @@
   <title>Cahier de Prépa&nbsp;: installation</title>
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0">
-  <link rel="stylesheet" href="css/style.min.css">
-  <link rel="stylesheet" href="css/icones.min.css">
+  <link rel="stylesheet" href="css/style1810.min.css">
+  <link rel="stylesheet" href="css/icones1810.min.css">
   <script type="text/javascript" src="js/jquery.min.js"></script>
-  <!--[if lte IE 8]><script src="js/html5shiv.min.js"></script><![endif]--> 
-  <!--[if lte IE 8]><script src="js/respond.min.js"></script><![endif]-->
   <style>
     p.ligne { padding: 0 2%; }
     .warning { margin: 0 auto; }
@@ -40,7 +38,7 @@
   // Si $site est vide : config.php n'a pas été modifié correctement
   if ( !strlen($site) )
     exit($header.'<h3 class="warning">Le fichier de configuration n\'existe pas, n\'est pas lisible ou est mal rempli.</h3><article><p>Il doit s\'appeler <code>config.php</code>, se trouver au même endroit que l\'ensemble des fichiers constituant le site et être lisible par l\'utilisateur d\'Apache (<code>'.`whoami | tr -d '\n'`.'</code>).</p><p>Vous devez l\'éditer pour y mettre les bonnes valeurs.</p></article>'.$footer);
-  header('Location: http'.($https?'s':'')."://$site/installation");
+  header("Location: https://$site/installation");
   exit();
 }
 
@@ -52,7 +50,7 @@
 else
   exit($header.'<h3 class="warning">Le fichier de configuration n\'existe pas ou n\'est pas lisible.</h3><article><p>Il doit s\'appeler config.php, se trouver au même endroit que l\'ensemble des fichiers constituant le site et être lisible par l\'utilisateur d\'Apache (<code>'.`whoami | tr -d '\n'`.'</code>).</p></article>'.$footer);
 // Vérification des données du fichier de configuration
-if ( !isset($site) || !isset($https) || !isset($serveur) || !isset($base) || !isset($mdp) )
+if ( !isset($site) || !isset($serveur) || !isset($base) || !isset($mdp) )
   exit($header.'<h3 class="warning">Le fichier de configuration est incomplet.</h3><article><p>Il manque des données nécessaire au fonctionnement du site dans le fichier <code>config.php</code>. Il faut l\'éditer et le modifier manuellement.</p></article>'.$footer);
 if ( strlen($base) > 12 )
   exit($header.'<h3 class="warning">Le nom de la base de données est trop long.</h3><article><p>Le nom de la base de données ne doit pas être supérieur à 12 caractères. Il faut corriger manuellement cela dans le fichier de configuration  <code>config.php</code>.</p></article>'.$footer);
@@ -81,17 +79,18 @@
   echo <<<FIN
 $header
   <article>
-    <h3>Bienvenue sur <a href="http://cahier-de-prepa.fr">Cahier de Prépa</a> et merci d'essayer ce gestionnaire de site&nbsp;!</h3>
-    <p><a href="http://cahier-de-prepa.fr">Cahier de Prépa</a> est un gestionnaire de sites web pour la communication des professeurs vers leurs élèves de CPGE. Il permet de&nbsp;:</p>
+    <h3>Bienvenue sur <a href="https://cahier-de-prepa.fr">Cahier de Prépa</a> et merci d'essayer ce gestionnaire de site&nbsp;!</h3>
+    <p><a href="https://cahier-de-prepa.fr">Cahier de Prépa</a> est un gestionnaire de sites web pour la communication des professeurs vers leurs élèves de CPGE. Il permet de&nbsp;:</p>
     <ul>
       <li>Donner aux élèves un lien unique regroupant les informations de toutes les matières</li>
       <li>Faire passer des informations de façon rapide (changement d'horaires, erratum de polycopié, petites choses oubliées...)</li>
       <li>Disposer d'informations de façon un peu plus pérenne, sur un nombre de pages sans limite, pages associées à une matière ou non</li>
+      <li>Posséder un calendrier propre à la classe</li>
       <li>Annoncer les programmes de colles (pour les élèves et les colleurs)</li>
       <li>Tenir à jour le cahier de texte numérique</li>
       <li>Mettre à disposition des documents, distribués ou non</li>
-      <li>Saisir les notes de colles (colleurs), Consulter ses notes de colles (élèves), Consulter la synthèse des notes de colles (professeurs)</li>
-      <li>Envoyer (professeurs et colleurs) des mails à l'ensemble des élèves/colleurs/professeurs ou à certains seulement</li>
+      <li>Saisir les notes de colles (colleurs), Consulter ses notes de colles (élèves), Consulter la synthèse des notes de colles (professeurs), Relever les notes (administration)</li>
+      <li>Envoyer des mails à l'ensemble des élèves/colleurs/professeurs ou à certains seulement, de façon très paramétrable</li>
       <li>Restreindre l'accès de façon sure aux élèves ou aux colleurs, et indépendamment pour chaque ressource et chaque matière</li>
       <li>Obtenir rapidement les nouveaux contenus du site, sur chaque page ou par flux RSS</li>
       <li>Ajouter des formules en LaTeX directement sur le site</li>
@@ -164,11 +163,11 @@
           // Envoi du courriel d'invitation 
           if ( !isset($mailadmin) )
             $mailadmin = 'admin@cahier-de-prepa.fr';
-          $lien = ( $https ? 'https' : 'http' )."://$site/connexion?reponseinvitation&mail=$mail&p=".sha1($site.$mdp.$mail);
+          $lien = "https://$site/gestioncompte?invitation&mail=$mail&p=".sha1($site.$mdp.$mail);
           mail($mail,'=?UTF-8?B?'.base64_encode('[Cahier de Prépa] Invitation').'?=',
-'Bonjour
+"Bonjour
 
-Le Cahier de Prépa vient d\'être créé à l\'adresse <http'.($https?'s':'')."://$site>. Vous devez avant toute chose vous rendre à la page qui vous permettra de saisir un mot de passe :
+Le Cahier de Prépa vient d'être créé à l'adresse <https://$site>. Vous devez avant toute chose vous rendre à la page qui vous permettra de saisir un mot de passe :
   $lien
 
 Les mots de passe sont chiffrés avant d'être stockés dans la base de données. Cela signifie que, sauf s'il est trop simple, votre mot de passe sera en complète sécurité : personne ne pourra jamais y avoir accès. Il est quand même dangereux que ce mot de passe soit évident, parce qu'un élève pourrait le deviner en vous voyant de loin le taper par exemple. Un bon mot de passe est un mot de passe d'au moins 8 caractères contenant des lettres, des chiffres et au moins un symbole parmi « ? ; : ! . , - _ ».
@@ -255,7 +254,7 @@
 }
 
 // Installation
-affiche('Ce Cahier de Prépa semble correctement installé. Page d\'accueil&nbsp;: <a href=".">http'.($https?'s':'')."://$site/</a>",true);
+affiche("Ce Cahier de Prépa semble correctement installé. Page d'accueil&nbsp;: <a href=\".\">https://$site/</a>",true);
 
 // Connexion en lecture
 affiche('Connexion à la base de donnée en lecture',true);
@@ -269,7 +268,7 @@
 
 // Tables dans la base de données
 $resultat = $mysqli->query('SHOW TABLES');
-$tables = array('cdt','cdt-seances','cdt-types','colles','docs','infos','matieres','notes','pages','recents','reps','semaines','utilisateurs','groupes');
+$tables = array('agenda','agenda-types','cdt','cdt-seances','cdt-types','colles','docs','infos','heurescolles','matieres','notes','pages','prefs','recents','reps','semaines','utilisateurs','groupes');
 if ( $resultat->num_rows )  {
   while ( $r = $resultat->fetch_row() )
     $tables = array_diff($tables,$r);
@@ -278,7 +277,7 @@
 affiche('Définition des tables',empty($tables));
 
 // La base de données doit contenir au moins un professeur
-$resultat = $mysqli->query('SELECT id FROM utilisateurs WHERE autorisation = 4 LIMIT 1');
+$resultat = $mysqli->query('SELECT id FROM utilisateurs WHERE autorisation = 5 LIMIT 1');
 affiche('Présence d\'au moins un utilisateur de type professeur',$resultat->num_rows);
 $resultat->free();
 
@@ -292,9 +291,9 @@
 affiche('Présence du répertoire Général',$resultat->num_rows);
 $resultat->free();
 
-// La base de données doit contenir 45 semaines en 2016-17
-$resultat = $mysqli->query('SELECT id FROM semaines WHERE debut > \'2016-08-01\' AND debut < \'2017-08-01\'');
-affiche('Définition des semaines',$resultat->num_rows == 45);
+// La base de données doit contenir 44 semaines en 2018-19
+$resultat = $mysqli->query('SELECT id FROM semaines WHERE debut > \'2018-08-01\' AND debut < \'2019-08-01\'');
+affiche('Définition des semaines',$resultat->num_rows == 44);
 $resultat->free();
 $mysqli->close();
 
@@ -318,9 +317,9 @@
 
 // Vérification de la quantité de données téléchargeable
 if ( '2M' == ini_get('upload_max_filesize') )
-  affiche('Les documents envoyés ne pourront excéder 2&nbsp;Mo, ce qui est plutôt faible. Il serait bien d\'augmenter cette limite. Plus d\'informations sur la page <a href="http://cahier-de-prepa.fr/technique">technique</a> de cahier-de-prepa.fr.',false);
+  affiche('Les documents envoyés ne pourront excéder 2&nbsp;Mo, ce qui est plutôt faible. Il serait bien d\'augmenter cette limite. Plus d\'informations sur la page <a href="https://cahier-de-prepa.fr/technique">technique</a> de cahier-de-prepa.fr.',false);
 else
-  affiche('La taille des documents envoyés peut aller jusqu\'à'.ini_get('upload_max_filesize').'o. Cela est modifiable dans la configuration du serveur web. Plus d\'informations sur la page <a href="http://cahier-de-prepa.fr/technique">technique</a> de cahier-de-prepa.fr.',true);
+  affiche('La taille des documents envoyés peut aller jusqu\'à'.ini_get('upload_max_filesize').'o. Cela est modifiable dans la configuration du serveur web. Plus d\'informations sur la page <a href="https://cahier-de-prepa.fr/technique">technique</a> de cahier-de-prepa.fr.',true);
 
 exit($footer);
 ?>
Binary files cahier-de-prepa6.2.0/js/ajax-loader.gif and cahier-de-prepa8.0.1/js/ajax-loader.gif differ
diff -urN cahier-de-prepa6.2.0/js/edition.js cahier-de-prepa8.0.1/js/edition.js
--- cahier-de-prepa6.2.0/js/edition.js	2016-08-25 02:11:31.794538731 +0200
+++ cahier-de-prepa8.0.1/js/edition.js	2018-10-16 00:55:38.831031973 +0200
@@ -3,14 +3,12 @@
 
 * Les éléments de classe "editable" peuvent être transformés en mode édition par
 la fonction transforme, automatiquement exécutée. L'attribut data-id est alors
-nécessaire, et doit être de la forme table|champ|id.
+nécessaire, et doit être de la forme table|champ|id (table=action).
 * Les éléments de classe "edithtml" sont considérés comme contenant des
 informations présentées en html : la fonction textareahtml crée alors un élément
 de type textarea, un élément de type div éditable (contenteditable=true), et
 ajoute des boutons facilitant les modifications. La textarea et la div éditable
 sont alternativement visibles, à la demande.
-* Les éléments possédant un attribut data-placeholder ont droit, par la fonction
-placeholder, à une valeur affichée lorsque l'élément (en mode édition) est vide.
 * Les liens de classe icon-aide lancent automatiquement une aide par la fonction
 popup. Le contenu est récupéré dans les div de classe aide-xxx où xxx est la
 valeur de l'attribut data-id du lien (ces div sont automatiquement non affichées
@@ -24,50 +22,57 @@
 
 // Notification de résultat de requête AJAX
 function affiche(message,etat) {
-  $('#log').removeClass().addClass(etat).html(message).append('<span class="icon-ferme"></span>').show().off("click").on("click",function() {
-    window.clearTimeout(t);
-    $(this).hide(500);
-  });
-  var t = window.setTimeout(function() {
-    $('#log').hide(500);
-  },10000);
+  $('#log').removeClass().addClass(etat).html(message).append('<span class="icon-ferme"></span>').fadeIn().off("click").on("click",function() {
+    window.clearTimeout(extinction);
+    $(this).fadeOut(800);
+  });
+  extinction = window.setTimeout(function() { $('#log').fadeOut(800); },6000);
 }
 
-// Demande de reconnexion si timeout
-function afficher_login(settings) {
+// Demande de reconnexion si connexion perdue 
+// settings : paramètres du premier envoi ajax auquel le serveur a répondu login ou mdp
+// light : true si connexion par cookie à compléter (mdp seul), false si connexion
+//         complète nécessaire (login,mdp)
+function reconnect(settings,light) {
   // Suppression d'une éventuelle fenêtre de la fonction popup existante
   $('#fenetre,#fenetre_fond').remove();
+  if ( settings.url == 'recup.php' )
+    var action = ( settings.data.indexOf('prefs') ) ? 'récupérer les préférences de cet utilisateur' : 'récupérer la liste des répertoires et documents disponibles';
+  else  {
+    var action = 'valider cette action';
+    // Pour ne pas créer d'erreur si afficheform n'existe pas
+    // afficheform est la fonction d'affichage du formulaire dans le cas de 
+    // récupération de données sur recup.php
+    settings.afficheform = Function.prototype ;
+  }
   // Création de la fenêtre d'indentification
-  popup('<a class="icon-ok" title="Valider"></a><h3>Connexion nécessaire</h3>\
-  <p>Vous avez été automatiquement déconnecté. Vous devez vous connecter à nouveau pour valider vos modifications.</p>\
-  <form>\
-  <p class="ligne"><label for="login">Identifiant&nbsp;: </label><input type="text" name="login" id="login"></p>\
-  <p class="ligne"><label for="motdepasse">Mot de passe&nbsp;: </label><input type="password" name="motdepasse" id="motdepasse"></p>\
-  </form>',true);
-  $('#login').focus();
-  // Envoi
+  if ( light )
+    popup('<a class="icon-ok" title="Valider"></a><h3>Connexion nécessaire</h3>\
+           <p>Vous êtes déjà connecté, mais vous devez saisir de nouveau votre mot de passe pour '+action+'.</p>\
+           <form>\
+           <p class="ligne"><label for="motdepasse">Mot de passe&nbsp;: </label><input type="password" name="motdepasse" id="motdepasse"></p>\
+           </form>',true);
+  else
+    popup('<a class="icon-ok" title="Valider"></a><h3>Connexion nécessaire</h3>\
+           <p>Vous avez été automatiquement déconnecté. Vous devez vous connecter à nouveau pour '+action+'.</p>\
+           <form>\
+           <p class="ligne"><label for="login">Identifiant&nbsp;: </label><input type="text" name="login" id="login"></p>\
+           <p class="ligne"><label for="motdepasse">Mot de passe&nbsp;: </label><input type="password" name="motdepasse" id="motdepasse"></p>\
+           </form>',true);
+  $('#fenetre input:first').focus();
+  // Envoi (les données déjà envoyées settings.data ont été automatiquement sérialisées)
   $('#fenetre a.icon-ok').on("click",function () {
-    $.ajax({url: 'ajax.php',
+    $.ajax({url: settings.url,
             method: "post",
             data: $("#fenetre form").serialize()+'&'+settings.data,
             dataType: 'json',
-            el: '',
-            fonction: function() {
-              return true;
-            }
+            el: settings.el,
+            afficheform: settings.afficheform,
+            fonction: settings.fonction
     }).done( function(data) {
       // Si erreur d'identification, on reste bloqué là
-      if ( data['etat'] == 'login_nok' )
-        $('#fenetre > p').html(data['message']).addClass('warning');
-      // Sinon, et si l'identification a fonctionné, on exécute la fonction
-      // passée dans l'argument settings, qui doit contenir la fonction fonction
-      // et l'élément el. Remarque : les événements globaux ajaxSend, ajaxStop
-      // et ajaxSuccess s'appliquent bien. 
-      else {
+      if ( data['etat'] != 'mdpnok' )
         $('#fenetre,#fenetre_fond').remove();
-        if ( data['etat'] == 'ok' )
-          settings.fonction(settings.el);
-      }
      });
   });
   // À la suppression : ajout d'une notification
@@ -78,6 +83,7 @@
   $('#fenetre input').on('keypress',function (e) {
     if (e.which == 13)
       $('#fenetre a.icon-ok').click();
+    return false;
   });
 }
 
@@ -88,7 +94,7 @@
   // Suppression d'une éventuelle fenêtre de la fonction popup existante
   $('#fenetre,#fenetre_fond').remove();
   // Création
-  var el = $('<div id="fenetre"></div>').appendTo('body').html(contenu).focus();
+  var el = $('<article id="fenetre"></article>').appendTo('body').html(contenu).focus();
   if ( modal )
     $('<div id="fenetre_fond"></div>').appendTo('body').click(function() {
       $('#fenetre,#fenetre_fond').remove();
@@ -98,7 +104,7 @@
     $('<a class="icon-epingle" title="Épingler à la page"></a>').prependTo(el).on("click",function() {
       $('#fenetre_fond').remove();
       $(this).remove();
-      $('section').children('div,article').first().before(el.removeAttr('id'));
+      el.removeAttr('id').insertBefore($('article,#calendrier,#parentsdoc+*').first());
     });
   // Bouton de fermeture
   $('<a class="icon-ferme" title="Fermer"></a>').prependTo(el).on("click",function() {
@@ -107,54 +113,45 @@
   });
 }
 
+// Demande de confirmation, pour une suppression par exemple
+function confirmation(question, element, action) {
+  popup('<h3>Demande de confirmation</h3><p>'+question+'</p><p class="confirmation"><button class="icon-ok"></button>&nbsp;&nbsp;&nbsp;<button class="icon-annule"></button></p>',true);
+  $('#fenetre .icon-ok').on("click", function () {
+    action(element);
+    $('#fenetre,#fenetre_fond').remove();
+  });
+  $('#fenetre .icon-annule').on("click",function () {
+    $('#fenetre,#fenetre_fond').remove();
+  });
+}
+
+// Fonction de pliage/dépliage vertical des lignes dans un tableau
+function plie() {
+  $(this).parent().parent().nextUntil('.categorie').fadeToggle("slow");
+  $(this).toggleClass('icon-plie icon-deplie');
+}
+
 //////////////////////////////////////////////////////////////
 // Édition de textes : facilités de modication des éléments //
 //////////////////////////////////////////////////////////////
 
-// Affichage d'un placeholder (valeur si élement vide)
-// Fonction à appliquer sur les input, textarea et div éditables, qui possèdent
-// un attribut data-placeholder, éventuellement après textareahtml
-$.fn.placeholder = function() {
-  this.each( function() {
-    var el = $(this);
-    // Pour les div éditables, réalisation par css : ajout/suppression
-    // automatique de la classe placeholder à chaque modification
-    if ( el.is('div') )
-      el.on("change keyup mouseup",function() {
-        if ( $(this).text().length )
-          $(this).removeClass('placeholder');
-        else
-          $(this).addClass('placeholder');
-      }).change();
-    // Pour les input et textarea, on crée un span avant l'élément, qui sera
-    // automatiquement affiché ou caché à chaque modification de l'élément
-    else {
-      $('<span class="placeholder">'+this.getAttribute('data-placeholder')+'</span>').insertBefore(el).on('click',function() {
-        $(this).next().focus();
-      }).hide();
-      el.on("change keyup mouseup",function() {
-        if ( ( this.value != undefined ) && !(this.value.length) && ( $(this).is(':visible') ) )
-          // bug avec show() : donne du display:block pour certains éléments
-          $(this).prev().css('display','inline');
-        else
-          $(this).prev().hide();
-      }).change();
-    }
-  });
-}
-
 // Transformation d'une textarea pour l'édition de code HTML
 // Ajoute des boutons d'édition et la possibilité de commuter l'affichage avec
 // une div éditable. Fonction à appliquer sur les textarea de classe edithtml
 $.fn.textareahtml = function() {
   this.each(function() {
     var ta = $(this);
-    var placeholder = this.getAttribute('data-placeholder');
+    var placeholder = this.getAttribute('placeholder');
     // Modification du placeholder
-    this.setAttribute('data-placeholder',placeholder+'. Formattage en HTML, balises visibles.');
+    this.setAttribute('placeholder',placeholder+'. Formattage en HTML, balises visibles.');
     // Ajout d'éléments : boutons, div éditable
-    var ce = $('<div contenteditable="true" data-placeholder="'+placeholder+'"></div>').insertAfter(ta.before(boutons)).hide();
+    var ce = $('<div contenteditable="true" placeholder="'+placeholder+'"></div>').insertAfter(ta.before(boutons)).hide();
     var boutonretour = ta.prev().children(".icon-retour");
+    // Classe 'ligne' aux boutons et à la div éditable si c'est le cas du textarea
+    if ( ta.hasClass('ligne') ) {
+      ce.addClass('ligne');
+      ta.prev().addClass('ligne');
+    }
     // Retour à la ligne par Entrée, nettoyage au copié-collé
     ta.on("keypress",function(e) {
         if (e.which == 13)
@@ -181,7 +178,6 @@
       e.preventDefault();
       // Modification des visibilités
       ta.hide();
-      ta.prev().hide();
       ce.show().css("min-height",ta.outerHeight());
       $(this).hide().prev().show();
       // Nettoyage et synchronisation (change -> mise à jour du placeholder)
@@ -210,7 +206,7 @@
       ta.show(0).css("height",ce.height());
       $(this).hide().next().show();
       // Nettoyage et synchronisation (change -> mise à jour du placeholder)
-      ta.focus().val(nettoie(ce.html())).change();
+      ta.focus().val(nettoie(ce.html()));
     }).hide();
     // Clic bouton aide
     ta.prev().children('.icon-aide').on("click",function(e) {
@@ -231,7 +227,7 @@
   this.each(function() {
     var el = $(this);
     // Enregistrement de la valeur originale, pour rétablissement si abandon
-    this.setAttribute('data-original',( el.is('h3') ) ? el.text() : el.html());
+    el.data('original',( el.is('h3') ) ? el.text() : el.html());
     // Transformation
     $('<a class="icon-edite" title="Modifier"></a>').appendTo(el).on("click",transforme);
   });
@@ -242,42 +238,37 @@
   var el = $(this).parent().addClass('avecform');
   // Création d'une textarea si div, d'un input sinon
   if ( el.is('div') )
-    el.html('<form><textarea name="val" rows="'+(el.attr('data-original').split(/\r\n|\r|\n/).length+3)+'"></textarea></form>');
+    el.html('<form><textarea name="val" rows="'+(el.data('original').split(/\r\n|\r|\n/).length+3)+'"></textarea></form>');
   else
     el.html('<form class="edition" onsubmit="$(this).children(\'a.icon-ok\').click(); return false;"><input type="text" name="val" value=""></form>');
   // Identifiant de l'input ou de la textarea
-  var input = el.find('[name="val"]').val(el.attr('data-original'));
-  // Facilités (boutons si edithtml, placeholder)
-  if ( el.attr('data-placeholder') ) {
-    input.attr('data-placeholder',el.attr('data-placeholder'));
-    if ( el.hasClass('edithtml') ) {
-      input.textareahtml(); input.next().placeholder();
-    }
-    input.placeholder();
-  }
+  var input = el.find('[name="val"]').val(el.data('original')).attr('placeholder',el.attr('placeholder'));
+  // Boutons si edithtml
+  if ( el.hasClass('edithtml') )
+    input.textareahtml();
   // Envoi
   $('<a class="icon-ok" title="Valider"></a>').appendTo(el.children()).on("click",function() {
-    // Identifiant de l'entrée à modifier : table|champ|id
-    var id = el.attr('data-id').split('|');
+    // Identifiant de l'entrée à modifier : action|champ|id
+    var id = el.data('id').split('|');
     // Nettoyage et synchronisation si besoin
     if (el.hasClass('edithtml') )
       input.val(nettoie( ( input.is(':visible') ) ? input.val() : input.next().html() ));
     // Envoi
     $.ajax({url: 'ajax.php',
             method: "post",
-            data: { table:id[0], champ:id[1], id:id[2], val:input.val() },
+            data: { action:id[0], champ:id[1], id:id[2], val:input.val() },
             dataType: 'json',
             el: el,
             fonction: function(el) {
               var val = el.find('[name="val"]').val();
-              el.removeClass('avecform').html(val).attr('data-original',val);
+              el.removeClass('avecform').html(val).data('original',val);
               $('<a class="icon-edite" title="Modifier"></a>').appendTo(el).on("click",transforme);
             }
     });
   });
   // Annulation
   $('<a class="icon-annule" title="Annuler"></a>').appendTo(el.children()).on("click",function() {
-    el.removeClass('avecform').html(el.attr('data-original'));
+    el.removeClass('avecform').html(el.data('original'));
     $('<a class="icon-edite" title="Modifier"></a>').appendTo(el).on("click",transforme);
   });
   // Récupération du focus avec curseur à la fin
@@ -289,7 +280,7 @@
 $.fn.editinplacecdt = function() {
   this.each(function() {
     // Enregistrement de la valeur originale, pour rétablissement si abandon
-    $(this).wrapInner('<span></span>').attr('data-original',$(this).text());
+    $(this).wrapInner('<span></span>').data('original',$(this).text());
     // Transformation
     $('<a class="icon-edite" title="Modifier"></a>').appendTo($(this)).on("click",transformecdt);
   });
@@ -298,54 +289,57 @@
 // Transformation d'un élément de classe titrecdt pour son édition
 function transformecdt() {
   var el = $(this).parent();
-  el.children('.icon-edite').remove();
+  // Création du formulaire
+  $('.icon-edite',el).remove();
   var form = $('<form class="titrecdt"></form>').insertBefore(el.parent().children('div')).html($('#form-cdt').html());
+  // Création de l'identifiant des champs à partir du name
+  $('input, select',form).attr('id',function(){ return this.getAttribute('name'); });
   // Récupération des valeurs et modification initiale du formulaire
-  var valeurs = JSON.parse(el.attr('data-donnees'));
+  var valeurs = el.data('donnees');
   for ( var cle in valeurs )
-    form.find('[name="'+cle+'"]').val(valeurs[cle]);
+    $('#'+cle).val(valeurs[cle]);
   // Mise en place des facilités  
   form.init_cdt_boutons();
   // Mise à jour du titre si modification
-  form.find('input,[name="demigroupe"]').on('change keyup', function() {
-    var t = new Date(form.find('[name="jour"]').val().replace(/(.{2})\/(.{2})\/(.{4})/,function(tout,x,y,z) {return z+'-'+y+'-'+x; }));
-    var dg = ( form.find('[name="demigroupe"]').val() == 1 ) ? ' (en demi-groupe)' : '';
-    switch ( parseInt(seances[form.find('[name="tid"]').val()]) ) {
+  $('input,#demigroupe',form).on('change keyup', function() {
+    var t = new Date($('#jour').val().replace(/(.{2})\/(.{2})\/(.{4})/,function(tout,x,y,z) {return z+'-'+y+'-'+x; }));
+    var dg = ( $('#demigroupe').val() == 1 ) ? ' (en demi-groupe)' : '';
+    switch ( parseInt(seances[$('#tid').val()]) ) {
       case 0:
-        var titre = jours[t.getDay()]+' '+form.find('[name="jour"]').val()+' à '+form.find('[name="h_debut"]').val()+' : '+form.find('[name="tid"] option:selected').text()+dg;
+        var titre = jours[t.getDay()]+' '+$('#jour').val()+' à '+$('#h_debut').val()+' : '+$('#tid option:selected').text()+dg;
         break;
       case 1:
-        var titre = jours[t.getDay()]+' '+form.find('[name="jour"]').val()+' de '+form.find('[name="h_debut"]').val()+' à '+form.find('[name="h_fin"]').val()+' : '+form.find('[name="tid"] option:selected').text()+dg;
+        var titre = jours[t.getDay()]+' '+$('#jour').val()+' de '+$('#h_debut').val()+' à '+$('#h_fin').val()+' : '+$('#tid option:selected').text()+dg;
         break;
       case 2:
-        var titre = jours[t.getDay()]+' '+form.find('[name="jour"]').val()+' : '+form.find('[name="tid"] option:selected').text()+' pour le '+form.find('[name="pour"]').val()+dg;
+        var titre = jours[t.getDay()]+' '+$('#jour').val()+' : '+$('#tid option:selected').text()+' pour le '+$('#pour').val()+dg;
         break;
       case 3:
-        var titre = jours[t.getDay()]+' '+form.find('[name="jour"]').val()+' : '+form.find('[name="tid"] option:selected').text()+dg;
+        var titre = jours[t.getDay()]+' '+$('#jour').val()+' : '+$('#tid option:selected').text()+dg;
         break;
       case 4:
-        var titre = jours[t.getDay()]+' '+form.find('[name="jour"]').val();
+        var titre = jours[t.getDay()]+' '+$('#jour').val();
         break;
       case 5:
         var titre = '[Entrée hebdomadaire]';
     }
-    el.children('span').text(titre);
+    $('span',el).text(titre);
   });
   // Envoi
   $('<a class="icon-ok" title="Valider"></a>').appendTo(el).on("click",function() {
-    // Identifiant de l'entrée à modifier : table|id
-    var id = el.parent().attr('data-id').split('|');
+    // Identifiant de l'entrée à modifier : action|id
+    var id = el.parent().data('id').split('|');
     // Envoi
     $.ajax({url: 'ajax.php',
             method: "post",
-            data: 'table=cdt&id='+id[1]+'&'+form.serialize(),
+            data: 'action=cdt-elems&id='+id[1]+'&'+form.serialize(),
             dataType: 'json',
             el: el,
             fonction: function(el) {
               var form = el.siblings('form');
-              el.attr('data-original',el.children('span').text()).attr('data-donnees','{"tid":'+form.find('[name="tid"]').val()+',"jour":"'+form.find('[name="jour"]').val()+'","h_debut":"'+form.find('[name="h_debut"]').val()+'","h_fin":"'+form.find('[name="h_fin"]').val()+'","pour":"'+form.find('[name="pour"]').val()+'","demigroupe":'+form.find('[name="demigroupe"]').val()+'}');
+              el.data('original',$('span',el).text()).data('donnees',{tid:$('#tid').val(),jour:$('#jour').val(),h_debut:$('#h_debut').val(),h_fin:$('#h_fin').val(),pour:$('#pour').val(),demigroupe:$('#demigroupe').val()});
               form.remove();
-              el.children('a').remove();
+              $('a',el).remove();
               $('<a class="icon-edite" title="Modifier"></a>').appendTo(el).on("click",transformecdt);
             }
     }).done( function(data) {
@@ -356,152 +350,12 @@
   // Annulation
   $('<a class="icon-annule" title="Annuler"></a>').appendTo(el).on("click",function() {
     form.remove();
-    el.children('span').html(el.attr('data-original'));
-    el.children('a').remove();
+    $('span',el).html(el.data('original'));
+    $('a',el).remove();
     $('<a class="icon-edite" title="Modifier"></a>').appendTo(el).on("click",transformecdt);
   });
 }
 
-// Facilités du formulaire de modification des propriétés des éléments de cahier
-// de texte, utilisé par transformecdt() (élément existant) et formulaire()
-// (nouvel élément)
-$.fn.init_cdt_boutons = function() {
-  var form = this;
-  form.find('[name="jour"],[name="pour"]').datetimepicker({ format: 'd/m/Y', timepicker: false });
-  form.find('[name="h_debut"]').datetimepicker({ format: 'Ghi', datepicker: false,
-    onClose: function(t,input) {
-      form.find('[name="h_fin"]').val(function(i,v){ return v || ( input.val().length ? (parseInt(input.val().slice(0,-3))+2)+input.val().slice(-3) : ''); });
-    }
-  });
-  form.find('[name="h_fin"]').datetimepicker({ format: 'Ghi', datepicker: false });
-  // Ajout du zéro devant les dates et heures à 1 chiffre
-  var zero = function(n) {
-    return ( String(n).length == 1 ) ? '0'+n : String(n);
-  }
-  // Action lancée à la modification du raccourci
-  form.find('[name="raccourci"]').on('change keyup', function() {
-    var valeurs = raccourcis[this.value];
-    for ( var cle in valeurs ) {
-      // Modification de la date (prochain lundi/mardi..)
-      if ( cle == 'jour' ) {
-        var t = new Date;
-        var j = parseInt(valeurs['jour']);
-        t.setDate( ( j > t.getDay() ) ? t.getDate()-t.getDay()-7+j : t.getDate()-t.getDay()+j );
-        form.find('[name="jour"]').val(zero(t.getDate())+'/'+zero(t.getMonth()+1)+'/'+t.getFullYear());
-      }
-      // Modification des autres jours
-      else
-        form.find('[name="'+cle+'"]').val(valeurs[cle]);
-    }
-    // Pour éviter d'être remis à zéro immédiatement
-    this.setAttribute('data-modif',1);
-    // Modifie les champs visibles
-    form.find('[name="tid"]').change();
-  }).attr('data-modif',0);
-  // Action lancée à la modification du type de séance
-  form.find('[name="tid"]').on('change keyup', function() {
-    switch ( parseInt(seances[this.value]) ) {
-      case 0:
-        form.find('[name="h_debut"]').parent().show();
-        form.find('[name="h_fin"]').parent().hide();
-        form.find('[name="pour"]').parent().hide();
-        form.find('[name="demigroupe"]').parent().show();
-        break;
-      case 1:
-        form.find('[name="h_debut"]').parent().show();
-        form.find('[name="h_fin"]').parent().show();
-        form.find('[name="pour"]').parent().hide();
-        form.find('[name="demigroupe"]').parent().show();
-        break;
-      case 2:
-        form.find('[name="h_debut"]').parent().hide();
-        form.find('[name="h_fin"]').parent().hide();
-        form.find('[name="pour"]').parent().show();
-        form.find('[name="demigroupe"]').parent().show();
-        break;
-      case 3:
-        form.find('[name="h_debut"]').parent().hide();
-        form.find('[name="h_fin"]').parent().hide();
-        form.find('[name="pour"]').parent().hide();
-        form.find('[name="demigroupe"]').parent().show();
-        break;
-      default:
-        form.find('[name="h_debut"]').parent().hide();
-        form.find('[name="h_fin"]').parent().hide();
-        form.find('[name="pour"]').parent().hide();
-        form.find('[name="demigroupe"]').parent().hide();
-        break;
-    }
-    // Mise à jour du champ de raccourci
-    form.find('[name="jour"]').change();
-  });
-  // Action lancée à la modification des autres champs
-  form.find('input,[name="demigroupe"]').on('change keyup', function() {
-    // Remise à zéro du raccourci s'il n'a pas été modifié immédiatement
-    if ( form.find('[name="raccourci"]').attr('data-modif') == 0 )
-      form.find('[name="raccourci"]').val(0);
-    else
-      form.find('[name="raccourci"]').attr('data-modif',0);
-  });
-  // Envoi par appui sur Entrée
-  form.find('input,select').on("keypress",function(e) {
-    if ( e.which == 13 )
-      el.find('a.icon-ok').click();
-  });
-  // Focus sur le premier champ et modification initiale
-  form.find('select:first').focus();
-  form.find('[name="tid"]').change();
-}
-
-// Facilités du formulaire de modification des propriétés des raccourcis de
-// cahier de texte, utilisé sur les éléments de classe cdt-raccourcis
-$.fn.init_cdt_raccourcis = function() {
-  this.each(function() {
-    var form = $(this);
-    form.find('[name="h_debut"]').datetimepicker({ format: 'Ghi', datepicker: false,
-      onClose: function(t,input) {
-        form.find('[name="h_fin"]').val(function(i,v){ return v || ( input.val().length ? (parseInt(input.val().slice(0,-3))+2)+input.val().slice(-3) : ''); });
-      }
-    });
-    form.find('[name="h_fin"]').datetimepicker({ format: 'Ghi', datepicker: false });
-    // Action lancée à la modification du type de séance
-    form.find('[name="type"]').on('change keyup', function() {
-      switch ( parseInt(seances[this.value]) ) {
-        case 0:
-          form.find('[name="h_debut"]').parent().show();
-          form.find('[name="h_fin"]').parent().hide();
-          form.find('[name="demigroupe"]').parent().show();
-          break;
-        case 1:
-          form.find('[name="h_debut"]').parent().show();
-          form.find('[name="h_fin"]').parent().show();
-          form.find('[name="demigroupe"]').parent().show();
-          break;
-        case 2:
-          form.find('[name="h_debut"]').parent().hide();
-          form.find('[name="h_fin"]').parent().hide();
-          form.find('[name="demigroupe"]').parent().show();
-          break;
-        case 3:
-          form.find('[name="h_debut"]').parent().hide();
-          form.find('[name="h_fin"]').parent().hide();
-          form.find('[name="demigroupe"]').parent().show();
-          break;
-        default:
-          form.find('[name="h_debut"]').parent().hide();
-          form.find('[name="h_fin"]').parent().hide();
-          form.find('[name="demigroupe"]').parent().hide();
-          break;
-      }
-    }).change();
-    // Envoi par appui sur Entrée
-    form.find('input,select').on("keypress",function(e) {
-      if ( e.which == 13 )
-        form.find('a.icon-ok').click();
-    });
-  });
-}
-
 // Nettoyage des contenus de textarea pour l'édition de code HTML
 function nettoie(html) {
   // Suppression du span cdptmp ajouté par la fonction insert()
@@ -554,8 +408,6 @@
     contenant.innerHTML = contenu.replace(/[ÍÌ]/g,'');
   // Suppression des attributs liés à la sélection
   marqueselection(el,true);
-  // Mise à jour éventuelle du placeholder
-  $(contenant).change();
   // Resélection
   // Cas des textarea, navigateurs modernes
   if ( ( contenant.tagName == 'TEXTAREA' ) && ( contenant.selectionStart !== undefined ) ) {
@@ -672,7 +524,7 @@
   <button class="icon-tex" title="LATEX!"></button>\
   <button class="icon-source" title="Voir et éditer le code html"></button>\
   <button class="icon-nosource" title="Voir et éditer le texte formaté"></button>\
-  <button class="icon-aide" title="Voir et éditer le texte formaté"></button>\
+  <button class="icon-aide" title="Aide pour cet éditeur de texte"></button>\
 </p>';
 
 // Fonctions lancées par les boutons appelant une fenêtre par la fonction popup
@@ -809,9 +661,9 @@
     marqueselection(el,true);
   });
   // Récupération des listes de répertoires et de documents
-  $.ajax({url: 'ajax.php',
+  $.ajax({url: 'recup.php',
           method: "post",
-          data: { recupdoc:'' },
+          data: { action:'docs' },
           dataType: 'json'})
     .done( function(data) {
       // Fonction de mise à jour de l'aperçu
@@ -897,8 +749,10 @@
           // Visibilité des cases à cocher
           if ( 'pdfjpgpegpng'.indexOf(texte.slice(-4,-1)) > -1 )
             $('#vue').change().parent().show();
-          else
+          else {
             $('#vue, #largeur, #format').parent().hide();
+            $('#vue').prop('checked',false);
+          }
           // Mise à jour de l'aperçu
           majapercu();
         });
@@ -910,11 +764,11 @@
             $('#fenetre a.icon-ok').click();
           // Si vide : modification automatique
           if ( this.value.length == 0 )  {
-            this.setAttribute('data-auto',1);
+            $(this).data('auto',1);
             $('#doc').change();
           }
           else  {
-            this.setAttribute('data-auto',0);
+            $(this).data('auto',0);
             majapercu();
           }
         });
@@ -953,8 +807,8 @@
             return;
           if ( e.which == 13 )
             $('#fenetre a.icon-ok').click();
-          if ( this.value != this.getAttribute('data-valeur') ) {
-            this.setAttribute('data-valeur', this.value);
+          if ( this.value != $(this).data('valeur') ) {
+            $(this).data('valeur', this.value);
             majapercu();
           }
         }).attr('data-valeur',100);
@@ -984,34 +838,7 @@
         });
       }
       
-      // Si besoin de connection (mais ne pas lancer la fonction afficher_login)
-      if ( data['etat'] == 'login_' ) {
-        $('#fenetre > div:first').html('<p>Vous n\'êtes actuellement plus connecté. Vous devez vous identifier à nouveau pour récupérer la liste des répertoires et documents disponibles.</p>\
-        <form>\
-        <p class="ligne"><label for="login">Identifiant&nbsp;: </label><input type="text" name="login" id="login"></p>\
-        <p class="ligne"><label for="motdepasse">Mot de passe&nbsp;: </label><input type="password" name="motdepasse" id="motdepasse"></p>\
-        </form>'); 
-        $('#fenetre input').on('keypress',function (e) {
-          if (e.which == 13)
-            $('#fenetre a.icon-ok').click();
-        });
-        $('#fenetre a.icon-ok').on('click', function () {
-          $.ajax({url: 'ajax.php',
-                  method: "post",
-                  data: { login: $('#login').val(), motdepasse: $('#motdepasse').val(), recupdoc: '' },
-                  dataType: 'json'})
-            .done( function(data) {
-              // Si erreur d'identification, on reste bloqué là
-              if ( data['etat'] == 'login_nok' )
-                $('#fenetre > div:first > p:first').html(data['message']).addClass('warning');
-              // Sinon et si l'identification est ok, on y va
-              else if ( data['etat'] == 'ok_' )
-                affichedocs(data);
-          });
-        });
-      }
-      // Si retour de récupération ok
-      else if ( data['etat'] == 'ok_' )
+      if ( 'mats' in data )
         affichedocs(data);
   });
 }
@@ -1153,7 +980,7 @@
   var prop = el.parent().attr('data-id').split('|');
   $.ajax({url: 'ajax.php',
           method: "post",
-          data: { cache:1, table:prop[0], id:prop[1] },
+          data: { cache:1, action:prop[0], id:prop[1] },
           dataType: 'json',
           el: el,
           fonction: function(el) {
@@ -1170,7 +997,7 @@
   var prop = el.parent().attr('data-id').split('|');
   $.ajax({url: 'ajax.php',
           method: "post",
-          data: { montre:1, table:prop[0], id:prop[1] },
+          data: { montre:1, action:prop[0], id:prop[1] },
           dataType: 'json',
           el: el,
           fonction: function(el) {
@@ -1188,7 +1015,7 @@
   var prop = parent.attr('data-id').split('|');
   $.ajax({url: 'ajax.php',
           method: "post",
-          data: { monte:1, table:prop[0], id:prop[1] },
+          data: { monte:1, action:prop[0], id:prop[1] },
           dataType: 'json',
           el: parent,
           fonction: function(el) {
@@ -1211,7 +1038,7 @@
   var prop = parent.attr('data-id').split('|');
   $.ajax({url: 'ajax.php',
           method: "post",
-          data: { descend:1, table:prop[0], id:prop[1] },
+          data: { descend:1, action:prop[0], id:prop[1] },
           dataType: 'json',
           el: parent,
           fonction: function(el) {
@@ -1231,648 +1058,1348 @@
 // Suppression
 function supprime(el) {
   var parent = el.parent();
-  popup('<h3>Confirmer la demande de suppression</h3><p class="suppression"><button class="icon-ok" title="Confirmer la suppression"></button>&nbsp;&nbsp;&nbsp;<button class="icon-annule" title="Sortir sans supprimer"></button></p>',true);
-  $('#fenetre .icon-ok').on("click",function () {
-    $('#fenetre,#fenetre_fond').remove();
-    var prop = parent.attr('data-id').split('|');
+  var prop = parent.data('id').split('|');
+  var item = 'un élément';
+  switch ( prop[0] ) {
+    case 'infos': item = 'une information'; break;
+    case 'pages': item = 'la matière <em>'+$('h3', parent).text()+'</em>. Les informations qui y sont écrites seront aussi supprimées'; break;
+    case 'reps': item = 'le répertoire <em>'+$('.nom', parent).map(function() { return this.textContent || $(this).find('input').val(); }).get(0)+'</em>. Tous les sous-répertoires et documents qui s\'y trouvent seront aussi supprimés'; break;
+    case 'docs': item = 'le document <em>'+$('.nom', parent).map(function() { return this.textContent || $(this).find('input').val(); }).get(0)+'</em>'; break;
+    case 'colles': item = 'le programme de colle de la '+$('.edition',parent).text().toLowerCase(); break;
+    case 'cdt-elems': item = 'un élément du cahier de texte'; break;
+    case 'cdt-types': item = 'le type de séances <em>'+$('h3', parent).text()+'</em>. Les éléments du cahier de texte associés à ce type seront aussi supprimés'; break;
+    case 'cdt-raccourcis': item = 'le raccourci de séance <em>'+$('h3', parent).text()+'</em>. Aucun élément du cahier de texte ne sera supprimé'; break;
+    case 'notes': parent = parent.parent(); item = 'une colle du <em>'+$('td:first',parent).text()+'</em>, d\'une durée de '+$('td:eq(3)',parent).text()+'. Toutes les notes associées seront supprimées'; break;
+    case 'matieres': item = 'la matière <em>'+$('h3', parent).text()+'</em>. Les programmes de colles, le cahier de texte et les notes correspondantes seront supprimées. Les répertoires, les documents, les pages d\'informations spécifiques et les éléments de l\'agenda associés à la matière seront conservés mais ne seront plus associés à une matières, donc visibles dans le contexte «&nbsp;général&nbsp;»'; break;
+    case 'groupes': item = 'le groupe <em>'+( $('.editable', parent).text() || $('input:first', parent).val())+'</em>. Les utilisateurs concernés ne seront pas supprimés'; break;
+    case 'agenda-elems': item = 'un événement de l\'agenda'; break;
+    case 'agenda-types': item = 'le type d\'événement <em>'+$('h3', parent).text()+'</em>. Les événements de l\'agenda associés à ce type seront aussi supprimés'; break;
+  }
+  confirmation('Vous allez supprimer XXX.<br>Cette opération n\'est pas annulable.'.replace('XXX',item),this,function(el) {
     $.ajax({url: 'ajax.php',
             method: "post",
-            data: { supprime:1, table:prop[0], id:prop[1] },
+            data: { supprime:1, action:prop[0], id:prop[1] },
             dataType: 'json',
             el: parent,
             fonction: function(el) {
-              if ( prop[0] == 'colles' ) {
-                el.removeClass('cache');
-                el.children('.icon-cache,.icon-montre,.icon-supprime').remove();
-                el.children('.icon-ajoute').show();
-                el.children('div.editable').each( function() {
-                  this.setAttribute('class',this.getAttribute('class').replace('editable','editable-remplace'));
-                }).html('<p>Le programme de colles de cette semaine n\'est pas encore défini.</p>');
-              }
-              else if ( ( prop[0] == 'utilisateurs' ) || ( prop[0] == 'agenda' ) )
+              if ( prop[0].match(/^(agenda|colles)$/) )
                 location.reload(true);
               else
                 el.remove();
             }
     });
   });
-  $('#fenetre .icon-annule').on("click",function () {
-    $('#fenetre,#fenetre_fond').remove();
-  });
 }
 
-// Formulaire généré lors des clics sur les boutons icon-ajoute et icon-prefs
-function formulaire(el) {
-  var action = el.getAttribute('data-id');
-  // Si validation de compte utilisateur, pas de formulaire à afficher
-  if ( action == 'valide_utilisateur' ) {
+// Modification de protection -- Informations uniquement 
+function lock(el) {
+  var parent = el.parent();
+  var prop = parent.data('id').split('|');
+  var protection = el.data('val');
+  // Fenêtre de sélection
+  popup('<a class="icon-ok" title="Valider ce choix"></a><h3>Accès à l\'information</h3><table id="selmult">\
+  <tr class="categorie"><td>Accès public</td><td><input value="0" type="checkbox"></td></tr>\
+  <tr class="categorie"><td>Utilisateurs identifiés</td><td><input value="6" type="checkbox"></td></tr>\
+  <tr class="element"><td>Invités</td><td><input value="1" type="checkbox"></td></tr>\
+  <tr class="element"><td>Élèves</td><td><input value="2" type="checkbox"></td></tr>\
+  <tr class="element"><td>Colleurs</td><td><input value="3" type="checkbox"></td></tr>\
+  <tr class="element"><td>Administratifs</td><td><input value="4" type="checkbox"></td></tr>\
+  <tr class="element"><td>Professeurs</td><td><input value="5" type="checkbox"></td></tr>\
+  <tr class="categorie"><td>Information invisible</td><td><input value="32" type="checkbox"></td></tr>\
+  </tbody></table>',true);
+  // Initialisation
+  var f = $('#fenetre');
+  if ( ( protection == 0 ) || ( protection == 32 ) )
+    $('input[value="'+protection+'"]', f).prop("checked",true).change();
+  else  {
+    $('input[value=6]', f).prop("checked",true).change();
+    for ( var a=1; a<6; a++ )
+      if ( ( (protection-1)>>(a-1) & 1 ) == 0 )
+        $('input[value="'+a+'"]', f).prop('checked',true).change();
+  }
+  // Différenciation des lignes (pour cochage multiple et css)
+  var f = $('#fenetre');
+  $('input[value=0],input[value=6],input[value=32]', f).parent().parent().addClass('categorie');
+  $('tr:not(.categorie)', f).addClass('element');
+  // Décochage automatique
+  $('input', f).on("click", function() {
+    if ( ( this.value == 0 ) || ( this.value == 32 ) )
+      $(this).parent().parent().siblings().find('input[type=checkbox]').prop("checked",false).change();
+    else {
+      $('input[value=0],input[value=32]', f).prop("checked",false).change();
+      $('input[value=6]', f).prop("checked",true).change();
+      // Cochage multiple si on clique sur "Utilisateurs identifiés"
+      if ( this.value == 6 )
+        $('tr:not(.categorie) input', f).prop("checked",true).change();
+    }
+  });
+  // Clic sur toute la ligne
+  $('tr', f).on("click",function(e) {
+    if ( !$(e.target).is('input') )
+      $(this).find('input').click();
+  });
+  // Mise en évidence
+  $('input', f).on("change", function () {
+    $(this).parent().parent().removeClass('sel');
+    if ( this.checked )
+      $(this).parent().parent().addClass('sel');
+  });
+  // Validation
+  $('.icon-ok', f).on("click", function() {
+    if ( $('input[value="32"]', f).prop('checked') )
+      var val = 32;
+    else if ( $('input[value="0"]', f).prop('checked') )
+      var val = 0;
+    else
+      var val = 32 - $('input:checked:not([value=6])', f).map(function() { return this.value|0; }).get().reduce( function(acc,v) { return acc + Math.pow(2,(v-1)); },0);
+    el.data('val',val);
     $.ajax({url: 'ajax.php',
-            method: "post",
-            data: { table:'utilisateurs', id:$(el).parent().attr('data-id').split('|')[1] },
-            dataType: 'json',
-            el: el,
-            fonction: function(el) {
-              location.reload(true);
-            }
-    });
-    return true;
-  }
-  // Si l'attribut data-remplace est fourni, il s'agit de l'identifiant de
-  // l'article qui sera modifié après validation 
-  if ( el.hasAttribute('data-remplace') ) {
-    // Modification de l'article et insertion du formulaire
-    var article = $('article#'+el.getAttribute('data-remplace'));
-    article.children('.icon-ajoute').after('<a class="icon-annule" title="Annuler"></a><a class="icon-ok" title="Valider"></a>');
-    article.children('[class*="-remplace"],.icon-ajoute').hide();
-    var form = $('<form></form>').appendTo(article).html($('#form-'+action).html());
-    // Modification de l'identifiant à envoyer
-    form.children('[name="id"]').val(el.getAttribute('data-remplace'));
-  }
-  // Cas sans remplacement : insertion en haut de page
-  else {
-    // Suppression d'un éventuel contenu épinglé existant
-    $('#epingle').remove();
-    // Création du nouveau formulaire
-    var article = $('<article id="epingle"><a class="icon-ferme" title="Fermer"></a>\
-  <a class="icon-aide" title="Aide pour ce formulaire"></a>\
-  <a class="icon-ok" title="Valider"></a></article>').prependTo('section');
-    var form = $('<form></form>').appendTo(article).html($('#form-'+action).html());
-  }
-  // Facilités et peuplement du formulaire
-  form.find('.edithtml').textareahtml();
-  form.find('[data-placeholder]').placeholder();
-  if ( $.colpick )  form.find('[name="couleur"]').colpick();
-  switch ( action ) {
-    case 'ajoute-cdt':
-      form.init_cdt_boutons();
-      break;
-    case 'ajoute-cdt-raccourci':
-      form.init_cdt_raccourcis();
-      break;
-    case 'notes':
-    case 'ajoute-notes':
-      notes(el);
-      break;
-    case 'evenement':
-      if ( el.id[0] == 'e' )  {
-        var id = el.id.substr(1);
-        var valeurs = evenements[id];
-        var cles = ['type','matiere','debut','fin','texte'];
-        for ( var i = 0 ; i < 6 ; i++ )  {
-          form.find('[name="'+cles[i]+'"]').val(valeurs[cles[i]]);
-        };
-        form.find('[name="texte"]').change();
-        form.find('[name="id"]').val(id);
-        form.find('[name="jours"]').prop('checked',valeurs['je']);
-        $('<a class="icon-supprime" title="Supprimer cette information"></a>').insertBefore($('.icon-ok'))
-          .on('click',function(){ supprime($(this)); })
-          .parent().attr('data-id','agenda|'+id);
-      }
-      // Gestion des sélections de date/heure
-      form.find('[name="debut"]').datetimepicker({
-        onShow: function()  {
-          this.setOptions({maxDate: form.find('[name="fin"]').val() || false });
-        },
-        onClose: function(t,input) {
-          form.find('[name="fin"]').val(function(i,v){ return v || input.val(); });
-        }
-      });
-      form.find('[name="fin"]').datetimepicker({
-        onShow: function()  {
-          this.setOptions({minDate: form.find('[name="debut"]').val() || false });
-        },
-        onClose: function(t,input) {
-          form.find('[name="debut"]').val(function(i,v){ return v || input.val(); });
-        }
-      });
-      // Case "dates seulement" : changement de format, conservation de l'heure
-      form.find('[name="jours"]').on('change',function() {
-        var v;
-        if ( this.checked )  {
-          form.find('[name="debut"],[name="fin"]').each( function() {
-            v = this.value.split(' ');
-            $(this).val(v[0]).attr('data-heure',v[1]).datetimepicker({ format: 'd/m/Y', timepicker: false });
-          });
+        method: "post",
+        data: { action:prop[0], id:prop[1], champ:'protection', val:val },
+        dataType: 'json',
+        el: parent,
+        fonction: function(el) {
+          // Recharger pour modifier si besoin l'affichage de l'information
+          location.reload(true);
         }
-        else  {
-          form.find('[name="debut"],[name="fin"]').each( function() {
-            if ( this.hasAttribute('data-heure') )
-              $(this).val(this.value+' '+$(this).attr('data-heure')).removeAttr('data-heure');
-            $(this).datetimepicker({ format: 'd/m/Y Ghi', timepicker: true });
-          });
-        }
-      }).change();
-      break;
-    case 'deplacement-colle':
-      form.find('[name="ancien"],[name="nouveau"]').each( function() {
-        $(this).datetimepicker();
-        $(this).next().on('click',function() { $(this).prev().val('').change(); });
-      });
-      break;
-  }
-  // Édition des élèves d'un nouveau groupe
-  form.find('.usergrp > .icon-edite').on("click", function() {
-    utilisateursgroupe(this);
-  });
-  // Actions des boutons fermeture, annulation, aide, envoi
-  article.children('.icon-ferme').on("click",function() {
-    $('#epingle').remove();
-  });
-  article.children('.icon-annule').on("click",function() {
-    article.children('form,.icon-annule,.icon-ok').remove();
-    article.children('[class*="-remplace"],.icon-ajoute').show();
-  });
-  article.children('a.icon-aide').on("click", function() {
-    popup($('#aide-'+action).html(),false);
+    });
   });
-  article.children('a.icon-ok').on("click", function() {
-    // Nettoyage et synchronisation si besoin
-    form.children('.edithtml').each( function() {
+}
+
+// Ajout de colle : édition et remplacement
+function ajoutecolle(el) {
+  // Modification de l'article et insertion du formulaire
+  var article = el.parent();
+  el.before('<a class="icon-annule" title="Annuler"></a><a class="icon-ok" title="Valider"></a>');
+  el.next().addBack().hide();
+  var form = $('<form></form>').appendTo(article).html($('#form-ajoutecolle').html());
+  $('textarea',form).textareahtml();  
+  $('input',form).attr('id','cache');
+  // Actions des boutons annulation, aide, validation
+  $('.icon-annule', article).on("click",function() {
+    $('form,.icon-annule,.icon-ok', article).remove();
+    el.next().addBack().show();
+  });
+  $('a.icon-ok', article).on("click", function() {
+    // Nettoyage du texte
+    $('textarea',form).each( function() {
       this.value = nettoie( ( $(this).is(':visible') ) ? this.value : $(this).next().html() );
     });
-    // Nettoyage des notes à ne pas mettre
-    if ( ( action == 'notes' ) || ( action == 'ajoute-notes' ) )
-      $('#epingle select:not(:visible)').val('x');
     // Envoi
+    var id = article.data('id').split('|')[1];
     $.ajax({url: 'ajax.php',
             method: "post",
-            data: form.serialize(),
+            data: form.serialize()+'&action=ajout-colle&id='+id,
             dataType: 'json',
             el: article,
             fonction: function(el) {
-              // Sans remplacement : on recharge la page
-              if ( el.attr('id') == 'epingle' ) {
-                // Sauf si #noreload présent : ne rien faire
-                if ( !$('#epingle #noreload').length )
-                  location.reload(true);
-              }
-              // Avec remplacement : on modifie l'article pour permettre à nouveau l'édition
-              else {
-                el.children('.icon-supprime').show();
-                if ( el.find('[name="cache"]').is(":checked") ) {
-                  el.children('.icon-montre').show();
-                  el.addClass('cache');
-                }
-                else
-                  el.children('.icon-cache').show();
-                el.children('[class*="-remplace"]').show().each( function() {
-                  this.setAttribute('class',this.getAttribute('class').replace('-remplace',''));
-                  var id = this.getAttribute('data-id');
-                  $(this).html(article.find('[name="'+id.slice(id.indexOf('|')+1,id.lastIndexOf('|'))+'"]').val());
-                });
-                el.children('.editable').editinplace();
-                el.children('.icon-annule,.icon-ok,form').remove();
-              }
+              var texte = $('textarea',el).val();
+              var cache = $('input',el).is(":checked");
+              if ( cache ) 
+                el.addClass('cache');
+              el.data('id','colles|'+id);
+              $('.icon-aide',el).nextAll().remove();
+              $('.icon-aide',el).after(
+                ( cache ? '<a class="icon-montre" title="Afficher le programme de colles sur la partie publique"></a>' : '<a class="icon-cache" title="Rendre invisible le programme de colles sur la partie publique"></a>' )
+                + '<a class="icon-supprime" title="Supprimer ce programme de colles"></a><div class="editable edithtml" data-id="colles|texte|'+id+'" placeholder="Texte du programme de colles">'+texte+'</div>');
+              $('a.icon-cache,a.icon-montre,a.icon-supprime', el).on("click", function() {
+                window[this.className.substring(5)]($(this));
+              });
+              $('.editable',el).editinplace();
             }
     });
   });
   // Envoi par appui sur Entrée
-  form.find('input,select').on('keypress',function (e) {
+  $('input,select',form).on('keypress',function (e) {
     if ( e.which == 13 ) {
       e.preventDefault();
-      article.children('a.icon-ok').click();
+      $('a.icon-ok', article).click();
     }
   });
 }
 
+/////////////////
+// Formulaires //
+/////////////////
+
 // Envoi automatique : pour les formulaire déjà présents
-function valide(el) {
+function valide() {
   var data = '';
-  // Envoi de courriel
-  if ( $('#mail').length ) {
-    // Pas d'envoi si pas de destinataire ou si sujet vide
-    if ( $('.editabledest').children('span').text() == '[Personne]' )
-      affiche('Il faut au moins un destinataire pour envoyer le courriel.','nok');
-    else if ( !$('[name="sujet"]').val().length )
-      affiche('Il faut un sujet non vide pour envoyer le courriel.','nok');
-    else
-      data = $('#mail').serialize();
-  }
   // Modification de planning
-  else if ( $('#planning').length ) {
-    data = $('form').serialize();
-  }
+  if ( $('#planning').length )
+    data = 'action=planning&'+$('form').serialize();
   else {
-    var form = $(el).parent();
-    var id = form.parent().attr('data-id').split('|');
-    data = 'table='+id[0]+'&id='+id[1]+'&'+form.serialize();
+    var id = $(this).parent().attr('data-id').split('|');
+    data = 'action='+id[0]+'&id='+id[1]+'&'+$(this).nextAll('form').serialize();
   }
   if ( data.length )
     $.ajax({url: 'ajax.php',
             method: "post",
             data: data,
             dataType: 'json',
-            el: el,
+            el: this,
             fonction: function(el) {
-              if ( !$(el).is('[data-noreload]') )
+              if ( el.classList[1] != 'noreload' )
                 location.reload(true);
             }
     });
+  else
+    affiche('<p>Aucune donnée envoyée.</p>','nok');
 }
 
-////////////////////
-// Autres actions //
-////////////////////
-
-// Suppression massive des éléments d'une matière
-function suppressionmultiple(el) {
-  popup('<h3>Confirmer la demande de suppression</h3><p class="suppression"><button class="icon-ok" title="Confirmer la suppression"></button>&nbsp;&nbsp;&nbsp;<button class="icon-annule" title="Sortir sans supprimer"></button></p>',true);
-  $('#fenetre .icon-ok').on("click",function () {
-    var prop = el.getAttribute('data-id').split('|');
-    $('#fenetre,#fenetre_fond').remove();
-    $.ajax({url: 'ajax.php',
-            method: "post",
-            data: 'table='+prop[0]+'&id='+prop[1]+'&supprime_'+prop[2]+'=1',
-            dataType: 'json',
-            el: $(el),
-            fonction: function(el) {
-              el.remove();
-            }
+// Formulaire généré lors des clics sur les boutons généraux
+function formulaire() {
+  var idform = this.className.split(' ')[0].substring(5); // .modifevnmt -> evnmt
+  var action = $('#form-'+idform).data('action');
+  // Suppression d'un éventuel contenu épinglé existant
+  $('#epingle').remove();
+  // Création du nouveau formulaire
+  var article = $('<article id="epingle"><a class="icon-ferme" title="Fermer"></a>\
+  <a class="icon-aide" title="Aide pour ce formulaire"></a>\
+  <a class="icon-ok" title="Valider"></a></article>').insertBefore($('article,#calendrier,#parentsdoc+*').first());
+  var form = $('<form></form>').appendTo(article).html($('#form-'+idform).html());
+  // Facilités et peuplement du formulaire
+  $('.edithtml',form).textareahtml();
+  // Création de l'identifiant des champs à partir du name
+  $('input[name], select[name]:not([multiple])',form).attr('id',function(){ return this.getAttribute('name'); });
+  // Actions spécifiques
+  switch ( action ) {
+    case 'reps': $(this).init_reps(); break;
+    case 'ajout-rep': form.append('<input type="hidden" name="parent" value="'+$(this).parent().data('id').split('|')[1]+'">'); break;
+    case 'docs': 
+    case 'ajout-doc': $(this).init_docs(action); break;
+    case 'cdt-elems': form.init_cdt_boutons(); break;
+    case 'ajout-cdt-raccourci': form.init_cdt_raccourcis(); break;
+    case 'notes':
+    case 'ajout-notes': $(this).init_notes(action); break;
+    case 'agenda-elems': $(this).init_evenements(); break;
+    case 'deplcolle': $('#ancien,#nouveau').each( function() { $(this).datetimepicker({ format: 'd/m/Y Ghi', timepicker: true }); }); break;
+    case 'ajout-utilisateurs': form.init_ajout_utilisateurs(); break;
+    case 'ajout-groupe': $('.usergrp span',form).on("click", utilisateursgroupe); break;
+  }
+  // Selections multiples (matière, accès)
+  $('select[multiple]', form).each(selmult);
+  // Actions des boutons fermeture, aide, validation
+  $('#epingle .icon-ferme').on("click",function() { $('#epingle').remove(); });
+  $('#epingle a.icon-aide').on("click", function() { popup($('#aide-'+idform).html(),false); });
+  $('#epingle a.icon-ok').on("click", function() {
+    // Nettoyage et synchronisation si besoin
+    $('.edithtml',form).each( function() {
+      this.value = nettoie( ( $(this).is(':visible') ) ? this.value : $(this).next().html() );
     });
-  });
-  $('#fenetre .icon-annule').on("click",function () {
-    $('#fenetre,#fenetre_fond').remove();
-  });
-}
-
-// Édition des utilisateurs associés à une matière
-function utilisateursmatiere(el) {
-  var bouton = el;
-  var matiere = bouton.getAttribute('data-matiere').split('|'); // 0: nom, 1: id
-  popup($('#form-utilisateurs').html(),true);
-  // Modification de la matière indiquée
-  $('#fenetre').addClass('usermat').children('h3').append(matiere[0]);
-  // Ajout des bons id pour les checkboxes et envoi des données
-  $('#fenetre :checkbox').attr('id',function() { return this.name; }).on('change',function() {
-    var box = this;
-    var id = box.id.substr(1);
+    // Nettoyage des notes à ne pas mettre
+    if ( ( action == 'notes' ) || ( action == 'ajoute-notes' ) )
+      $('#epingle select:not(:visible)').val('x');
+    // Envoi
     $.ajax({url: 'ajax.php',
             method: "post",
-            data: { table:'utilisateurs', id:id, matiere:matiere[1], ok:(box.checked?1:0) },
+            data: form.serialize()+'&action='+action,
             dataType: 'json',
-            el: box,
+            el: '',
             fonction: function(el) {
-              // Mise à jour de la liste des utilisateurs
-              if ( el.checked ) {
-                utilisateurs = utilisateurs+','+id;
-                $(box).prev().addClass('labelchecked');
-              }
-              else {
-                utilisateurs = (','+utilisateurs+',').replace(','+id+',',',').slice(1,-1);
-                $(box).prev().removeClass('labelchecked');
-              }
-              bouton.setAttribute('data-uid',utilisateurs);
-              // Mise à jour du descriptif de la matière
-              var np = $('#fenetre .a4:checked').length;
-              var nc = $('#fenetre .a3:checked').length;
-              var ne = $('#fenetre .a2:checked').length;
-              var ni = $('#fenetre .a1:checked').length;
-              var n = np+nc+ne+ni;
-              if ( n )  {
-                var u = '';
-                if ( np )  u += ', '+np+' professeur'+(np>1?'s':'');
-                if ( nc )  u += ', '+nc+' colleur'+(nc>1?'s':'');
-                if ( ne )  u += ', '+ne+' élève'+(ne>1?'s':'');
-                if ( ni )  u += ', '+ni+' invité'+(ni>1?'s':'');
-                $(bouton).parent().children('span').text('Cette matière concerne '+n+' utilisateur'+(n>1?'s':'')+' dont '+u.substr(1)+'.');
-              }
-              else
-                $(bouton).parent().children('span').text('Cette matière ne concerne aucun utilisateur.');
+              location.reload(true);
             }
-    }).done( function(data) {
-      // Si erreur, on décoche
-      if ( data['etat'] != 'ok' )
-        box.checked = !box.checked;
     });
   });
-  // Cochage des utilisateurs déjà associés
-  var utilisateurs = bouton.getAttribute('data-uid');
-  $('#u'+utilisateurs.replace(/,/g,',#u')).prop("checked",true);
-  $('#fenetre :checked').prev().addClass('labelchecked');
+  // Envoi par appui sur Entrée
+  $('input,select',form).on('keypress',function (e) {
+    if ( e.which == 13 ) {
+      e.preventDefault();
+      $('#epingle a.icon-ok').click();
+    }
+  });
 }
 
-// Édition des utilisateurs (élèves) associés à un groupe
-function utilisateursgroupe(el) {
-  var bouton = el;
-  var article = $(el).parent().parent();
-  popup($('#form-utilisateurs').html(),true);
-  // Modification de la matière indiquée
-  $('#fenetre').addClass('usergrp').children('h3').append(article.find('span.editable').text() || article.find('input').val());
-  // Ajout des bons id pour les checkboxes
-  $('#fenetre :checkbox').attr('id',function() { return this.name; });
-  // Si article est en fait une div, on est dans le formulaire de nouveau groupe
-  // donc mise à jour à la fin
-  if ( article.is('div') ) {
-    $('#fenetre :checkbox').on('change',function() {
-      $(this).prev().toggleClass('labelchecked',el.checked);
-    });
-    $('<a class="icon-ok" title="Valider"></a>').insertAfter('#fenetre .icon-ferme').on("click",function() {
-      var utilisateurs = $('#fenetre input:checked').map(function() { return this.id.replace('u',''); }).get().join();
-      bouton.setAttribute('data-uid',utilisateurs);
-      $(bouton).parent().next().val(utilisateurs);
-      $(bouton).parent().children('span').text($('#fenetre input:checked').parent().map(function() {
-        return $(this).text().replace(' (identifiant)','').trim();
-      }).get().join(', '));
-      $('#fenetre, #fenetre_fond').remove();
-    });
+// Facilités du formulaire de modification des répertoires
+// Attention, this est le bouton cliqué
+$.fn.init_reps = function() {
+  var el = $(this);
+  var form = $('#epingle form');
+  var sel = $('select[multiple]', form);
+  // Identifiant
+  var id = el.parent().data('id').split('|')[1];
+  // Donnees : parent,menu,protection
+  var donnees = el.parent().data('donnees').split('|');
+  var protection = donnees[2];
+  // Nom du répertoire : il faut enlever les parents éventuels, si on est dans #parentsdoc
+  // split(/\/\s/) : regexp = "/"+"espace" ("&nbsp;" passé dans text())
+  var nom = el.siblings('.nom').text().split(/\/\s/).pop() || el.parent().find('input').val();
+  // Remplissage du formulaire
+  $('em', form).text(nom);
+  if ( ( protection == 0 ) || ( protection == 32 ) )
+    sel.val(protection);
+  else  {
+    sel.val(6);
+    for ( var a=1; a<6; a++ )
+      if ( ( (protection-1)>>(a-1) & 1 ) == 0 )
+        $('option[value="'+a+'"]', sel).prop('selected',true);
   }
-  // Sinon envoi des données à chaque fois
+  if ( donnees[0] == 0 )
+    $('#nom,#parent,#menu', form).parent().remove();
   else {
-    $('#fenetre :checkbox').on('change',function() {
-      var box = this;
-      var eleve = box.id.substr(1);
+    $('#nom', form).val(nom);
+    if ( donnees[1] == '1' )
+      $('#menu', form).prop('checked',true);
+    // Désactivation des déplacements impossibles
+    $('[data-parents*=",'+id+',"]', form).prop('disabled', true);
+  }
+  form.append('<input type="hidden" name="id" value="'+id+'">');
+  // Boutons 
+  $('input[type="button"]', form).on("click", function() {
+    var action = ( this.value.substring(0,4) == 'Prop' ) ? 'propage' : 'vide';
+    var contexte = $(this).parent().find('em').text();
+    var texte = ( action == 'propage' ) ? 'Vous allez propager le réglage d\'accès du répertoire <em>'+contexte+'</em> à l\'ensemble de ses sous-répertoires et des documents qu\'ils contiennent.<br>Vous pourrez modifier à nouveau le réglage de chaque document ou répertoire, de façon individuelle.'
+                                        : 'Vous allez vider le répertoire <em>'+contexte+'</em>. Cela supprimera définitivement l\'ensemble de ses sous-répertoires et des documents qu\'ils contiennent.<br>Cette opération n\'est pas annulable.';
+    confirmation(texte,this,function(el) {
       $.ajax({url: 'ajax.php',
               method: "post",
-              data: { table:'groupes', id:article.attr('data-id').split('|')[1], eleve:eleve, ok:(box.checked?1:0) },
+              data: 'action=reps&id='+id+'&'+action+'=1',
               dataType: 'json',
-              el: box,
+              el: '',
               fonction: function(el) {
-                // Mise à jour de la liste des utilisateurs
-                if ( el.checked ) {
-                  utilisateurs = utilisateurs+','+eleve;
-                  $(box).prev().addClass('labelchecked');
-                }
-                else {
-                  utilisateurs = (','+utilisateurs+',').replace(','+eleve+',',',').slice(1,-1);
-                  $(box).prev().removeClass('labelchecked');
-                }
-                bouton.setAttribute('data-uid',utilisateurs);
-                // Mise à jour du descriptif du groupe
-                $(bouton).parent().children('span').text($('#fenetre input:checked').parent().map(function() {
-                  return $(this).text().replace(' (identifiant)','').trim();
-                }).get().join(', '));
+                location.reload(true);
               }
-      }).done( function(data) {
-        // Si erreur, on décoche
-        if ( data['etat'] != 'ok' )
-          box.checked = !box.checked;
       });
     });
-  }
-  // Cochage des utilisateurs déjà associés
-  var utilisateurs = bouton.getAttribute('data-uid');
-  $('#u'+utilisateurs.replace(/,/g,',#u')).prop("checked",true);
-  $('#fenetre :checked').prev().addClass('labelchecked');
-}
-
-// Édition des utilisateurs destinataires d'un courriel
-function utilisateursmail(el) {
-  popup($('#form-utilisateurs').html(),true);
-  // Ajout des identifiants pour utilisation des labels
-  $('#fenetre [name="dest[]"]').each( function() {
-    $(this).attr('id','u'+this.value);
-  });
-  // Sélection automatique des utilisateurs déjà choisis
-  var ids = $('[name="id-copie"]').val().split(',');
-  for(var i=0; i<ids.length; i++)
-    $('#fenetre [name="dest[]"][value="'+ids[i]+'"]').prop("checked",true);
-  ids = $('[name="id-bcc"]').val().split(',');
-  for(var i=0; i<ids.length; i++)
-    $('#fenetre [name="dest_bcc[]"][value="'+ids[i]+'"]').prop("checked",true);
-  // Bouton de sélection multiple
-  $('#fenetre .icon-cocher').on("click keyup", function () {
-    var classe = this.getAttribute('data-classe');
-    $('#fenetre .'+classe+':not(:disabled)').prop("checked",true);
-    $(this).hide();
-    $(this).next().show();
-    classe = ( classe.indexOf('bcc') > 0 ) ? classe.replace('bcc','c') : classe.replace('c','bcc');
-    $('#fenetre .'+classe+':not(:disabled)').prop("checked",false);
-    $('#fenetre .icon-cocher[data-classe="'+classe+'"]').show();
-    $('#fenetre .icon-decocher[data-classe="'+classe+'"]').hide();
-  });
-  $('#fenetre .icon-decocher').on("click keyup", function () {
-    $('#fenetre .'+this.getAttribute('data-classe')+':not(:disabled)').prop("checked",false);
-    $(this).hide();
-    $(this).prev().show();
-  }).hide();
-  // Décochage automatique de l'autre case du même utilisateur
-  $('#fenetre :checkbox[name]').on("change", function () {
-    if( $(this).is(':checked') )
-      $('#fenetre :checkbox[name][value="'+this.value+'"][name!="'+this.name+'"]').prop("checked",false);
-  });
-  // Groupes
-  $('#fenetre :checkbox:not([name])').on("click", function () {
-    var ids = this.value.split(',');
-    for (var i=0; i<ids.length; i++)
-      $('#fenetre :checkbox[name="'+this.className+'[]"][value="'+ids[i]+'"]').prop("checked",$(this).prop('checked')).change();
-    if ( $(this).is(':checked') )
-      $('#fenetre :checkbox[class="'+((this.className=='dest')?'dest_bcc':'dest')+'"][value="'+this.value+'"]').prop("checked",false);
-  });
-  // Récupération des valeurs
-  $('#fenetre .icon-ok').on("click", function() {
-    $('[name="id-copie"]').val(
-      $('#fenetre [name="dest[]"]:checked').map(function() { return this.value; }).get().join(',')
-    );
-    $('[name="id-bcc"]').val(
-      $('#fenetre [name="dest_bcc[]"]:checked').map(function() { return this.value; }).get().join(',')
-    );
-    $(el).prev().text(
-      $('#fenetre [name="dest[]"]:checked').parent().prev().map(function() { return $(this).text().replace(' (identifiant)',''); }).get()
-      .concat(
-        $('#fenetre [name="dest_bcc[]"]:checked').parent().prev().prev().map(function() { return $(this).text().replace(' (identifiant)','')+' (CC)'; }).get()
-      ).join(', ')
-    )
-    if ( !$(el).prev().text().length )
-      $(el).prev().text('[Personne]');
-    $('#fenetre, #fenetre_fond').remove();
   });
 }
 
-// Édition des notes (on est déjà passé par la fonction formulaire)
-function notes(el)  {
-  // Clic sur les groupes
-  $('#epingle :checkbox').on("click", function () {
-    // Récupération des élèves du groupe
-    var ids = $('#epingle .grpnote:checked').map(function() { return this.value.split(','); }).get().concat();
-    // On cache tous les élèves sauf ceux ayant déjà une note pour l'édition
-    // de notes déjà saisies (lignes repérées par data-orig=true)
-    $('#epingle tr[data-id]:not([data-orig])').hide();
-    for (var i=0; i<ids.length; i++)
-      $('#epingle tr[data-id="'+ids[i]+'"]').show();
-  });
-  // Génération des select de notes
-  $('#epingle tr[data-id]').each( function() {
-    $(this).children('td:last').html($('#epingle div').html());
-    $(this).find('select').attr('name','e'+$(this).attr('data-id'));
-  });
-  $('#epingle div').remove();
-  // Modification initiale
-  // On cache tous les élèves a priori s'il y a des groupes
-  if ( $('#epingle :checkbox').length )
-    $('#epingle tr[data-id]').hide();
-  // Si édition de notes, on affiche les notes déjà mises
-  if ( el.getAttribute('data-eleves') ) {
-    // Titre et identifiant de la semaine 
-    $('#epingle h4').text($(el).parent().children('h3').text());
-    $('#epingle [name="id"]').val($(el).parent().attr('data-id').split('|')[1]);
-    // Récupération des élèves et des notes
-    var eleves = el.getAttribute('data-eleves').split('|');
-    var notes = el.getAttribute('data-notes').split('|');
-    // Modification et affichage. Les lignes sont marquées data-orig=true pour
-    // rester affichées lors du clic sur une checkbox de groupe, mais ce
-    // marquage est supprimé à la première modification de la note
-    for (var i=0; i<eleves.length; i++)
-      $('#epingle tr[data-id="'+eleves[i]+'"]').attr('data-orig',true).show().find('select').val(notes[i]).on('change',function() {
-        $(this).parent().parent().removeAttr('data-orig');
-      });
-    // Impossible de modifier les élèves ayant déjà une note
-    var dn = dejanotes[$('#epingle [name="id"]').val().split('-')[0]].split(',');
-    for (i=0; i<dn.length; i++)
-      $('#epingle tr[data-id="'+dn[i]+'"]:not(:visible)').addClass('dejanote').find('select').prop('disabled',true);
-    $('.dejanote td:first-child').each( function() {
-      $(this).text($(this).text()+' (noté par un autre colleur)');
-    });
+// Facilités du formulaire de modification des répertoires
+// Attention, this est le bouton cliqué
+// action vaut docs ou ajout-docs
+$.fn.init_docs = function(action) {
+  var el = $(this);
+  var form = $('#epingle form');
+  // Nom : du document si modification, du répertoire parent si ajout
+  // On enlève les parents éventuels, si on est dans #parentsdoc
+  var nom = el.siblings('.nom').text().split(/\/\s/).pop() || el.parent().find('input').val();
+  $('em', form).text(nom);
+  // Identifiant : celui du document, ou du répertoire parent si ajout
+  var id = el.parent().data('id').split('|')[1];
+  // Si modification : ajout de l'identifiant et du nom
+  if ( action == 'docs' ) {
+    var protection = el.parent().data('protection');
+    form.append('<input type="hidden" name="id" value="'+id+'">');
+    $('#nom', form).val(nom);
   }
+  // Si ajout : récupération de la protection du répertoire parent
   else {
-    $('#epingle #semaine').on("change keyup", function() {
-      var semaine = $(this).val().split('-')[0];
-      // Récupération des "déjànotés" précédents
-      $('.dejanote td:first-child').each( function() {
-        $(this).text($(this).text().replace(' (noté par un autre colleur)',''));
-      });
-      $('.dejanote').removeClass('dejanote').find('select').prop('disabled',false);
-      // Impossible de modifier les élèves ayant déjà une note
-      if ( semaine > 0 ) {
-        var dn = dejanotes[semaine].split(',');
-        for (var i=0; i<dn.length; i++)
-          $('#epingle tr[data-id="'+dn[i]+'"]').addClass('dejanote').find('select').prop('disabled',true);
-        $('.dejanote td:first-child').each( function() {
-          $(this).text($(this).text()+' (noté par un autre colleur)');
-        });
+    var protection = el.parent().data('donnees').split('|')[2];
+    // Modification automatique du champ nom
+    $('#fichier', form).on('change',function () {
+      if ( !$('#nom', form).val().length ) {
+        var f = this.value;
+        $('#nom', form).val(f.substring(f.lastIndexOf('\\')+1,f.lastIndexOf('.')) || f);
       }
     });
+    form.append('<input type="hidden" name="parent" value="'+id+'">');
   }
+  // Modification de la protection initiale
+  var sel = $('select[multiple]', form);
+  if ( ( protection == 0 ) || ( protection == 32 ) )
+    sel.val(protection);
+  else  {
+    sel.val(6);
+    for ( var a=1; a<6; a++ )
+      if ( ( (protection-1)>>(a-1) & 1 ) == 0 )
+        $('option[value="'+a+'"]', sel).prop('selected',true);
+  }
+
+  form.append('<input type="hidden" name="action" value="'+action+'">');
+  // Envoi du fichier
+  $('#epingle a.icon-ok').off("click").on("click", function() {
+    var data = new FormData(form[0]);
+    // Envoi
+    $.ajax({url: 'ajax.php',
+            method: "post",
+            data: data,
+            dataType: 'json',
+            contentType:false,
+            processData:false,
+            el: '',
+            fonction: function(el) {
+              location.reload(true);
+            }
+    });
+  });
 }
 
-// Envoi des courriels
-function envoimail(el) {
-  // Pas d'envoi si pas de destinataire
-  if ( $('.editabledest').children('span').text() == '[Personne]' )
-    affiche('Il faut au moins un destinataire pour envoyer le courriel.','nok');
-  else
-  $.ajax({url: 'ajax.php',
-          method: "post",
-          data: $('#mail').serialize(),
-          dataType: 'json',
-          el: '',
-          fonction: function(el) {
-            location.reload(true);
-          }
-  });
-}
-
-// Édition des répertoires
-function modifierepertoire(el) {
-  var action = ( el.className == 'icon-edite' ) ? 'repertoire' : 'ajouterep';
-  // Suppression d'un éventuel contenu épinglé existant
-  $('#epingle').remove();
-  // Création du nouveau formulaire
-  var article = $('<article id="epingle"><a class="icon-ferme" title="Fermer"></a>\
-  <a class="icon-aide" title="Aide pour ce formulaire"></a>\
-  <a class="icon-ok" title="Valider"></a></article>').insertAfter('#parentsdoc');
-  var form = $('<form onsubmit="return false;"></form>').appendTo(article).html($('#form-'+action).html());
-  if ( action == 'repertoire' ) {
-    // Identifiant
-    var id = $(el).parent().attr('data-id').split('|')[1];
-    // Donnees : parent,menu,protection
-    var donnees = $(el).parent().attr('data-donnees').split('|');
-    // Remplissage du formulaire par les données du répertoire
-    // split(/\/\s/) : regexp = "/"+"espace" ("&nbsp;" passé dans text())
-    var nom = $(el).parent().children('.nom').text().split(/\/\s/).pop() || $(el).parent().find('input').val();
-    form.find('em').text(nom);
-    if ( donnees[0] == 0 ) {
-      form.find('#nom,#parent,#menu').parent().remove();
+// Facilités du formulaire de modification des propriétés des éléments de cahier
+// de texte, utilisé par transformecdt() (élément existant) et formulaire()
+// (nouvel élément)
+$.fn.init_cdt_boutons = function() {
+  var form = this;
+  $('#jour,#pour').datetimepicker({ format: 'd/m/Y', timepicker: false });
+  $('#h_debut').datetimepicker({ format: 'Ghi', datepicker: false,
+    onClose: function(t,input) {
+      $('#h_fin').val(function(i,v){ return v || ( input.val().length ? (parseInt(input.val().slice(0,-3))+2)+input.val().slice(-3) : ''); });
+    }
+  });
+  $('#h_fin').datetimepicker({ format: 'Ghi', datepicker: false });
+  // Ajout du zéro devant les dates et heures à 1 chiffre
+  var zero = function(n) {
+    return ( String(n).length == 1 ) ? '0'+n : String(n);
+  }
+  // Action lancée à la modification du raccourci
+  $('#raccourci').on('change keyup', function() {
+    var valeurs = raccourcis[this.value];
+    for ( var cle in valeurs ) {
+      // Modification de la date (prochain lundi/mardi..)
+      if ( cle == 'jour' ) {
+        var t = new Date;
+        var j = parseInt(valeurs['jour']);
+        t.setDate( ( j > t.getDay() ) ? t.getDate()-t.getDay()-7+j : t.getDate()-t.getDay()+j );
+        $('#jour').val(zero(t.getDate())+'/'+zero(t.getMonth()+1)+'/'+t.getFullYear());
+      }
+      // Modification des autres jours
+      else
+        $('#'+cle).val(valeurs[cle]);
+    }
+    // Pour éviter d'être remis à zéro immédiatement
+    $(this).data('modif',1);
+    // Modifie les champs visibles
+    $('#tid').change();
+  }).data('modif',0);
+  // Action lancée à la modification du type de séance
+  $('#tid').on('change keyup', function() {
+    switch ( parseInt(seances[this.value]) ) {
+      case 0:
+        $('#h_debut,#demigroupe').parent().show();
+        $('#h_fin,#pour').parent().hide();
+        break;
+      case 1:
+        $('#h_debut,#h_fin,#demigroupe').parent().show();
+        $('#pour').parent().hide();
+        break;
+      case 2:
+        $('#h_debut,#h_fin').parent().hide();
+        $('#pour,#demigroupe').parent().show();
+        break;
+      case 3:
+        $('#h_debut,#h_fin,#pour').parent().hide();
+        $('#demigroupe').parent().show();
+        break;
+      default:
+        $('#h_debut,#h_fin,#pour,#demigroupe').parent().hide();
+    }
+    // Mise à jour du champ de raccourci
+    $('#jour').change();
+  });
+  // Action lancée à la modification des autres champs
+  $('input,#demigroupe',form).on('change keyup', function() {
+    // Remise à zéro du raccourci s'il n'a pas été modifié immédiatement
+    if ( $('#raccourci').data('modif') == 0 )
+      $('#raccourci').val(0);
+    else
+      $('#raccourci').data('modif',0);
+  });
+  // Envoi par appui sur Entrée
+  $('input,select',form).on("keypress",function(e) {
+    if ( e.which == 13 )
+      $('a.icon-ok',el).click();
+  });
+  // Focus sur le premier champ et modification initiale
+  $('select:first',form).focus();
+  $('#tid').change();
+}
+
+// Facilités du formulaire de modification des propriétés des raccourcis de
+// cahier de texte, utilisé sur les éléments de classe cdt-raccourcis
+$.fn.init_cdt_raccourcis = function() {
+  this.each(function() {
+    var form = $(this);
+    $('[id^="h_d"]',form).datetimepicker({ format: 'Ghi', datepicker: false,
+      onClose: function(t,input) {
+        $('[id^="h_f"]',form).val(function(i,v){ return v || ( input.val().length ? (parseInt(input.val().slice(0,-3))+2)+input.val().slice(-3) : ''); });
+      }
+    });
+    $('[id^="h_fin"]').datetimepicker({ format: 'Ghi', datepicker: false });
+    // Action lancée à la modification du type de séance
+    $('[id^="type"]',form).on('change keyup', function() {
+      switch ( parseInt(seances[this.value]) ) {
+        case 0:
+          $('[id^="h_d"],[id^="dem"]',form).parent().show();
+          $('[id^="h_f"]',form).parent().hide();
+          break;
+        case 1:
+          $('[id^="h_d"],[id^="h_f"],[id^="dem"]',form).parent().show();
+          break;
+        case 2:
+        case 3:
+          $('[id^="h_d"],[id^="h_f"]',form).parent().hide();
+          $('[id^="dem"]',form).parent().show();
+          break;
+        default:
+          $('[id^="h_d"],[id^="h_f"],[id^="dem"]',form).parent().hide();
+      }
+    }).change();
+    // Envoi par appui sur Entrée
+    $('input,select',form).on("keypress",function(e) {
+      if ( e.which == 13 )
+        $('a.icon-ok',form).click();
+    });
+  });
+}
+
+// Facilités spécifiques du formulaire d'édition des notes
+$.fn.init_notes = function(action) {
+  var el = $(this);
+  var form = $('#epingle form').append($('#form-notes').html());
+  // Création de l'identifiant des champs à partir du name
+  $('input, select',form).attr('id',function(){ return this.getAttribute('name'); });
+  
+  // Génération des select de notes
+  $('tr[data-id]',form).append('<td>'+$('div',form).html()+'</td>');
+  $('table select',form).attr('name',function() {
+    return 'e'+$(this).parent().parent().data('id');
+  });
+  $('div',form).remove();
+  
+  // On cache tous les élèves a priori s'il y a des groupes
+  if ( $('input:checkbox',form).length ) {
+    $('tr[data-id]',form).hide();
+    // Clic sur les groupes
+    $('input:checkbox', form).on("click", function () {
+      if ( $('input:checkbox:last', form).prop('checked') )
+        return $('tr[data-id]',form).show();
+      // Récupération des élèves du groupe
+      var ids = $('input:checked', form).map(function() { return this.value.split(','); }).get().concat();
+      // On cache tous les élèves sauf ceux ayant déjà une note pour l'édition
+      // de notes déjà saisies (lignes repérées par la classe "orig")
+      $('tr[data-id]:not(.orig)',form).hide();
+      for (var i=0; i<ids.length; i++)
+        $('tr[data-id="'+ids[i]+'"]',form).show();
+    });
+  }
+
+  // Fonction de marquage des déjà notés pour en prévenir la notation, utilisée
+  // à l'initialisation et à la modification de semaine
+  function marque_dejanotes(sid) {
+    if ( sid == 0 )
+      return true;
+    // Récupération des déjà notés par les autres colleurs
+    var dn = dejanotesautres[sid].split(',');
+    for (var i=0; i<dn.length; i++)
+      $('tr[data-id="'+dn[i]+'"]', form).addClass('dejanote').find('td:eq(0)').text( function() {
+        return this.textContent+' (noté par un autre colleur)';
+      });
+    // Récupération des déjà notés par l'utilisateur
+    var dn = dejanotesperso[sid].split(',');
+    for (var i=0; i<dn.length; i++)
+      $('tr[data-id="'+dn[i]+'"]:not(.orig)', form).addClass('dejanote').find('td:eq(0)').text( function() {
+        return this.textContent+' (déjà noté par vous-même)';
+      });
+    // Désactivation et effacement des valeurs
+    $('.dejanote select').prop('disabled',true).val('x');
+  }
+
+  // Facilités
+  $('#jour').datetimepicker({ format: 'd/m/Y', timepicker: false, onShow: function() {
+      this.setOptions({ minDate: debut || $('#sid option:selected').data('date') ,
+                        maxDate: new Date( new Date(( fin || $('#sid option:selected').next().data('date') ).replace(/(.{2})\/(.{2})\/(.{4})/,function(tout,x,y,z) {return z+'-'+y+'-'+x; })).getTime()-86400000 ) });
+    }
+  });
+  $('#heure').datetimepicker({ format: 'Ghi', datepicker: false, defaultTime: '15h30' });
+  $('#duree').datetimepicker({ format: 'Ghi', datepicker: false, defaultTime: '0h00', step: 10 })
+             .on("change", function() { $(this).removeClass('auto'); });
+  // Changement de semaine : seulement ajout de notes
+  $('#sid').on("change keyup", function() {
+    // Nettoyage des déjà notés précédents
+    $('.dejanote td:first-child').text( function() {
+      return this.textContent.replace(' (noté par un autre colleur)','').replace(' (déjà noté par vous-même)','');
+    });
+    $('.dejanote').removeClass('dejanote').find('select').prop('disabled',false);
+    // Marquage des déjà notés
+    marque_dejanotes($('#sid').val());
+    // Réglage du jour si hors de la semaine
+    var jour = new Date($('#jour').val().replace(/(.{2})\/(.{2})\/(.{4})/,function(tout,x,y,z) {return z+'-'+y+'-'+x; }));
+    var debut = new Date($('#sid option:selected').data('date').replace(/(.{2})\/(.{2})\/(.{4})/,function(tout,x,y,z) {return z+'-'+y+'-'+x; }));
+    var fin = new Date($('#sid option:selected').next().data('date').replace(/(.{2})\/(.{2})\/(.{4})/,function(tout,x,y,z) {return z+'-'+y+'-'+x; }));
+    if ( ( jour < debut ) || ( jour > fin ) ) {
+      debut = debut.toJSON();
+      $('#jour').val(debut.substr(8,2)+'/'+debut.substr(5,2)+'/'+debut.substr(0,4));
+    }
+  }).change();
+  // Mise à jour de la durée
+  $('select', form).on("change keyup", function() {
+    var nb = $('table select:visible',form).filter(function() { return this.value != "x"; }).length;
+    var duree = nb*(dureecolle || 20);
+    if ( $('#duree').is('.auto') || duree > $('#duree').val().replace(/^(\d*)h(\d*)$/,function(tout,x,y) {return 60*(x|0)+(y|0); }) )
+      $('#duree').val( (duree/60|0)+'h'+(duree%60||'') ).addClass('auto');
+  });
+
+  // Si édition de notes : affichage des notes modifiables, ajout ou modification
+  // de la durée possible seulement pour les colles non encore relevées. 
+  // Modification de semaine impossible
+  if ( action == 'notes' ) {
+    var tr = el.parent().parent();
+    // Récupération des données : semaine, début, fin pour l'utilisation globale
+    // dans le onShow de #jour et dans marque_dejanotes.
+    var sid = el.data('sid');
+    var debut = $('select[name="sid"] option[value="'+sid+'"]').data('date');
+    var fin = $('select[name="sid"] option[value="'+sid+'"]').next().data('date');
+    // Affichage des notes déjà mises. Les lignes sont marquées avec la classe
+    // orig pour rester affichées lors du clic sur une checkbox de groupe, mais
+    // ce marquage est supprimé à la première modification de la note
+    var eleves = el.data('eleves').split('|');
+    var notes = el.data('notes').split('|');
+    for (var i=0; i<eleves.length; i++)
+      $('tr[data-id="'+eleves[i]+'"]', form).addClass('orig').show()
+        .find('select').val(notes[i]).on('change',function() {
+          $(this).parent().parent().removeClass('orig');
+        });
+    // Initialisation du titre, de l'identifiant, du jour, de l'heure, de la durée
+    $('h3',form).text('Modifier des notes - semaine du '+$('select[name="sid"] option[value="'+sid+'"]').text().split(' ').slice(0,3).join(' '));
+    $('#id').val(el.parent().data('id').split('|')[1]);
+    $('#jour').val($('td:eq(0)',tr).text().replace(/(.{6})(.{2})/,function(tout,x,y) {return x+'20'+y; }));
+    $('#heure').val($('td:eq(1)',tr).text().replace('-',''));
+    $('#duree').val($('td:eq(3)',tr).text());
+    // Si colle non déjà relevée (pas d'icône de suppression), on peut tout modifier
+    if ( el.next().length ) {
+      // Marquage des déjà notés de la semaine
+      marque_dejanotes(sid);
+    }
+    // Si colle déjà relevée : on ne peut pas ajouter/supprimer des notes ni
+    // modifier la durée
+    else {
+      $('tr:not(.orig)',form).hide();
+      $('.orig option[value="x"]',form).remove();
+      $('#duree').prop('disabled',true);
+      form.append('<p>Cette colle a déjà été relevée&nbsp;: il est impossible de modifier quels élèves ont été interrogés ou la durée de la colle. Vous pouvez corriger la date et l\'heure (dans la limite de la semaine enregistrée) ou les notes que vous avez mises vous pouvez mettre une note à un élève initialement absent qui a rattrapé sa colle.</p>');
+    }
+  }
+
+}
+
+// Facilités spécifiques du formulaire d'ajout d'utilisateurs
+$.fn.init_ajout_utilisateurs = function() {
+  // Gestion des apparitions sélectives des paragraphes
+  $('#autorisation,#saisie').on("change",function() {
+    var f = $('#epingle form');
+    var a = $('#autorisation', f).val();
+    if ( a == 0 ) {
+      $('.affichesiinvite,.affichesiinvitation,.affichesimotdepasse,.mailenvoi,.mailliste', f).hide(0);
+      $('textarea', f).prop('disabled',true).attr('placeholder','Zone de saisie des utilisateurs\nSélectionnez d\'abord un type d\'utilisateur');
     }
     else {
-      form.find('#nom').val(nom);
-      form.find('[data-parents*=",'+id+',"]').prop('disabled', true);
-      if ( donnees[1] == '1' )
-        form.find('#menu').prop('checked',true);
-    }
-    $('#protection option[value="'+donnees[2]+'"]').prop('selected', true);
-    form.find('[name="id"]').val(id);
-  }
-  // Actions des boutons fermeture, annulation, aide, envoi
-  article.children('.icon-ferme').on("click",function() {
-    $('#epingle').remove();
+      var inv = ( a == 1 );
+      var mdp = ( $('#saisie', f).val() == 2 );
+      $('#saisie', f).parent().toggle(!inv);
+      $('.affichesiinvite', f).toggle(inv);
+      $('.affichesiinvitation', f).toggle(!inv && !mdp);
+      $('.affichesimotdepasse', f).toggle(!inv && mdp);
+      $('.mailenvoi', f).toggle(!inv);
+      $('.mailliste', f).toggle(a>2);
+      $('#mailenvoi', f).prop('disabled',mdp).change();
+      $('textarea', f).prop('disabled',false).attr('placeholder',function() {
+        if ( inv )       return 'identifiant_1,motdepasse_1\nidentifiant_2,motdepasse_2\nidentifiant_3,motdepasse_3\n...';
+        else if ( mdp )  return 'nom_1,prénom_1,motdepasse_1\nnom_2,prénom_2,motdepasse_2\nnom_3,prénom_3,motdepasse_3\n...';
+        else             return 'nom_1,prénom_1,adresse_1\nnom_2,prénom_2,adresse_2\nnom_3,prénom_3,adresse_3\n...';
+      });
+    }
+  }).change();
+  $('#epingle #mailenvoi').on("change",function() {
+    $('#epingle #mailliste').attr('disabled',!this.checked || this.disabled);
+  }).change();
+}
+
+// Facilités spécifiques de modification des événements
+$.fn.init_evenements = function() {
+  var el = $(this);
+  var form = $('#epingle form');
+  $('textarea',form).attr('id','texte');
+  
+  // Cas de l'événement à modifier
+  if ( el.is('.modifevnmt') )  {
+    var id = el.attr('id').substr(1);
+    var valeurs = evenements[id];
+    var cles = ['type','matiere','debut','fin','texte'];
+    for ( var i = 0 ; i < 6 ; i++ )  {
+      $('#'+cles[i]).val(valeurs[cles[i]]);
+    };
+    $('#id').val(id);
+    $('#texte').change();
+    $('#jours').prop('checked',valeurs['je']);
+    $('<a class="icon-supprime" title="Supprimer cette information"></a>').insertBefore($('.icon-ok'))
+      .on('click',function(){ supprime($(this)); })
+      .parent().data('id','agenda-elems|'+id);
+  }
+  
+  // Gestion des sélections de date/heure
+  $('#debut').datetimepicker({
+    onShow: function()  {
+      this.setOptions({maxDate: $('#fin').val() || false });
+    },
+    onClose: function(t,input) {
+      $('#fin').val(function(i,v){ return v || input.val(); });
+    }
   });
-  article.children('a.icon-aide').on("click", function() {
-    popup($('#aide-'+action).html(),false);
+  $('#fin').datetimepicker({
+    onShow: function()  {
+      this.setOptions({minDate: $('#debut').val() || false });
+    },
+    onClose: function(t,input) {
+      $('#debut').val(function(i,v){ return v || input.val(); });
+    }
   });
-  article.children('a.icon-ok').on("click", function() {
-    // Envoi
+  // Case "dates seulement" : changement de format, conservation de l'heure
+  $('#jours').on('change',function() {
+    var v;
+    if ( this.checked )  {
+      $('#debut,#fin').each( function() {
+        v = this.value.split(' ');
+        $(this).val(v[0]).attr('data-heure',v[1]).datetimepicker({ format: 'd/m/Y', timepicker: false });
+      });
+    }
+    else  {
+      $('#debut,#fin').each( function() {
+        if ( this.hasAttribute('data-heure') )
+          $(this).val(this.value+' '+$(this).attr('data-heure')).removeAttr('data-heure');
+        $(this).datetimepicker({ format: 'd/m/Y Ghi', timepicker: true });
+      });
+    }
+  }).change();
+}
+
+// Modification des selects multiples (accès ou matière)
+function selmult() {
+  var sel = $(this);
+  var isacces = this.getAttribute('name').indexOf('protection')+1 ? 1 : 0;
+  
+  // Fonction de mise à jour du texte affiché 
+  function majselect(sel) {
+    // Désélection et resélection pour raffraichissement
+    sel.prev().children().prop('selected',false).text( function() {
+      var options = $(isacces?'option:selected:not([value=6])':'option:selected',sel);
+      if ( isacces && ( options.length == 5 ) ) return 'Tout utilisateur identifié'
+      if ( options.length == 0 ) return 'Choisir ...';
+      else                       return options.map(function() { return this.textContent; }).get().join(', ');
+    }).prop('selected',true);
+  }
+  
+  // Faux élément select de remplacement, correspondant au label, générant au clic une fenêtre de sélection
+  $('<select id='+sel.prev().attr('for')+'><option selected hidden></select>').insertBefore(sel.hide(0)).attr('disabled',sel.attr('disabled')).on("click",function(e)  {
+    e.preventDefault();
+    this.blur();
+    // Fenêtre de sélection
+    popup('<a class="icon-ok" title="Valider ce choix"></a><h3>'+sel.prev().prev().text().replace(':','')+'</h3><table id="selmult">'
+      +$('option',sel).map(function() {
+        return '<tr'+(this.selected?' class="sel"':'')+'><td>'+this.textContent+'</td><td><input type="checkbox" '+(this.selected?'checked ':'')+'value="'+this.value+'"></td></tr>'
+      }).get().join('')
+      +'</table>',true);
+    var f = $('#fenetre');
+    // Différenciation des lignes si accès (pour cochage multiple et css)
+    if ( isacces ) {
+      $('input[value=0],input[value=6],input[value=32]', f).parent().parent().addClass('categorie');
+      $('tr:not(.categorie)', f).addClass('element');
+      // Décochage automatique
+      $('input', f).on("click", function() {
+        if ( ( this.value == 0 ) || ( this.value == 32 ) )
+          $(this).parent().parent().siblings().find('input[type=checkbox]').prop("checked",false).change();
+        else {
+          $('input[value=0],input[value=32]', f).prop("checked",false).change();
+          $('input[value=6]', f).prop("checked",true).change();
+          // Cochage multiple si on clique sur "Utilisateurs identifiés"
+          if ( this.value == 6 )
+            $('tr:not(.categorie) input', f).prop("checked",true).change();
+        }
+      });
+    }
+    // Cochage multiple si matières 
+    else {
+      $('#selmult', f).prepend('<tr class="categorie"><th></th><th><a class="icon-cocher"></a></th></tr>');
+      $('.icon-cocher', f).on("click", cocher_utilisateurs);
+    }
+    // Clic sur toute la ligne
+    $('tr', f).on("click",function(e) {
+      if ( !$(e.target).is('input') )
+        $(this).find('input').click();
+    });
+    // Mise en évidence
+    $('input', f).on("change", function () {
+      $(this).parent().parent().removeClass('sel');
+      if ( this.checked )
+        $(this).parent().parent().addClass('sel');
+    });
+    // Validation
+    $('.icon-ok', f).on("click", function() {
+      // Mise à jour du select, valeur passée sous forme d'array
+      sel.val( $('input:checked', f).map(function() { return this.value; }).get() );
+      // Mise à jour de l'affichage
+      majselect(sel);
+      $('#fenetre, #fenetre_fond').remove();
+    });
+  });
+  majselect(sel);
+}
+
+///////////////////////////////////////////////////////
+// Modifications des utilisateurs, matières, groupes //
+///////////////////////////////////////////////////////
+
+// Cochage/Décochage multiple des utilisateurs
+function cocher_utilisateurs() {
+  $(this).toggleClass('icon-cocher icon-decocher').parent().parent().nextUntil('.categorie').find('input').prop('checked',$(this).hasClass('icon-decocher')).change();
+}
+
+// Initialisation du tableau des utilisateurs
+function init_utilisateurs() {
+  // Affichage des icônes d'envoi de mail dans le tableau utilisateurs
+  // La case de tableau HTML contient la valeur 1 pour oui 0 pour non.
+  $('.icones.mailenvoi').each( function() {
+    this.innerHTML = ( this.textContent == 1 ) ? '<a class="icon-ok" title="Supprimer la possibilité d\'envoyer des courriels"></a>'
+                                               : '<a class="icon-nok" title="Accorder la possibilité d\'envoyer des courriels"></a>';
+  });
+  $('.mailenvoi a').on("click", mail_utilisateur);
+
+  // Modification multiple des préférences d'envoi de mail
+  $('th .icon-mail').on("click", mail_utilisateurs);
+
+  // Icônes de cochage multiple
+  $('.icon-cocher').on("click", cocher_utilisateurs);
+
+  // Édition de comptes uniques
+  $('td .icon-edite').on("click", edite_utilisateur);
+
+  // Désactivation, réactivation, suppression de comptes uniques et validation de demandes
+  $('td .icon-desactive, td .icon-active, td .icon-supprutilisateur, td .icon-validutilisateur').on("click", modif_utilisateur);
+
+  // Édition, désactivation, réactivation, suppression de comptes multiples et validation de demandes
+  $('th .icon-desactive, th .icon-active, th .icon-supprutilisateur, th .icon-validutilisateur').on("click", modif_utilisateurs);
+
+  // Clic sur toute la ligne (premières cases seulement)
+  $('td:not(.icones)').on("click",function(e) {
+    $(this).parent().find('input').click();
+  });
+
+  // Mise en évidence
+  $('#utilisateurs input').on("change", function () {
+    $(this).parent().parent().removeClass('sel');
+    if ( this.checked )
+      $(this).parent().parent().addClass('sel');
+  });
+  
+  /////////////
+  // Fonctions
+
+  // Modification de la propriété d'envoi de courriels
+  function mail_utilisateur() {
+    var val = $(this).hasClass("icon-ok");
     $.ajax({url: 'ajax.php',
             method: "post",
-            data: form.serialize(),
+            data: { action:'utilisateur', modif:'mailenvoi', id:$(this).parent().parent().data('id'), val:1-val },
             dataType: 'json',
-            el: article,
+            el: $(this),
             fonction: function(el) {
+              el.toggleClass('icon-ok icon-nok').attr("title",(val?'Accorder':'Supprimer')+' la possibilité d\'envoyer des courriels' );
+            }
+    });
+  }
+  
+  // Édition d'un compte utilisateur
+  function edite_utilisateur() {
+    var id = $(this).parent().parent().data('id');
+    // Récupération des données associées au compte
+    $.ajax({url: 'recup.php',
+            method: "post",
+            data: { action:'prefs', id:id },
+            dataType: 'json',
+            afficheform: function(data) {
+              if ( 'nom' in data ) {
+                popup($('#form-edite').html(),true);
+                var f = $('#fenetre');
+                // Création de l'identifiant des champs à partir du name
+                $('input[name]',f).attr('id',function(){ return this.getAttribute('name'); });
+                // Suppression des paragraphes et des questions non valables
+                if ( data['valide'] )             $('#comptedesactive, #demande, #invitation', f).remove();
+                else if ( data['demande'] )       $('#compteactif, #comptedesactive, #invitation', f).remove();
+                else if ( data['invitation'] )    $('#compteactif, #comptedesactive, #demande', f).remove();
+                else                              $('#compteactif, #demande, #invitation', f).remove();
+                if ( data['autorisation'] == 2 )         $('#mailliste', f).parent().remove();
+                else if ( data['autorisation'] == 1 )    $('#mailenvoi, #mailexp, #mailcopie, #mailliste', f).parent().remove();
+                // Personalisation du premier paragraphe
+                $('p:first', f).html(function(i,code){ return code.replace('XXX', data['prenom'].length ? 'de <em>'+data['prenom']+' '+data['nom']+'</em>' : '<em>'+data['login']+'</em>')
+                                                                  .replace('YYY','<em>'+['Invité','Élève','Colleur','Administratif','Professeur'][data['autorisation']-1]+'</em>'); });
+                // Peuplement du formulaire
+                $('input[type="text"],input[type="email"]',f).val(function(){ return data[this.id]; });
+                $('input[type="checkbox"]',f).prop("checked",function(){ return data[this.id]; });
+                // Comportement dynamique des propriétés d'envoi de courriels
+                $('#mailenvoi', f).on("click", function() {
+                  $('#mailexp, #mailcopie, #mailliste', f).prop('disabled',!this.checked);
+                  if ( this.checked && !$('#mailexp', f).val() )
+                    $('#mailexp', f).val(data['prenom']+' '+data['nom'])
+                });
+                $('#mailexp, #mailcopie, #mailliste', f).prop('disabled',!$('#mailenvoi', f).prop('checked'));
+                // Envoi par clic sur l'icône
+                $('a.icon-ok', f).on("click", function() {
+                  $.ajax({url: 'ajax.php',
+                          method: "post",
+                          data: 'action=utilisateur&modif=prefs&id='+id+'&'+$('form', f).serialize(),
+                          dataType: 'json',
+                          el: '',
+                          fonction: function(el) {
+                            location.reload(true);
+                          }
+                  });
+                });
+                // Envoi par appui sur Entrée
+                $('input', f).on('keypress',function (e) {
+                  if ( e.which == 13 ) {
+                    e.preventDefault();
+                    $('a.icon-ok', f).click();
+                  }
+                });
+              }
+            }
+          });
+  }
+  
+  // Désactivation, réactivation, suppression, validation d'un compte utilisateur
+  function modif_utilisateur() {
+    var question = '';
+    var compte = $(this).parent().siblings().first().text();
+    compte = ( compte.length ) ? 'de <em>'+compte+'</em>' : 'd\'identifiant <em>'+$(this).parent().siblings().first().next().text()+'</em>';
+    //var categorie = $(this).parent().parent().prevUntil('.categorie').last().prev().text();
+    var categorie = $(this).parent().parent().prevUntil('.categorie').last().data('val');
+    switch ( this.className.substring(5) ) {
+      case 'desactive':
+        if ( categorie == 'Invités' )
+          question = 'Vous allez désactiver le compte invité '+compte+'. Cela signifie que le compte ne sera pas supprimé mais sera non utilisable pour une connexion. Les associations éventuelles avec les matières seront conservées. Ce compte sera listé dans la partie inférieure du tableau.';
+        else 
+          question = 'Vous allez désactiver le compte '+compte+'. Cela signifie que le compte sera toujours visible pour les professeurs mais que l\'utilisateur correspondant ne pourra plus se connecter. Les notes de colles éventuelles seront conservées. Les données associées au compte seront conservées. Les accès spécifiques éventuels pourront être rétablis en réactivant le compte. Ce compte sera listé dans la partie inférieure du tableau.';
+        break;
+      case 'active':
+        if ( categorie == 'Invités' )
+          question = 'Vous allez réactiver le compte invité '+compte+'. La connexion sera à nouveau possible. Ce compte apparaîtra à nouveau dans la partie principale du tableau.';
+        else
+          question = 'Vous allez réactiver le compte '+compte+'. Cela signifie que l\'utilisateur correspondant pourra à nouveau se connecter. Il retrouvera son compte, ses notes de colles éventuelles, ses préférences, ses accès spécifiques éventuels, sans modification. Ce compte apparaîtra à nouveau dans la partie principale du tableau.';
+        break;
+      case 'supprutilisateur':
+        if ( categorie == 'Demandes' )
+          question = 'Vous allez supprimer la demande '+compte+'. Cela signifie que cette demande ne conduira pas à une création de compte. Le demandeur ne sera pas prévenu de votre décision. Une fois réalisée, cette opération est définitive.';
+        else if ( categorie == 'Invitations' )
+          question = 'Vous allez supprimer l\'invitation '+compte+'. Cela signifie que cette invitation ne sera plus valable et que si la personne invitée clique sur le lien reçu par courriel, une erreur apparaîtra devant elle. Cette invitation n\'a pas de délai&nbsp;: il est inutile de supprimer l\'invitation pour la refaire, à moins de s\'être trompé d\'adresse électronique. La personne invitée ne sera pas prévenue de votre décision. Une fois réalisée, cette opération est définitive.';
+        else if ( categorie == 'Professeurs' )
+          question = 'Vous allez supprimer le compte professeur '+compte+'. Cela signifie que toutes les préférences de ce compte seront perdues, ainsi que les éventuelles notes de colles. Les données des matières auxquelles il est associé sont indépendantes&nbsp;: elles ne seront pas supprimées. Une fois réalisée, cette opération est définitive.';
+        else if ( categorie == 'Administatifs' )
+          question ='Vous allez supprimer le compte administratif '+compte+'. Cela signifie que toutes les préférences de ce compte seront perdues. Une fois réalisée, cette opération est définitive.';
+        else if ( categorie == 'Colleurs' )
+          question = 'Vous allez supprimer le compte colleur '+compte+'. Cela signifie que toutes les préférences de ce compte seront perdues, ainsi que les éventuelles notes de colles. Une fois réalisée, cette opération est définitive.';
+        else if ( categorie == 'Élèves' )
+          question = 'Vous allez supprimer le compte élève '+compte+'. Cela signifie que toutes les données correspondant à ce compte seront perdues. Les groupes où il apparaît seront modifiés, les notes de colles éventuelles seront supprimées. Une fois réalisée, cette opération est définitive.';
+        else if ( categorie == 'Invités' )
+          question ='Vous allez supprimer le compte invité '+compte+'. Cela signifie que la connexion par ce compte ne sera plus possible. Une fois réalisée, cette opération est définitive.';
+        else
+          question = 'Vous allez supprimer le compte '+compte+' déjà désactivé. Cela signifie que toutes les données correspondant à ce compte seront perdues définitivement. Les groupes où il apparaît seront modifiés, les notes de colles éventuelles seront supprimées. Dans le cas d\'un compte professeur, les données des matières associées ne seront pas supprimées. Une fois réalisée, cette opération est définitive.';
+        break;
+      case 'validutilisateur':
+        question = 'Vous allez valider la demande '+compte+'. Son compte sera immédiatement actif et un courriel va immédiatement être envoyé pour le/la prévenir. Il sera automatiquement associé à toutes les matières, pensez à aller supprimer les matières qui ne le concernent pas sur la page de gestion des associations utilisateurs-matières.';
+    }
+    confirmation(question, this, function(el) {
+      $.ajax({url: 'ajax.php',
+              method: "post",
+              data: { action:'utilisateur', modif:el.className.substring(5), id:$(el).parent().parent().data('id') },
+              dataType: 'json',
+              el: '',
+              fonction: function() {
+                location.reload(true);
+              }
+      });
+    });
+  }
+  
+  // Modification multiple de la propriété d'envoi de courriels pour plusieurs comptes
+  function mail_utilisateurs() {
+    var cases = $(this).parent().parent().nextUntil('.categorie').find(':checked');
+    if ( cases.length == 0 ) {
+      affiche('<p>Aucune case n\'est cochée, aucune action ne peut être réalisée.</p>','nok');
+      return
+    }
+    var ids = cases.map(function() { return $(this).parent().parent().data('id'); }).get().join(',');
+    var tds = cases.parent().prev().prev();
+    var val = ( tds.find('.icon-ok').length >= tds.find('.icon-nok').length ) ? 0 : 1;
+    $.ajax({url: 'ajax.php',
+            method: "post",
+            data: { action:'utilisateurs', modif:'mailenvoi', ids:ids, val:val },
+            dataType: 'json',
+            el: '',
+            fonction: function() {
               location.reload(true);
             }
     });
+  }
+  
+  // Désactivation, réactivation, suppression, validation multiple de comptes utilisateurs
+  function modif_utilisateurs() {
+    var cases = $(this).parent().parent().nextUntil('.categorie').find(':checked');
+    if ( cases.length == 0 ) {
+      affiche('<p>Aucune case n\'est cochée, aucune action ne peut être réalisée.</p>','nok');
+      return
+    }
+    var ids = cases.map(function() { return $(this).parent().parent().data('id'); }).get().join(',');
+    var comptes = cases.map(function() {
+      var compte = $(this).parent().siblings().first().text();
+      return ( compte.length ) ? '<em>'+compte+'</em>' : '<em>'+$(this).parent().siblings().first().next().text()+'</em>';
+    }).get().join(', ');
+    var pos = comptes.lastIndexOf(',');
+    if ( pos > 0 )
+      comptes = comptes.substring(0,pos)+' et'+comptes.substring(pos+1);
+    var question = '';
+    var categorie = $(this).parent().parent().prev().children().text().split(' ')[0]
+    switch ( this.className.substring(5) ) {
+      case 'desactive':
+        if ( categorie == 'Invités' )
+          question = 'Vous allez désactiver les comptes invités '+comptes+'. Cela signifie que ces compte ne seront pas supprimés mais seront non utilisables pour une connexion. Les associations éventuelles avec les matières seront conservées. Ces comptes seront listés dans la partie inférieure du tableau.';
+        else 
+          question = 'Vous allez désactiver les comptes de '+comptes+'. Cela signifie que ces comptes seront toujours visibles pour les professeurs mais que les utilisateurs correspondant ne pourront plus se connecter. Les notes de colles éventuelles seront conservées. Les données associées aux comptes seront conservées. Les accès spécifiques éventuels pourront être rétablis en réactivant les comptes. Ces comptes seront listés dans la partie inférieure du tableau.';
+        break;
+      case 'active':
+        if ( categorie == 'Invités' )
+          question = 'Vous allez réactiver les comptes invité '+comptes+'. La connexion sera à nouveau possible. Ces comptes apparaîtront à nouveau dans la partie principale du tableau.';
+        else
+          question = 'Vous allez réactiver les comptes de '+comptes+'. Cela signifie que les utilisateurs correspondant pourront à nouveau se connecter. Ils retrouveront leur compte, leurs notes de colles éventuelles, leurs préférences, leurs accès spécifiques éventuels, sans modification. Ces comptes apparaîtront à nouveau dans la partie principale du tableau.';
+        break;
+      case 'supprutilisateur':
+        if ( categorie == 'Demandes' )
+          question = 'Vous allez supprimer les demandes de '+comptes+'. Cela signifie que ces demandes ne conduiront pas à des créations de compte. Les demandeurs ne seront pas prévenus de votre décision. Une fois réalisée, cette opération est définitive.';
+        else if ( categorie == 'Invitations' )
+          question = 'Vous allez supprimer les invitations de '+comptes+'. Cela signifie que ces invitations ne seront plus valables et que si les personnes invitées cliquent sur le lien reçu par courriel, une erreur apparaîtra devant elles. Ces invitations n\'ont pas de délai&nbsp;: il est inutile de supprimer une invitation pour la refaire, à moins de s\'être trompé d\'adresse électronique. Les personnes invitées ne seront pas prévenues de votre décision. Une fois réalisée, cette opération est définitive.';
+        else if ( categorie == 'Professeurs' )
+          question = 'Vous allez supprimer les comptes professeurs de '+comptes+'. Cela signifie que toutes les préférences de ces comptes seront perdues, ainsi que les éventuelles notes de colles. Les données des matières auxquelles ils sont associés sont indépendantes&nbsp;: elles ne seront pas supprimées. Une fois réalisée, cette opération est définitive.';
+        else if ( categorie == 'Administratifs' )
+          question ='Vous allez supprimer les comptes administratifs de '+comptes+'. Cela signifie que toutes les préférences de ces comptes seront perdues. Une fois réalisée, cette opération est définitive.';
+        else if ( categorie == 'Colleurs' )
+          question = 'Vous allez supprimer les comptes colleurs de '+comptes+'. Cela signifie que toutes les préférences de ces comptes seront perdues, ainsi que les éventuelles notes de colles. Une fois réalisée, cette opération est définitive.';
+        else if ( categorie == 'Élèves' )
+          question = 'Vous allez supprimer les comptes élèves de '+comptes+'. Cela signifie que toutes les données correspondant à ces comptes seront perdues. Les groupes où ils apparaîssent seront modifiés, les notes de colles éventuelles seront supprimées. Une fois réalisée, cette opération est définitive.';
+        else if ( categorie == 'Invités' )
+          question ='Vous allez supprimer les comptes invités de '+comptes+'. Cela signifie que la connexion par ces comptes ne sera plus possible. Une fois réalisée, cette opération est définitive.';
+        else
+          question = 'Vous allez supprimer les comptes de '+comptes+' déjà désactivés. Cela signifie que toutes les données correspondant à ces comptes seront perdues définitivement. Les groupes où ils apparaîssent seront modifiés, les notes de colles éventuelles seront supprimées. Dans le cas des comptes professeurs, les données des matières associées ne seront pas supprimées. Une fois réalisée, cette opération est définitive.';
+        break;
+      case 'validutilisateur':
+        question = 'Vous allez valider les demandes de '+comptes+'. Leurs comptes seront immédiatement actifs et un courriel va immédiatement leur être envoyé pour les prévenir. Ils seront automatiquement associés à toutes les matières, pensez à aller supprimer les matières qui ne les concernent pas sur la page de gestion des associations utilisateurs-matières.';
+    }
+    confirmation(question, this, function(el) {
+      $.ajax({url: 'ajax.php',
+              method: "post",
+              data: { action:'utilisateurs', modif:el.className.substring(5), ids:ids },
+              dataType: 'json',
+              el: '',
+              fonction: function() {
+                location.reload(true);
+              }
+      });
+    });
+  }
+}
+
+// Initialisation du tableau des associations utilisateurs-matières
+function init_utilisateurs_matieres() {
+  
+  // Affichage des icônes d'association
+  // La case de tableau HTML contient deux valeurs séparées de "|" :
+  // l'identifiant de la matière et la valeur 1 pour oui 0 pour non.
+  $('tbody tr:not(.categorie) td:not(:first-child,:last-child)').each( function() {
+      var valeurs = this.textContent.split('|');
+      this.innerHTML = ( valeurs[1] == 1 ) ? '<a class="icon-ok" data-id="'+valeurs[0]+'" title="Supprimer l\'association à la matière"></a>'
+                                           : '<a class="icon-nok" data-id="'+valeurs[0]+'" title="Établir l\'association à la matière"></a>';
   });
-  // Envoi par appui sur Entrée
-  form.find('input,select').on('keypress',function (e) {
-    if ( e.which == 13 )
-      article.children('a.icon-ok').click();
+  $('tbody tr:not(.categorie) a').on("click", association_um);
+  
+  // Icônes de cochage multiple
+  $('.categorie [data-id]').on("click", association_ums).hide(0);
+  $('.icon-cocher').on("click", cocher_utilisateurs).on("click",majicones)
+  $('input[type="checkbox"]').on("click", majicones).on("change", function() {
+    // Mise en évidence
+    $(this).parent().parent().removeClass('sel');
+    if ( this.checked )
+      $(this).parent().parent().addClass('sel');
+  });;
+  
+  // Clic sur la case de nom pour cocher
+  $('td:first-child').on("click",function() {
+    $(this).parent().find('input').click();
+  });
+
+  // Mise à jour des icônes de modifications multiples
+  function majicones() {
+    // Ligne de catégorie correspondant à l'icone ou à la case cliquée
+    var tr = $(this).parent().parent();
+    if ( !tr.hasClass('categorie') )
+      tr = tr.prevAll('.categorie').first();
+    // Cases cochées
+    var cases = tr.nextUntil('.categorie').find(':checked');
+    if ( cases.length == 0 ) {
+      $('[data-id]',tr).hide(0);
+      return
+    }
+    $('[data-id]',tr).each( function() {
+      var avant = $(this).hasClass("icon-ok");
+      var apres = cases.parent().prevAll().find('.icon-ok[data-id="'+this.getAttribute('data-id')+'"]').length < cases.length/2;
+      if ( avant != apres )
+        $(this).toggleClass('icon-ok icon-nok').attr('title', (apres?'Établir':'Supprimer')+' l\'association à la matière de tous les cochés');
+    }).show(0);
+  }
+
+  // Fonction d'association unique
+  function association_um() {
+    var val = $(this).hasClass("icon-ok");
+    $.ajax({url: 'ajax.php',
+            method: "post",
+            data: { action:'utilisateur-matiere', id:$(this).parent().parent().data('id'), matiere:$(this).data('id'), val:1-val },
+            dataType: 'json',
+            el: $(this),
+            fonction: function(el) {
+              el.toggleClass('icon-ok icon-nok').attr('title', (val?'Établir':'Supprimer')+' l\'association à la matière');
+            }
+    });
+  }
+  
+  // Fonction d'association multiple
+  function association_ums() {
+    var cases = $(this).parent().parent().nextUntil('.categorie').find(':checked');
+    if ( cases.length == 0 ) {
+      affiche('<p>Aucune case n\'est cochée, aucune action ne peut être réalisée.</p>','nok');
+      return
+    }
+    var ids = cases.map(function() { return $(this).parent().parent().data('id'); }).get().join(',');
+    var comptes = cases.map(function() { return $(this).parent().siblings().first().text().split('(')[0].trim(); }).get().join(', ');
+    var pos = comptes.lastIndexOf(',');
+    if ( pos > 0 )
+      comptes = comptes.substring(0,pos)+' et'+comptes.substring(pos+1);
+    var val = $(this).hasClass("icon-ok");
+    var mid = this.getAttribute('data-id');
+    var question = val ? 'Vous allez établir l\'association à la matière '+$('#m'+mid).text()+' pour les comptes de '+comptes+'. Cela signifie que ces utilisateurs auront accès aux ressources liées à cette matière, en fonction de l\'autorisation que vous avez fixée pour ces ressources.' : 'Vous allez supprimer l\'association à la matière '+$('#m'+mid).text()+' pour les comptes de '+comptes+'. Cela signifie que ces utilisateurs n\'auront plus accès aux ressources liées à cette matière. Si des notes de colles ont été saisies, elles seront automatiquement et définitivement supprimées de la base.';
+    confirmation(question, this, function(el) {
+      $.ajax({url: 'ajax.php',
+              method: "post",
+              data: { action:'utilisateurs-matieres', ids:ids, matiere:mid, val:val|0 },
+              dataType: 'json',
+              el: '',
+              fonction: function(el) {
+                location.reload(true);
+              }
+      });
+    });
+  }
+  
+}
+
+// Édition des utilisateurs associés à un groupe
+function init_utilisateurs_groupes() {
+  // Cases à cocher : utilisation des groupes pour les mails et/ou les notes
+  $('article input[type="checkbox"]').on("change", function() {
+    $.ajax({url: 'ajax.php',
+            method: "post",
+            data: { action:'groupes', champ:this.id.substr(0,5), id:this.id.substr(5), val:(this.checked|0) },
+            dataType: 'json',
+            el: '',
+            fonction: function(el) {
+              return true;
+            }
+    });
   });
+  // Édition des utilisateurs des groupes
+  $('.usergrp span').append('&nbsp;<a class="icon-edite" title="Éditer les utilisateurs de ce groupe"></a>').on("click",utilisateursgroupe);
 }
 
-// Édition des documents
-function modifiedocument(el) {
-  var action = ( el.className == 'icon-edite' ) ? 'document' : 'ajoutedoc';
-  // Suppression d'un éventuel contenu épinglé existant
-  $('#epingle').remove();
-  // Création du nouveau formulaire
-  var article = $('<article id="epingle"><a class="icon-ferme" title="Fermer"></a>\
-  <a class="icon-aide" title="Aide pour ce formulaire"></a>\
-  <a class="icon-ok" title="Valider"></a></article>').insertAfter('#parentsdoc');
-  form = $('<form onsubmit="return false;"></form>').appendTo(article).html($('#form-'+action).html());
-  // Remplissage du formulaire par les données du document
-  // Nom du fichier ou nom du répertoire parent pour l'ajout
-  var nom = $(el).parent().children('.nom').text() || $(el).parent().find('input').val();
-  form.find('em').text(nom);
-  if ( action == 'document' ) {
-    form.find('#nom').val(nom);
-    $('#protection option[value="'+$(el).parent().attr('data-protection')+'"]').prop('selected', true);
-    form.find('[name="id"]').val($(el).parent().attr('data-id').split('|')[1]);
+// Affichage du tableau de sélection des utilisateurs d'un groupe
+function utilisateursgroupe() { 
+  // Création de la fenêtre et variables
+  popup($('#form-utilisateurs').html(),true);
+  var f = $('#fenetre');
+  var span = $(this);
+  article = span.parent().parent();
+  $('table', f).attr('id','utilisateurs');
+
+  // Modification du groupe indiqué en titre
+  $('h3', f).append($('.editable', article).text() || $('input:first', article).val());
+
+  // Pliage/dépliage (les spans ont déjà été ajoutés)
+  $('.icon-deplie', f).on("click", plie)
+
+  // Cochage multiple
+  $('.icon-cocher', f).on("click", cocher_utilisateurs);
+
+  // Clic sur toute la ligne
+  $('tr:not(.categorie)', f).on("click",function(e) {
+    if ( !$(e.target).is('input') )
+      $(this).find('input').click();
+  });
+
+  // Mise en évidence
+  $('input', f).on("change", function () {
+    $(this).parent().parent().removeClass('sel');
+    if ( this.checked )
+      $(this).parent().parent().addClass('sel');
+  });
+  
+  // Sélection automatique des utilisateurs déjà choisis
+  var ids = span.data('uids');
+  $('#u'+ids.replace(/,/g,',#u'), f).prop("checked",true).change();
+
+  // Récupération des valeurs et envoi
+  $('.icon-ok', f).on("click", function() {
+    var ids = $('input:checked', f).map(function() { return this.id.replace('u',''); }).get().join(',');
+    var noms = $('input:checked', f).parent().prev().map(function() { return this.textContent.split('(')[0].trim(); }).get().join(', ') || '[Personne]';
+    // Si formulaire d'ajout, simple mise à jour du formulaire et du span
+    if ( article.is('div') ) {
+      $('#uids', article).val(ids);
+      span.data('uids',ids);
+      span.html(noms+'&nbsp;<a class="icon-edite" title="Éditer les utilisateurs de ce groupe"></a>');
+      $('#fenetre, #fenetre_fond').remove();
+    }
+    // Si groupe déjà existant, envoi des données
+    else
+      $.ajax({url: 'ajax.php',
+              method: "post",
+              data: { action:'groupes', champ:'utilisateurs', id:article.data('id').split('|')[1], uids:ids },
+              dataType: 'json',
+              el: span,
+              fonction: function(el) {
+                // Mise à jour de la liste des utilisateurs
+                el.data('uids',ids);
+                el.html(noms+'&nbsp;<a class="icon-edite" title="Éditer les utilisateurs de ce groupe"></a>');
+                $('#fenetre, #fenetre_fond').remove();
+              }
+      });
+  });
+};
+
+// Suppression massive des éléments d'une matière ou des informations d'une page
+function suppressionmultiple() {
+  var prop = $(this).data('id').split('|');
+  var contexte = $(this).parent().find('h3').text();
+  var item = '';
+  switch ( prop[2] ) {
+    case 'infos': item = 'toutes les informations de la page <em>'+contexte+'</em>'; break;
+    case 'colles': item = 'tous les programmes de colles de la matière <em>'+contexte+'</em>'; break;
+    case 'cdt': item = 'tout le contenu du cahier de texte de la matière <em>'+contexte+'</em>'; break;
+    case 'docs': item = 'tous les répertoires et documents de la matière <em>'+contexte+'</em>'; break;
+    case 'notes': item = 'toutes les notes de la matière <em>'+contexte+'</em>'; break;
   }
-  else {
-    $('#protection option[value="'+$(el).parent().attr('data-donnees').split('|')[2]+'"]').prop('selected', true);
-    form.find('[name="parent"]').val($(el).parent().attr('data-id').split('|')[1]);
-    // Modification automatique du champ nom si ajout d'un fichier
-    form.find('#fichier').on('change',function () {
-      if ( !form.find('#nom').val().length )
-        var f = this.value;
-        form.find('#nom').val(f.substring(f.lastIndexOf('\\')+1,f.lastIndexOf('.')) || f);
+  confirmation('Vous allez supprimer XXX.<br>Cette opération n\'est pas annulable.'.replace('XXX',item),this,function(el) {
+    $.ajax({url: 'ajax.php',
+            method: "post",
+            data: 'action='+prop[0]+'&id='+prop[1]+'&supprime_'+prop[2]+'=1',
+            dataType: 'json',
+            el: $(el),
+            fonction: function(el) {
+              el.remove();
+            }
     });
-  }
-  // Actions des boutons fermeture, annulation, aide, envoi
-  article.children('.icon-ferme').on("click",function() {
-    $('#epingle').remove();
   });
-  article.children('a.icon-aide').on("click", function() {
-    popup($('#aide-'+action).html(),false);
+}
+
+////////////////////////////////////////
+// Paramétrage et envoi des courriels //
+////////////////////////////////////////
+
+// Affichage du tableau de sélection des utilisateurs destinataires d'un courriel
+function destinatairesmail() {
+  popup($('#form-destinataires').html(),true);
+  var f = $('#fenetre');
+  $('table', f).attr('id','utilisateurs');
+  
+  // Pliage/dépliage (les spans ont déjà été ajoutés)
+  $('.icon-deplie', f).on("click", plie)
+  
+  // Ajouts des identifiants pour la sélection automatique (début et groupes)
+  $('tr:not(.gr) input.dest', f).attr('id',function() { return 'u'+this.value; });
+  
+  // Clic sur toute la ligne (deux premières cases seulement)
+  $('tr:not(.categorie) td:nth-child(-n+2)', f).on("click",function(e) {
+    if ( !$(e.target).is('input') )
+      $(this).parent().find('input:first').click();
   });
-  article.children('a.icon-ok').on("click", function() {
-    var data = new FormData(form[0]);
-    // Envoi
+  
+  // Décochage automatique de l'autre case du même utilisateur et mise en évidence
+  $('input', f).on("change", function () {
+    var tr = $(this).parent().parent().removeClass('sel');
+    if ( this.checked )
+      tr.find('input:not(.'+this.className+')').prop("checked",false);
+    if ( tr.find('input:checked').length )
+      tr.addClass('sel');
+  });
+  
+  // Sélection automatique des utilisateurs déjà choisis
+  var ids = $('[name="id-copie"]').val();
+  $('#u'+ids.replace(/,/g,',#u')).prop("checked",true).change();
+  ids = $('[name="id-bcc"]').val();
+  $('#u'+ids.replace(/,/g,',#u')).parent().next().children().prop("checked",true).change();
+
+  // Bouton de sélection multiple
+  $('.categorie a', f).on("click keyup", function () {
+    // Récupération des valeurs
+    var classe = this.className.split(' ')[1]; // dest ou bcc
+    var etat = (this.className.split(' ')[0] == 'icon-cocher');
+    var titre = this.title;
+
+    // Cochage et modifications
+    $(this).parent().parent().nextUntil('.categorie').find('.'+classe+':not(:disabled)').prop('checked',etat).change();
+    this.className = (etat?'icon-decocher ':'icon-cocher ')+classe;
+    this.title = this.title.replace((etat?'Cocher':'Décocher'),(etat?'Décocher':'Cocher'));
+    var classe2 = (classe == 'dest') ? 'bcc' : 'dest';
+    $(this).parent().parent().find('.icon-decocher.'+classe2).each( function() {
+      this.className = 'icon-cocher '+classe2;
+      this.title = 'C'+this.title.substr(3); 
+    });
+  });
+  
+  // Groupes
+  $('.gr input', f).on("click", function () {
+    var ids = this.value;
+    if ( this.className == 'dest' )
+      $('#u'+ids.replace(/,/g,',#u')).prop("checked",this.checked).change();
+    else
+      $('#u'+ids.replace(/,/g,',#u')).parent().next().children().prop("checked",this.checked).change();
+  });
+  
+  // Récupération des valeurs
+  $('.icon-ok', f).on("click", function() {
+    $('[name="id-copie"]').val( $('tr:not(.gr) .dest:checked',f).map(function() { return this.value; }).get().join(',') );
+    $('[name="id-bcc"]').val(   $('tr:not(.gr) .bcc:checked', f).map(function() { return this.value; }).get().join(',') );
+    $('#maildest').text(        $('tr:not(.gr) .dest:checked', f).parent().prev().map(function() { return this.textContent; }).get()
+                        .concat($('tr:not(.gr) .bcc:checked', f).parent().prev().prev().map(function() { return this.textContent+' (CC)'; }).get())
+                        .join(', ') || '[Personne]' );
+    $('#fenetre, #fenetre_fond').remove();
+  });
+}
+
+// Envoi des courriels
+function envoimail() {
+  // Pas d'envoi si pas de destinataire
+  if ( $('.maildest').children('span').text() == '[Personne]' )
+    affiche('Il faut au moins un destinataire pour envoyer le courriel.','nok');
+  else if ( !$('[name="sujet"]').val().length )
+    affiche('Il faut un sujet non vide pour envoyer le courriel.','nok');
+  else
     $.ajax({url: 'ajax.php',
             method: "post",
-            data: data,
+            data: $('#mail').serialize(),
             dataType: 'json',
-            contentType:false,
-            processData:false,
-            el: article,
+            el: '',
+            fonction: function(el) {
+              location.reload(true);
+            }
+    });
+}
+
+////////////////////////////////
+// Relève des notes de colles //
+////////////////////////////////
+function relevenotes() { 
+  confirmation('<p>Vous allez réaliser une relève des notes de colles. Cela consiste à marquer comme relevées toutes les heures déclarées jusqu\'à maintenant et non encore relevées. Vous pourrez alors télécharger le nouveau relevé au sein du tableau en bas de page.</p><p>Cette opération n\'est pas annulable.</p><p>Une fois que vous aurez réalisé ce relevé, les professeurs et colleurs ne pourront pas modifier le nombre d\'élèves et la durée correspondant aux colles relevées.</p>',this,function(el) {
+    $.ajax({url: 'ajax.php',
+            method: "post",
+            data: 'action=releve-notes',
+            dataType: 'json',
+            el: '',
             fonction: function(el) {
               location.reload(true);
             }
     });
-  });
-  // Envoi par appui sur Entrée
-  form.find('input,select').on('keypress',function (e) {
-    if ( e.which == 13 )
-      article.children('a.icon-ok').click();
   });
 }
 
@@ -1880,18 +2407,19 @@
 // Requêtes AJAX //
 ///////////////////
 $(document).ajaxSend( function(ev,xhr,settings) {
-              $('body').css('cursor','wait');
-              if ( settings.data.append ) // Pour les FormData
+              $('#load').show(200);
+              // Sécurité anti XSS : Ajout du token CSRF
+              if ( settings.data.append )
                 settings.data.append('csrf-token',$('body').attr('data-csrf-token'));
               else
                 settings.data = 'csrf-token='+$('body').attr('data-csrf-token')+'&'+settings.data;
             })
            .ajaxStop( function() {
-              $('body').css('cursor','auto');
+              $('#load').hide(200);
             })
            .ajaxSuccess( function(ev,xhr,settings) {
               var data = xhr.responseJSON;
-              switch (data['etat']) {
+              switch ( data['etat'] ) {
                 // Si ok, on l'affiche dans le log et on lance la "fonction" de mise à jour de l'affichage
                 case 'ok':
                   affiche(data['message'],'ok');
@@ -1902,8 +2430,14 @@
                   affiche(data['message'],'nok');
                   break;
                 // Si 'login' : il faut se reconnecter
+                // Si 'mdp' : il faut compléter une connexion light obtenue par cookie
                 case 'login':
-                  afficher_login(settings);
+                case 'mdp':
+                  reconnect(settings,data['etat']=='mdp');
+                  break;
+                // Si 'recupok' : récupération de données
+                case 'recupok':
+                  settings.afficheform(data);
               }
             });
 
@@ -1912,128 +2446,97 @@
 ////////////////////////////////////////////////////////////////////////////
 $( function() {
 
-  // Édition en place des éléments de classe "editable"
-  $('.editable').editinplace();
+  // Formulaires affichés par clic sur les icônes
+  $('a.formulaire, .modifevnmt').on("click", formulaire);
 
-  // Édition des propriétés des éléments des cahiers de texte
-  $('.titrecdt').editinplacecdt();
-
-  // Édition des propriétés des raccourcis des cahiers de texte
-  $('.cdt-raccourcis').init_cdt_raccourcis();
-
-  // Boutons de suppression massive des éléments de matière et des informations d'une page
-  $('.supprmultiple').on("click", function() {
-    suppressionmultiple(this);
+  // Aide
+  $('a.icon-aide').on("click", function() {
+    popup($('#aide-'+( $(this).parent().data('id') || 'page' ).split('|')[0]).html(),false);
   });
+  // Validation des formulaires déjà présents (pas pour les transformés)
+  $('a.icon-ok').on("click", valide);
 
-  // Édition des utilisateurs d'une matière
-  $('.usermat .icon-edite').on("click", function() {
-    utilisateursmatiere(this);
+  // Boutons cache, montre, monte, descend, supprime, protection (lock), ajoute-colle
+  $('a.icon-cache,a.icon-montre,a.icon-monte,a.icon-descend,a.icon-supprime,a.icon-lock,a.icon-ajoutecolle').on("click", function() {
+    window[this.className.substring(5)]($(this));
   });
 
-  // Édition des utilisateurs destinataires d'un courriel
-  $('.editabledest .icon-edite').on("click", function() {
-    utilisateursmail(this);
+  // Div d'affichage des résultats des requêtes AJAX
+  $('#log').hide().on("click", function() {
+    $(this).hide(300);
   });
-  
-  // Édition de la préférence de copie de courriel
-  $('[name="copie"]').on("change", function() {
+
+  // Édition en place des éléments de classe "editable"
+  $('.editable').editinplace();
+
+  // Déconnexion
+  $('a.icon-deconnexion').on('click',function(e) {
     $.ajax({url: 'ajax.php',
             method: "post",
-            data: { table:'mailprefs', champ:'mailcopy', id:0, val:$(this).prop('checked')?1:0 },
+            data: { action:'deconnexion' },
             dataType: 'json',
             el: '',
             fonction: function(el) {
-              return true;
+              location.reload(true);
             }
     });
   });
 
-  // Édition des répertoires
-  $('#parentsdoc .icon-edite, .rep > .icon-edite, .icon-ajouterep').on("click", function() {
-    modifierepertoire(this);
-  });
+  /////////////////////////////
+  // Spécifique cahier de texte
 
-  // Édition des documents
-  $('.doc > .icon-edite, .icon-ajoutedoc').on("click", function() {
-    modifiedocument(this);
-  });
-
-  // Édition des événements de l'agenda
-  $('.evnmt').on("click", function() {
-    this.setAttribute('data-id','evenement');
-    formulaire(this);
-  });
+  // Édition des propriétés des éléments des cahiers de texte
+  $('.titrecdt').editinplacecdt();
 
-  // Édition de l'utilisation des groupes d'élèves
-  $('[name="mailnotes"]').on("change", function() {
-    $.ajax({url: 'ajax.php',
-            method: "post",
-            data: { table:'groupes', champ:'mailnotes', id:$(this).attr('id').substr(9), val:$(this).find('option:selected').val() },
-            dataType: 'json',
-            el: '',
-            fonction: function(el) {
-              return true;
-            }
-    });
-  });
+  // Édition des propriétés des raccourcis des cahiers de texte
+  $('.cdt-raccourcis').init_cdt_raccourcis();
 
-  // Édition des utilisateurs (élèves) d'un groupe
-  $('.usergrp .icon-edite').on("click", function() {
-    utilisateursgroupe(this);
-  });
+  ///////////////////////////////
+  // Spécifique envoi de courriel
 
-  // Div d'affichage des résultats des requêtes AJAX
-  $('#log').hide().on("click", function() {
-    $(this).hide();
-  });
+  // Envoi de courriel
+  $('.icon-mailenvoi').on("click", envoimail);
+  
+  // Édition des utilisateurs destinataires d'un courriel
+  $('#maildest, #maildest + .icon-edite').on("click", destinatairesmail);
 
-  // Placeholders à afficher au chargement de la page
-  $('[data-placeholder]').placeholder();
+  ///////////////////////////////
+  // Spécifique réglages des utilisateurs, matières, groupes
 
-  // Sélecteurs de couleurs pour les formulaire affichés au chargement
-  if ( $.colpick )  $('[name="couleur"]').colpick();
+  // Affichage des icônes de pliage sur les lignes ".categorie" des tableaux d'utilisateurs
+  $('.categorie th:first-child').prepend(
+    $('<span class="icon-deplie" title="Déplier/Replier cette catégorie"></span>').on("click", plie)
+  );
 
-  // Boutons cache, montre, monte, descend, supprime
-  $('a.icon-cache,a.icon-montre,a.icon-monte,a.icon-descend,a.icon-supprime').on("click", function() {
-    window[this.className.substring(5)]($(this));
-  });
+  // Gestion des matières
+  $('article select[multiple]').each(selmult);
+  
+  // Boutons de suppression massive des éléments de matière et des informations d'une page
+  $('.supprmultiple').on("click", suppressionmultiple);
+  
+  // Gestion des utilisateurs
+  $('#utilisateurs').each(init_utilisateurs);
+  
+  // Gestion des associations utilisateurs-matières
+  $('#utilisateurs-matieres').each(init_utilisateurs_matieres);
 
-  // Aide
-  $('a.icon-aide').on("click", function() {
-    popup($('#aide-'+this.getAttribute('data-id')).html(),false);
-  });
+  // Édition des utilisateurs des groupes - fonction à ne lancer qu'une fois
+  $('.usergrp').first().each(init_utilisateurs_groupes);
 
-  // Formulaires affichés par clic sur les icônes ajouts et préférences
-  $('a.icon-prefs.general,a.icon-ajoute,a.icon-ajout-colle').on("click", function() {
-    formulaire(this);
-  });
+  /////////////////////////////
+  // Spécifique planning annuel
 
-  // Validation des formulaires déjà présents (pas pour les transformés)
-  // Aussi utilisé pour l'envoi de courriel
-  $('a.icon-ok').on("click", function() {
-    valide(this);
+  // Modification des valeurs du planning
+  $('#planning select').change( function() {
+    $(this).parent().prev().children('input').prop('checked',this.value == 0);
   });
-
-  // Déconnexion
-  $('a.icon-deconnexion').on('click',function(e) {
-    $.ajax({url: 'ajax.php',
-            method: "post",
-            data: { deconnexion:1 },
-            dataType: 'json',
-            el: '',
-            fonction: function(el) {
-              location.reload(true);
-            }
-    });
+  $('#planning input').change( function() {
+      $(this).parent().next().children('select').val(0);
   });
-
-  // Attribut title (infobulle) pour les icônes indiquant le niveau de protection
-  $('.icon-lock1').attr('title','Visible uniquement par les utilisateurs connectés');
-  $('.icon-lock2').attr('title','Visible uniquement par les élèves/colleurs/professeurs connectés');
-  $('.icon-lock3').attr('title','Visible uniquement par les colleurs/professeurs connectés');
-  $('.icon-lock4').attr('title','Visible uniquement par les professeurs connectés');
-  $('.icon-lock5').attr('title','Complètement invisible (hors professeurs de cette matière)');
+  
+  /////////////////////////////
+  // Spécifique relève de notes
+  $('#relevenotes').on("click", relevenotes);
 
   // Menu pour les petits écrans
   $('.icon-menu').on("click", function() {
diff -urN cahier-de-prepa6.2.0/js/edition.min18.js cahier-de-prepa8.0.1/js/edition.min18.js
--- cahier-de-prepa6.2.0/js/edition.min18.js	1970-01-01 01:00:00.000000000 +0100
+++ cahier-de-prepa8.0.1/js/edition.min18.js	2018-10-16 00:58:00.772698767 +0200
@@ -0,0 +1,3 @@
+function affiche(message,etat){$("#log").removeClass().addClass(etat).html(message).append('<span class="icon-ferme"></span>').fadeIn().off("click").on("click",function(){window.clearTimeout(extinction);$(this).fadeOut(800)});extinction=window.setTimeout(function(){$("#log").fadeOut(800)},6e3)}function reconnect(settings,light){$("#fenetre,#fenetre_fond").remove();if(settings.url=="recup.php")var action=settings.data.indexOf("prefs")?"récupérer les préférences de cet utilisateur":"récupérer la liste des répertoires et documents disponibles";else{var action="valider cette action";settings.afficheform=Function.prototype}if(light)popup('<a class="icon-ok" title="Valider"></a><h3>Connexion nécessaire</h3>           <p>Vous êtes déjà connecté, mais vous devez saisir de nouveau votre mot de passe pour '+action+'.</p>           <form>           <p class="ligne"><label for="motdepasse">Mot de passe&nbsp;: </label><input type="password" name="motdepasse" id="motdepasse"></p>           </form>',true);else popup('<a class="icon-ok" title="Valider"></a><h3>Connexion nécessaire</h3>           <p>Vous avez été automatiquement déconnecté. Vous devez vous connecter à nouveau pour '+action+'.</p>           <form>           <p class="ligne"><label for="login">Identifiant&nbsp;: </label><input type="text" name="login" id="login"></p>           <p class="ligne"><label for="motdepasse">Mot de passe&nbsp;: </label><input type="password" name="motdepasse" id="motdepasse"></p>           </form>',true);$("#fenetre input:first").focus();$("#fenetre a.icon-ok").on("click",function(){$.ajax({url:settings.url,method:"post",data:$("#fenetre form").serialize()+"&"+settings.data,dataType:"json",el:settings.el,afficheform:settings.afficheform,fonction:settings.fonction}).done(function(data){if(data["etat"]!="mdpnok")$("#fenetre,#fenetre_fond").remove()})});$("#fenetre a.icon-ferme").on("click",function(){affiche("Modification non effectuée, connexion nécessaire","nok")});$("#fenetre input").on("keypress",function(e){if(e.which==13)$("#fenetre a.icon-ok").click();return false})}function popup(contenu,modal){$("#fenetre,#fenetre_fond").remove();var el=$('<article id="fenetre"></article>').appendTo("body").html(contenu).focus();if(modal)$('<div id="fenetre_fond"></div>').appendTo("body").click(function(){$("#fenetre,#fenetre_fond").remove()});else $('<a class="icon-epingle" title="Épingler à la page"></a>').prependTo(el).on("click",function(){$("#fenetre_fond").remove();$(this).remove();el.removeAttr("id").insertBefore($("article,#calendrier,#parentsdoc+*").first())});$('<a class="icon-ferme" title="Fermer"></a>').prependTo(el).on("click",function(){el.remove();$("#fenetre_fond").remove()})}function confirmation(question,element,action){popup("<h3>Demande de confirmation</h3><p>"+question+'</p><p class="confirmation"><button class="icon-ok"></button>&nbsp;&nbsp;&nbsp;<button class="icon-annule"></button></p>',true);$("#fenetre .icon-ok").on("click",function(){action(element);$("#fenetre,#fenetre_fond").remove()});$("#fenetre .icon-annule").on("click",function(){$("#fenetre,#fenetre_fond").remove()})}function plie(){$(this).parent().parent().nextUntil(".categorie").fadeToggle("slow");$(this).toggleClass("icon-plie icon-deplie")}$.fn.textareahtml=function(){this.each(function(){var ta=$(this);var placeholder=this.getAttribute("placeholder");this.setAttribute("placeholder",placeholder+". Formattage en HTML, balises visibles.");var ce=$('<div contenteditable="true" placeholder="'+placeholder+'"></div>').insertAfter(ta.before(boutons)).hide();var boutonretour=ta.prev().children(".icon-retour");if(ta.hasClass("ligne")){ce.addClass("ligne");ta.prev().addClass("ligne")}ta.on("keypress",function(e){if(e.which==13)this.value=nettoie(this.value)}).on("paste cut",function(){var el=this;setTimeout(function(){el.value=nettoie(el.value)},100)});ce.on("keypress",function(e){if(e.which==13)boutonretour.click()}).on("paste cut",function(){var el=this;setTimeout(function(){el.innerHTML=nettoie(el.innerHTML)+"<br>"},100)});ta.prev().children(".icon-nosource").on("click",function(e){e.preventDefault();ta.hide();ce.show().css("min-height",ta.outerHeight());$(this).hide().prev().show();ce.focus().html(nettoie(ta.val())).change();if(window.getSelection){var r=document.createRange();r.selectNodeContents(ce[0]);r.collapse(false);var s=window.getSelection();s.removeAllRanges();s.addRange(r)}else{var r=document.body.createTextRange();r.moveToElementText(ce[0]);r.collapse(false);r.select()}});ta.prev().children(".icon-source").on("click",function(e){e.preventDefault();ce.hide(0);ta.show(0).css("height",ce.height());$(this).hide().next().show();ta.focus().val(nettoie(ce.html()))}).hide();ta.prev().children(".icon-aide").on("click",function(e){e.preventDefault();aidetexte()});ta.prev().children().not(".icon-nosource,.icon-source,.icon-aide").on("click",function(e){e.preventDefault();window["insertion_"+this.className.substring(5)]($(this))})})};$.fn.editinplace=function(){this.each(function(){var el=$(this);el.data("original",el.is("h3")?el.text():el.html());$('<a class="icon-edite" title="Modifier"></a>').appendTo(el).on("click",transforme)})};function transforme(){var el=$(this).parent().addClass("avecform");if(el.is("div"))el.html('<form><textarea name="val" rows="'+(el.data("original").split(/\r\n|\r|\n/).length+3)+'"></textarea></form>');else el.html('<form class="edition" onsubmit="$(this).children(\'a.icon-ok\').click(); return false;"><input type="text" name="val" value=""></form>');var input=el.find('[name="val"]').val(el.data("original")).attr("placeholder",el.attr("placeholder"));if(el.hasClass("edithtml"))input.textareahtml();$('<a class="icon-ok" title="Valider"></a>').appendTo(el.children()).on("click",function(){var id=el.data("id").split("|");if(el.hasClass("edithtml"))input.val(nettoie(input.is(":visible")?input.val():input.next().html()));$.ajax({url:"ajax.php",method:"post",data:{action:id[0],champ:id[1],id:id[2],val:input.val()},dataType:"json",el:el,fonction:function(el){var val=el.find('[name="val"]').val();el.removeClass("avecform").html(val).data("original",val);$('<a class="icon-edite" title="Modifier"></a>').appendTo(el).on("click",transforme)}})});$('<a class="icon-annule" title="Annuler"></a>').appendTo(el.children()).on("click",function(){el.removeClass("avecform").html(el.data("original"));$('<a class="icon-edite" title="Modifier"></a>').appendTo(el).on("click",transforme)});input.focus().val(el.hasClass("edithtml")?nettoie(input.val()):input.val())}$.fn.editinplacecdt=function(){this.each(function(){$(this).wrapInner("<span></span>").data("original",$(this).text());$('<a class="icon-edite" title="Modifier"></a>').appendTo($(this)).on("click",transformecdt)})};function transformecdt(){var el=$(this).parent();$(".icon-edite",el).remove();var form=$('<form class="titrecdt"></form>').insertBefore(el.parent().children("div")).html($("#form-cdt").html());$("input, select",form).attr("id",function(){return this.getAttribute("name")});var valeurs=el.data("donnees");for(var cle in valeurs)$("#"+cle).val(valeurs[cle]);form.init_cdt_boutons();$("input,#demigroupe",form).on("change keyup",function(){var t=new Date($("#jour").val().replace(/(.{2})\/(.{2})\/(.{4})/,function(tout,x,y,z){return z+"-"+y+"-"+x}));var dg=$("#demigroupe").val()==1?" (en demi-groupe)":"";switch(parseInt(seances[$("#tid").val()])){case 0:var titre=jours[t.getDay()]+" "+$("#jour").val()+" à "+$("#h_debut").val()+" : "+$("#tid option:selected").text()+dg;break;case 1:var titre=jours[t.getDay()]+" "+$("#jour").val()+" de "+$("#h_debut").val()+" à "+$("#h_fin").val()+" : "+$("#tid option:selected").text()+dg;break;case 2:var titre=jours[t.getDay()]+" "+$("#jour").val()+" : "+$("#tid option:selected").text()+" pour le "+$("#pour").val()+dg;break;case 3:var titre=jours[t.getDay()]+" "+$("#jour").val()+" : "+$("#tid option:selected").text()+dg;break;case 4:var titre=jours[t.getDay()]+" "+$("#jour").val();break;case 5:var titre="[Entrée hebdomadaire]"}$("span",el).text(titre)});$('<a class="icon-ok" title="Valider"></a>').appendTo(el).on("click",function(){var id=el.parent().data("id").split("|");$.ajax({url:"ajax.php",method:"post",data:"action=cdt-elems&id="+id[1]+"&"+form.serialize(),dataType:"json",el:el,fonction:function(el){var form=el.siblings("form");el.data("original",$("span",el).text()).data("donnees",{tid:$("#tid").val(),jour:$("#jour").val(),h_debut:$("#h_debut").val(),h_fin:$("#h_fin").val(),pour:$("#pour").val(),demigroupe:$("#demigroupe").val()});form.remove();$("a",el).remove();$('<a class="icon-edite" title="Modifier"></a>').appendTo(el).on("click",transformecdt)}}).done(function(data){if(data["etat"]=="ok"&&data["reload"]=="oui")location.reload(true)})});$('<a class="icon-annule" title="Annuler"></a>').appendTo(el).on("click",function(){form.remove();$("span",el).html(el.data("original"));$("a",el).remove();$('<a class="icon-edite" title="Modifier"></a>').appendTo(el).on("click",transformecdt)})}function nettoie(html){if(html.indexOf("cdptmp")>0){var tmp=$("<div>"+html+"</div>");tmp.find(".cdptmp").contents().unwrap();html=tmp.html();if(html.indexOf("cdptmp")>0)html=html.replace(/<span class="cdptmp"><\/span>/g,"")}return html.replace(/(<\/?[A-Z]+)([^>]*>)/g,function(tout,x,y){return x.toLowerCase()+y}).replace(/[\r\n ]+/g," ").replace(/(<br>)+[ ]?<\/(p|div|li|h)/g,function(tout,x,y){return"</"+y}).replace(/<br>/g,"<br>\n").replace(/<(p|div|li|h)/g,function(x){return"\n"+x}).replace(/<\/(p|div|li|h.)>/g,function(x){return x+"\n"}).replace(/<\/?(ul|ol)[^>]*>/g,function(x){return"\n"+x+"\n"}).replace(/^(?!(<p|<div|<ul|<ol|<li|<h))(.+)<br>$/gm,function(tout,x,y){return"<p>"+y+"</p>"}).replace(/^(?!(<(p|div|ul|ol|li)))[ ]?(.+)[ ]?$/gm,function(t,x,y,z){return z.match(/.*(p|div|ul|ol|li|h.)>$/)?z:"<p>"+z+"</p>"}).replace(/^[ ]?(<\/?(br|p|div|h.)>){0,2}[ ]?(<\/(p|div|h.)>)?[ ]?$/gm,"").replace(/^\n/gm,"").replace(/<li/g,"  <li")}function insert(el,debut,fin,milieu){var contenant=el.parent().siblings("textarea,[contenteditable]").filter(":visible")[0];if(!contenant.hasAttribute("data-selection"))marqueselection(el);var texte=milieu===undefined?debut+"Í"+contenant.getAttribute("data-selection")+"Ì"+fin:debut+"Í"+milieu+"Ì"+fin;var contenu=nettoie(contenant.getAttribute("data-contenu").replace(/Í.*Ì/,texte));if(contenant.tagName=="TEXTAREA")contenant.value=contenu.replace(/[ÍÌ]/g,"");else contenant.innerHTML=contenu.replace(/[ÍÌ]/g,"");marqueselection(el,true);if(contenant.tagName=="TEXTAREA"&&contenant.selectionStart!==undefined){contenant.selectionStart=contenu.indexOf("Í");contenant.selectionEnd=contenu.indexOf("Ì")-1;contenant.focus()}else if(document.selection){if(contenant.tagName!="TEXTAREA")contenu=contenu.replace(/(<([^>]+)>)[\n]*/g,"");range=document.body.createTextRange();range.moveToElementText(contenant);range.collapse(true);range.moveEnd("character",contenu.indexOf("Ì")-1);range.moveStart("character",contenu.indexOf("Í"));range.select()}else if(window.getSelection){contenant.innerHTML=contenu.replace("Í",'<span class="cdptmp">').replace("Ì","</span>")+"<br>";selection=window.getSelection();range=document.createRange();range.selectNodeContents($(contenant).find(".cdptmp")[0]);selection.removeAllRanges();selection.addRange(range);contenant.focus()}}function marqueselection(el,efface){var contenant=el.parent().siblings("textarea,[contenteditable]").filter(":visible")[0];if(efface){contenant.removeAttribute("data-selection");contenant.removeAttribute("data-contenu");return true}var original=contenant.tagName=="TEXTAREA"?contenant.value:contenant.innerHTML;var sel="";if(contenant.tagName=="TEXTAREA"&&contenant.selectionStart!==undefined){contenant.focus();sel=contenant.value.substring(contenant.selectionStart,contenant.selectionEnd);contenant.value=contenant.value.substr(0,contenant.selectionStart)+"Í"+sel+"Ì"+contenant.value.substring(contenant.selectionEnd)}else if(window.getSelection){var range=window.getSelection().getRangeAt(0);if(contenant==range.commonAncestorContainer||$.contains(contenant,range.commonAncestorContainer)){var sel=window.getSelection().toString();range.deleteContents();range.insertNode(document.createTextNode("Í"+sel+"Ì"))}}else{var range=document.selection.createRange();if(contenant==range.parentElement()||$.contains(contenant,range.parentElement())){var sel=document.selection.createRange().text;document.selection.createRange().text="Í"+sel+"Ì"}}if(contenant.tagName=="TEXTAREA"){var contenu=contenant.value;contenant.value=original}else{var contenu=contenant.innerHTML;$(contenant).html(original)}if(contenu.indexOf("Ì")<0)contenu=contenu+"ÍÌ";contenant.setAttribute("data-selection",sel);contenant.setAttribute("data-contenu",contenu);return sel}var boutons='<p class="boutons">  <button class="icon-titres" title="Niveaux de titres"></button>  <button class="icon-par1" title="Paragraphe"></button>  <button class="icon-par2" title="Paragraphe important"></button>  <button class="icon-par3" title="Paragraphe très important"></button>  <button class="icon-retour" title="Retour à la ligne"></button>  <button class="icon-gras" title="Gras"></button>  <button class="icon-italique" title="Italique"></button>  <button class="icon-souligne" title="Souligné"></button>  <button class="icon-omega" title="Insérer une lettre grecque"></button>  <button class="icon-sigma" title="Insérer un signe mathématique"></button>  <button class="icon-exp" title="Exposant"></button>  <button class="icon-ind" title="Indice"></button>  <button class="icon-ol" title="Liste énumérée"></button>  <button class="icon-ul" title="Liste à puces"></button>  <button class="icon-lien1" title="Lien vers un document du site"></button>  <button class="icon-lien2" title="Lien internet"></button>  <button class="icon-tex" title="LATEX!"></button>  <button class="icon-source" title="Voir et éditer le code html"></button>  <button class="icon-nosource" title="Voir et éditer le texte formaté"></button>  <button class="icon-aide" title="Aide pour cet éditeur de texte"></button></p>';function insertion_titres(el){popup('<a class="icon-ok" title="Valider"></a><h3>Insertion d\'un titre</h3>  <p>Choisissez le type du titre ci-dessous. Vous pouvez éventuellement modifier le texte (ou pourrez le faire ultérieurement). Il est conseillé d\'utiliser des titres de niveau 2 pour les titres dans les programmes de colle.</p>  <input type="radio" name="titre" id="t3" value="3" checked><h3><label for="t3">Titre de niveau 1 (pour les I,II...)</label></h3><br>  <input type="radio" name="titre" id="t4" value="4"><h4><label for="t4">Titre de niveau 2 (pour les 1,2...)</label></h4><br>  <input type="radio" name="titre" id="t5" value="5"><h5><label for="t5">Titre de niveau 3 (pour les a,b...)</label></h5><br>  <input type="radio" name="titre" id="t6" value="6"><h6><label for="t6">Titre de niveau 4</label></h6><br>  <p class="ligne"><label for="texte">Texte&nbsp;: </label><input type="text" id="texte" value="'+marqueselection(el)+'" size="80"></p>  <hr><h3>Aperçu</h3><div id="apercu"></div>',true);$("#fenetre input").on("click keyup",function(){var balise="h"+$("[name='titre']:checked").val();$("#apercu").html("<"+balise+">"+($("#texte").val().length?$("#texte").val():"Texte du titre")+"</"+balise+">")}).first().keyup();$("#texte").on("keypress",function(e){if(e.which==13)$("#fenetre a.icon-ok").click()}).focus();$("#fenetre a.icon-ok").on("click",function(){var balise="h"+$("[name='titre']:checked").val();insert(el,"<"+balise+">","</"+balise+">",$("#texte").val());$("#fenetre,#fenetre_fond").remove()});$("#fenetre a.icon-ferme,#fenetre_fond").on("click",function(){marqueselection(el,true)})}function insertion_omega(el){popup("<h3>Insertion d'une lettre grecque</h3>  <p>Cliquez sur la lettre à insérer&nbsp;:</p>  <button>&alpha;</button> <button>&beta;</button> <button>&gamma;</button> <button>&Delta;</button> <button>&delta;</button> <button>&epsilon;</button> <button>&eta;</button> <button>&Theta;</button> <button>&theta;</button> <button>&Lambda;</button> <button>&lambda;</button> <button>&mu;</button> <button>&nu;</button> <button>&xi;</button> <button>&Pi;</button> <button>&pi;</button> <button>&rho;</button> <button>&Sigma;</button> <button>&sigma;</button> <button>&tau;</button> <button>&upsilon;</button> <button>&Phi;</button> <button>&phi;</button> <button>&Psi;</button> <button>&psi;</button> <button>&Omega;</button> <button>&omega;</button>",true);$("#fenetre button").on("click",function(){insert(el,"","",$(this).text());$("#fenetre,#fenetre_fond").remove()})}function insertion_sigma(el){popup("<h3>Insertion d'un symbole mathématique</h3>  <p>Cliquez sur le symbole à insérer&nbsp;:</p>  <button>&forall;</button> <button>&exist;</button> <button>&part;</button> <button>&nabla;</button> <button>&prod;</button> <button>&sum;</button> <button>&plusmn;</button> <button>&radic;</button> <button>&infin;</button> <button>&int;</button> <button>&prop;</button> <button>&sim;</button> <button>&cong;</button> <button>&asymp;</button> <button>&ne;</button> <button>&equiv;</button> <button>&le;</button> <button>&ge;</button> <button>&sub;</button> <button>&sup;</button> <button>&nsub;</button> <button>&sube;</button> <button>&supe;</button> <button>&isin;</button> <button>&notin;</button> <button>&ni;</button> <button>&oplus;</button> <button>&otimes;</button> <button>&sdot;</button> <button>&and;</button> <button>&or;</button> <button>&cap;</button> <button>&cup;</button> <button>&real;</button> <button>&image;</button> <button>&empty;</button> <button>&deg;</button> <button>&prime;</button> <button>&micro;</button> <button>&larr;</button> <button>&uarr;</button> <button>&rarr;</button> <button>&darr;</button> <button>&harr;</button> <button>&lArr;</button> <button>&uArr;</button> <button>&rArr;</button> <button>&dArr;</button> <button>&hArr;</button>",true);$("#fenetre button").on("click",function(){insert(el,"","",$(this).text());$("#fenetre,#fenetre_fond").remove()})}function insertion_ol(el){popup('<a class="icon-ok" title="Valider"></a><h3>Insertion d\'une liste numérotée</h3>  <p>Choisissez le type de numérotation et la valeur de départ de la liste ci-dessous. Vous pouvez éventuellement modifier les différents éléments en les écrivant ligne par ligne. Vous pourrez ajouter un élément ultérieurement en l\'encadrant par les balises &lt;li&gt; et &lt;/li&gt;.</p>  <p class="ligne"><label for="t1">Numérotation numérique (1, 2, 3...)</label><input type="radio" name="type" id="t1" value="1" checked></p>  <p class="ligne"><label for="t2">Numérotation alphabétique majuscule (A, B, C...)</label><input type="radio" name="type" id="t2" value="A"></p>  <p class="ligne"><label for="t3">Numérotation alphabétique minuscule (a, b, c...)</label><input type="radio" name="type" id="t3" value="a"></p>  <p class="ligne"><label for="t4">Numérotation romaine majuscule (I, II, III...)</label><input type="radio" name="type" id="t4" value="I"></p>  <p class="ligne"><label for="t5">Numérotation romaine minuscule (i, ii, iii...)</label><input type="radio" name="type" id="t5" value="i"></p>  <p class="ligne"><label for="debut">Valeur de début (numérique)</label><input type="text" id="debut" value="1"></p>  <p class="ligne"><label for="lignes">Textes (chaque ligne correspond à un élément de la liste)&nbsp;: </label></p>  <textarea id="lignes" rows="5">'+marqueselection(el)+'</textarea>  <hr><h3>Aperçu</h3><div id="apercu"></div>',true);$("#fenetre :input").on("click keyup",function(){var debut=$("#debut").val();debut=debut.length&&debut>1?' start="'+debut+'"':"";$("#apercu").html('<ol type="'+$("[name='type']:checked").val()+'"'+debut+"><li>"+($("#lignes").val().length?$("#lignes").val().trim("\n").replace(/\n/g,"</li><li>"):"Première ligne</li><li>Deuxième ligne</li><li>...")+"</li></ol>")}).first().keyup();$("#lignes").focus();$("#fenetre a.icon-ok").on("click",function(){var debut=$("#debut").val();debut=debut.length&&debut>1?' start="'+debut+'"':"";var elements=$("#lignes").val().trim("\n");var index=elements.lastIndexOf("\n");if(index>0){var dernier=elements.substring(index+1);elements=elements.substring(0,index)}else var dernier="";insert(el,'<ol type="'+$("[name='type']:checked").val()+'"'+debut+"><li>"+elements.replace(/\n/g,"</li><li>")+"</li><li>","</li></ol>",dernier);$("#fenetre,#fenetre_fond").remove()});$("#fenetre a.icon-ferme,#fenetre_fond").on("click",function(){marqueselection(el,true)})}function insertion_ul(el){popup('<a class="icon-ok" title="Valider"></a><h3>Insertion d\'une liste à puces</h3>  <p>Vous pouvez éventuellement modifier les différents éléments en les écrivant ligne par ligne (chaque ligne correspond à un élément de la la liste). Vous pourrez ajouter un élément ultérieurement en l\'encadrant par les balises &lt;li&gt; et &lt;/li&gt;.</p>  <textarea id="lignes" rows="5">'+marqueselection(el)+'</textarea>  <hr><h3>Aperçu</h3><div id="apercu"></div>',true);$("#lignes").on("click keyup",function(){$("#apercu").html("<ul><li>"+($("#lignes").val().length?$("#lignes").val().trim("\n").replace(/\n/g,"</li><li>"):"Première ligne</li><li>Deuxième ligne</li><li>...")+"</li></ul>")}).keyup().focus();$("#fenetre a.icon-ok").on("click",function(){var elements=$("#lignes").val().trim("\n");var index=elements.lastIndexOf("\n");if(index>0){var dernier=elements.substring(index+1);elements=elements.substring(0,index)}else var dernier="";insert(el,"<ul><li>"+elements.replace(/\n/g,"</li><li>")+"</li><li>","</li></ul>",dernier);$("#fenetre,#fenetre_fond").remove()});$("#fenetre a.icon-ferme,#fenetre_fond").on("click",function(){marqueselection(el,true)})}function insertion_lien1(el){var sel=marqueselection(el);popup('<a class="icon-ok" title="Valider"></a><h3>Insertion d\'un lien vers un document de Cahier de Prépa</h3>  <div><p style="text-align:center; margin: 2em 0;">[Récupération des listes de documents]</p></div>  <div style="display:none;"><hr><h3>Aperçu</h3><div id="apercu" style="text-align:center;">[Veuillez choisir un document]</div></div>',true);$("#fenetre a.icon-ferme,#fenetre_fond").on("click",function(){marqueselection(el,true)});$.ajax({url:"recup.php",method:"post",data:{action:"docs"},dataType:"json"}).done(function(data){var majapercu=function(){var apercu=$("#apercu");var id=$("#doc").val();var texte=$("#doc option:selected").text();if(id==0)apercu.html(texte);else if($("#vue").is(":checked")){var l=$("#largeur").val();if(texte.slice(-4,-1)=="pdf"){if(apercu.children(".pdf").length==0)apercu.html('<div><object data="download?id='+id+'" type="application/pdf" height="100%" width="100%"> <a href="download?id='+id+'">'+texte+"</a> </object></div>");else if(apercu.find("object").attr("data").substr(12)!=id)apercu.find("object").attr("data","download?id="+id).html('<a href="download?id='+id+'">'+texte+"</a>");apercu.children().attr("class","pdf "+$("#format").val());if(l){if(l==100)apercu.children().removeAttr("style").children().attr("width","100%").removeAttr("style");else{apercu.children().css("padding-bottom",$('<div class="'+$("#format").val()+'"></div>').css("padding-bottom").slice(0,-1)*l/100+"%");apercu.find("object").attr("width",l+"%").css("left",(100-l)/2+"%")}}}else if("jpgpegpng".indexOf(texte.slice(-4,-1))>-1){if(apercu.children("img").length==0)apercu.css("text-align","").html('<img src="download?id='+id+'">');else if(apercu.children().attr("src").substr(12)!=id)apercu.children().attr("src","download?id="+id);if(l){if(l==100)apercu.children().removeAttr("style");else apercu.children().css("width",l+"%").css("margin-left",(100-l)/2+"%")}}}else $("#apercu").css("text-align","center").html('<a onclick="return false;" href="download?id='+this.value+'">'+$("#texte").val()+"</a>")};var affichedocs=function(data){$("#fenetre > div:first").html('  <p>Choisissez ci-dessous le répertoire puis le document à insérer. Vous pouvez aussi modifier le texte visible. Cela reste modifiable ultérieurement&nbsp;: le texte est situé entre les deux balises &lt;a...&gt; et &lt;/a&gt;.</p>  <p class="ligne"><label for="mat">Matière&nbsp;:</label><select id="mat">'+data.mats+'</select></p>  <p class="ligne"><label for="rep">Répertoire&nbsp;:</label><select id="rep"></select></p>  <p class="ligne"><label for="doc">Document&nbsp;:</label><select id="doc"></select></p>  <p class="ligne"><label for="texte">Texte visible&nbsp;:</label><input type="text" id="texte" value="'+sel+'" size="80" data-auto="1"></p>  <p class="ligne"><label for="vue">Afficher dans la page (PDF et image uniquement)</label><input type="checkbox" id="vue">  <p class="ligne"><label for="largeur">Largeur en %&nbsp;:</label><input type="text" id="largeur" value="100" size="3"></p>  <p class="ligne"><label for="format">Format (PDF uniquement)</label><select id="format">    <option value="portrait">A4 vertical</option><option value="paysage">A4 horizontal</option><option value="hauteur50">Hauteur 50%</option>  </select>');$("#fenetre > div:last").show();if($("#texte").val().length)$("#texte").attr("data-auto",0);$("#doc").on("change keyup",function(e){if(e.which==13)$("#fenetre a.icon-ok").click();var texte=$("#doc option:selected").text();if($("#texte").attr("data-auto")==1)$("#texte").val(this.value>0?texte.substr(0,texte.lastIndexOf("(")-1):"---");if("pdfjpgpegpng".indexOf(texte.slice(-4,-1))>-1)$("#vue").change().parent().show();else{$("#vue, #largeur, #format").parent().hide();$("#vue").prop("checked",false)}majapercu()});$("#texte").on("change keypress",function(e){if(e.which==0)return;if(e.which==13)$("#fenetre a.icon-ok").click();if(this.value.length==0){$(this).data("auto",1);$("#doc").change()}else{$(this).data("auto",0);majapercu()}});$("#vue").on("change",function(){if($("#vue").is(":checked")){if($("#doc option:selected").text().slice(-4,-1)=="pdf"){$("#largeur, #format").parent().show();$("#texte").parent().hide()}else if("jpgpegpng".indexOf($("#doc option:selected").text().slice(-4,-1))>-1){$("#largeur").parent().show();$("#format, #texte").parent().hide()}}else{$("#texte").parent().show();$("#largeur, #format").parent().hide()}majapercu()});$("#format").on("change keyup",function(e){if(e.which==13)$("#fenetre a.icon-ok").click();majapercu()});$("#largeur").on("keydown",function(e){if(e.which==38)++this.value;else if(e.which==40)--this.value}).on("change keyup",function(e){if(e.which==0)return;if(e.which==13)$("#fenetre a.icon-ok").click();if(this.value!=$(this).data("valeur")){$(this).data("valeur",this.value);majapercu()}}).attr("data-valeur",100);$("#rep").on("change",function(){$("#doc").html(data.docs[this.value]).change()});$("#mat").on("change",function(){$("#rep").html(data.reps[this.value]).change()}).focus().change();$("#fenetre a.icon-ok").on("click",function(){if($("#doc").val()){if($("#vue").is(":checked")&&"pdfjpgpegpng".indexOf($("#doc option:selected").text().slice(-4,-1))>-1)insert(el,$("#apercu").html(),"","");else insert(el,'<a href="download?id='+$("#doc").val()+'">',"</a>",$("#texte").val());$("#fenetre,#fenetre_fond").remove()}});$("#mat option").each(function(){if($("body").attr("data-matiere")==this.value)$("#mat").val(this.value).change()})};if("mats"in data)affichedocs(data)})}function insertion_lien2(el){popup('<a class="icon-ok" title="Valider"></a><h3>Insertion d\'un lien</h3>  <p class="ligne"><label for="texte">Texte visible&nbsp;: </label><input type="text" id="texte" value="'+marqueselection(el)+'" size="80"></p>  <p class="ligne"><label for="url">Adresse&nbsp;: </label><input type="text" id="url" value="http://" size="80"></p>  <hr><h3>Aperçu</h3><div id="apercu" style="text-align:center;"></div>',true);$("#fenetre input").on("click keyup",function(){$("#apercu").html($("#texte").val().length?'<a onclick="return false;" href="'+$("#url").val()+'">'+$("#texte").val()+"</a>":"[Écrivez un texte visible]")}).on("keypress",function(e){if(e.which==13)$("#fenetre a.icon-ok").click()}).first().keyup().focus();$("#fenetre a.icon-ok").on("click",function(){insert(el,'<a href="'+$("#url").val()+'">',"</a>",$("#texte").val());$("#fenetre,#fenetre_fond").remove()});$("#fenetre a.icon-ferme,#fenetre_fond").on("click",function(){marqueselection(el,true)})}function insertion_tex(el){var chargement=typeof MathJax=="undefined"?'<script type="text/javascript" src="/MathJax/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script><script type="text/x-mathjax-config">MathJax.Hub.Config({tex2jax:{inlineMath:[["$","$"],["\\\\(","\\\\)"]]}});</script>':"";var sel=marqueselection(el);var type="t1";if(sel.length)switch(sel.substring(0,2)){case"\\[":case"$$":type="t2";case"\\(":sel=sel.substring(2,sel.length-2);break;default:sel=sel.trim("$")}popup(chargement+'<a class="icon-montre" title="Mettre à jour l\'aperçu"></a><a class="icon-ok" title="Valider"></a><h3>Insertion de formules LaTeX</h3>  <p>Vous pouvez ci-dessous entrer et modifier une formule LaTeX. L\'aperçu présent en bas sera mis à jour uniquement lorsque vous cliquez sur l\'icône <span class="icon-montre"></span>.</p>  <p class="ligne"><label for="t1">La formule est en ligne (pas de retour)</label><input type="radio" name="type" id="t1" value="1"></p>  <p class="ligne"><label for="t2">La formule est hors ligne (formule centrée)</label><input type="radio" name="type" id="t2" value="2"></p>  <textarea id="formule" rows="3">'+sel+'</textarea>  <hr><h3>Aperçu</h3><div id="apercu" style="text-align:center;">[Demandez l\'aperçu en cliquant sur l\'icône <span class="icon-montre"></span>]</div>',true);$("#"+type).prop("checked",true);$("#formule").focus();$("#fenetre a.icon-montre").on("click",function(){if($("#formule").val().length){$("#apercu").html($("#t1").is(":checked")?"$"+$("#formule").val()+"$":"\\["+$("#formule").val()+"\\]").css("text-align","left");MathJax.Hub.Queue(["Typeset",MathJax.Hub,"apercu"])}else $("#apercu").html("[Écrivez une formule]").css("text-align","center")});$("#fenetre a.icon-ok").on("click",function(){if($("#t1").is(":checked"))insert(el,"$","$",$("#formule").val());else insert(el,"\\[","\\]",$("#formule").val());$("#fenetre,#fenetre_fond").remove()});$("#fenetre a.icon-ferme,#fenetre_fond").on("click",function(){marqueselection(el,true)})}function insertion_par1(el){insert(el,"<p>","</p>")}function insertion_par2(el){insert(el,"<div class='note'>","</div>")}function insertion_par3(el){insert(el,"<div class='annonce'>","</div>")}function insertion_retour(el){insert(el,"<br>","")}function insertion_gras(el){insert(el,"<strong>","</strong>")}function insertion_italique(el){insert(el,"<em>","</em>")}function insertion_souligne(el){insert(el,"<u>","</u>")}function insertion_exp(el){insert(el,"<sup>","</sup>")}function insertion_ind(el){insert(el,"<sub>","</sub>")}function aidetexte(){popup('<h3>Aide et explications</h3>  <p>Il y a deux modes d\'éditions possibles pour éditer un texte&nbsp;: le mode «&nbsp;balises visibles&nbsp;» et le mode «&nbsp;balises invisibles&nbsp;». Il est possible de passer de l\'un à l\'autre&nbsp;:</p>  <ul>    <li><span class="icon-source"></span> permet de passer en mode «&nbsp;balises visibles&nbsp;» (par défaut), où le texte à taper est le code HTML de l\'article. Ce mode est plus précis. Les boutons aux dessus aident à utiliser les bonnes balises.</li>    <li><span class="icon-nosource"></span> permet de passer en mode «&nbsp;balises invisibles&nbsp;», où le texte est tel qu\'il sera affiché sur la partie publique, et modifiable. Ce mode est moins précis, mais permet le copié-collé depuis une page web ou un document Word/LibreOffice.  </ul>  <p>Une fonction de nettoyage du code HTML, permettant d\'assurer une homogénéité et une qualité d\'affichage optimales, est lancée à chaque commutation entre les deux modes, à chaque clic sur un des boutons disponibles, à chaque copie/coupe de texte et à chaque passage à la ligne.</p>  <p>En HTML, toutes les mises en formes sont réalisées par un encadrement de texte entre deux balises&nbsp;: &lt;h3&gt; et &lt;/h3&gt; pour un gros titre, &lt;p&gt; et &lt;/p&gt; pour un paragraphe. Le retour à la ligne simple, qui ne doit exister que très rarement, est une balise simple &lt;br&gt;. Mais les boutons disponibles sont là pour vous permettre de réaliser le formattage que vous souhaitez&nbsp;:</p>  <ul>    <li><span class="icon-titres"></span>&nbsp;: différentes tailles de titres (fenêtre supplémentaire pour choisir)</li>    <li><span class="icon-par1"></span>&nbsp;: paragraphe classique, qui doit obligatoirement encadrer au minimum chaque ligne de texte. Apparaît automatiquement au passage à la ligne si on l\'oublie.</li>    <li><span class="icon-par2"></span>&nbsp;: paragraphe important, écrit en rouge</li>    <li><span class="icon-par3"></span>&nbsp;: paragraphe très important, écrit en rouge et encadré</li>    <li><span class="icon-retour"></span>&nbsp;: retour à la ligne. Identique à un appui sur Entrée, et souvent inutile.</li>    <li><span class="icon-gras"></span>&nbsp;: mise en gras du texte entre les balises</li>    <li><span class="icon-italique"></span>&nbsp;: mise en italique du texte entre les balises</li>    <li><span class="icon-souligne"></span>&nbsp;: soulignement du texte entre les balises</li>    <li><span class="icon-omega"></span>&nbsp;: lettres grecques (fenêtre supplémentaire pour choisir)</li>    <li><span class="icon-sigma"></span>&nbsp;: symboles mathématiques (fenêtre supplémentaire pour choisir)</li>    <li><span class="icon-exp"></span>&nbsp;: mise en exposant du texte entre les balises</li>    <li><span class="icon-ind"></span>&nbsp;: mise en indice du texte entre les balises</li>    <li><span class="icon-ol"></span>&nbsp;: liste numérotée. Une fenêtre supplémentaire permet de choisir le type (1,A,a,I,i) et la première valeur. Les différentes lignes de la liste sont constituées par les balises &lt;li&gt; et &lt;/li&gt;</li>    <li><span class="icon-ul"></span>&nbsp;: liste à puces. Les différentes lignes de la liste sont constituées par les balises &lt;li&gt; et &lt;/li&gt;</li>    <li><span class="icon-lien1"></span>&nbsp;: lien d\'un document disponible ici (fenêtre supplémentaire pour choisir)</li>    <li><span class="icon-lien2"></span>&nbsp;: lien vers un autre site web (fenêtre supplémentaire pour entre l\'adresse)</li>    <li><span class="icon-tex"></span>&nbsp;: insertion de code LaTeX (fenêtre supplémentaire pour le taper)</li>  </ul>  <p class="tex2jax_ignore">Il est possible d\'insérer du code en LaTeX, sur une ligne séparée (balises \\[...\\] ou balises $$...$$) ou au sein d\'une phrase (balises $...$ ou balises \\(...\\)). Il faut ensuite taper du code en LaTeX à l\'intérieur. La prévisualisation est réalisée en direct.</p>',false)
+}function echange(el1,el2){if(el1.length&&el2.length){$("article").css("position","relative");el1.css("opacity",.3);el2.css("opacity",.3);el2.animate({top:el1.position().top-el2.position().top},1e3);el1.animate({top:(el2.outerHeight(true)+el2.outerHeight())/2},1e3,function(){el1.css("opacity",1);el2.css("opacity",1);el1.insertAfter(el2);el1.css({position:"static",top:0});el2.css({position:"static",top:0})})}}function cache(el){var prop=el.parent().attr("data-id").split("|");$.ajax({url:"ajax.php",method:"post",data:{cache:1,action:prop[0],id:prop[1]},dataType:"json",el:el,fonction:function(el){el.parent().addClass("cache");el.removeClass("icon-cache").addClass("icon-montre").off("click").on("click",function(){montre($(this))}).attr("title","Montrer à nouveau")}})}function montre(el){var prop=el.parent().attr("data-id").split("|");$.ajax({url:"ajax.php",method:"post",data:{montre:1,action:prop[0],id:prop[1]},dataType:"json",el:el,fonction:function(el){el.parent().removeClass("cache");el.removeClass("icon-montre").addClass("icon-cache").off("click").on("click",function(){cache($(this))}).attr("title","Cacher à nouveau")}})}function monte(el){var parent=el.parent();var prop=parent.attr("data-id").split("|");$.ajax({url:"ajax.php",method:"post",data:{monte:1,action:prop[0],id:prop[1]},dataType:"json",el:parent,fonction:function(el){if(!el.prev().prev().is("article")){el.children(".icon-monte").hide(1e3);el.prev().children(".icon-monte").show(1e3)}if(!el.next().is("article")){el.children(".icon-descend").show(1e3);el.prev().children(".icon-descend").hide(1e3)}echange(el.prev(),el)}})}function descend(el){var parent=el.parent();var prop=parent.attr("data-id").split("|");$.ajax({url:"ajax.php",method:"post",data:{descend:1,action:prop[0],id:prop[1]},dataType:"json",el:parent,fonction:function(el){if(!el.prev().is("article")){el.children(".icon-monte").show(1e3);el.next().children(".icon-monte").hide(1e3)}if(!el.next().next().is("article")){el.children(".icon-descend").hide(1e3);el.next().children(".icon-descend").show(1e3)}echange(el,el.next())}})}function supprime(el){var parent=el.parent();var prop=parent.data("id").split("|");var item="un élément";switch(prop[0]){case"infos":item="une information";break;case"pages":item="la matière <em>"+$("h3",parent).text()+"</em>. Les informations qui y sont écrites seront aussi supprimées";break;case"reps":item="le répertoire <em>"+$(".nom",parent).map(function(){return this.textContent||$(this).find("input").val()}).get(0)+"</em>. Tous les sous-répertoires et documents qui s'y trouvent seront aussi supprimés";break;case"docs":item="le document <em>"+$(".nom",parent).map(function(){return this.textContent||$(this).find("input").val()}).get(0)+"</em>";break;case"colles":item="le programme de colle de la "+$(".edition",parent).text().toLowerCase();break;case"cdt-elems":item="un élément du cahier de texte";break;case"cdt-types":item="le type de séances <em>"+$("h3",parent).text()+"</em>. Les éléments du cahier de texte associés à ce type seront aussi supprimés";break;case"cdt-raccourcis":item="le raccourci de séance <em>"+$("h3",parent).text()+"</em>. Aucun élément du cahier de texte ne sera supprimé";break;case"notes":parent=parent.parent();item="une colle du <em>"+$("td:first",parent).text()+"</em>, d'une durée de "+$("td:eq(3)",parent).text()+". Toutes les notes associées seront supprimées";break;case"matieres":item="la matière <em>"+$("h3",parent).text()+"</em>. Les programmes de colles, le cahier de texte et les notes correspondantes seront supprimées. Les répertoires, les documents, les pages d'informations spécifiques et les éléments de l'agenda associés à la matière seront conservés mais ne seront plus associés à une matières, donc visibles dans le contexte «&nbsp;général&nbsp;»";break;case"groupes":item="le groupe <em>"+($(".editable",parent).text()||$("input:first",parent).val())+"</em>. Les utilisateurs concernés ne seront pas supprimés";break;case"agenda-elems":item="un événement de l'agenda";break;case"agenda-types":item="le type d'événement <em>"+$("h3",parent).text()+"</em>. Les événements de l'agenda associés à ce type seront aussi supprimés";break}confirmation("Vous allez supprimer XXX.<br>Cette opération n'est pas annulable.".replace("XXX",item),this,function(el){$.ajax({url:"ajax.php",method:"post",data:{supprime:1,action:prop[0],id:prop[1]},dataType:"json",el:parent,fonction:function(el){if(prop[0].match(/^(agenda|colles)$/))location.reload(true);else el.remove()}})})}function lock(el){var parent=el.parent();var prop=parent.data("id").split("|");var protection=el.data("val");popup('<a class="icon-ok" title="Valider ce choix"></a><h3>Accès à l\'information</h3><table id="selmult">  <tr class="categorie"><td>Accès public</td><td><input value="0" type="checkbox"></td></tr>  <tr class="categorie"><td>Utilisateurs identifiés</td><td><input value="6" type="checkbox"></td></tr>  <tr class="element"><td>Invités</td><td><input value="1" type="checkbox"></td></tr>  <tr class="element"><td>Élèves</td><td><input value="2" type="checkbox"></td></tr>  <tr class="element"><td>Colleurs</td><td><input value="3" type="checkbox"></td></tr>  <tr class="element"><td>Administratifs</td><td><input value="4" type="checkbox"></td></tr>  <tr class="element"><td>Professeurs</td><td><input value="5" type="checkbox"></td></tr>  <tr class="categorie"><td>Information invisible</td><td><input value="32" type="checkbox"></td></tr>  </tbody></table>',true);var f=$("#fenetre");if(protection==0||protection==32)$('input[value="'+protection+'"]',f).prop("checked",true).change();else{$("input[value=6]",f).prop("checked",true).change();for(var a=1;a<6;a++)if((protection-1>>a-1&1)==0)$('input[value="'+a+'"]',f).prop("checked",true).change()}var f=$("#fenetre");$("input[value=0],input[value=6],input[value=32]",f).parent().parent().addClass("categorie");$("tr:not(.categorie)",f).addClass("element");$("input",f).on("click",function(){if(this.value==0||this.value==32)$(this).parent().parent().siblings().find("input[type=checkbox]").prop("checked",false).change();else{$("input[value=0],input[value=32]",f).prop("checked",false).change();$("input[value=6]",f).prop("checked",true).change();if(this.value==6)$("tr:not(.categorie) input",f).prop("checked",true).change()}});$("tr",f).on("click",function(e){if(!$(e.target).is("input"))$(this).find("input").click()});$("input",f).on("change",function(){$(this).parent().parent().removeClass("sel");if(this.checked)$(this).parent().parent().addClass("sel")});$(".icon-ok",f).on("click",function(){if($('input[value="32"]',f).prop("checked"))var val=32;else if($('input[value="0"]',f).prop("checked"))var val=0;else var val=32-$("input:checked:not([value=6])",f).map(function(){return this.value|0}).get().reduce(function(acc,v){return acc+Math.pow(2,v-1)},0);el.data("val",val);$.ajax({url:"ajax.php",method:"post",data:{action:prop[0],id:prop[1],champ:"protection",val:val},dataType:"json",el:parent,fonction:function(el){location.reload(true)}})})}function ajoutecolle(el){var article=el.parent();el.before('<a class="icon-annule" title="Annuler"></a><a class="icon-ok" title="Valider"></a>');el.next().addBack().hide();var form=$("<form></form>").appendTo(article).html($("#form-ajoutecolle").html());$("textarea",form).textareahtml();$("input",form).attr("id","cache");$(".icon-annule",article).on("click",function(){$("form,.icon-annule,.icon-ok",article).remove();el.next().addBack().show()});$("a.icon-ok",article).on("click",function(){$("textarea",form).each(function(){this.value=nettoie($(this).is(":visible")?this.value:$(this).next().html())});var id=article.data("id").split("|")[1];$.ajax({url:"ajax.php",method:"post",data:form.serialize()+"&action=ajout-colle&id="+id,dataType:"json",el:article,fonction:function(el){var texte=$("textarea",el).val();var cache=$("input",el).is(":checked");if(cache)el.addClass("cache");el.data("id","colles|"+id);$(".icon-aide",el).nextAll().remove();$(".icon-aide",el).after((cache?'<a class="icon-montre" title="Afficher le programme de colles sur la partie publique"></a>':'<a class="icon-cache" title="Rendre invisible le programme de colles sur la partie publique"></a>')+'<a class="icon-supprime" title="Supprimer ce programme de colles"></a><div class="editable edithtml" data-id="colles|texte|'+id+'" placeholder="Texte du programme de colles">'+texte+"</div>");$("a.icon-cache,a.icon-montre,a.icon-supprime",el).on("click",function(){window[this.className.substring(5)]($(this))});$(".editable",el).editinplace()}})});$("input,select",form).on("keypress",function(e){if(e.which==13){e.preventDefault();$("a.icon-ok",article).click()}})}function valide(){var data="";if($("#planning").length)data="action=planning&"+$("form").serialize();else{var id=$(this).parent().attr("data-id").split("|");data="action="+id[0]+"&id="+id[1]+"&"+$(this).nextAll("form").serialize()}if(data.length)$.ajax({url:"ajax.php",method:"post",data:data,dataType:"json",el:this,fonction:function(el){if(el.classList[1]!="noreload")location.reload(true)}});else affiche("<p>Aucune donnée envoyée.</p>","nok")}function formulaire(){var idform=this.className.split(" ")[0].substring(5);var action=$("#form-"+idform).data("action");$("#epingle").remove();var article=$('<article id="epingle"><a class="icon-ferme" title="Fermer"></a>  <a class="icon-aide" title="Aide pour ce formulaire"></a>  <a class="icon-ok" title="Valider"></a></article>').insertBefore($("article,#calendrier,#parentsdoc+*").first());var form=$("<form></form>").appendTo(article).html($("#form-"+idform).html());$(".edithtml",form).textareahtml();$("input[name], select[name]:not([multiple])",form).attr("id",function(){return this.getAttribute("name")});switch(action){case"reps":$(this).init_reps();break;case"ajout-rep":form.append('<input type="hidden" name="parent" value="'+$(this).parent().data("id").split("|")[1]+'">');break;case"docs":case"ajout-doc":$(this).init_docs(action);break;case"cdt-elems":form.init_cdt_boutons();break;case"ajout-cdt-raccourci":form.init_cdt_raccourcis();break;case"notes":case"ajout-notes":$(this).init_notes(action);break;case"agenda-elems":$(this).init_evenements();break;case"deplcolle":$("#ancien,#nouveau").each(function(){$(this).datetimepicker({format:"d/m/Y Ghi",timepicker:true})});break;case"ajout-utilisateurs":form.init_ajout_utilisateurs();break;case"ajout-groupe":$(".usergrp span",form).on("click",utilisateursgroupe);break}$("select[multiple]",form).each(selmult);$("#epingle .icon-ferme").on("click",function(){$("#epingle").remove()});$("#epingle a.icon-aide").on("click",function(){popup($("#aide-"+idform).html(),false)});$("#epingle a.icon-ok").on("click",function(){$(".edithtml",form).each(function(){this.value=nettoie($(this).is(":visible")?this.value:$(this).next().html())});if(action=="notes"||action=="ajoute-notes")$("#epingle select:not(:visible)").val("x");$.ajax({url:"ajax.php",method:"post",data:form.serialize()+"&action="+action,dataType:"json",el:"",fonction:function(el){location.reload(true)}})});$("input,select",form).on("keypress",function(e){if(e.which==13){e.preventDefault();$("#epingle a.icon-ok").click()}})}$.fn.init_reps=function(){var el=$(this);var form=$("#epingle form");var sel=$("select[multiple]",form);var id=el.parent().data("id").split("|")[1];var donnees=el.parent().data("donnees").split("|");var protection=donnees[2];var nom=el.siblings(".nom").text().split(/\/\s/).pop()||el.parent().find("input").val();$("em",form).text(nom);if(protection==0||protection==32)sel.val(protection);else{sel.val(6);for(var a=1;a<6;a++)if((protection-1>>a-1&1)==0)$('option[value="'+a+'"]',sel).prop("selected",true)}if(donnees[0]==0)$("#nom,#parent,#menu",form).parent().remove();else{$("#nom",form).val(nom);if(donnees[1]=="1")$("#menu",form).prop("checked",true);$('[data-parents*=",'+id+',"]',form).prop("disabled",true)}form.append('<input type="hidden" name="id" value="'+id+'">');$('input[type="button"]',form).on("click",function(){var action=this.value.substring(0,4)=="Prop"?"propage":"vide";var contexte=$(this).parent().find("em").text();var texte=action=="propage"?"Vous allez propager le réglage d'accès du répertoire <em>"+contexte+"</em> à l'ensemble de ses sous-répertoires et des documents qu'ils contiennent.<br>Vous pourrez modifier à nouveau le réglage de chaque document ou répertoire, de façon individuelle.":"Vous allez vider le répertoire <em>"+contexte+"</em>. Cela supprimera définitivement l'ensemble de ses sous-répertoires et des documents qu'ils contiennent.<br>Cette opération n'est pas annulable.";confirmation(texte,this,function(el){$.ajax({url:"ajax.php",method:"post",data:"action=reps&id="+id+"&"+action+"=1",dataType:"json",el:"",fonction:function(el){location.reload(true)}})})})};$.fn.init_docs=function(action){var el=$(this);var form=$("#epingle form");var nom=el.siblings(".nom").text().split(/\/\s/).pop()||el.parent().find("input").val();$("em",form).text(nom);var id=el.parent().data("id").split("|")[1];if(action=="docs"){var protection=el.parent().data("protection");form.append('<input type="hidden" name="id" value="'+id+'">');$("#nom",form).val(nom)}else{var protection=el.parent().data("donnees").split("|")[2];$("#fichier",form).on("change",function(){if(!$("#nom",form).val().length){var f=this.value;$("#nom",form).val(f.substring(f.lastIndexOf("\\")+1,f.lastIndexOf("."))||f)}});form.append('<input type="hidden" name="parent" value="'+id+'">')}var sel=$("select[multiple]",form);if(protection==0||protection==32)sel.val(protection);else{sel.val(6);for(var a=1;a<6;a++)if((protection-1>>a-1&1)==0)$('option[value="'+a+'"]',sel).prop("selected",true)}form.append('<input type="hidden" name="action" value="'+action+'">');$("#epingle a.icon-ok").off("click").on("click",function(){var data=new FormData(form[0]);$.ajax({url:"ajax.php",method:"post",data:data,dataType:"json",contentType:false,processData:false,el:"",fonction:function(el){location.reload(true)}})})};$.fn.init_cdt_boutons=function(){var form=this;$("#jour,#pour").datetimepicker({format:"d/m/Y",timepicker:false});$("#h_debut").datetimepicker({format:"Ghi",datepicker:false,onClose:function(t,input){$("#h_fin").val(function(i,v){return v||(input.val().length?parseInt(input.val().slice(0,-3))+2+input.val().slice(-3):"")})}});$("#h_fin").datetimepicker({format:"Ghi",datepicker:false});var zero=function(n){return String(n).length==1?"0"+n:String(n)};$("#raccourci").on("change keyup",function(){var valeurs=raccourcis[this.value];for(var cle in valeurs){if(cle=="jour"){var t=new Date;var j=parseInt(valeurs["jour"]);t.setDate(j>t.getDay()?t.getDate()-t.getDay()-7+j:t.getDate()-t.getDay()+j);$("#jour").val(zero(t.getDate())+"/"+zero(t.getMonth()+1)+"/"+t.getFullYear())}else $("#"+cle).val(valeurs[cle])}$(this).data("modif",1);$("#tid").change()}).data("modif",0);$("#tid").on("change keyup",function(){switch(parseInt(seances[this.value])){case 0:$("#h_debut,#demigroupe").parent().show();$("#h_fin,#pour").parent().hide();break;case 1:$("#h_debut,#h_fin,#demigroupe").parent().show();$("#pour").parent().hide();break;case 2:$("#h_debut,#h_fin").parent().hide();$("#pour,#demigroupe").parent().show();break;case 3:$("#h_debut,#h_fin,#pour").parent().hide();$("#demigroupe").parent().show();break;default:$("#h_debut,#h_fin,#pour,#demigroupe").parent().hide()}$("#jour").change()});$("input,#demigroupe",form).on("change keyup",function(){if($("#raccourci").data("modif")==0)$("#raccourci").val(0);else $("#raccourci").data("modif",0)});$("input,select",form).on("keypress",function(e){if(e.which==13)$("a.icon-ok",el).click()});$("select:first",form).focus();$("#tid").change()};$.fn.init_cdt_raccourcis=function(){this.each(function(){var form=$(this);$('[id^="h_d"]',form).datetimepicker({format:"Ghi",datepicker:false,onClose:function(t,input){$('[id^="h_f"]',form).val(function(i,v){return v||(input.val().length?parseInt(input.val().slice(0,-3))+2+input.val().slice(-3):"")})}});$('[id^="h_fin"]').datetimepicker({format:"Ghi",datepicker:false});$('[id^="type"]',form).on("change keyup",function(){switch(parseInt(seances[this.value])){case 0:$('[id^="h_d"],[id^="dem"]',form).parent().show();$('[id^="h_f"]',form).parent().hide();break;case 1:$('[id^="h_d"],[id^="h_f"],[id^="dem"]',form).parent().show();break;case 2:case 3:$('[id^="h_d"],[id^="h_f"]',form).parent().hide();$('[id^="dem"]',form).parent().show();break;default:$('[id^="h_d"],[id^="h_f"],[id^="dem"]',form).parent().hide()}}).change();$("input,select",form).on("keypress",function(e){if(e.which==13)$("a.icon-ok",form).click()})})};$.fn.init_notes=function(action){var el=$(this);var form=$("#epingle form").append($("#form-notes").html());$("input, select",form).attr("id",function(){return this.getAttribute("name")});$("tr[data-id]",form).append("<td>"+$("div",form).html()+"</td>");$("table select",form).attr("name",function(){return"e"+$(this).parent().parent().data("id")});$("div",form).remove();if($("input:checkbox",form).length){$("tr[data-id]",form).hide();$("input:checkbox",form).on("click",function(){if($("input:checkbox:last",form).prop("checked"))return $("tr[data-id]",form).show();var ids=$("input:checked",form).map(function(){return this.value.split(",")}).get().concat();$("tr[data-id]:not(.orig)",form).hide();for(var i=0;i<ids.length;i++)$('tr[data-id="'+ids[i]+'"]',form).show()})}function marque_dejanotes(sid){if(sid==0)return true;var dn=dejanotesautres[sid].split(",");for(var i=0;i<dn.length;i++)$('tr[data-id="'+dn[i]+'"]',form).addClass("dejanote").find("td:eq(0)").text(function(){return this.textContent+" (noté par un autre colleur)"});var dn=dejanotesperso[sid].split(",");for(var i=0;i<dn.length;i++)$('tr[data-id="'+dn[i]+'"]:not(.orig)',form).addClass("dejanote").find("td:eq(0)").text(function(){return this.textContent+" (déjà noté par vous-même)"});$(".dejanote select").prop("disabled",true).val("x")}$("#jour").datetimepicker({format:"d/m/Y",timepicker:false,onShow:function(){this.setOptions({minDate:debut||$("#sid option:selected").data("date"),maxDate:new Date(new Date((fin||$("#sid option:selected").next().data("date")).replace(/(.{2})\/(.{2})\/(.{4})/,function(tout,x,y,z){return z+"-"+y+"-"+x})).getTime()-864e5)})}});$("#heure").datetimepicker({format:"Ghi",datepicker:false,defaultTime:"15h30"});$("#duree").datetimepicker({format:"Ghi",datepicker:false,defaultTime:"0h00",step:10}).on("change",function(){$(this).removeClass("auto")});$("#sid").on("change keyup",function(){$(".dejanote td:first-child").text(function(){return this.textContent.replace(" (noté par un autre colleur)","").replace(" (déjà noté par vous-même)","")});$(".dejanote").removeClass("dejanote").find("select").prop("disabled",false);marque_dejanotes($("#sid").val());var jour=new Date($("#jour").val().replace(/(.{2})\/(.{2})\/(.{4})/,function(tout,x,y,z){return z+"-"+y+"-"+x}));var debut=new Date($("#sid option:selected").data("date").replace(/(.{2})\/(.{2})\/(.{4})/,function(tout,x,y,z){return z+"-"+y+"-"+x}));var fin=new Date($("#sid option:selected").next().data("date").replace(/(.{2})\/(.{2})\/(.{4})/,function(tout,x,y,z){return z+"-"+y+"-"+x}));if(jour<debut||jour>fin){debut=debut.toJSON();$("#jour").val(debut.substr(8,2)+"/"+debut.substr(5,2)+"/"+debut.substr(0,4))}}).change();$("select",form).on("change keyup",function(){var nb=$("table select:visible",form).filter(function(){return this.value!="x"}).length;var duree=nb*(dureecolle||20);if($("#duree").is(".auto")||duree>$("#duree").val().replace(/^(\d*)h(\d*)$/,function(tout,x,y){return 60*(x|0)+(y|0)}))$("#duree").val((duree/60|0)+"h"+(duree%60||"")).addClass("auto")});if(action=="notes"){var tr=el.parent().parent();var sid=el.data("sid");var debut=$('select[name="sid"] option[value="'+sid+'"]').data("date");var fin=$('select[name="sid"] option[value="'+sid+'"]').next().data("date");var eleves=el.data("eleves").split("|");var notes=el.data("notes").split("|");for(var i=0;i<eleves.length;i++)$('tr[data-id="'+eleves[i]+'"]',form).addClass("orig").show().find("select").val(notes[i]).on("change",function(){$(this).parent().parent().removeClass("orig")});$("h3",form).text("Modifier des notes - semaine du "+$('select[name="sid"] option[value="'+sid+'"]').text().split(" ").slice(0,3).join(" "));$("#id").val(el.parent().data("id").split("|")[1]);$("#jour").val($("td:eq(0)",tr).text().replace(/(.{6})(.{2})/,function(tout,x,y){return x+"20"+y}));$("#heure").val($("td:eq(1)",tr).text().replace("-",""));$("#duree").val($("td:eq(3)",tr).text());if(el.next().length){marque_dejanotes(sid)}else{$("tr:not(.orig)",form).hide();$('.orig option[value="x"]',form).remove();$("#duree").prop("disabled",true);form.append("<p>Cette colle a déjà été relevée&nbsp;: il est impossible de modifier quels élèves ont été interrogés ou la durée de la colle. Vous pouvez corriger la date et l'heure (dans la limite de la semaine enregistrée) ou les notes que vous avez mises vous pouvez mettre une note à un élève initialement absent qui a rattrapé sa colle.</p>")}}};$.fn.init_ajout_utilisateurs=function(){$("#autorisation,#saisie").on("change",function(){var f=$("#epingle form");var a=$("#autorisation",f).val();if(a==0){$(".affichesiinvite,.affichesiinvitation,.affichesimotdepasse,.mailenvoi,.mailliste",f).hide(0);$("textarea",f).prop("disabled",true).attr("placeholder","Zone de saisie des utilisateurs\nSélectionnez d'abord un type d'utilisateur")}else{var inv=a==1;var mdp=$("#saisie",f).val()==2;$("#saisie",f).parent().toggle(!inv);$(".affichesiinvite",f).toggle(inv);$(".affichesiinvitation",f).toggle(!inv&&!mdp);$(".affichesimotdepasse",f).toggle(!inv&&mdp);$(".mailenvoi",f).toggle(!inv);$(".mailliste",f).toggle(a>2);$("#mailenvoi",f).prop("disabled",mdp).change();$("textarea",f).prop("disabled",false).attr("placeholder",function(){if(inv)return"identifiant_1,motdepasse_1\nidentifiant_2,motdepasse_2\nidentifiant_3,motdepasse_3\n...";else if(mdp)return"nom_1,prénom_1,motdepasse_1\nnom_2,prénom_2,motdepasse_2\nnom_3,prénom_3,motdepasse_3\n...";else return"nom_1,prénom_1,adresse_1\nnom_2,prénom_2,adresse_2\nnom_3,prénom_3,adresse_3\n..."})}}).change();$("#epingle #mailenvoi").on("change",function(){$("#epingle #mailliste").attr("disabled",!this.checked||this.disabled)}).change()};$.fn.init_evenements=function(){var el=$(this);var form=$("#epingle form");$("textarea",form).attr("id","texte");if(el.is(".modifevnmt")){var id=el.attr("id").substr(1);var valeurs=evenements[id];var cles=["type","matiere","debut","fin","texte"];for(var i=0;i<6;i++){$("#"+cles[i]).val(valeurs[cles[i]])}$("#id").val(id);$("#texte").change();$("#jours").prop("checked",valeurs["je"]);$('<a class="icon-supprime" title="Supprimer cette information"></a>').insertBefore($(".icon-ok")).on("click",function(){supprime($(this))}).parent().data("id","agenda-elems|"+id)}$("#debut").datetimepicker({onShow:function(){this.setOptions({maxDate:$("#fin").val()||false})},onClose:function(t,input){$("#fin").val(function(i,v){return v||input.val()})}});$("#fin").datetimepicker({onShow:function(){this.setOptions({minDate:$("#debut").val()||false})},onClose:function(t,input){$("#debut").val(function(i,v){return v||input.val()})}});$("#jours").on("change",function(){var v;if(this.checked){$("#debut,#fin").each(function(){v=this.value.split(" ");$(this).val(v[0]).attr("data-heure",v[1]).datetimepicker({format:"d/m/Y",timepicker:false})})}else{$("#debut,#fin").each(function(){if(this.hasAttribute("data-heure"))$(this).val(this.value+" "+$(this).attr("data-heure")).removeAttr("data-heure");$(this).datetimepicker({format:"d/m/Y Ghi",timepicker:true})})}}).change()};function selmult(){var sel=$(this);var isacces=this.getAttribute("name").indexOf("protection")+1?1:0;function majselect(sel){sel.prev().children().prop("selected",false).text(function(){var options=$(isacces?"option:selected:not([value=6])":"option:selected",sel);if(isacces&&options.length==5)return"Tout utilisateur identifié";if(options.length==0)return"Choisir ...";else return options.map(function(){return this.textContent}).get().join(", ")}).prop("selected",true)}$("<select id="+sel.prev().attr("for")+"><option selected hidden></select>").insertBefore(sel.hide(0)).attr("disabled",sel.attr("disabled")).on("click",function(e){e.preventDefault();this.blur();popup('<a class="icon-ok" title="Valider ce choix"></a><h3>'+sel.prev().prev().text().replace(":","")+'</h3><table id="selmult">'+$("option",sel).map(function(){return"<tr"+(this.selected?' class="sel"':"")+"><td>"+this.textContent+'</td><td><input type="checkbox" '+(this.selected?"checked ":"")+'value="'+this.value+'"></td></tr>'}).get().join("")+"</table>",true);var f=$("#fenetre");if(isacces){$("input[value=0],input[value=6],input[value=32]",f).parent().parent().addClass("categorie");$("tr:not(.categorie)",f).addClass("element");$("input",f).on("click",function(){if(this.value==0||this.value==32)$(this).parent().parent().siblings().find("input[type=checkbox]").prop("checked",false).change();else{$("input[value=0],input[value=32]",f).prop("checked",false).change();$("input[value=6]",f).prop("checked",true).change();if(this.value==6)$("tr:not(.categorie) input",f).prop("checked",true).change()}})}else{$("#selmult",f).prepend('<tr class="categorie"><th></th><th><a class="icon-cocher"></a></th></tr>');$(".icon-cocher",f).on("click",cocher_utilisateurs)}$("tr",f).on("click",function(e){if(!$(e.target).is("input"))$(this).find("input").click()});$("input",f).on("change",function(){$(this).parent().parent().removeClass("sel");if(this.checked)$(this).parent().parent().addClass("sel")});$(".icon-ok",f).on("click",function(){sel.val($("input:checked",f).map(function(){return this.value}).get());majselect(sel);$("#fenetre, #fenetre_fond").remove()})});majselect(sel)}function cocher_utilisateurs(){$(this).toggleClass("icon-cocher icon-decocher").parent().parent().nextUntil(".categorie").find("input").prop("checked",$(this).hasClass("icon-decocher")).change()}function init_utilisateurs(){$(".icones.mailenvoi").each(function(){this.innerHTML=this.textContent==1?'<a class="icon-ok" title="Supprimer la possibilité d\'envoyer des courriels"></a>':'<a class="icon-nok" title="Accorder la possibilité d\'envoyer des courriels"></a>'});$(".mailenvoi a").on("click",mail_utilisateur);$("th .icon-mail").on("click",mail_utilisateurs);$(".icon-cocher").on("click",cocher_utilisateurs);$("td .icon-edite").on("click",edite_utilisateur);$("td .icon-desactive, td .icon-active, td .icon-supprutilisateur, td .icon-validutilisateur").on("click",modif_utilisateur);$("th .icon-desactive, th .icon-active, th .icon-supprutilisateur, th .icon-validutilisateur").on("click",modif_utilisateurs);$("td:not(.icones)").on("click",function(e){$(this).parent().find("input").click()});$("#utilisateurs input").on("change",function(){$(this).parent().parent().removeClass("sel");if(this.checked)$(this).parent().parent().addClass("sel")});function mail_utilisateur(){var val=$(this).hasClass("icon-ok");$.ajax({url:"ajax.php",method:"post",data:{action:"utilisateur",modif:"mailenvoi",id:$(this).parent().parent().data("id"),val:1-val},dataType:"json",el:$(this),fonction:function(el){el.toggleClass("icon-ok icon-nok").attr("title",(val?"Accorder":"Supprimer")+" la possibilité d'envoyer des courriels")}})}function edite_utilisateur(){var id=$(this).parent().parent().data("id");$.ajax({url:"recup.php",method:"post",data:{action:"prefs",id:id},dataType:"json",afficheform:function(data){if("nom"in data){popup($("#form-edite").html(),true);var f=$("#fenetre");$("input[name]",f).attr("id",function(){return this.getAttribute("name")});if(data["valide"])$("#comptedesactive, #demande, #invitation",f).remove();else if(data["demande"])$("#compteactif, #comptedesactive, #invitation",f).remove();else if(data["invitation"])$("#compteactif, #comptedesactive, #demande",f).remove();else $("#compteactif, #demande, #invitation",f).remove();if(data["autorisation"]==2)$("#mailliste",f).parent().remove();else if(data["autorisation"]==1)$("#mailenvoi, #mailexp, #mailcopie, #mailliste",f).parent().remove();$("p:first",f).html(function(i,code){return code.replace("XXX",data["prenom"].length?"de <em>"+data["prenom"]+" "+data["nom"]+"</em>":"<em>"+data["login"]+"</em>").replace("YYY","<em>"+["Invité","Élève","Colleur","Administratif","Professeur"][data["autorisation"]-1]+"</em>")});$('input[type="text"],input[type="email"]',f).val(function(){return data[this.id]});$('input[type="checkbox"]',f).prop("checked",function(){return data[this.id]});$("#mailenvoi",f).on("click",function(){$("#mailexp, #mailcopie, #mailliste",f).prop("disabled",!this.checked);if(this.checked&&!$("#mailexp",f).val())$("#mailexp",f).val(data["prenom"]+" "+data["nom"])});$("#mailexp, #mailcopie, #mailliste",f).prop("disabled",!$("#mailenvoi",f).prop("checked"));$("a.icon-ok",f).on("click",function(){$.ajax({url:"ajax.php",method:"post",data:"action=utilisateur&modif=prefs&id="+id+"&"+$("form",f).serialize(),dataType:"json",el:"",fonction:function(el){location.reload(true)}})});$("input",f).on("keypress",function(e){if(e.which==13){e.preventDefault();$("a.icon-ok",f).click()}})}}})}function modif_utilisateur(){var question="";var compte=$(this).parent().siblings().first().text();compte=compte.length?"de <em>"+compte+"</em>":"d'identifiant <em>"+$(this).parent().siblings().first().next().text()+"</em>";var categorie=$(this).parent().parent().prevUntil(".categorie").last().data("val");switch(this.className.substring(5)){case"desactive":if(categorie=="Invités")question="Vous allez désactiver le compte invité "+compte+". Cela signifie que le compte ne sera pas supprimé mais sera non utilisable pour une connexion. Les associations éventuelles avec les matières seront conservées. Ce compte sera listé dans la partie inférieure du tableau.";else question="Vous allez désactiver le compte "+compte+". Cela signifie que le compte sera toujours visible pour les professeurs mais que l'utilisateur correspondant ne pourra plus se connecter. Les notes de colles éventuelles seront conservées. Les données associées au compte seront conservées. Les accès spécifiques éventuels pourront être rétablis en réactivant le compte. Ce compte sera listé dans la partie inférieure du tableau.";break;case"active":if(categorie=="Invités")question="Vous allez réactiver le compte invité "+compte+". La connexion sera à nouveau possible. Ce compte apparaîtra à nouveau dans la partie principale du tableau.";else question="Vous allez réactiver le compte "+compte+". Cela signifie que l'utilisateur correspondant pourra à nouveau se connecter. Il retrouvera son compte, ses notes de colles éventuelles, ses préférences, ses accès spécifiques éventuels, sans modification. Ce compte apparaîtra à nouveau dans la partie principale du tableau.";break;case"supprutilisateur":if(categorie=="Demandes")question="Vous allez supprimer la demande "+compte+". Cela signifie que cette demande ne conduira pas à une création de compte. Le demandeur ne sera pas prévenu de votre décision. Une fois réalisée, cette opération est définitive.";else if(categorie=="Invitations")question="Vous allez supprimer l'invitation "+compte+". Cela signifie que cette invitation ne sera plus valable et que si la personne invitée clique sur le lien reçu par courriel, une erreur apparaîtra devant elle. Cette invitation n'a pas de délai&nbsp;: il est inutile de supprimer l'invitation pour la refaire, à moins de s'être trompé d'adresse électronique. La personne invitée ne sera pas prévenue de votre décision. Une fois réalisée, cette opération est définitive.";else if(categorie=="Professeurs")question="Vous allez supprimer le compte professeur "+compte+". Cela signifie que toutes les préférences de ce compte seront perdues, ainsi que les éventuelles notes de colles. Les données des matières auxquelles il est associé sont indépendantes&nbsp;: elles ne seront pas supprimées. Une fois réalisée, cette opération est définitive.";else if(categorie=="Administatifs")question="Vous allez supprimer le compte administratif "+compte+". Cela signifie que toutes les préférences de ce compte seront perdues. Une fois réalisée, cette opération est définitive.";
+else if(categorie=="Colleurs")question="Vous allez supprimer le compte colleur "+compte+". Cela signifie que toutes les préférences de ce compte seront perdues, ainsi que les éventuelles notes de colles. Une fois réalisée, cette opération est définitive.";else if(categorie=="Élèves")question="Vous allez supprimer le compte élève "+compte+". Cela signifie que toutes les données correspondant à ce compte seront perdues. Les groupes où il apparaît seront modifiés, les notes de colles éventuelles seront supprimées. Une fois réalisée, cette opération est définitive.";else if(categorie=="Invités")question="Vous allez supprimer le compte invité "+compte+". Cela signifie que la connexion par ce compte ne sera plus possible. Une fois réalisée, cette opération est définitive.";else question="Vous allez supprimer le compte "+compte+" déjà désactivé. Cela signifie que toutes les données correspondant à ce compte seront perdues définitivement. Les groupes où il apparaît seront modifiés, les notes de colles éventuelles seront supprimées. Dans le cas d'un compte professeur, les données des matières associées ne seront pas supprimées. Une fois réalisée, cette opération est définitive.";break;case"validutilisateur":question="Vous allez valider la demande "+compte+". Son compte sera immédiatement actif et un courriel va immédiatement être envoyé pour le/la prévenir. Il sera automatiquement associé à toutes les matières, pensez à aller supprimer les matières qui ne le concernent pas sur la page de gestion des associations utilisateurs-matières."}confirmation(question,this,function(el){$.ajax({url:"ajax.php",method:"post",data:{action:"utilisateur",modif:el.className.substring(5),id:$(el).parent().parent().data("id")},dataType:"json",el:"",fonction:function(){location.reload(true)}})})}function mail_utilisateurs(){var cases=$(this).parent().parent().nextUntil(".categorie").find(":checked");if(cases.length==0){affiche("<p>Aucune case n'est cochée, aucune action ne peut être réalisée.</p>","nok");return}var ids=cases.map(function(){return $(this).parent().parent().data("id")}).get().join(",");var tds=cases.parent().prev().prev();var val=tds.find(".icon-ok").length>=tds.find(".icon-nok").length?0:1;$.ajax({url:"ajax.php",method:"post",data:{action:"utilisateurs",modif:"mailenvoi",ids:ids,val:val},dataType:"json",el:"",fonction:function(){location.reload(true)}})}function modif_utilisateurs(){var cases=$(this).parent().parent().nextUntil(".categorie").find(":checked");if(cases.length==0){affiche("<p>Aucune case n'est cochée, aucune action ne peut être réalisée.</p>","nok");return}var ids=cases.map(function(){return $(this).parent().parent().data("id")}).get().join(",");var comptes=cases.map(function(){var compte=$(this).parent().siblings().first().text();return compte.length?"<em>"+compte+"</em>":"<em>"+$(this).parent().siblings().first().next().text()+"</em>"}).get().join(", ");var pos=comptes.lastIndexOf(",");if(pos>0)comptes=comptes.substring(0,pos)+" et"+comptes.substring(pos+1);var question="";var categorie=$(this).parent().parent().prev().children().text().split(" ")[0];switch(this.className.substring(5)){case"desactive":if(categorie=="Invités")question="Vous allez désactiver les comptes invités "+comptes+". Cela signifie que ces compte ne seront pas supprimés mais seront non utilisables pour une connexion. Les associations éventuelles avec les matières seront conservées. Ces comptes seront listés dans la partie inférieure du tableau.";else question="Vous allez désactiver les comptes de "+comptes+". Cela signifie que ces comptes seront toujours visibles pour les professeurs mais que les utilisateurs correspondant ne pourront plus se connecter. Les notes de colles éventuelles seront conservées. Les données associées aux comptes seront conservées. Les accès spécifiques éventuels pourront être rétablis en réactivant les comptes. Ces comptes seront listés dans la partie inférieure du tableau.";break;case"active":if(categorie=="Invités")question="Vous allez réactiver les comptes invité "+comptes+". La connexion sera à nouveau possible. Ces comptes apparaîtront à nouveau dans la partie principale du tableau.";else question="Vous allez réactiver les comptes de "+comptes+". Cela signifie que les utilisateurs correspondant pourront à nouveau se connecter. Ils retrouveront leur compte, leurs notes de colles éventuelles, leurs préférences, leurs accès spécifiques éventuels, sans modification. Ces comptes apparaîtront à nouveau dans la partie principale du tableau.";break;case"supprutilisateur":if(categorie=="Demandes")question="Vous allez supprimer les demandes de "+comptes+". Cela signifie que ces demandes ne conduiront pas à des créations de compte. Les demandeurs ne seront pas prévenus de votre décision. Une fois réalisée, cette opération est définitive.";else if(categorie=="Invitations")question="Vous allez supprimer les invitations de "+comptes+". Cela signifie que ces invitations ne seront plus valables et que si les personnes invitées cliquent sur le lien reçu par courriel, une erreur apparaîtra devant elles. Ces invitations n'ont pas de délai&nbsp;: il est inutile de supprimer une invitation pour la refaire, à moins de s'être trompé d'adresse électronique. Les personnes invitées ne seront pas prévenues de votre décision. Une fois réalisée, cette opération est définitive.";else if(categorie=="Professeurs")question="Vous allez supprimer les comptes professeurs de "+comptes+". Cela signifie que toutes les préférences de ces comptes seront perdues, ainsi que les éventuelles notes de colles. Les données des matières auxquelles ils sont associés sont indépendantes&nbsp;: elles ne seront pas supprimées. Une fois réalisée, cette opération est définitive.";else if(categorie=="Administratifs")question="Vous allez supprimer les comptes administratifs de "+comptes+". Cela signifie que toutes les préférences de ces comptes seront perdues. Une fois réalisée, cette opération est définitive.";else if(categorie=="Colleurs")question="Vous allez supprimer les comptes colleurs de "+comptes+". Cela signifie que toutes les préférences de ces comptes seront perdues, ainsi que les éventuelles notes de colles. Une fois réalisée, cette opération est définitive.";else if(categorie=="Élèves")question="Vous allez supprimer les comptes élèves de "+comptes+". Cela signifie que toutes les données correspondant à ces comptes seront perdues. Les groupes où ils apparaîssent seront modifiés, les notes de colles éventuelles seront supprimées. Une fois réalisée, cette opération est définitive.";else if(categorie=="Invités")question="Vous allez supprimer les comptes invités de "+comptes+". Cela signifie que la connexion par ces comptes ne sera plus possible. Une fois réalisée, cette opération est définitive.";else question="Vous allez supprimer les comptes de "+comptes+" déjà désactivés. Cela signifie que toutes les données correspondant à ces comptes seront perdues définitivement. Les groupes où ils apparaîssent seront modifiés, les notes de colles éventuelles seront supprimées. Dans le cas des comptes professeurs, les données des matières associées ne seront pas supprimées. Une fois réalisée, cette opération est définitive.";break;case"validutilisateur":question="Vous allez valider les demandes de "+comptes+". Leurs comptes seront immédiatement actifs et un courriel va immédiatement leur être envoyé pour les prévenir. Ils seront automatiquement associés à toutes les matières, pensez à aller supprimer les matières qui ne les concernent pas sur la page de gestion des associations utilisateurs-matières."}confirmation(question,this,function(el){$.ajax({url:"ajax.php",method:"post",data:{action:"utilisateurs",modif:el.className.substring(5),ids:ids},dataType:"json",el:"",fonction:function(){location.reload(true)}})})}}function init_utilisateurs_matieres(){$("tbody tr:not(.categorie) td:not(:first-child,:last-child)").each(function(){var valeurs=this.textContent.split("|");this.innerHTML=valeurs[1]==1?'<a class="icon-ok" data-id="'+valeurs[0]+'" title="Supprimer l\'association à la matière"></a>':'<a class="icon-nok" data-id="'+valeurs[0]+'" title="Établir l\'association à la matière"></a>'});$("tbody tr:not(.categorie) a").on("click",association_um);$(".categorie [data-id]").on("click",association_ums).hide(0);$(".icon-cocher").on("click",cocher_utilisateurs).on("click",majicones);$('input[type="checkbox"]').on("click",majicones).on("change",function(){$(this).parent().parent().removeClass("sel");if(this.checked)$(this).parent().parent().addClass("sel")});$("td:first-child").on("click",function(){$(this).parent().find("input").click()});function majicones(){var tr=$(this).parent().parent();if(!tr.hasClass("categorie"))tr=tr.prevAll(".categorie").first();var cases=tr.nextUntil(".categorie").find(":checked");if(cases.length==0){$("[data-id]",tr).hide(0);return}$("[data-id]",tr).each(function(){var avant=$(this).hasClass("icon-ok");var apres=cases.parent().prevAll().find('.icon-ok[data-id="'+this.getAttribute("data-id")+'"]').length<cases.length/2;if(avant!=apres)$(this).toggleClass("icon-ok icon-nok").attr("title",(apres?"Établir":"Supprimer")+" l'association à la matière de tous les cochés")}).show(0)}function association_um(){var val=$(this).hasClass("icon-ok");$.ajax({url:"ajax.php",method:"post",data:{action:"utilisateur-matiere",id:$(this).parent().parent().data("id"),matiere:$(this).data("id"),val:1-val},dataType:"json",el:$(this),fonction:function(el){el.toggleClass("icon-ok icon-nok").attr("title",(val?"Établir":"Supprimer")+" l'association à la matière")}})}function association_ums(){var cases=$(this).parent().parent().nextUntil(".categorie").find(":checked");if(cases.length==0){affiche("<p>Aucune case n'est cochée, aucune action ne peut être réalisée.</p>","nok");return}var ids=cases.map(function(){return $(this).parent().parent().data("id")}).get().join(",");var comptes=cases.map(function(){return $(this).parent().siblings().first().text().split("(")[0].trim()}).get().join(", ");var pos=comptes.lastIndexOf(",");if(pos>0)comptes=comptes.substring(0,pos)+" et"+comptes.substring(pos+1);var val=$(this).hasClass("icon-ok");var mid=this.getAttribute("data-id");var question=val?"Vous allez établir l'association à la matière "+$("#m"+mid).text()+" pour les comptes de "+comptes+". Cela signifie que ces utilisateurs auront accès aux ressources liées à cette matière, en fonction de l'autorisation que vous avez fixée pour ces ressources.":"Vous allez supprimer l'association à la matière "+$("#m"+mid).text()+" pour les comptes de "+comptes+". Cela signifie que ces utilisateurs n'auront plus accès aux ressources liées à cette matière. Si des notes de colles ont été saisies, elles seront automatiquement et définitivement supprimées de la base.";confirmation(question,this,function(el){$.ajax({url:"ajax.php",method:"post",data:{action:"utilisateurs-matieres",ids:ids,matiere:mid,val:val|0},dataType:"json",el:"",fonction:function(el){location.reload(true)}})})}}function init_utilisateurs_groupes(){$('article input[type="checkbox"]').on("change",function(){$.ajax({url:"ajax.php",method:"post",data:{action:"groupes",champ:this.id.substr(0,5),id:this.id.substr(5),val:this.checked|0},dataType:"json",el:"",fonction:function(el){return true}})});$(".usergrp span").append('&nbsp;<a class="icon-edite" title="Éditer les utilisateurs de ce groupe"></a>').on("click",utilisateursgroupe)}function utilisateursgroupe(){popup($("#form-utilisateurs").html(),true);var f=$("#fenetre");var span=$(this);article=span.parent().parent();$("table",f).attr("id","utilisateurs");$("h3",f).append($(".editable",article).text()||$("input:first",article).val());$(".icon-deplie",f).on("click",plie);$(".icon-cocher",f).on("click",cocher_utilisateurs);$("tr:not(.categorie)",f).on("click",function(e){if(!$(e.target).is("input"))$(this).find("input").click()});$("input",f).on("change",function(){$(this).parent().parent().removeClass("sel");if(this.checked)$(this).parent().parent().addClass("sel")});var ids=span.data("uids");$("#u"+ids.replace(/,/g,",#u"),f).prop("checked",true).change();$(".icon-ok",f).on("click",function(){var ids=$("input:checked",f).map(function(){return this.id.replace("u","")}).get().join(",");var noms=$("input:checked",f).parent().prev().map(function(){return this.textContent.split("(")[0].trim()}).get().join(", ")||"[Personne]";if(article.is("div")){$("#uids",article).val(ids);span.data("uids",ids);span.html(noms+'&nbsp;<a class="icon-edite" title="Éditer les utilisateurs de ce groupe"></a>');$("#fenetre, #fenetre_fond").remove()}else $.ajax({url:"ajax.php",method:"post",data:{action:"groupes",champ:"utilisateurs",id:article.data("id").split("|")[1],uids:ids},dataType:"json",el:span,fonction:function(el){el.data("uids",ids);el.html(noms+'&nbsp;<a class="icon-edite" title="Éditer les utilisateurs de ce groupe"></a>');$("#fenetre, #fenetre_fond").remove()}})})}function suppressionmultiple(){var prop=$(this).data("id").split("|");var contexte=$(this).parent().find("h3").text();var item="";switch(prop[2]){case"infos":item="toutes les informations de la page <em>"+contexte+"</em>";break;case"colles":item="tous les programmes de colles de la matière <em>"+contexte+"</em>";break;case"cdt":item="tout le contenu du cahier de texte de la matière <em>"+contexte+"</em>";break;case"docs":item="tous les répertoires et documents de la matière <em>"+contexte+"</em>";break;case"notes":item="toutes les notes de la matière <em>"+contexte+"</em>";break}confirmation("Vous allez supprimer XXX.<br>Cette opération n'est pas annulable.".replace("XXX",item),this,function(el){$.ajax({url:"ajax.php",method:"post",data:"action="+prop[0]+"&id="+prop[1]+"&supprime_"+prop[2]+"=1",dataType:"json",el:$(el),fonction:function(el){el.remove()}})})}function destinatairesmail(){popup($("#form-destinataires").html(),true);var f=$("#fenetre");$("table",f).attr("id","utilisateurs");$(".icon-deplie",f).on("click",plie);$("tr:not(.gr) input.dest",f).attr("id",function(){return"u"+this.value});$("tr:not(.categorie) td:nth-child(-n+2)",f).on("click",function(e){if(!$(e.target).is("input"))$(this).parent().find("input:first").click()});$("input",f).on("change",function(){var tr=$(this).parent().parent().removeClass("sel");if(this.checked)tr.find("input:not(."+this.className+")").prop("checked",false);if(tr.find("input:checked").length)tr.addClass("sel")});var ids=$('[name="id-copie"]').val();$("#u"+ids.replace(/,/g,",#u")).prop("checked",true).change();ids=$('[name="id-bcc"]').val();$("#u"+ids.replace(/,/g,",#u")).parent().next().children().prop("checked",true).change();$(".categorie a",f).on("click keyup",function(){var classe=this.className.split(" ")[1];var etat=this.className.split(" ")[0]=="icon-cocher";var titre=this.title;$(this).parent().parent().nextUntil(".categorie").find("."+classe+":not(:disabled)").prop("checked",etat).change();this.className=(etat?"icon-decocher ":"icon-cocher ")+classe;this.title=this.title.replace(etat?"Cocher":"Décocher",etat?"Décocher":"Cocher");var classe2=classe=="dest"?"bcc":"dest";$(this).parent().parent().find(".icon-decocher."+classe2).each(function(){this.className="icon-cocher "+classe2;this.title="C"+this.title.substr(3)})});$(".gr input",f).on("click",function(){var ids=this.value;if(this.className=="dest")$("#u"+ids.replace(/,/g,",#u")).prop("checked",this.checked).change();else $("#u"+ids.replace(/,/g,",#u")).parent().next().children().prop("checked",this.checked).change()});$(".icon-ok",f).on("click",function(){$('[name="id-copie"]').val($("tr:not(.gr) .dest:checked",f).map(function(){return this.value}).get().join(","));$('[name="id-bcc"]').val($("tr:not(.gr) .bcc:checked",f).map(function(){return this.value}).get().join(","));$("#maildest").text($("tr:not(.gr) .dest:checked",f).parent().prev().map(function(){return this.textContent}).get().concat($("tr:not(.gr) .bcc:checked",f).parent().prev().prev().map(function(){return this.textContent+" (CC)"}).get()).join(", ")||"[Personne]");$("#fenetre, #fenetre_fond").remove()})}function envoimail(){if($(".maildest").children("span").text()=="[Personne]")affiche("Il faut au moins un destinataire pour envoyer le courriel.","nok");else if(!$('[name="sujet"]').val().length)affiche("Il faut un sujet non vide pour envoyer le courriel.","nok");else $.ajax({url:"ajax.php",method:"post",data:$("#mail").serialize(),dataType:"json",el:"",fonction:function(el){location.reload(true)}})}function relevenotes(){confirmation("<p>Vous allez réaliser une relève des notes de colles. Cela consiste à marquer comme relevées toutes les heures déclarées jusqu'à maintenant et non encore relevées. Vous pourrez alors télécharger le nouveau relevé au sein du tableau en bas de page.</p><p>Cette opération n'est pas annulable.</p><p>Une fois que vous aurez réalisé ce relevé, les professeurs et colleurs ne pourront pas modifier le nombre d'élèves et la durée correspondant aux colles relevées.</p>",this,function(el){$.ajax({url:"ajax.php",method:"post",data:"action=releve-notes",dataType:"json",el:"",fonction:function(el){location.reload(true)}})})}$(document).ajaxSend(function(ev,xhr,settings){$("#load").show(200);if(settings.data.append)settings.data.append("csrf-token",$("body").attr("data-csrf-token"));else settings.data="csrf-token="+$("body").attr("data-csrf-token")+"&"+settings.data}).ajaxStop(function(){$("#load").hide(200)}).ajaxSuccess(function(ev,xhr,settings){var data=xhr.responseJSON;switch(data["etat"]){case"ok":affiche(data["message"],"ok");settings.fonction(settings.el);break;case"nok":affiche(data["message"],"nok");break;case"login":case"mdp":reconnect(settings,data["etat"]=="mdp");break;case"recupok":settings.afficheform(data)}});$(function(){$("a.formulaire, .modifevnmt").on("click",formulaire);$("a.icon-aide").on("click",function(){popup($("#aide-"+($(this).parent().data("id")||"page").split("|")[0]).html(),false)});$("a.icon-ok").on("click",valide);$("a.icon-cache,a.icon-montre,a.icon-monte,a.icon-descend,a.icon-supprime,a.icon-lock,a.icon-ajoutecolle").on("click",function(){window[this.className.substring(5)]($(this))});$("#log").hide().on("click",function(){$(this).hide(300)});$(".editable").editinplace();$("a.icon-deconnexion").on("click",function(e){$.ajax({url:"ajax.php",method:"post",data:{action:"deconnexion"},dataType:"json",el:"",fonction:function(el){location.reload(true)}})});$(".titrecdt").editinplacecdt();$(".cdt-raccourcis").init_cdt_raccourcis();$(".icon-mailenvoi").on("click",envoimail);$("#maildest, #maildest + .icon-edite").on("click",destinatairesmail);$(".categorie th:first-child").prepend($('<span class="icon-deplie" title="Déplier/Replier cette catégorie"></span>').on("click",plie));$("article select[multiple]").each(selmult);$(".supprmultiple").on("click",suppressionmultiple);$("#utilisateurs").each(init_utilisateurs);$("#utilisateurs-matieres").each(init_utilisateurs_matieres);$(".usergrp").first().each(init_utilisateurs_groupes);$("#planning select").change(function(){$(this).parent().prev().children("input").prop("checked",this.value==0)});$("#planning input").change(function(){$(this).parent().next().children("select").val(0)});$("#relevenotes").on("click",relevenotes);$(".icon-menu").on("click",function(){$("#colonne,nav").toggleClass("visible")})});
diff -urN cahier-de-prepa6.2.0/js/edition.min.js cahier-de-prepa8.0.1/js/edition.min.js
--- cahier-de-prepa6.2.0/js/edition.min.js	2016-08-30 18:09:47.802139046 +0200
+++ cahier-de-prepa8.0.1/js/edition.min.js	1970-01-01 01:00:00.000000000 +0100
@@ -1,23 +0,0 @@
-/*//////////////////////////////////////////////////////////////////////////////
-Éléments JavaScript pour l'administration de Cahier de Prépa
-
-* Les éléments de classe "editable" peuvent être transformés en mode édition par
-la fonction transforme, automatiquement exécutée. L'attribut data-id est alors
-nécessaire, et doit être de la forme table|champ|id.
-* Les éléments de classe "edithtml" sont considérés comme contenant des
-informations présentées en html : la fonction textareahtml crée alors un élément
-de type textarea, un élément de type div éditable (contenteditable=true), et
-ajoute des boutons facilitant les modifications. La textarea et la div éditable
-sont alternativement visibles, à la demande.
-* Les éléments possédant un attribut data-placeholder ont droit, par la fonction
-placeholder, à une valeur affichée lorsque l'élément (en mode édition) est vide.
-* Les liens de classe icon-aide lancent automatiquement une aide par la fonction
-popup. Le contenu est récupéré dans les div de classe aide-xxx où xxx est la
-valeur de l'attribut data-id du lien (ces div sont automatiquement non affichées
-en css).
-//////////////////////////////////////////////////////////////////////////////*////////////////
-// Affichage //
-///////////////
-// Notification de résultat de requête AJAX
-function affiche(e,t){$("#log").removeClass().addClass(t).html(e).append('<span class="icon-ferme"></span>').show().off("click").on("click",function(){window.clearTimeout(n),$(this).hide(500)});var n=window.setTimeout(function(){$("#log").hide(500)},1e4)}function afficher_login(e){$("#fenetre,#fenetre_fond").remove(),popup('<a class="icon-ok" title="Valider"></a><h3>Connexion nécessaire</h3>  <p>Vous avez été automatiquement déconnecté. Vous devez vous connecter à nouveau pour valider vos modifications.</p>  <form>  <p class="ligne"><label for="login">Identifiant&nbsp;: </label><input type="text" name="login" id="login"></p>  <p class="ligne"><label for="motdepasse">Mot de passe&nbsp;: </label><input type="password" name="motdepasse" id="motdepasse"></p>  </form>',!0),$("#login").focus(),$("#fenetre a.icon-ok").on("click",function(){$.ajax({url:"ajax.php",method:"post",data:$("#fenetre form").serialize()+"&"+e.data,dataType:"json",el:"",fonction:function(){return!0}}).done(function(t){t["etat"]=="login_nok"?$("#fenetre > p").html(t.message).addClass("warning"):($("#fenetre,#fenetre_fond").remove(),t["etat"]=="ok"&&e.fonction(e.el))})}),$("#fenetre a.icon-ferme").on("click",function(){affiche("Modification non effectuée, connexion nécessaire","nok")}),$("#fenetre input").on("keypress",function(e){e.which==13&&$("#fenetre a.icon-ok").click()})}function popup(e,t){$("#fenetre,#fenetre_fond").remove();var n=$('<div id="fenetre"></div>').appendTo("body").html(e).focus();t?$('<div id="fenetre_fond"></div>').appendTo("body").click(function(){$("#fenetre,#fenetre_fond").remove()}):$('<a class="icon-epingle" title="Épingler à la page"></a>').prependTo(n).on("click",function(){$("#fenetre_fond").remove(),$(this).remove(),$("section").children("div,article").first().before(n.removeAttr("id"))}),$('<a class="icon-ferme" title="Fermer"></a>').prependTo(n).on("click",function(){n.remove(),$("#fenetre_fond").remove()})}function transforme(){var e=$(this).parent().addClass("avecform");e.is("div")?e.html('<form><textarea name="val" rows="'+(e.attr("data-original").split(/\r\n|\r|\n/).length+3)+'"></textarea></form>'):e.html('<form class="edition" onsubmit="$(this).children(\'a.icon-ok\').click(); return false;"><input type="text" name="val" value=""></form>');var t=e.find('[name="val"]').val(e.attr("data-original"));e.attr("data-placeholder")&&(t.attr("data-placeholder",e.attr("data-placeholder")),e.hasClass("edithtml")&&(t.textareahtml(),t.next().placeholder()),t.placeholder()),$('<a class="icon-ok" title="Valider"></a>').appendTo(e.children()).on("click",function(){var n=e.attr("data-id").split("|");e.hasClass("edithtml")&&t.val(nettoie(t.is(":visible")?t.val():t.next().html())),$.ajax({url:"ajax.php",method:"post",data:{table:n[0],champ:n[1],id:n[2],val:t.val()},dataType:"json",el:e,fonction:function(e){var t=e.find('[name="val"]').val();e.removeClass("avecform").html(t).attr("data-original",t),$('<a class="icon-edite" title="Modifier"></a>').appendTo(e).on("click",transforme)}})}),$('<a class="icon-annule" title="Annuler"></a>').appendTo(e.children()).on("click",function(){e.removeClass("avecform").html(e.attr("data-original")),$('<a class="icon-edite" title="Modifier"></a>').appendTo(e).on("click",transforme)}),t.focus().val(e.hasClass("edithtml")?nettoie(t.val()):t.val())}function transformecdt(){var e=$(this).parent();e.children(".icon-edite").remove();var t=$('<form class="titrecdt"></form>').insertBefore(e.parent().children("div")).html($("#form-cdt").html()),n=JSON.parse(e.attr("data-donnees"));for(var r in n)t.find('[name="'+r+'"]').val(n[r]);t.init_cdt_boutons(),t.find('input,[name="demigroupe"]').on("change keyup",function(){var n=new Date(t.find('[name="jour"]').val().replace(/(.{2})\/(.{2})\/(.{4})/,function(e,t,n,r){return r+"-"+n+"-"+t})),r=t.find('[name="demigroupe"]').val()==1?" (en demi-groupe)":"";switch(parseInt(seances[t.find('[name="tid"]').val()])){case 0:var i=jours[n.getDay()]+" "+t.find('[name="jour"]').val()+" à "+t.find('[name="h_debut"]').val()+" : "+t.find('[name="tid"] option:selected').text()+r;break;case 1:var i=jours[n.getDay()]+" "+t.find('[name="jour"]').val()+" de "+t.find('[name="h_debut"]').val()+" à "+t.find('[name="h_fin"]').val()+" : "+t.find('[name="tid"] option:selected').text()+r;break;case 2:var i=jours[n.getDay()]+" "+t.find('[name="jour"]').val()+" : "+t.find('[name="tid"] option:selected').text()+" pour le "+t.find('[name="pour"]').val()+r;break;case 3:var i=jours[n.getDay()]+" "+t.find('[name="jour"]').val()+" : "+t.find('[name="tid"] option:selected').text()+r;break;case 4:var i=jours[n.getDay()]+" "+t.find('[name="jour"]').val();break;case 5:var i="[Entrée hebdomadaire]"}e.children("span").text(i)}),$('<a class="icon-ok" title="Valider"></a>').appendTo(e).on("click",function(){var n=e.parent().attr("data-id").split("|");$.ajax({url:"ajax.php",method:"post",data:"table=cdt&id="+n[1]+"&"+t.serialize(),dataType:"json",el:e,fonction:function(e){var t=e.siblings("form");e.attr("data-original",e.children("span").text()).attr("data-donnees",'{"tid":'+t.find('[name="tid"]').val()+',"jour":"'+t.find('[name="jour"]').val()+'","h_debut":"'+t.find('[name="h_debut"]').val()+'","h_fin":"'+t.find('[name="h_fin"]').val()+'","pour":"'+t.find('[name="pour"]').val()+'","demigroupe":'+t.find('[name="demigroupe"]').val()+"}"),t.remove(),e.children("a").remove(),$('<a class="icon-edite" title="Modifier"></a>').appendTo(e).on("click",transformecdt)}}).done(function(e){e["etat"]=="ok"&&e["reload"]=="oui"&&location.reload(!0)})}),$('<a class="icon-annule" title="Annuler"></a>').appendTo(e).on("click",function(){t.remove(),e.children("span").html(e.attr("data-original")),e.children("a").remove(),$('<a class="icon-edite" title="Modifier"></a>').appendTo(e).on("click",transformecdt)})}function nettoie(e){if(e.indexOf("cdptmp")>0){var t=$("<div>"+e+"</div>");t.find(".cdptmp").contents().unwrap(),e=t.html(),e.indexOf("cdptmp")>0&&(e=e.replace(/<span class="cdptmp"><\/span>/g,""))}return e.replace(/(<\/?[A-Z]+)([^>]*>)/g,function(e,t,n){return t.toLowerCase()+n}).replace(/[\r\n ]+/g," ").replace(/(<br>)+[ ]?<\/(p|div|li|h)/g,function(e,t,n){return"</"+n}).replace(/<br>/g,"<br>\n").replace(/<(p|div|li|h)/g,function(e){return"\n"+e}).replace(/<\/(p|div|li|h.)>/g,function(e){return e+"\n"}).replace(/<\/?(ul|ol)[^>]*>/g,function(e){return"\n"+e+"\n"}).replace(/^(?!(<p|<div|<ul|<ol|<li|<h))(.+)<br>$/gm,function(e,t,n){return"<p>"+n+"</p>"}).replace(/^(?!(<(p|div|ul|ol|li)))[ ]?(.+)[ ]?$/gm,function(e,t,n,r){return r.match(/.*(p|div|ul|ol|li|h.)>$/)?r:"<p>"+r+"</p>"}).replace(/^[ ]?(<\/?(br|p|div|h.)>){0,2}[ ]?(<\/(p|div|h.)>)?[ ]?$/gm,"").replace(/^\n/gm,"").replace(/<li/g,"  <li")}function insert(e,t,n,r){var i=e.parent().siblings("textarea,[contenteditable]").filter(":visible")[0];i.hasAttribute("data-selection")||marqueselection(e);var s=r===undefined?t+"Í"+i.getAttribute("data-selection")+"Ì"+n:t+"Í"+r+"Ì"+n,o=nettoie(i.getAttribute("data-contenu").replace(/Í.*Ì/,s));i.tagName=="TEXTAREA"?i.value=o.replace(/[ÍÌ]/g,""):i.innerHTML=o.replace(/[ÍÌ]/g,""),marqueselection(e,!0),$(i).change(),i.tagName=="TEXTAREA"&&i.selectionStart!==undefined?(i.selectionStart=o.indexOf("Í"),i.selectionEnd=o.indexOf("Ì")-1,i.focus()):document.selection?(i.tagName!="TEXTAREA"&&(o=o.replace(/(<([^>]+)>)[\n]*/g,"")),range=document.body.createTextRange(),range.moveToElementText(i),range.collapse(!0),range.moveEnd("character",o.indexOf("Ì")-1),range.moveStart("character",o.indexOf("Í")),range.select()):window.getSelection&&(i.innerHTML=o.replace("Í",'<span class="cdptmp">').replace("Ì","</span>")+"<br>",selection=window.getSelection(),range=document.createRange(),range.selectNodeContents($(i).find(".cdptmp")[0]),selection.removeAllRanges(),selection.addRange(range),i.focus())}function marqueselection(e,t){var n=e.parent().siblings("textarea,[contenteditable]").filter(":visible")[0];if(t)return n.removeAttribute("data-selection"),n.removeAttribute("data-contenu"),!0;var r=n.tagName=="TEXTAREA"?n.value:n.innerHTML,i="";if(n.tagName=="TEXTAREA"&&n.selectionStart!==undefined)n.focus(),i=n.value.substring(n.selectionStart,n.selectionEnd),n.value=n.value.substr(0,n.selectionStart)+"Í"+i+"Ì"+n.value.substring(n.selectionEnd);else if(window.getSelection){var s=window.getSelection().getRangeAt(0);if(n==s.commonAncestorContainer||$.contains(n,s.commonAncestorContainer)){var i=window.getSelection().toString();s.deleteContents(),s.insertNode(document.createTextNode("Í"+i+"Ì"))}}else{var s=document.selection.createRange();if(n==s.parentElement()||$.contains(n,s.parentElement())){var i=document.selection.createRange().text;document.selection.createRange().text="Í"+i+"Ì"}}if(n.tagName=="TEXTAREA"){var o=n.value;n.value=r}else{var o=n.innerHTML;$(n).html(r)}return o.indexOf("Ì")<0&&(o+="ÍÌ"),n.setAttribute("data-selection",i),n.setAttribute("data-contenu",o),i}function insertion_titres(e){popup('<a class="icon-ok" title="Valider"></a><h3>Insertion d\'un titre</h3>  <p>Choisissez le type du titre ci-dessous. Vous pouvez éventuellement modifier le texte (ou pourrez le faire ultérieurement). Il est conseillé d\'utiliser des titres de niveau 2 pour les titres dans les programmes de colle.</p>  <input type="radio" name="titre" id="t3" value="3" checked><h3><label for="t3">Titre de niveau 1 (pour les I,II...)</label></h3><br>  <input type="radio" name="titre" id="t4" value="4"><h4><label for="t4">Titre de niveau 2 (pour les 1,2...)</label></h4><br>  <input type="radio" name="titre" id="t5" value="5"><h5><label for="t5">Titre de niveau 3 (pour les a,b...)</label></h5><br>  <input type="radio" name="titre" id="t6" value="6"><h6><label for="t6">Titre de niveau 4</label></h6><br>  <p class="ligne"><label for="texte">Texte&nbsp;: </label><input type="text" id="texte" value="'+marqueselection(e)+'" size="80"></p>  <hr><h3>Aperçu</h3><div id="apercu"></div>',!0),$("#fenetre input").on("click keyup",function(){var e="h"+$("[name='titre']:checked").val();$("#apercu").html("<"+e+">"+($("#texte").val().length?$("#texte").val():"Texte du titre")+"</"+e+">")}).first().keyup(),$("#texte").on("keypress",function(e){e.which==13&&$("#fenetre a.icon-ok").click()}).focus(),$("#fenetre a.icon-ok").on("click",function(){var t="h"+$("[name='titre']:checked").val();insert(e,"<"+t+">","</"+t+">",$("#texte").val()),$("#fenetre,#fenetre_fond").remove()}),$("#fenetre a.icon-ferme,#fenetre_fond").on("click",function(){marqueselection(e,!0)})}function insertion_omega(e){popup("<h3>Insertion d'une lettre grecque</h3>  <p>Cliquez sur la lettre à insérer&nbsp;:</p>  <button>&alpha;</button> <button>&beta;</button> <button>&gamma;</button> <button>&Delta;</button> <button>&delta;</button> <button>&epsilon;</button> <button>&eta;</button> <button>&Theta;</button> <button>&theta;</button> <button>&Lambda;</button> <button>&lambda;</button> <button>&mu;</button> <button>&nu;</button> <button>&xi;</button> <button>&Pi;</button> <button>&pi;</button> <button>&rho;</button> <button>&Sigma;</button> <button>&sigma;</button> <button>&tau;</button> <button>&upsilon;</button> <button>&Phi;</button> <button>&phi;</button> <button>&Psi;</button> <button>&psi;</button> <button>&Omega;</button> <button>&omega;</button>",!0),$("#fenetre button").on("click",function(){insert(e,"","",$(this).text()),$("#fenetre,#fenetre_fond").remove()})}function insertion_sigma(e){popup("<h3>Insertion d'un symbole mathématique</h3>  <p>Cliquez sur le symbole à insérer&nbsp;:</p>  <button>&forall;</button> <button>&exist;</button> <button>&part;</button> <button>&nabla;</button> <button>&prod;</button> <button>&sum;</button> <button>&plusmn;</button> <button>&radic;</button> <button>&infin;</button> <button>&int;</button> <button>&prop;</button> <button>&sim;</button> <button>&cong;</button> <button>&asymp;</button> <button>&ne;</button> <button>&equiv;</button> <button>&le;</button> <button>&ge;</button> <button>&sub;</button> <button>&sup;</button> <button>&nsub;</button> <button>&sube;</button> <button>&supe;</button> <button>&isin;</button> <button>&notin;</button> <button>&ni;</button> <button>&oplus;</button> <button>&otimes;</button> <button>&sdot;</button> <button>&and;</button> <button>&or;</button> <button>&cap;</button> <button>&cup;</button> <button>&real;</button> <button>&image;</button> <button>&empty;</button> <button>&deg;</button> <button>&prime;</button> <button>&micro;</button> <button>&larr;</button> <button>&uarr;</button> <button>&rarr;</button> <button>&darr;</button> <button>&harr;</button> <button>&lArr;</button> <button>&uArr;</button> <button>&rArr;</button> <button>&dArr;</button> <button>&hArr;</button>",!0),$("#fenetre button").on("click",function(){insert(e,"","",$(this).text()),$("#fenetre,#fenetre_fond").remove()})}function insertion_ol(e){popup('<a class="icon-ok" title="Valider"></a><h3>Insertion d\'une liste numérotée</h3>  <p>Choisissez le type de numérotation et la valeur de départ de la liste ci-dessous. Vous pouvez éventuellement modifier les différents éléments en les écrivant ligne par ligne. Vous pourrez ajouter un élément ultérieurement en l\'encadrant par les balises &lt;li&gt; et &lt;/li&gt;.</p>  <p class="ligne"><label for="t1">Numérotation numérique (1, 2, 3...)</label><input type="radio" name="type" id="t1" value="1" checked></p>  <p class="ligne"><label for="t2">Numérotation alphabétique majuscule (A, B, C...)</label><input type="radio" name="type" id="t2" value="A"></p>  <p class="ligne"><label for="t3">Numérotation alphabétique minuscule (a, b, c...)</label><input type="radio" name="type" id="t3" value="a"></p>  <p class="ligne"><label for="t4">Numérotation romaine majuscule (I, II, III...)</label><input type="radio" name="type" id="t4" value="I"></p>  <p class="ligne"><label for="t5">Numérotation romaine minuscule (i, ii, iii...)</label><input type="radio" name="type" id="t5" value="i"></p>  <p class="ligne"><label for="debut">Valeur de début (numérique)</label><input type="text" id="debut" value="1"></p>  <p class="ligne"><label for="lignes">Textes (chaque ligne correspond à un élément de la liste)&nbsp;: </label></p>  <textarea id="lignes" rows="5">'+marqueselection(e)+'</textarea>  <hr><h3>Aperçu</h3><div id="apercu"></div>',!0),$("#fenetre :input").on("click keyup",function(){var e=$("#debut").val();e=e.length&&e>1?' start="'+e+'"':"",$("#apercu").html('<ol type="'+$("[name='type']:checked").val()+'"'+e+"><li>"+($("#lignes").val().length?$("#lignes").val().trim("\n").replace(/\n/g,"</li><li>"):"Première ligne</li><li>Deuxième ligne</li><li>...")+"</li></ol>")}).first().keyup(),$("#lignes").focus(),$("#fenetre a.icon-ok").on("click",function(){var t=$("#debut").val();t=t.length&&t>1?' start="'+t+'"':"";var n=$("#lignes").val().trim("\n"),r=n.lastIndexOf("\n");if(r>0){var i=n.substring(r+1);n=n.substring(0,r)}else var i="";insert(e,'<ol type="'+$("[name='type']:checked").val()+'"'+t+"><li>"+n.replace(/\n/g,"</li><li>")+"</li><li>","</li></ol>",i),$("#fenetre,#fenetre_fond").remove()}),$("#fenetre a.icon-ferme,#fenetre_fond").on("click",function(){marqueselection(e,!0)})}function insertion_ul(e){popup('<a class="icon-ok" title="Valider"></a><h3>Insertion d\'une liste à puces</h3>  <p>Vous pouvez éventuellement modifier les différents éléments en les écrivant ligne par ligne (chaque ligne correspond à un élément de la la liste). Vous pourrez ajouter un élément ultérieurement en l\'encadrant par les balises &lt;li&gt; et &lt;/li&gt;.</p>  <textarea id="lignes" rows="5">'+marqueselection(e)+'</textarea>  <hr><h3>Aperçu</h3><div id="apercu"></div>',!0),$("#lignes").on("click keyup",function(){$("#apercu").html("<ul><li>"+($("#lignes").val().length?$("#lignes").val().trim("\n").replace(/\n/g,"</li><li>"):"Première ligne</li><li>Deuxième ligne</li><li>...")+"</li></ul>")}).keyup().focus(),$("#fenetre a.icon-ok").on("click",function(){var t=$("#lignes").val().trim("\n"),n=t.lastIndexOf("\n");if(n>0){var r=t.substring(n+1);t=t.substring(0,n)}else var r="";insert(e,"<ul><li>"+t.replace(/\n/g,"</li><li>")+"</li><li>","</li></ul>",r),$("#fenetre,#fenetre_fond").remove()}),$("#fenetre a.icon-ferme,#fenetre_fond").on("click",function(){marqueselection(e,!0)})}function insertion_lien1(e){var t=marqueselection(e);popup('<a class="icon-ok" title="Valider"></a><h3>Insertion d\'un lien vers un document de Cahier de Prépa</h3>  <div><p style="text-align:center; margin: 2em 0;">[Récupération des listes de documents]</p></div>  <div style="display:none;"><hr><h3>Aperçu</h3><div id="apercu" style="text-align:center;">[Veuillez choisir un document]</div></div>',!0),$("#fenetre a.icon-ferme,#fenetre_fond").on("click",function(){marqueselection(e,!0)}),$.ajax({url:"ajax.php",method:"post",data:{recupdoc:""},dataType:"json"}).done(function(n){var r=function(){var e=$("#apercu"),t=$("#doc").val(),n=$("#doc option:selected").text();if(t==0)e.html(n);else if($("#vue").is(":checked")){var r=$("#largeur").val();n.slice(-4,-1)=="pdf"?(e.children(".pdf").length==0?e.html('<div><object data="download?id='+t+'" type="application/pdf" height="100%" width="100%"> <a href="download?id='+t+'">'+n+"</a> </object></div>"):e.find("object").attr("data").substr(12)!=t&&e.find("object").attr("data","download?id="+t).html('<a href="download?id='+t+'">'+n+"</a>"),e.children().attr("class","pdf "+$("#format").val()),r&&(r==100?e.children().removeAttr("style").children().attr("width","100%").removeAttr("style"):(e.children().css("padding-bottom",$('<div class="'+$("#format").val()+'"></div>').css("padding-bottom").slice(0,-1)*r/100+"%"),e.find("object").attr("width",r+"%").css("left",(100-r)/2+"%")))):"jpgpegpng".indexOf(n.slice(-4,-1))>-1&&(e.children("img").length==0?e.css("text-align","").html('<img src="download?id='+t+'">'):e.children().attr("src").substr(12)!=t&&e.children().attr("src","download?id="+t),r&&(r==100?e.children().removeAttr("style"):e.children().css("width",r+"%").css("margin-left",(100-r)/2+"%")))}else $("#apercu").css("text-align","center").html('<a onclick="return false;" href="download?id='+this.value+'">'+$("#texte").val()+"</a>")},i=function(n){$("#fenetre > div:first").html('  <p>Choisissez ci-dessous le répertoire puis le document à insérer. Vous pouvez aussi modifier le texte visible. Cela reste modifiable ultérieurement&nbsp;: le texte est situé entre les deux balises &lt;a...&gt; et &lt;/a&gt;.</p>  <p class="ligne"><label for="mat">Matière&nbsp;:</label><select id="mat">'+n.mats+'</select></p>  <p class="ligne"><label for="rep">Répertoire&nbsp;:</label><select id="rep"></select></p>  <p class="ligne"><label for="doc">Document&nbsp;:</label><select id="doc"></select></p>  <p class="ligne"><label for="texte">Texte visible&nbsp;:</label><input type="text" id="texte" value="'+t+'" size="80" data-auto="1"></p>  <p class="ligne"><label for="vue">Afficher dans la page (PDF et image uniquement)</label><input type="checkbox" id="vue">  <p class="ligne"><label for="largeur">Largeur en %&nbsp;:</label><input type="text" id="largeur" value="100" size="3"></p>  <p class="ligne"><label for="format">Format (PDF uniquement)</label><select id="format">    <option value="portrait">A4 vertical</option><option value="paysage">A4 horizontal</option><option value="hauteur50">Hauteur 50%</option>  </select>'),$("#fenetre > div:last").show(),$("#texte").val().length&&$("#texte").attr("data-auto",0),$("#doc").on("change keyup",function(e){e.which==13&&$("#fenetre a.icon-ok").click();var t=$("#doc option:selected").text();$("#texte").attr("data-auto")==1&&$("#texte").val(this.value>0?t.substr(0,t.lastIndexOf("(")-1):"---"),"pdfjpgpegpng".indexOf(t.slice(-4,-1))>-1?$("#vue").change().parent().show():$("#vue, #largeur, #format").parent().hide(),r()}),$("#texte").on("change keypress",function(e){if(e.which==0)return;e.which==13&&$("#fenetre a.icon-ok").click(),this.value.length==0?(this.setAttribute("data-auto",1),$("#doc").change()):(this.setAttribute("data-auto",0),r())}),$("#vue").on("change",function(){$("#vue").is(":checked")?$("#doc option:selected").text().slice(-4,-1)=="pdf"?($("#largeur, #format").parent().show(),$("#texte").parent().hide()):"jpgpegpng".indexOf($("#doc option:selected").text().slice(-4,-1))>-1&&($("#largeur").parent().show(),$("#format, #texte").parent().hide()):($("#texte").parent().show(),$("#largeur, #format").parent().hide()),r()}),$("#format").on("change keyup",function(e){e.which==13&&$("#fenetre a.icon-ok").click(),r()}),$("#largeur").on("keydown",function(e){e.which==38?++this.value:e.which==40&&--this.value}).on("change keyup",function(e){if(e.which==0)return;e.which==13&&$("#fenetre a.icon-ok").click(),this.value!=this.getAttribute("data-valeur")&&(this.setAttribute("data-valeur",this.value),r())}).attr("data-valeur",100),$("#rep").on("change",function(){$("#doc").html(n.docs[this.value]).change()}),$("#mat").on("change",function(){$("#rep").html(n.reps[this.value]).change()}).focus().change(),$("#fenetre a.icon-ok").on("click",function(){$("#doc").val()&&($("#vue").is(":checked")&&"pdfjpgpegpng".indexOf($("#doc option:selected").text().slice(-4,-1))>-1?insert(e,$("#apercu").html(),"",""):insert(e,'<a href="download?id='+$("#doc").val()+'">',"</a>",$("#texte").val()),$("#fenetre,#fenetre_fond").remove())}),$("#mat option").each(function(){$("body").attr("data-matiere")==this.value&&$("#mat").val(this.value).change()})};n["etat"]=="login_"?($("#fenetre > div:first").html('<p>Vous n\'êtes actuellement plus connecté. Vous devez vous identifier à nouveau pour récupérer la liste des répertoires et documents disponibles.</p>        <form>        <p class="ligne"><label for="login">Identifiant&nbsp;: </label><input type="text" name="login" id="login"></p>        <p class="ligne"><label for="motdepasse">Mot de passe&nbsp;: </label><input type="password" name="motdepasse" id="motdepasse"></p>        </form>'),$("#fenetre input").on("keypress",function(e){e.which==13&&$("#fenetre a.icon-ok").click()}),$("#fenetre a.icon-ok").on("click",function(){$.ajax({url:"ajax.php",method:"post",data:{login:$("#login").val(),motdepasse:$("#motdepasse").val(),recupdoc:""},dataType:"json"}).done(function(e){e["etat"]=="login_nok"?$("#fenetre > div:first > p:first").html(e.message).addClass("warning"):e["etat"]=="ok_"&&i(e)})})):n["etat"]=="ok_"&&i(n)})}function insertion_lien2(e){popup('<a class="icon-ok" title="Valider"></a><h3>Insertion d\'un lien</h3>  <p class="ligne"><label for="texte">Texte visible&nbsp;: </label><input type="text" id="texte" value="'+marqueselection(e)+'" size="80"></p>  <p class="ligne"><label for="url">Adresse&nbsp;: </label><input type="text" id="url" value="http://" size="80"></p>  <hr><h3>Aperçu</h3><div id="apercu" style="text-align:center;"></div>',!0),$("#fenetre input").on("click keyup",function(){$("#apercu").html($("#texte").val().length?'<a onclick="return false;" href="'+$("#url").val()+'">'+$("#texte").val()+"</a>":"[Écrivez un texte visible]")}).on("keypress",function(e){e.which==13&&$("#fenetre a.icon-ok").click()}).first().keyup().focus(),$("#fenetre a.icon-ok").on("click",function(){insert(e,'<a href="'+$("#url").val()+'">',"</a>",$("#texte").val()),$("#fenetre,#fenetre_fond").remove()}),$("#fenetre a.icon-ferme,#fenetre_fond").on("click",function(){marqueselection(e,!0)})}function insertion_tex(e){var t=typeof MathJax=="undefined"?'<script type="text/javascript" src="/MathJax/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script><script type="text/x-mathjax-config">MathJax.Hub.Config({tex2jax:{inlineMath:[["$","$"],["\\\\(","\\\\)"]]}});</script>':"",n=marqueselection(e),r="t1";if(n.length)switch(n.substring(0,2)){case"\\[":case"$$":r="t2";case"\\(":n=n.substring(2,n.length-2);break;default:n=n.trim("$")}popup(t+'<a class="icon-montre" title="Mettre à jour l\'aperçu"></a><a class="icon-ok" title="Valider"></a><h3>Insertion de formules LaTeX</h3>  <p>Vous pouvez ci-dessous entrer et modifier une formule LaTeX. L\'aperçu présent en bas sera mis à jour uniquement lorsque vous cliquez sur l\'icône <span class="icon-montre"></span>.</p>  <p class="ligne"><label for="t1">La formule est en ligne (pas de retour)</label><input type="radio" name="type" id="t1" value="1"></p>  <p class="ligne"><label for="t2">La formule est hors ligne (formule centrée)</label><input type="radio" name="type" id="t2" value="2"></p>  <textarea id="formule" rows="3">'+n+'</textarea>  <hr><h3>Aperçu</h3><div id="apercu" style="text-align:center;">[Demandez l\'aperçu en cliquant sur l\'icône <span class="icon-montre"></span>]</div>',!0),$("#"+r).prop("checked",!0),$("#formule").focus(),$("#fenetre a.icon-montre").on("click",function(){$("#formule").val().length?($("#apercu").html($("#t1").is(":checked")?"$"+$("#formule").val()+"$":"\\["+$("#formule").val()+"\\]").css("text-align","left"),MathJax.Hub.Queue(["Typeset",MathJax.Hub,"apercu"])):$("#apercu").html("[Écrivez une formule]").css("text-align","center")}),$("#fenetre a.icon-ok").on("click",function(){$("#t1").is(":checked")?insert(e,"$","$",$("#formule").val()):insert(e,"\\[","\\]",$("#formule").val()),$("#fenetre,#fenetre_fond").remove()}),$("#fenetre a.icon-ferme,#fenetre_fond").on("click",function(){marqueselection(e,!0)})}function insertion_par1(e){insert(e,"<p>","</p>")}function insertion_par2(e){insert(e,"<div class='note'>","</div>")}function insertion_par3(e){insert(e,"<div class='annonce'>","</div>")}function insertion_retour(e){insert(e,"<br>","")}function insertion_gras(e){insert(e,"<strong>","</strong>")}function insertion_italique(e){insert(e,"<em>","</em>")}function insertion_souligne(e){insert(e,"<u>","</u>")}function insertion_exp(e){insert(e,"<sup>","</sup>")}function insertion_ind(e){insert(e,"<sub>","</sub>")}function aidetexte(){popup('<h3>Aide et explications</h3>  <p>Il y a deux modes d\'éditions possibles pour éditer un texte&nbsp;: le mode «&nbsp;balises visibles&nbsp;» et le mode «&nbsp;balises invisibles&nbsp;». Il est possible de passer de l\'un à l\'autre&nbsp;:</p>  <ul>    <li><span class="icon-source"></span> permet de passer en mode «&nbsp;balises visibles&nbsp;» (par défaut), où le texte à taper est le code HTML de l\'article. Ce mode est plus précis. Les boutons aux dessus aident à utiliser les bonnes balises.</li>    <li><span class="icon-nosource"></span> permet de passer en mode «&nbsp;balises invisibles&nbsp;», où le texte est tel qu\'il sera affiché sur la partie publique, et modifiable. Ce mode est moins précis, mais permet le copié-collé depuis une page web ou un document Word/LibreOffice.  </ul>  <p>Une fonction de nettoyage du code HTML, permettant d\'assurer une homogénéité et une qualité d\'affichage optimales, est lancée à chaque commutation entre les deux modes, à chaque clic sur un des boutons disponibles, à chaque copie/coupe de texte et à chaque passage à la ligne.</p>  <p>En HTML, toutes les mises en formes sont réalisées par un encadrement de texte entre deux balises&nbsp;: &lt;h3&gt; et &lt;/h3&gt; pour un gros titre, &lt;p&gt; et &lt;/p&gt; pour un paragraphe. Le retour à la ligne simple, qui ne doit exister que très rarement, est une balise simple &lt;br&gt;. Mais les boutons disponibles sont là pour vous permettre de réaliser le formattage que vous souhaitez&nbsp;:</p>  <ul>    <li><span class="icon-titres"></span>&nbsp;: différentes tailles de titres (fenêtre supplémentaire pour choisir)</li>    <li><span class="icon-par1"></span>&nbsp;: paragraphe classique, qui doit obligatoirement encadrer au minimum chaque ligne de texte. Apparaît automatiquement au passage à la ligne si on l\'oublie.</li>    <li><span class="icon-par2"></span>&nbsp;: paragraphe important, écrit en rouge</li>    <li><span class="icon-par3"></span>&nbsp;: paragraphe très important, écrit en rouge et encadré</li>    <li><span class="icon-retour"></span>&nbsp;: retour à la ligne. Identique à un appui sur Entrée, et souvent inutile.</li>    <li><span class="icon-gras"></span>&nbsp;: mise en gras du texte entre les balises</li>    <li><span class="icon-italique"></span>&nbsp;: mise en italique du texte entre les balises</li>    <li><span class="icon-souligne"></span>&nbsp;: soulignement du texte entre les balises</li>    <li><span class="icon-omega"></span>&nbsp;: lettres grecques (fenêtre supplémentaire pour choisir)</li>    <li><span class="icon-sigma"></span>&nbsp;: symboles mathématiques (fenêtre supplémentaire pour choisir)</li>    <li><span class="icon-exp"></span>&nbsp;: mise en exposant du texte entre les balises</li>    <li><span class="icon-ind"></span>&nbsp;: mise en indice du texte entre les balises</li>    <li><span class="icon-ol"></span>&nbsp;: liste numérotée. Une fenêtre supplémentaire permet de choisir le type (1,A,a,I,i) et la première valeur. Les différentes lignes de la liste sont constituées par les balises &lt;li&gt; et &lt;/li&gt;</li>    <li><span class="icon-ul"></span>&nbsp;: liste à puces. Les différentes lignes de la liste sont constituées par les balises &lt;li&gt; et &lt;/li&gt;</li>    <li><span class="icon-lien1"></span>&nbsp;: lien d\'un document disponible ici (fenêtre supplémentaire pour choisir)</li>    <li><span class="icon-lien2"></span>&nbsp;: lien vers un autre site web (fenêtre supplémentaire pour entre l\'adresse)</li>    <li><span class="icon-tex"></span>&nbsp;: insertion de code LaTeX (fenêtre supplémentaire pour le taper)</li>  </ul>  <p class="tex2jax_ignore">Il est possible d\'insérer du code en LaTeX, sur une ligne séparée (balises \\[...\\] ou balises $$...$$) ou au sein d\'une phrase (balises $...$ ou balises \\(...\\)). Il faut ensuite taper du code en LaTeX à l\'intérieur. La prévisualisation est réalisée en direct.</p>',!1)}function echange(e,t){e.length&&t.length&&($("article").css("position","relative"),e.css("opacity",.3),t.css("opacity",.3),t.animate({top:e.position().top-t.position().top},1e3),e.animate({top:(t.outerHeight(!0)+t.outerHeight())/2},1e3,function(){e.css("opacity",1),t.css("opacity",1),e.insertAfter(t),e.css({position:"static",top:0}),t.css({position:"static",top:0})}))}function cache(e){var t=e.parent().attr("data-id").split("|");$.ajax({url:"ajax.php",method:"post",data:{cache:1,table:t[0],id:t[1]},dataType:"json",el:e,fonction:function(e){e.parent().addClass("cache"),e.removeClass("icon-cache").addClass("icon-montre").off("click").on("click",function(){montre($(this))}).attr("title","Montrer à nouveau")}})}function montre(e){var t=e.parent().attr("data-id").split("|");$.ajax({url:"ajax.php",method:"post",data:{montre:1,table:t[0],id:t[1]},dataType:"json",el:e,fonction:function(e){e.parent().removeClass("cache"),e.removeClass("icon-montre").addClass("icon-cache").off("click").on("click",function(){cache($(this))}).attr("title","Cacher à nouveau")}})}function monte(e){var t=e.parent(),n=t.attr("data-id").split("|");$.ajax({url:"ajax.php",method:"post",data:{monte:1,table:n[0],id:n[1]},dataType:"json",el:t,fonction:function(e){e.prev().prev().is("article")||(e.children(".icon-monte").hide(1e3),e.prev().children(".icon-monte").show(1e3)),e.next().is("article")||(e.children(".icon-descend").show(1e3),e.prev().children(".icon-descend").hide(1e3)),echange(e.prev(),e)}})}function descend(e){var t=e.parent(),n=t.attr("data-id").split("|");$.ajax({url:"ajax.php",method:"post",data:{descend:1,table:n[0],id:n[1]},dataType:"json",el:t,fonction:function(e){e.prev().is("article")||(e.children(".icon-monte").show(1e3),e.next().children(".icon-monte").hide(1e3)),e.next().next().is("article")||(e.children(".icon-descend"
-).hide(1e3),e.next().children(".icon-descend").show(1e3)),echange(e,e.next())}})}function supprime(e){var t=e.parent();popup('<h3>Confirmer la demande de suppression</h3><p class="suppression"><button class="icon-ok" title="Confirmer la suppression"></button>&nbsp;&nbsp;&nbsp;<button class="icon-annule" title="Sortir sans supprimer"></button></p>',!0),$("#fenetre .icon-ok").on("click",function(){$("#fenetre,#fenetre_fond").remove();var e=t.attr("data-id").split("|");$.ajax({url:"ajax.php",method:"post",data:{supprime:1,table:e[0],id:e[1]},dataType:"json",el:t,fonction:function(t){e[0]=="colles"?(t.removeClass("cache"),t.children(".icon-cache,.icon-montre,.icon-supprime").remove(),t.children(".icon-ajoute").show(),t.children("div.editable").each(function(){this.setAttribute("class",this.getAttribute("class").replace("editable","editable-remplace"))}).html("<p>Le programme de colles de cette semaine n'est pas encore défini.</p>")):e[0]=="utilisateurs"||e[0]=="agenda"?location.reload(!0):t.remove()}})}),$("#fenetre .icon-annule").on("click",function(){$("#fenetre,#fenetre_fond").remove()})}function formulaire(e){var t=e.getAttribute("data-id");if(t=="valide_utilisateur")return $.ajax({url:"ajax.php",method:"post",data:{table:"utilisateurs",id:$(e).parent().attr("data-id").split("|")[1]},dataType:"json",el:e,fonction:function(e){location.reload(!0)}}),!0;if(e.hasAttribute("data-remplace")){var n=$("article#"+e.getAttribute("data-remplace"));n.children(".icon-ajoute").after('<a class="icon-annule" title="Annuler"></a><a class="icon-ok" title="Valider"></a>'),n.children('[class*="-remplace"],.icon-ajoute').hide();var r=$("<form></form>").appendTo(n).html($("#form-"+t).html());r.children('[name="id"]').val(e.getAttribute("data-remplace"))}else{$("#epingle").remove();var n=$('<article id="epingle"><a class="icon-ferme" title="Fermer"></a>  <a class="icon-aide" title="Aide pour ce formulaire"></a>  <a class="icon-ok" title="Valider"></a></article>').prependTo("section"),r=$("<form></form>").appendTo(n).html($("#form-"+t).html())}r.find(".edithtml").textareahtml(),r.find("[data-placeholder]").placeholder(),$.colpick&&r.find('[name="couleur"]').colpick();switch(t){case"ajoute-cdt":r.init_cdt_boutons();break;case"ajoute-cdt-raccourci":r.init_cdt_raccourcis();break;case"notes":case"ajoute-notes":notes(e);break;case"evenement":if(e.id[0]=="e"){var i=e.id.substr(1),s=evenements[i],o=["type","matiere","debut","fin","texte"];for(var u=0;u<6;u++)r.find('[name="'+o[u]+'"]').val(s[o[u]]);r.find('[name="texte"]').change(),r.find('[name="id"]').val(i),r.find('[name="jours"]').prop("checked",s.je),$('<a class="icon-supprime" title="Supprimer cette information"></a>').insertBefore($(".icon-ok")).on("click",function(){supprime($(this))}).parent().attr("data-id","agenda|"+i)}r.find('[name="debut"]').datetimepicker({onShow:function(){this.setOptions({maxDate:r.find('[name="fin"]').val()||!1})},onClose:function(e,t){r.find('[name="fin"]').val(function(e,n){return n||t.val()})}}),r.find('[name="fin"]').datetimepicker({onShow:function(){this.setOptions({minDate:r.find('[name="debut"]').val()||!1})},onClose:function(e,t){r.find('[name="debut"]').val(function(e,n){return n||t.val()})}}),r.find('[name="jours"]').on("change",function(){var e;this.checked?r.find('[name="debut"],[name="fin"]').each(function(){e=this.value.split(" "),$(this).val(e[0]).attr("data-heure",e[1]).datetimepicker({format:"d/m/Y",timepicker:!1})}):r.find('[name="debut"],[name="fin"]').each(function(){this.hasAttribute("data-heure")&&$(this).val(this.value+" "+$(this).attr("data-heure")).removeAttr("data-heure"),$(this).datetimepicker({format:"d/m/Y Ghi",timepicker:!0})})}).change();break;case"deplacement-colle":r.find('[name="ancien"],[name="nouveau"]').each(function(){$(this).datetimepicker(),$(this).next().on("click",function(){$(this).prev().val("").change()})})}r.find(".usergrp > .icon-edite").on("click",function(){utilisateursgroupe(this)}),n.children(".icon-ferme").on("click",function(){$("#epingle").remove()}),n.children(".icon-annule").on("click",function(){n.children("form,.icon-annule,.icon-ok").remove(),n.children('[class*="-remplace"],.icon-ajoute').show()}),n.children("a.icon-aide").on("click",function(){popup($("#aide-"+t).html(),!1)}),n.children("a.icon-ok").on("click",function(){r.children(".edithtml").each(function(){this.value=nettoie($(this).is(":visible")?this.value:$(this).next().html())}),(t=="notes"||t=="ajoute-notes")&&$("#epingle select:not(:visible)").val("x"),$.ajax({url:"ajax.php",method:"post",data:r.serialize(),dataType:"json",el:n,fonction:function(e){e.attr("id")=="epingle"?$("#epingle #noreload").length||location.reload(!0):(e.children(".icon-supprime").show(),e.find('[name="cache"]').is(":checked")?(e.children(".icon-montre").show(),e.addClass("cache")):e.children(".icon-cache").show(),e.children('[class*="-remplace"]').show().each(function(){this.setAttribute("class",this.getAttribute("class").replace("-remplace",""));var e=this.getAttribute("data-id");$(this).html(n.find('[name="'+e.slice(e.indexOf("|")+1,e.lastIndexOf("|"))+'"]').val())}),e.children(".editable").editinplace(),e.children(".icon-annule,.icon-ok,form").remove())}})}),r.find("input,select").on("keypress",function(e){e.which==13&&(e.preventDefault(),n.children("a.icon-ok").click())})}function valide(e){var t="";if($("#mail").length)$(".editabledest").children("span").text()=="[Personne]"?affiche("Il faut au moins un destinataire pour envoyer le courriel.","nok"):$('[name="sujet"]').val().length?t=$("#mail").serialize():affiche("Il faut un sujet non vide pour envoyer le courriel.","nok");else if($("#planning").length)t=$("form").serialize();else{var n=$(e).parent(),r=n.parent().attr("data-id").split("|");t="table="+r[0]+"&id="+r[1]+"&"+n.serialize()}t.length&&$.ajax({url:"ajax.php",method:"post",data:t,dataType:"json",el:e,fonction:function(e){$(e).is("[data-noreload]")||location.reload(!0)}})}function suppressionmultiple(e){popup('<h3>Confirmer la demande de suppression</h3><p class="suppression"><button class="icon-ok" title="Confirmer la suppression"></button>&nbsp;&nbsp;&nbsp;<button class="icon-annule" title="Sortir sans supprimer"></button></p>',!0),$("#fenetre .icon-ok").on("click",function(){var t=e.getAttribute("data-id").split("|");$("#fenetre,#fenetre_fond").remove(),$.ajax({url:"ajax.php",method:"post",data:"table="+t[0]+"&id="+t[1]+"&supprime_"+t[2]+"=1",dataType:"json",el:$(e),fonction:function(e){e.remove()}})}),$("#fenetre .icon-annule").on("click",function(){$("#fenetre,#fenetre_fond").remove()})}function utilisateursmatiere(e){var t=e,n=t.getAttribute("data-matiere").split("|");popup($("#form-utilisateurs").html(),!0),$("#fenetre").addClass("usermat").children("h3").append(n[0]),$("#fenetre :checkbox").attr("id",function(){return this.name}).on("change",function(){var e=this,i=e.id.substr(1);$.ajax({url:"ajax.php",method:"post",data:{table:"utilisateurs",id:i,matiere:n[1],ok:e.checked?1:0},dataType:"json",el:e,fonction:function(n){n.checked?(r=r+","+i,$(e).prev().addClass("labelchecked")):(r=(","+r+",").replace(","+i+",",",").slice(1,-1),$(e).prev().removeClass("labelchecked")),t.setAttribute("data-uid",r);var s=$("#fenetre .a4:checked").length,o=$("#fenetre .a3:checked").length,u=$("#fenetre .a2:checked").length,a=$("#fenetre .a1:checked").length,f=s+o+u+a;if(f){var l="";s&&(l+=", "+s+" professeur"+(s>1?"s":"")),o&&(l+=", "+o+" colleur"+(o>1?"s":"")),u&&(l+=", "+u+" élève"+(u>1?"s":"")),a&&(l+=", "+a+" invité"+(a>1?"s":"")),$(t).parent().children("span").text("Cette matière concerne "+f+" utilisateur"+(f>1?"s":"")+" dont "+l.substr(1)+".")}else $(t).parent().children("span").text("Cette matière ne concerne aucun utilisateur.")}}).done(function(t){t["etat"]!="ok"&&(e.checked=!e.checked)})});var r=t.getAttribute("data-uid");$("#u"+r.replace(/,/g,",#u")).prop("checked",!0),$("#fenetre :checked").prev().addClass("labelchecked")}function utilisateursgroupe(e){var t=e,n=$(e).parent().parent();popup($("#form-utilisateurs").html(),!0),$("#fenetre").addClass("usergrp").children("h3").append(n.find("span.editable").text()||n.find("input").val()),$("#fenetre :checkbox").attr("id",function(){return this.name}),n.is("div")?($("#fenetre :checkbox").on("change",function(){$(this).prev().toggleClass("labelchecked",e.checked)}),$('<a class="icon-ok" title="Valider"></a>').insertAfter("#fenetre .icon-ferme").on("click",function(){var e=$("#fenetre input:checked").map(function(){return this.id.replace("u","")}).get().join();t.setAttribute("data-uid",e),$(t).parent().next().val(e),$(t).parent().children("span").text($("#fenetre input:checked").parent().map(function(){return $(this).text().replace(" (identifiant)","").trim()}).get().join(", ")),$("#fenetre, #fenetre_fond").remove()})):$("#fenetre :checkbox").on("change",function(){var e=this,i=e.id.substr(1);$.ajax({url:"ajax.php",method:"post",data:{table:"groupes",id:n.attr("data-id").split("|")[1],eleve:i,ok:e.checked?1:0},dataType:"json",el:e,fonction:function(n){n.checked?(r=r+","+i,$(e).prev().addClass("labelchecked")):(r=(","+r+",").replace(","+i+",",",").slice(1,-1),$(e).prev().removeClass("labelchecked")),t.setAttribute("data-uid",r),$(t).parent().children("span").text($("#fenetre input:checked").parent().map(function(){return $(this).text().replace(" (identifiant)","").trim()}).get().join(", "))}}).done(function(t){t["etat"]!="ok"&&(e.checked=!e.checked)})});var r=t.getAttribute("data-uid");$("#u"+r.replace(/,/g,",#u")).prop("checked",!0),$("#fenetre :checked").prev().addClass("labelchecked")}function utilisateursmail(e){popup($("#form-utilisateurs").html(),!0),$('#fenetre [name="dest[]"]').each(function(){$(this).attr("id","u"+this.value)});var t=$('[name="id-copie"]').val().split(",");for(var n=0;n<t.length;n++)$('#fenetre [name="dest[]"][value="'+t[n]+'"]').prop("checked",!0);t=$('[name="id-bcc"]').val().split(",");for(var n=0;n<t.length;n++)$('#fenetre [name="dest_bcc[]"][value="'+t[n]+'"]').prop("checked",!0);$("#fenetre .icon-cocher").on("click keyup",function(){var e=this.getAttribute("data-classe");$("#fenetre ."+e+":not(:disabled)").prop("checked",!0),$(this).hide(),$(this).next().show(),e=e.indexOf("bcc")>0?e.replace("bcc","c"):e.replace("c","bcc"),$("#fenetre ."+e+":not(:disabled)").prop("checked",!1),$('#fenetre .icon-cocher[data-classe="'+e+'"]').show(),$('#fenetre .icon-decocher[data-classe="'+e+'"]').hide()}),$("#fenetre .icon-decocher").on("click keyup",function(){$("#fenetre ."+this.getAttribute("data-classe")+":not(:disabled)").prop("checked",!1),$(this).hide(),$(this).prev().show()}).hide(),$("#fenetre :checkbox[name]").on("change",function(){$(this).is(":checked")&&$('#fenetre :checkbox[name][value="'+this.value+'"][name!="'+this.name+'"]').prop("checked",!1)}),$("#fenetre :checkbox:not([name])").on("click",function(){var e=this.value.split(",");for(var t=0;t<e.length;t++)$('#fenetre :checkbox[name="'+this.className+'[]"][value="'+e[t]+'"]').prop("checked",$(this).prop("checked")).change();$(this).is(":checked")&&$('#fenetre :checkbox[class="'+(this.className=="dest"?"dest_bcc":"dest")+'"][value="'+this.value+'"]').prop("checked",!1)}),$("#fenetre .icon-ok").on("click",function(){$('[name="id-copie"]').val($('#fenetre [name="dest[]"]:checked').map(function(){return this.value}).get().join(",")),$('[name="id-bcc"]').val($('#fenetre [name="dest_bcc[]"]:checked').map(function(){return this.value}).get().join(",")),$(e).prev().text($('#fenetre [name="dest[]"]:checked').parent().prev().map(function(){return $(this).text().replace(" (identifiant)","")}).get().concat($('#fenetre [name="dest_bcc[]"]:checked').parent().prev().prev().map(function(){return $(this).text().replace(" (identifiant)","")+" (CC)"}).get()).join(", ")),$(e).prev().text().length||$(e).prev().text("[Personne]"),$("#fenetre, #fenetre_fond").remove()})}function notes(e){$("#epingle :checkbox").on("click",function(){var e=$("#epingle .grpnote:checked").map(function(){return this.value.split(",")}).get().concat();$("#epingle tr[data-id]:not([data-orig])").hide();for(var t=0;t<e.length;t++)$('#epingle tr[data-id="'+e[t]+'"]').show()}),$("#epingle tr[data-id]").each(function(){$(this).children("td:last").html($("#epingle div").html()),$(this).find("select").attr("name","e"+$(this).attr("data-id"))}),$("#epingle div").remove(),$("#epingle :checkbox").length&&$("#epingle tr[data-id]").hide();if(e.getAttribute("data-eleves")){$("#epingle h4").text($(e).parent().children("h3").text()),$('#epingle [name="id"]').val($(e).parent().attr("data-id").split("|")[1]);var t=e.getAttribute("data-eleves").split("|"),n=e.getAttribute("data-notes").split("|");for(var r=0;r<t.length;r++)$('#epingle tr[data-id="'+t[r]+'"]').attr("data-orig",!0).show().find("select").val(n[r]).on("change",function(){$(this).parent().parent().removeAttr("data-orig")});var i=dejanotes[$('#epingle [name="id"]').val().split("-")[0]].split(",");for(r=0;r<i.length;r++)$('#epingle tr[data-id="'+i[r]+'"]:not(:visible)').addClass("dejanote").find("select").prop("disabled",!0);$(".dejanote td:first-child").each(function(){$(this).text($(this).text()+" (noté par un autre colleur)")})}else $("#epingle #semaine").on("change keyup",function(){var e=$(this).val().split("-")[0];$(".dejanote td:first-child").each(function(){$(this).text($(this).text().replace(" (noté par un autre colleur)",""))}),$(".dejanote").removeClass("dejanote").find("select").prop("disabled",!1);if(e>0){var t=dejanotes[e].split(",");for(var n=0;n<t.length;n++)$('#epingle tr[data-id="'+t[n]+'"]').addClass("dejanote").find("select").prop("disabled",!0);$(".dejanote td:first-child").each(function(){$(this).text($(this).text()+" (noté par un autre colleur)")})}})}function envoimail(e){$(".editabledest").children("span").text()=="[Personne]"?affiche("Il faut au moins un destinataire pour envoyer le courriel.","nok"):$.ajax({url:"ajax.php",method:"post",data:$("#mail").serialize(),dataType:"json",el:"",fonction:function(e){location.reload(!0)}})}function modifierepertoire(e){var t=e.className=="icon-edite"?"repertoire":"ajouterep";$("#epingle").remove();var n=$('<article id="epingle"><a class="icon-ferme" title="Fermer"></a>  <a class="icon-aide" title="Aide pour ce formulaire"></a>  <a class="icon-ok" title="Valider"></a></article>').insertAfter("#parentsdoc"),r=$('<form onsubmit="return false;"></form>').appendTo(n).html($("#form-"+t).html());if(t=="repertoire"){var i=$(e).parent().attr("data-id").split("|")[1],s=$(e).parent().attr("data-donnees").split("|"),o=$(e).parent().children(".nom").text().split(/\/\s/).pop()||$(e).parent().find("input").val();r.find("em").text(o),s[0]==0?r.find("#nom,#parent,#menu").parent().remove():(r.find("#nom").val(o),r.find('[data-parents*=",'+i+',"]').prop("disabled",!0),s[1]=="1"&&r.find("#menu").prop("checked",!0)),$('#protection option[value="'+s[2]+'"]').prop("selected",!0),r.find('[name="id"]').val(i)}n.children(".icon-ferme").on("click",function(){$("#epingle").remove()}),n.children("a.icon-aide").on("click",function(){popup($("#aide-"+t).html(),!1)}),n.children("a.icon-ok").on("click",function(){$.ajax({url:"ajax.php",method:"post",data:r.serialize(),dataType:"json",el:n,fonction:function(e){location.reload(!0)}})}),r.find("input,select").on("keypress",function(e){e.which==13&&n.children("a.icon-ok").click()})}function modifiedocument(e){var t=e.className=="icon-edite"?"document":"ajoutedoc";$("#epingle").remove();var n=$('<article id="epingle"><a class="icon-ferme" title="Fermer"></a>  <a class="icon-aide" title="Aide pour ce formulaire"></a>  <a class="icon-ok" title="Valider"></a></article>').insertAfter("#parentsdoc");form=$('<form onsubmit="return false;"></form>').appendTo(n).html($("#form-"+t).html());var r=$(e).parent().children(".nom").text()||$(e).parent().find("input").val();form.find("em").text(r),t=="document"?(form.find("#nom").val(r),$('#protection option[value="'+$(e).parent().attr("data-protection")+'"]').prop("selected",!0),form.find('[name="id"]').val($(e).parent().attr("data-id").split("|")[1])):($('#protection option[value="'+$(e).parent().attr("data-donnees").split("|")[2]+'"]').prop("selected",!0),form.find('[name="parent"]').val($(e).parent().attr("data-id").split("|")[1]),form.find("#fichier").on("change",function(){if(!form.find("#nom").val().length)var e=this.value;form.find("#nom").val(e.substring(e.lastIndexOf("\\")+1,e.lastIndexOf("."))||e)})),n.children(".icon-ferme").on("click",function(){$("#epingle").remove()}),n.children("a.icon-aide").on("click",function(){popup($("#aide-"+t).html(),!1)}),n.children("a.icon-ok").on("click",function(){var e=new FormData(form[0]);$.ajax({url:"ajax.php",method:"post",data:e,dataType:"json",contentType:!1,processData:!1,el:n,fonction:function(e){location.reload(!0)}})}),form.find("input,select").on("keypress",function(e){e.which==13&&n.children("a.icon-ok").click()})}$.fn.placeholder=function(){this.each(function(){var e=$(this);e.is("div")?e.on("change keyup mouseup",function(){$(this).text().length?$(this).removeClass("placeholder"):$(this).addClass("placeholder")}).change():($('<span class="placeholder">'+this.getAttribute("data-placeholder")+"</span>").insertBefore(e).on("click",function(){$(this).next().focus()}).hide(),e.on("change keyup mouseup",function(){this.value!=undefined&&!this.value.length&&$(this).is(":visible")?$(this).prev().css("display","inline"):$(this).prev().hide()}).change())})},$.fn.textareahtml=function(){this.each(function(){var e=$(this),t=this.getAttribute("data-placeholder");this.setAttribute("data-placeholder",t+". Formattage en HTML, balises visibles.");var n=$('<div contenteditable="true" data-placeholder="'+t+'"></div>').insertAfter(e.before(boutons)).hide(),r=e.prev().children(".icon-retour");e.on("keypress",function(e){e.which==13&&(this.value=nettoie(this.value))}).on("paste cut",function(){var e=this;setTimeout(function(){e.value=nettoie(e.value)},100)}),n.on("keypress",function(e){e.which==13&&r.click()}).on("paste cut",function(){var e=this;setTimeout(function(){e.innerHTML=nettoie(e.innerHTML)+"<br>"},100)}),e.prev().children(".icon-nosource").on("click",function(t){t.preventDefault(),e.hide(),e.prev().hide(),n.show().css("min-height",e.outerHeight()),$(this).hide().prev().show(),n.focus().html(nettoie(e.val())).change();if(window.getSelection){var r=document.createRange();r.selectNodeContents(n[0]),r.collapse(!1);var i=window.getSelection();i.removeAllRanges(),i.addRange(r)}else{var r=document.body.createTextRange();r.moveToElementText(n[0]),r.collapse(!1),r.select()}}),e.prev().children(".icon-source").on("click",function(t){t.preventDefault(),n.hide(0),e.show(0).css("height",n.height()),$(this).hide().next().show(),e.focus().val(nettoie(n.html())).change()}).hide(),e.prev().children(".icon-aide").on("click",function(e){e.preventDefault(),aidetexte()}),e.prev().children().not(".icon-nosource,.icon-source,.icon-aide").on("click",function(e){e.preventDefault(),window["insertion_"+this.className.substring(5)]($(this))})})},$.fn.editinplace=function(){this.each(function(){var e=$(this);this.setAttribute("data-original",e.is("h3")?e.text():e.html()),$('<a class="icon-edite" title="Modifier"></a>').appendTo(e).on("click",transforme)})},$.fn.editinplacecdt=function(){this.each(function(){$(this).wrapInner("<span></span>").attr("data-original",$(this).text()),$('<a class="icon-edite" title="Modifier"></a>').appendTo($(this)).on("click",transformecdt)})},$.fn.init_cdt_boutons=function(){var e=this;e.find('[name="jour"],[name="pour"]').datetimepicker({format:"d/m/Y",timepicker:!1}),e.find('[name="h_debut"]').datetimepicker({format:"Ghi",datepicker:!1,onClose:function(t,n){e.find('[name="h_fin"]').val(function(e,t){return t||(n.val().length?parseInt(n.val().slice(0,-3))+2+n.val().slice(-3):"")})}}),e.find('[name="h_fin"]').datetimepicker({format:"Ghi",datepicker:!1});var t=function(e){return String(e).length==1?"0"+e:String(e)};e.find('[name="raccourci"]').on("change keyup",function(){var n=raccourcis[this.value];for(var r in n)if(r=="jour"){var i=new Date,s=parseInt(n.jour);i.setDate(s>i.getDay()?i.getDate()-i.getDay()-7+s:i.getDate()-i.getDay()+s),e.find('[name="jour"]').val(t(i.getDate())+"/"+t(i.getMonth()+1)+"/"+i.getFullYear())}else e.find('[name="'+r+'"]').val(n[r]);this.setAttribute("data-modif",1),e.find('[name="tid"]').change()}).attr("data-modif",0),e.find('[name="tid"]').on("change keyup",function(){switch(parseInt(seances[this.value])){case 0:e.find('[name="h_debut"]').parent().show(),e.find('[name="h_fin"]').parent().hide(),e.find('[name="pour"]').parent().hide(),e.find('[name="demigroupe"]').parent().show();break;case 1:e.find('[name="h_debut"]').parent().show(),e.find('[name="h_fin"]').parent().show(),e.find('[name="pour"]').parent().hide(),e.find('[name="demigroupe"]').parent().show();break;case 2:e.find('[name="h_debut"]').parent().hide(),e.find('[name="h_fin"]').parent().hide(),e.find('[name="pour"]').parent().show(),e.find('[name="demigroupe"]').parent().show();break;case 3:e.find('[name="h_debut"]').parent().hide(),e.find('[name="h_fin"]').parent().hide(),e.find('[name="pour"]').parent().hide(),e.find('[name="demigroupe"]').parent().show();break;default:e.find('[name="h_debut"]').parent().hide(),e.find('[name="h_fin"]').parent().hide(),e.find('[name="pour"]').parent().hide(),e.find('[name="demigroupe"]').parent().hide()}e.find('[name="jour"]').change()}),e.find('input,[name="demigroupe"]').on("change keyup",function(){e.find('[name="raccourci"]').attr("data-modif")==0?e.find('[name="raccourci"]').val(0):e.find('[name="raccourci"]').attr("data-modif",0)}),e.find("input,select").on("keypress",function(e){e.which==13&&el.find("a.icon-ok").click()}),e.find("select:first").focus(),e.find('[name="tid"]').change()},$.fn.init_cdt_raccourcis=function(){this.each(function(){var e=$(this);e.find('[name="h_debut"]').datetimepicker({format:"Ghi",datepicker:!1,onClose:function(t,n){e.find('[name="h_fin"]').val(function(e,t){return t||(n.val().length?parseInt(n.val().slice(0,-3))+2+n.val().slice(-3):"")})}}),e.find('[name="h_fin"]').datetimepicker({format:"Ghi",datepicker:!1}),e.find('[name="type"]').on("change keyup",function(){switch(parseInt(seances[this.value])){case 0:e.find('[name="h_debut"]').parent().show(),e.find('[name="h_fin"]').parent().hide(),e.find('[name="demigroupe"]').parent().show();break;case 1:e.find('[name="h_debut"]').parent().show(),e.find('[name="h_fin"]').parent().show(),e.find('[name="demigroupe"]').parent().show();break;case 2:e.find('[name="h_debut"]').parent().hide(),e.find('[name="h_fin"]').parent().hide(),e.find('[name="demigroupe"]').parent().show();break;case 3:e.find('[name="h_debut"]').parent().hide(),e.find('[name="h_fin"]').parent().hide(),e.find('[name="demigroupe"]').parent().show();break;default:e.find('[name="h_debut"]').parent().hide(),e.find('[name="h_fin"]').parent().hide(),e.find('[name="demigroupe"]').parent().hide()}}).change(),e.find("input,select").on("keypress",function(t){t.which==13&&e.find("a.icon-ok").click()})})};var boutons='<p class="boutons">  <button class="icon-titres" title="Niveaux de titres"></button>  <button class="icon-par1" title="Paragraphe"></button>  <button class="icon-par2" title="Paragraphe important"></button>  <button class="icon-par3" title="Paragraphe très important"></button>  <button class="icon-retour" title="Retour à la ligne"></button>  <button class="icon-gras" title="Gras"></button>  <button class="icon-italique" title="Italique"></button>  <button class="icon-souligne" title="Souligné"></button>  <button class="icon-omega" title="Insérer une lettre grecque"></button>  <button class="icon-sigma" title="Insérer un signe mathématique"></button>  <button class="icon-exp" title="Exposant"></button>  <button class="icon-ind" title="Indice"></button>  <button class="icon-ol" title="Liste énumérée"></button>  <button class="icon-ul" title="Liste à puces"></button>  <button class="icon-lien1" title="Lien vers un document du site"></button>  <button class="icon-lien2" title="Lien internet"></button>  <button class="icon-tex" title="LATEX!"></button>  <button class="icon-source" title="Voir et éditer le code html"></button>  <button class="icon-nosource" title="Voir et éditer le texte formaté"></button>  <button class="icon-aide" title="Voir et éditer le texte formaté"></button></p>';$(document).ajaxSend(function(e,t,n){$("body").css("cursor","wait"),n.data.append?n.data.append("csrf-token",$("body").attr("data-csrf-token")):n.data="csrf-token="+$("body").attr("data-csrf-token")+"&"+n.data}).ajaxStop(function(){$("body").css("cursor","auto")}).ajaxSuccess(function(e,t,n){var r=t.responseJSON;switch(r.etat){case"ok":affiche(r.message,"ok"),n.fonction(n.el);break;case"nok":affiche(r.message,"nok");break;case"login":afficher_login(n)}}),$(function(){$(".editable").editinplace(),$(".titrecdt").editinplacecdt(),$(".cdt-raccourcis").init_cdt_raccourcis(),$(".supprmultiple").on("click",function(){suppressionmultiple(this)}),$(".usermat .icon-edite").on("click",function(){utilisateursmatiere(this)}),$(".editabledest .icon-edite").on("click",function(){utilisateursmail(this)}),$('[name="copie"]').on("change",function(){$.ajax({url:"ajax.php",method:"post",data:{table:"mailprefs",champ:"mailcopy",id:0,val:$(this).prop("checked")?1:0},dataType:"json",el:"",fonction:function(e){return!0}})}),$("#parentsdoc .icon-edite, .rep > .icon-edite, .icon-ajouterep").on("click",function(){modifierepertoire(this)}),$(".doc > .icon-edite, .icon-ajoutedoc").on("click",function(){modifiedocument(this)}),$(".evnmt").on("click",function(){this.setAttribute("data-id","evenement"),formulaire(this)}),$('[name="mailnotes"]').on("change",function(){$.ajax({url:"ajax.php",method:"post",data:{table:"groupes",champ:"mailnotes",id:$(this).attr("id").substr(9),val:$(this).find("option:selected").val()},dataType:"json",el:"",fonction:function(e){return!0}})}),$(".usergrp .icon-edite").on("click",function(){utilisateursgroupe(this)}),$("#log").hide().on("click",function(){$(this).hide()}),$("[data-placeholder]").placeholder(),$.colpick&&$('[name="couleur"]').colpick(),$("a.icon-cache,a.icon-montre,a.icon-monte,a.icon-descend,a.icon-supprime").on("click",function(){window[this.className.substring(5)]($(this))}),$("a.icon-aide").on("click",function(){popup($("#aide-"+this.getAttribute("data-id")).html(),!1)}),$("a.icon-prefs.general,a.icon-ajoute,a.icon-ajout-colle").on("click",function(){formulaire(this)}),$("a.icon-ok").on("click",function(){valide(this)}),$("a.icon-deconnexion").on("click",function(e){$.ajax({url:"ajax.php",method:"post",data:{deconnexion:1},dataType:"json",el:"",fonction:function(e){location.reload(!0)}})}),$(".icon-lock1").attr("title","Visible uniquement par les utilisateurs connectés"),$(".icon-lock2").attr("title","Visible uniquement par les élèves/colleurs/professeurs connectés"),$(".icon-lock3").attr("title","Visible uniquement par les colleurs/professeurs connectés"),$(".icon-lock4").attr("title","Visible uniquement par les professeurs connectés"),$(".icon-lock5").attr("title","Complètement invisible (hors professeurs de cette matière)"),$(".icon-menu").on("click",function(){$("#colonne,nav").toggleClass("visible")})});
\ No newline at end of file
diff -urN cahier-de-prepa6.2.0/js/fonctions.js cahier-de-prepa8.0.1/js/fonctions.js
--- cahier-de-prepa6.2.0/js/fonctions.js	2016-08-08 11:18:16.233336341 +0200
+++ cahier-de-prepa8.0.1/js/fonctions.js	2018-10-16 10:56:18.090083852 +0200
@@ -6,34 +6,26 @@
 
 // Notification de résultat de requête AJAX
 function affiche(message,etat) {
-  $('#log').removeClass().addClass(etat).html(message).show().delay(5000).hide(500);
-}
-
-// Affichage d'un placeholder (valeur si élement vide)
-// Version simplifiée de celle écrite dans edition.js, valable pour les input
-$.fn.placeholder = function() {
-  this.each( function() {
-    var el = $(this);
-    $('<span class="placeholder">'+this.getAttribute('data-placeholder')+'</span>').insertBefore(el).on('click',function() {
-      $(this).next().focus();
-    }).hide();
-    el.on("change keyup mouseup",function() {
-      if ( !(this.value.length) && ( $(this).is(':visible') ) )
-        $(this).prev().show();
-      else
-        $(this).prev().hide();
-    }).change();
+  $('#log').removeClass().addClass(etat).html(message).append('<span class="icon-ferme"></span>').fadeIn().off("click").on("click",function() {
+    window.clearTimeout(extinction);
+    $(this).fadeOut(800);
   });
+  extinction = window.setTimeout(function() { $('#log').fadeOut(800); },6000);
 }
 
 ///////////////////
 // Requêtes AJAX //
 ///////////////////
 $(document).ajaxSend( function(ev,xhr,settings) {
-              $('body').css('cursor','wait');
+              $('#load').show(200);
+              // Sécurité anti XSS : Ajout du token CSRF
+              if ( settings.data.append )
+                settings.data.append('csrf-token',$('body').attr('data-csrf-token'));
+              else
+                settings.data = 'csrf-token='+$('body').attr('data-csrf-token')+'&'+settings.data;
             })
            .ajaxStop( function() {
-              $('body').css('cursor','auto');
+              $('#load').hide(200);
             })
            .ajaxSuccess( function(ev,xhr,settings) {
               var data = xhr.responseJSON;
@@ -70,12 +62,12 @@
 <a class="icon-ferme" title="Fermer"></a><a class="icon-ok" title="Valider"></a><h3>Connexion</h3>\
 <form>\
   <p>Veuillez entrer votre identifiant et votre mot de passe&nbsp;:</p>\
-  <input class="ligne" type="text" name="login" data-placeholder="Identifiant">\
-  <input class="ligne" type="password" name="motdepasse" data-placeholder="Mot de passe">\
-  <input class="ligne" type="hidden" name="connexion" value="1">\
-  <p class="oubli"><a href="connexion?oublimdp">Identifiant ou mot de passe oublié&nbsp;?</a></p>\
-  <p class="oubli"><a href="connexion?creationcompte">Créer un compte</a></p>\
-</form>').focus();
+  <input class="ligne" type="text" name="login" placeholder="Identifiant">\
+  <input class="ligne" type="password" name="motdepasse" placeholder="Mot de passe">\
+  <p class="oubli"><label for="permconn">Se souvenir de moi</label><input type="checkbox" name="permconn" id="permconn" value="1">\
+  <p class="oubli"><a href="gestioncompte?oublimdp">Identifiant ou mot de passe oublié&nbsp;?</a></p>\
+  <p class="oubli"><a href="gestioncompte?creation">Créer un compte</a></p>\
+</form>');
     // Fond grisé
     $('<div id="fenetre_fond"></div>').appendTo('body').click(function() {
       $('#fenetre,#fenetre_fond').remove();
@@ -84,29 +76,36 @@
     $('#fenetre a.icon-ferme').on("click",function() {
       $('#fenetre,#fenetre_fond').remove();
     });
-    // Placeholders
-    $('#fenetre input').placeholder();
     // Envoi
     $('#fenetre a.icon-ok').on("click",function () {
-      $.ajax({url: 'ajax.php', method: "post", data: $('#fenetre form').serialize(), dataType: 'json', el: '', fonction: function(el) { location.reload(true); } })
-      .done( function(data) {
-        // Si erreur d'identification, on reste bloqué là
-        if ( data['etat'] == 'login_nok' )
-          $('form p:first').html(data['message']).addClass('warning');
-      });
+      $.ajax({url: 'ajax.php',
+              method: "post",
+              data: $('#fenetre form').serialize()+'&connexion=1', 
+              dataType: 'json', 
+              el: '', 
+              beforeSend: function() { $('#load').show(200); },
+              complete: function() { $('#load').hide(200); },
+              fonction: function(el) { location.reload(true); } 
+            }).done( function(data) {
+              // Si erreur d'identification, on reste bloqué là
+              if ( data['etat'] == 'nok' )
+                $('#fenetre form p:first').html(data['message']).addClass('warning');
+            });
     });
     // Envoi par appui sur Entrée
     $('#fenetre input').on('keypress',function (e) {
-      if ( e.which == 13 )
+      if ( e.which == 13 ) {
         $('#fenetre a.icon-ok').click();
+        return false;
+      }
     });
-    // Focus avec du retard pour éviter un bug d'affichage du placeholder
-    setTimeout(function() { $('#fenetre input:first').focus() },500);
+    // Focus initial
+    $('#fenetre form input:first').focus();
   });
 
   // Déconnexion  
   $('a.icon-deconnexion').on('click',function(e) {
-    $.ajax({url: 'ajax.php', method: "post", data: { deconnexion:1 }, dataType: 'json', el: '', fonction: function(el) { location.reload(true); } });
+    $.ajax({url: 'ajax.php', method: "post", data: { action:'deconnexion' }, dataType: 'json', el: '', fonction: function(el) { location.reload(true); } });
   });
 
   // Menu pour les petits écrans
diff -urN cahier-de-prepa6.2.0/js/fonctions.min18.js cahier-de-prepa8.0.1/js/fonctions.min18.js
--- cahier-de-prepa6.2.0/js/fonctions.min18.js	1970-01-01 01:00:00.000000000 +0100
+++ cahier-de-prepa8.0.1/js/fonctions.min18.js	2018-10-16 10:57:56.471232706 +0200
@@ -0,0 +1 @@
+function affiche(message,etat){$("#log").removeClass().addClass(etat).html(message).append('<span class="icon-ferme"></span>').fadeIn().off("click").on("click",function(){window.clearTimeout(extinction);$(this).fadeOut(800)});extinction=window.setTimeout(function(){$("#log").fadeOut(800)},6e3)}$(document).ajaxSend(function(ev,xhr,settings){$("#load").show(200);if(settings.data.append)settings.data.append("csrf-token",$("body").attr("data-csrf-token"));else settings.data="csrf-token="+$("body").attr("data-csrf-token")+"&"+settings.data}).ajaxStop(function(){$("#load").hide(200)}).ajaxSuccess(function(ev,xhr,settings){var data=xhr.responseJSON;switch(data["etat"]){case"ok":affiche(data["message"],"ok");settings.fonction(settings.el);break;case"nok":affiche(data["message"],"nok");break}});$(function(){$("a.icon-connexion").on("click",function(e){if(!$("#log").length)$('<div id="log"></div>').appendTo("body").hide().on("click",function(){$(this).hide()});$("#colonne,nav").removeClass("visible");var el=$('<div id="fenetre"></div>').appendTo("body").html('<a class="icon-ferme" title="Fermer"></a><a class="icon-ok" title="Valider"></a><h3>Connexion</h3><form>  <p>Veuillez entrer votre identifiant et votre mot de passe&nbsp;:</p>  <input class="ligne" type="text" name="login" placeholder="Identifiant">  <input class="ligne" type="password" name="motdepasse" placeholder="Mot de passe">  <p class="oubli"><label for="permconn">Se souvenir de moi</label><input type="checkbox" name="permconn" id="permconn" value="1">  <p class="oubli"><a href="gestioncompte?oublimdp">Identifiant ou mot de passe oublié&nbsp;?</a></p>  <p class="oubli"><a href="gestioncompte?creation">Créer un compte</a></p></form>');$('<div id="fenetre_fond"></div>').appendTo("body").click(function(){$("#fenetre,#fenetre_fond").remove()});$("#fenetre a.icon-ferme").on("click",function(){$("#fenetre,#fenetre_fond").remove()});$("#fenetre a.icon-ok").on("click",function(){$.ajax({url:"ajax.php",method:"post",data:$("#fenetre form").serialize()+"&connexion=1",dataType:"json",el:"",beforeSend:function(){$("#load").show(200)},complete:function(){$("#load").hide(200)},fonction:function(el){location.reload(true)}}).done(function(data){if(data["etat"]=="nok")$("#fenetre form p:first").html(data["message"]).addClass("warning")})});$("#fenetre input").on("keypress",function(e){if(e.which==13){$("#fenetre a.icon-ok").click();return false}});$("#fenetre form input:first").focus()});$("a.icon-deconnexion").on("click",function(e){$.ajax({url:"ajax.php",method:"post",data:{action:"deconnexion"},dataType:"json",el:"",fonction:function(el){location.reload(true)}})});$(".icon-menu").on("click",function(){$("nav").toggleClass("visible");$("#recent").removeClass();$("#colonne").toggleClass("visible",$("nav").hasClass("visible"))});$(".icon-recent").on("click",function(){$("#recent").toggleClass("visible");$("nav").removeClass();$("#colonne").toggleClass("visible",$("#recent").hasClass("visible"))});$(".evnmt").on("click",function(){var donnees=evenements[this.id.substr(1)];var el=$('<div id="fenetre"></div>').appendTo("body").html("<h3>"+donnees.titrebis+'</h3>\n<h3 style="margin-bottom: 1em;">'+donnees.date+"</h3>\n<p>"+donnees.texte+"</p>").focus();$('<div id="fenetre_fond"></div>').appendTo("body").click(function(){$("#fenetre,#fenetre_fond").remove()});$('<a class="icon-ferme" title="Fermer"></a>').prependTo(el).on("click",function(){el.remove();$("#fenetre_fond").remove()})})});
diff -urN cahier-de-prepa6.2.0/js/fonctions.min.js cahier-de-prepa8.0.1/js/fonctions.min.js
--- cahier-de-prepa6.2.0/js/fonctions.min.js	2016-08-30 18:10:07.262329604 +0200
+++ cahier-de-prepa8.0.1/js/fonctions.min.js	1970-01-01 01:00:00.000000000 +0100
@@ -1,6 +0,0 @@
-/*//////////////////////////////////////////////////////////////////////////////
-Éléments JavaScript pour l'utilisation de base de Cahier de Prépa
-
-Copie partielle de edition.js
-//////////////////////////////////////////////////////////////////////////////*/// Notification de résultat de requête AJAX
-function affiche(e,t){$("#log").removeClass().addClass(t).html(e).show().delay(5e3).hide(500)}$.fn.placeholder=function(){this.each(function(){var e=$(this);$('<span class="placeholder">'+this.getAttribute("data-placeholder")+"</span>").insertBefore(e).on("click",function(){$(this).next().focus()}).hide(),e.on("change keyup mouseup",function(){!this.value.length&&$(this).is(":visible")?$(this).prev().show():$(this).prev().hide()}).change()})},$(document).ajaxSend(function(e,t,n){$("body").css("cursor","wait")}).ajaxStop(function(){$("body").css("cursor","auto")}).ajaxSuccess(function(e,t,n){var r=t.responseJSON;switch(r.etat){case"ok":affiche(r.message,"ok"),n.fonction(n.el);break;case"nok":affiche(r.message,"nok")}}),$(function(){$("a.icon-connexion").on("click",function(e){$("#log").length||$('<div id="log"></div>').appendTo("body").hide().on("click",function(){$(this).hide()}),$("#colonne,nav").removeClass("visible");var t=$('<div id="fenetre"></div>').appendTo("body").html('<a class="icon-ferme" title="Fermer"></a><a class="icon-ok" title="Valider"></a><h3>Connexion</h3><form>  <p>Veuillez entrer votre identifiant et votre mot de passe&nbsp;:</p>  <input class="ligne" type="text" name="login" data-placeholder="Identifiant">  <input class="ligne" type="password" name="motdepasse" data-placeholder="Mot de passe">  <input class="ligne" type="hidden" name="connexion" value="1">  <p class="oubli"><a href="connexion?oublimdp">Identifiant ou mot de passe oublié&nbsp;?</a></p>  <p class="oubli"><a href="connexion?creationcompte">Créer un compte</a></p></form>').focus();$('<div id="fenetre_fond"></div>').appendTo("body").click(function(){$("#fenetre,#fenetre_fond").remove()}),$("#fenetre a.icon-ferme").on("click",function(){$("#fenetre,#fenetre_fond").remove()}),$("#fenetre input").placeholder(),$("#fenetre a.icon-ok").on("click",function(){$.ajax({url:"ajax.php",method:"post",data:$("#fenetre form").serialize(),dataType:"json",el:"",fonction:function(e){location.reload(!0)}}).done(function(e){e["etat"]=="login_nok"&&$("form p:first").html(e.message).addClass("warning")})}),$("#fenetre input").on("keypress",function(e){e.which==13&&$("#fenetre a.icon-ok").click()}),setTimeout(function(){$("#fenetre input:first").focus()},500)}),$("a.icon-deconnexion").on("click",function(e){$.ajax({url:"ajax.php",method:"post",data:{deconnexion:1},dataType:"json",el:"",fonction:function(e){location.reload(!0)}})}),$(".icon-menu").on("click",function(){$("nav").toggleClass("visible"),$("#recent").removeClass(),$("#colonne").toggleClass("visible",$("nav").hasClass("visible"))}),$(".icon-recent").on("click",function(){$("#recent").toggleClass("visible"),$("nav").removeClass(),$("#colonne").toggleClass("visible",$("#recent").hasClass("visible"))}),$(".evnmt").on("click",function(){var e=evenements[this.id.substr(1)],t=$('<div id="fenetre"></div>').appendTo("body").html("<h3>"+e.titrebis+'</h3>\n<h3 style="margin-bottom: 1em;">'+e.date+"</h3>\n<p>"+e.texte+"</p>").focus();$('<div id="fenetre_fond"></div>').appendTo("body").click(function(){$("#fenetre,#fenetre_fond").remove()}),$('<a class="icon-ferme" title="Fermer"></a>').prependTo(t).on("click",function(){t.remove(),$("#fenetre_fond").remove()})})});
\ No newline at end of file
diff -urN cahier-de-prepa6.2.0/js/html5shiv.min.js cahier-de-prepa8.0.1/js/html5shiv.min.js
--- cahier-de-prepa6.2.0/js/html5shiv.min.js	2015-07-16 01:10:29.326317394 +0200
+++ cahier-de-prepa8.0.1/js/html5shiv.min.js	1970-01-01 01:00:00.000000000 +0100
@@ -1,4 +0,0 @@
-/**
-* @preserve HTML5 Shiv 3.7.3-pre | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed
-*/
-!function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x<style>"+b+"</style>",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.3-pre",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="<xyz></xyz>",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b),"object"==typeof module&&module.exports&&(module.exports=t)}("undefined"!=typeof window?window:this,document);
\ No newline at end of file
diff -urN cahier-de-prepa6.2.0/js/jquery.3.2.1.min.js cahier-de-prepa8.0.1/js/jquery.3.2.1.min.js
--- cahier-de-prepa6.2.0/js/jquery.3.2.1.min.js	1970-01-01 01:00:00.000000000 +0100
+++ cahier-de-prepa8.0.1/js/jquery.3.2.1.min.js	2017-08-18 01:31:49.956311435 +0200
@@ -0,0 +1,4 @@
+/*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */
+!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c<b?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:h,sort:c.sort,splice:c.splice},r.extend=r.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||r.isFunction(g)||(g={}),h===i&&(g=this,h--);h<i;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(r.isPlainObject(d)||(e=Array.isArray(d)))?(e?(e=!1,f=c&&Array.isArray(c)?c:[]):f=c&&r.isPlainObject(c)?c:{},g[b]=r.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},r.extend({expando:"jQuery"+(q+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===r.type(a)},isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=r.type(a);return("number"===b||"string"===b)&&!isNaN(a-parseFloat(a))},isPlainObject:function(a){var b,c;return!(!a||"[object Object]"!==k.call(a))&&(!(b=e(a))||(c=l.call(b,"constructor")&&b.constructor,"function"==typeof c&&m.call(c)===n))},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?j[k.call(a)]||"object":typeof a},globalEval:function(a){p(a)},camelCase:function(a){return a.replace(t,"ms-").replace(u,v)},each:function(a,b){var c,d=0;if(w(a)){for(c=a.length;d<c;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(s,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(w(Object(a))?r.merge(c,"string"==typeof a?[a]:a):h.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:i.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;d<c;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;f<g;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,f=0,h=[];if(w(a))for(d=a.length;f<d;f++)e=b(a[f],f,c),null!=e&&h.push(e);else for(f in a)e=b(a[f],f,c),null!=e&&h.push(e);return g.apply([],h)},guid:1,proxy:function(a,b){var c,d,e;if("string"==typeof b&&(c=a[b],b=a,a=c),r.isFunction(a))return d=f.call(arguments,2),e=function(){return a.apply(b||this,d.concat(f.call(arguments)))},e.guid=a.guid=a.guid||r.guid++,e},now:Date.now,support:o}),"function"==typeof Symbol&&(r.fn[Symbol.iterator]=c[Symbol.iterator]),r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){j["[object "+b+"]"]=b.toLowerCase()});function w(a){var b=!!a&&"length"in a&&a.length,c=r.type(a);return"function"!==c&&!r.isWindow(a)&&("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",M="\\["+K+"*("+L+")(?:"+K+"*([*^$|!~]?=)"+K+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+L+"))|)"+K+"*\\]",N=":("+L+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",O=new RegExp(K+"+","g"),P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\r\\' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c<b;c+=2)a.push(c);return a}),odd:pa(function(a,b){for(var c=1;c<b;c+=2)a.push(c);return a}),lt:pa(function(a,b,c){for(var d=c<0?c+b:c;--d>=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function ra(){}ra.prototype=d.filters=d.pseudos,d.setFilters=new ra,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){c&&!(e=Q.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=R.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(P," ")}),h=h.slice(c.length));for(g in d.filter)!(e=V[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function sa(a){for(var b=0,c=a.length,d="";b<c;b++)d+=a[b].value;return d}function ta(a,b,c){var d=b.dir,e=b.next,f=e||d,g=c&&"parentNode"===f,h=x++;return b.first?function(b,c,e){while(b=b[d])if(1===b.nodeType||g)return a(b,c,e);return!1}:function(b,c,i){var j,k,l,m=[w,h];if(i){while(b=b[d])if((1===b.nodeType||g)&&a(b,c,i))return!0}else while(b=b[d])if(1===b.nodeType||g)if(l=b[u]||(b[u]={}),k=l[b.uniqueID]||(l[b.uniqueID]={}),e&&e===b.nodeName.toLowerCase())b=b[d]||b;else{if((j=k[f])&&j[0]===w&&j[1]===h)return m[2]=j[2];if(k[f]=m,m[2]=a(b,c,i))return!0}return!1}}function ua(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d<e;d++)ga(a,b[d],c);return c}function wa(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;h<i;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function xa(a,b,c,d,e,f){return d&&!d[u]&&(d=xa(d)),e&&!e[u]&&(e=xa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||va(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:wa(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=wa(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i<f;i++)if(c=d.relative[a[i].type])m=[ta(ua(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;e<f;e++)if(d.relative[a[e].type])break;return xa(i>1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i<e&&ya(a.slice(i,e)),e<f&&ya(a=a.slice(e)),e<f&&sa(a))}m.push(c)}return ua(m)}function za(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b<d;b++)if(r.contains(e[b],this))return!0}));for(c=this.pushStack([]),b=0;b<d;b++)r.find(a,e[b],c);return d>1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a<c;a++)if(r.contains(this,b[a]))return!0})},closest:function(a,b){var c,d=0,e=this.length,f=[],g="string"!=typeof a&&r(a);if(!A.test(a))for(;d<e;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h<f.length)f[h].apply(c[0],c[1])===!1&&a.stopOnFalse&&(h=f.length,c=!1)}a.memory||(c=!1),b=!1,e&&(f=c?[]:"")},j={add:function(){return f&&(c&&!b&&(h=f.length-1,g.push(c)),function d(b){r.each(b,function(b,c){r.isFunction(c)?a.unique&&j.has(c)||f.push(c):c&&c.length&&"string"!==r.type(c)&&d(c)})}(arguments),c&&!b&&i()),this},remove:function(){return r.each(arguments,function(a,b){var c;while((c=r.inArray(b,f,c))>-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b<f)){if(a=d.apply(h,i),a===c.promise())throw new TypeError("Thenable self-resolution");j=a&&("object"==typeof a||"function"==typeof a)&&a.then,r.isFunction(j)?e?j.call(a,g(f,c,N,e),g(f,c,O,e)):(f++,j.call(a,g(f,c,N,e),g(f,c,O,e),g(f,c,N,c.notifyWith))):(d!==N&&(h=void 0,i=[a]),(e||c.resolveWith)(h,i))}},k=e?j:function(){try{j()}catch(a){r.Deferred.exceptionHook&&r.Deferred.exceptionHook(a,k.stackTrace),b+1>=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S),
+a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h<i;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},U=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function V(){this.expando=r.expando+V.uid++}V.uid=1,V.prototype={cache:function(a){var b=a[this.expando];return b||(b={},U(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[r.camelCase(b)]=c;else for(d in b)e[r.camelCase(d)]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][r.camelCase(b)]},access:function(a,b,c){return void 0===b||b&&"string"==typeof b&&void 0===c?this.get(a,b):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d=a[this.expando];if(void 0!==d){if(void 0!==b){Array.isArray(b)?b=b.map(r.camelCase):(b=r.camelCase(b),b=b in d?[b]:b.match(L)||[]),c=b.length;while(c--)delete d[b[c]]}(void 0===b||r.isEmptyObject(d))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!r.isEmptyObject(b)}};var W=new V,X=new V,Y=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Z=/[A-Z]/g;function $(a){return"true"===a||"false"!==a&&("null"===a?null:a===+a+""?+a:Y.test(a)?JSON.parse(a):a)}function _(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Z,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c=$(c)}catch(e){}X.set(a,b,c)}else c=void 0;return c}r.extend({hasData:function(a){return X.hasData(a)||W.hasData(a)},data:function(a,b,c){return X.access(a,b,c)},removeData:function(a,b){X.remove(a,b)},_data:function(a,b,c){return W.access(a,b,c)},_removeData:function(a,b){W.remove(a,b)}}),r.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=X.get(f),1===f.nodeType&&!W.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=r.camelCase(d.slice(5)),_(f,d,e[d])));W.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){X.set(this,a)}):T(this,function(b){var c;if(f&&void 0===b){if(c=X.get(f,a),void 0!==c)return c;if(c=_(f,a),void 0!==c)return c}else this.each(function(){X.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?r.queue(this[0],a):void 0===b?this:this.each(function(){var c=r.queue(this,a,b);r._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&r.dequeue(this,a)})},dequeue:function(a){return this.each(function(){r.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=r.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=W.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var aa=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,ba=new RegExp("^(?:([+-])=|)("+aa+")([a-z%]*)$","i"),ca=["Top","Right","Bottom","Left"],da=function(a,b){return a=b||a,"none"===a.style.display||""===a.style.display&&r.contains(a.ownerDocument,a)&&"none"===r.css(a,"display")},ea=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};function fa(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return r.css(a,b,"")},i=h(),j=c&&c[3]||(r.cssNumber[b]?"":"px"),k=(r.cssNumber[b]||"px"!==j&&+i)&&ba.exec(r.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,r.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var ga={};function ha(a){var b,c=a.ownerDocument,d=a.nodeName,e=ga[d];return e?e:(b=c.body.appendChild(c.createElement(d)),e=r.css(b,"display"),b.parentNode.removeChild(b),"none"===e&&(e="block"),ga[d]=e,e)}function ia(a,b){for(var c,d,e=[],f=0,g=a.length;f<g;f++)d=a[f],d.style&&(c=d.style.display,b?("none"===c&&(e[f]=W.get(d,"display")||null,e[f]||(d.style.display="")),""===d.style.display&&da(d)&&(e[f]=ha(d))):"none"!==c&&(e[f]="none",W.set(d,"display",c)));for(f=0;f<g;f++)null!=e[f]&&(a[f].style.display=e[f]);return a}r.fn.extend({show:function(){return ia(this,!0)},hide:function(){return ia(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){da(this)?r(this).show():r(this).hide()})}});var ja=/^(?:checkbox|radio)$/i,ka=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c<d;c++)W.set(a[c],"globalEval",!b||W.get(b[c],"globalEval"))}var pa=/<|&#?\w+;/;function qa(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],n=0,o=a.length;n<o;n++)if(f=a[n],f||0===f)if("object"===r.type(f))r.merge(m,f.nodeType?[f]:f);else if(pa.test(f)){g=g||l.appendChild(b.createElement("div")),h=(ka.exec(f)||["",""])[1].toLowerCase(),i=ma[h]||ma._default,g.innerHTML=i[1]+r.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;r.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",n=0;while(f=m[n++])if(d&&r.inArray(f,d)>-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c<arguments.length;c++)i[c]=arguments[c];if(b.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,b)!==!1){h=r.event.handlers.call(this,b,j),c=0;while((f=h[c++])&&!b.isPropagationStopped()){b.currentTarget=f.elem,d=0;while((g=f.handlers[d++])&&!b.isImmediatePropagationStopped())b.rnamespace&&!b.rnamespace.test(g.namespace)||(b.handleObj=g,b.data=g.data,e=((r.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(b.result=e)===!1&&(b.preventDefault(),b.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,b),b.result}},handlers:function(a,b){var c,d,e,f,g,h=[],i=b.delegateCount,j=a.target;if(i&&j.nodeType&&!("click"===a.type&&a.button>=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c<i;c++)d=b[c],e=d.selector+" ",void 0===g[e]&&(g[e]=d.needsContext?r(e,this).index(j)>-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i<b.length&&h.push({elem:j,handlers:b.slice(i)}),h},addProp:function(a,b){Object.defineProperty(r.Event.prototype,a,{enumerable:!0,configurable:!0,get:r.isFunction(b)?function(){if(this.originalEvent)return b(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[a]},set:function(b){Object.defineProperty(this,a,{enumerable:!0,configurable:!0,writable:!0,value:b})}})},fix:function(a){return a[r.expando]?a:new r.Event(a)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==xa()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===xa()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&B(this,"input"))return this.click(),!1},_default:function(a){return B(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}}},r.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c)},r.Event=function(a,b){return this instanceof r.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?va:wa,this.target=a.target&&3===a.target.nodeType?a.target.parentNode:a.target,this.currentTarget=a.currentTarget,this.relatedTarget=a.relatedTarget):this.type=a,b&&r.extend(this,b),this.timeStamp=a&&a.timeStamp||r.now(),void(this[r.expando]=!0)):new r.Event(a,b)},r.Event.prototype={constructor:r.Event,isDefaultPrevented:wa,isPropagationStopped:wa,isImmediatePropagationStopped:wa,isSimulated:!1,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=va,a&&!this.isSimulated&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=va,a&&!this.isSimulated&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=va,a&&!this.isSimulated&&a.stopImmediatePropagation(),this.stopPropagation()}},r.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(a){var b=a.button;return null==a.which&&sa.test(a.type)?null!=a.charCode?a.charCode:a.keyCode:!a.which&&void 0!==b&&ta.test(a.type)?1&b?1:2&b?3:4&b?2:0:a.which}},r.event.addProp),r.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){r.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return e&&(e===d||r.contains(d,e))||(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),r.fn.extend({on:function(a,b,c,d){return ya(this,a,b,c,d)},one:function(a,b,c,d){return ya(this,a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,r(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return b!==!1&&"function"!=typeof b||(c=b,b=void 0),c===!1&&(c=wa),this.each(function(){r.event.remove(this,a,c,b)})}});var za=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/<script|<style|<link/i,Ba=/checked\s*(?:[^=]|=\s*.checked.)/i,Ca=/^true\/(.*)/,Da=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Ea(a,b){return B(a,"table")&&B(11!==b.nodeType?b:b.firstChild,"tr")?r(">tbody",a)[0]||a:a}function Fa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ga(a){var b=Ca.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ha(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(W.hasData(a)&&(f=W.access(a),g=W.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c<d;c++)r.event.add(b,e,j[e][c])}X.hasData(a)&&(h=X.access(a),i=r.extend({},h),X.set(b,i))}}function Ia(a,b){var c=b.nodeName.toLowerCase();"input"===c&&ja.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function Ja(a,b,c,d){b=g.apply([],b);var e,f,h,i,j,k,l=0,m=a.length,n=m-1,q=b[0],s=r.isFunction(q);if(s||m>1&&"string"==typeof q&&!o.checkClone&&Ba.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ja(f,b,c,d)});if(m&&(e=qa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(na(e,"script"),Fa),i=h.length;l<m;l++)j=e,l!==n&&(j=r.clone(j,!0,!0),i&&r.merge(h,na(j,"script"))),c.call(a[l],j,l);if(i)for(k=h[h.length-1].ownerDocument,r.map(h,Ga),l=0;l<i;l++)j=h[l],la.test(j.type||"")&&!W.access(j,"globalEval")&&r.contains(k,j)&&(j.src?r._evalUrl&&r._evalUrl(j.src):p(j.textContent.replace(Da,""),k))}return a}function Ka(a,b,c){for(var d,e=b?r.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||r.cleanData(na(d)),d.parentNode&&(c&&r.contains(d.ownerDocument,d)&&oa(na(d,"script")),d.parentNode.removeChild(d));return a}r.extend({htmlPrefilter:function(a){return a.replace(za,"<$1></$2>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=na(h),f=na(a),d=0,e=f.length;d<e;d++)Ia(f[d],g[d]);if(b)if(c)for(f=f||na(a),g=g||na(h),d=0,e=f.length;d<e;d++)Ha(f[d],g[d]);else Ha(a,h);return g=na(h,"script"),g.length>0&&oa(g,!i&&na(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(U(c)){if(b=c[W.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[W.expando]=void 0}c[X.expando]&&(c[X.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ka(this,a,!0)},remove:function(a){return Ka(this,a)},text:function(a){return T(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.appendChild(a)}})},prepend:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(na(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return T(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!Aa.test(a)&&!ma[(ka.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c<d;c++)b=this[c]||{},1===b.nodeType&&(r.cleanData(na(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ja(this,arguments,function(b){var c=this.parentNode;r.inArray(this,a)<0&&(r.cleanData(na(this)),c&&c.replaceChild(b,this))},a)}}),r.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){r.fn[a]=function(a){for(var c,d=[],e=r(a),f=e.length-1,g=0;g<=f;g++)c=g===f?this:this.clone(!0),r(e[g])[b](c),h.apply(d,c.get());return this.pushStack(d)}});var La=/^margin/,Ma=new RegExp("^("+aa+")(?!px)[a-z%]+$","i"),Na=function(b){var c=b.ownerDocument.defaultView;return c&&c.opener||(c=a),c.getComputedStyle(b)};!function(){function b(){if(i){i.style.cssText="box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",i.innerHTML="",ra.appendChild(h);var b=a.getComputedStyle(i);c="1%"!==b.top,g="2px"===b.marginLeft,e="4px"===b.width,i.style.marginRight="50%",f="4px"===b.marginRight,ra.removeChild(h),i=null}}var c,e,f,g,h=d.createElement("div"),i=d.createElement("div");i.style&&(i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",o.clearCloneStyle="content-box"===i.style.backgroundClip,h.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",h.appendChild(i),r.extend(o,{pixelPosition:function(){return b(),c},boxSizingReliable:function(){return b(),e},pixelMarginRight:function(){return b(),f},reliableMarginLeft:function(){return b(),g}}))}();function Oa(a,b,c){var d,e,f,g,h=a.style;return c=c||Na(a),c&&(g=c.getPropertyValue(b)||c[b],""!==g||r.contains(a.ownerDocument,a)||(g=r.style(a,b)),!o.pixelMarginRight()&&Ma.test(g)&&La.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function Pa(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}var Qa=/^(none|table(?!-c[ea]).+)/,Ra=/^--/,Sa={position:"absolute",visibility:"hidden",display:"block"},Ta={letterSpacing:"0",fontWeight:"400"},Ua=["Webkit","Moz","ms"],Va=d.createElement("div").style;function Wa(a){if(a in Va)return a;var b=a[0].toUpperCase()+a.slice(1),c=Ua.length;while(c--)if(a=Ua[c]+b,a in Va)return a}function Xa(a){var b=r.cssProps[a];return b||(b=r.cssProps[a]=Wa(a)||a),b}function Ya(a,b,c){var d=ba.exec(b);return d?Math.max(0,d[2]-(c||0))+(d[3]||"px"):b}function Za(a,b,c,d,e){var f,g=0;for(f=c===(d?"border":"content")?4:"width"===b?1:0;f<4;f+=2)"margin"===c&&(g+=r.css(a,c+ca[f],!0,e)),d?("content"===c&&(g-=r.css(a,"padding"+ca[f],!0,e)),"margin"!==c&&(g-=r.css(a,"border"+ca[f]+"Width",!0,e))):(g+=r.css(a,"padding"+ca[f],!0,e),"padding"!==c&&(g+=r.css(a,"border"+ca[f]+"Width",!0,e)));return g}function $a(a,b,c){var d,e=Na(a),f=Oa(a,b,e),g="border-box"===r.css(a,"boxSizing",!1,e);return Ma.test(f)?f:(d=g&&(o.boxSizingReliable()||f===a.style[b]),"auto"===f&&(f=a["offset"+b[0].toUpperCase()+b.slice(1)]),f=parseFloat(f)||0,f+Za(a,b,c||(g?"border":"content"),d,e)+"px")}r.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Oa(a,"opacity");return""===c?"1":c}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=r.camelCase(b),i=Ra.test(b),j=a.style;return i||(b=Xa(h)),g=r.cssHooks[b]||r.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:j[b]:(f=typeof c,"string"===f&&(e=ba.exec(c))&&e[1]&&(c=fa(a,b,e),f="number"),null!=c&&c===c&&("number"===f&&(c+=e&&e[3]||(r.cssNumber[h]?"":"px")),o.clearCloneStyle||""!==c||0!==b.indexOf("background")||(j[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i?j.setProperty(b,c):j[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=r.camelCase(b),i=Ra.test(b);return i||(b=Xa(h)),g=r.cssHooks[b]||r.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=Oa(a,b,d)),"normal"===e&&b in Ta&&(e=Ta[b]),""===c||c?(f=parseFloat(e),c===!0||isFinite(f)?f||0:e):e}}),r.each(["height","width"],function(a,b){r.cssHooks[b]={get:function(a,c,d){if(c)return!Qa.test(r.css(a,"display"))||a.getClientRects().length&&a.getBoundingClientRect().width?$a(a,b,d):ea(a,Sa,function(){return $a(a,b,d)})},set:function(a,c,d){var e,f=d&&Na(a),g=d&&Za(a,b,d,"border-box"===r.css(a,"boxSizing",!1,f),f);return g&&(e=ba.exec(c))&&"px"!==(e[3]||"px")&&(a.style[b]=c,c=r.css(a,b)),Ya(a,c,g)}}}),r.cssHooks.marginLeft=Pa(o.reliableMarginLeft,function(a,b){if(b)return(parseFloat(Oa(a,"marginLeft"))||a.getBoundingClientRect().left-ea(a,{marginLeft:0},function(){return a.getBoundingClientRect().left}))+"px"}),r.each({margin:"",padding:"",border:"Width"},function(a,b){r.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];d<4;d++)e[a+ca[d]+b]=f[d]||f[d-2]||f[0];return e}},La.test(a)||(r.cssHooks[a+b].set=Ya)}),r.fn.extend({css:function(a,b){return T(this,function(a,b,c){var d,e,f={},g=0;if(Array.isArray(b)){for(d=Na(a),e=b.length;g<e;g++)f[b[g]]=r.css(a,b[g],!1,d);return f}return void 0!==c?r.style(a,b,c):r.css(a,b)},a,b,arguments.length>1)}});function _a(a,b,c,d,e){return new _a.prototype.init(a,b,c,d,e)}r.Tween=_a,_a.prototype={constructor:_a,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=_a.propHooks[this.prop];return a&&a.get?a.get(this):_a.propHooks._default.get(this)},run:function(a){var b,c=_a.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):_a.propHooks._default.set(this),this}},_a.prototype.init.prototype=_a.prototype,_a.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},_a.propHooks.scrollTop=_a.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=_a.prototype.init,r.fx.step={};var ab,bb,cb=/^(?:toggle|show|hide)$/,db=/queueHooks$/;function eb(){bb&&(d.hidden===!1&&a.requestAnimationFrame?a.requestAnimationFrame(eb):a.setTimeout(eb,r.fx.interval),r.fx.tick())}function fb(){return a.setTimeout(function(){ab=void 0}),ab=r.now()}function gb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ca[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function hb(a,b,c){for(var d,e=(kb.tweeners[b]||[]).concat(kb.tweeners["*"]),f=0,g=e.length;f<g;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,k,l="width"in b||"height"in b,m=this,n={},o=a.style,p=a.nodeType&&da(a),q=W.get(a,"fxshow");c.queue||(g=r._queueHooks(a,"fx"),null==g.unqueued&&(g.unqueued=0,h=g.empty.fire,g.empty.fire=function(){g.unqueued||h()}),g.unqueued++,m.always(function(){m.always(function(){g.unqueued--,r.queue(a,"fx").length||g.empty.fire()})}));for(d in b)if(e=b[d],cb.test(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}n[d]=q&&q[d]||r.style(a,d)}if(i=!r.isEmptyObject(b),i||!r.isEmptyObject(n)){l&&1===a.nodeType&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=q&&q.display,null==j&&(j=W.get(a,"display")),k=r.css(a,"display"),"none"===k&&(j?k=j:(ia([a],!0),j=a.style.display||j,k=r.css(a,"display"),ia([a]))),("inline"===k||"inline-block"===k&&null!=j)&&"none"===r.css(a,"float")&&(i||(m.done(function(){o.display=j}),null==j&&(k=o.display,j="none"===k?"":k)),o.display="inline-block")),c.overflow&&(o.overflow="hidden",m.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]})),i=!1;for(d in n)i||(q?"hidden"in q&&(p=q.hidden):q=W.access(a,"fxshow",{display:j}),f&&(q.hidden=!p),p&&ia([a],!0),m.done(function(){p||ia([a]),W.remove(a,"fxshow");for(d in n)r.style(a,d,n[d])})),i=hb(p?q[d]:0,d,m),d in q||(q[d]=i.start,p&&(i.end=i.start,i.start=0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=r.camelCase(c),e=b[d],f=a[c],Array.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=r.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=kb.prefilters.length,h=r.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=ab||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;g<i;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),f<1&&i?c:(i||h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:r.extend({},b),opts:r.extend(!0,{specialEasing:{},easing:r.easing._default},c),originalProperties:b,originalOptions:c,startTime:ab||fb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=r.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;c<d;c++)j.tweens[c].run(1);return b?(h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j,b])):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jb(k,j.opts.specialEasing);f<g;f++)if(d=kb.prefilters[f].call(j,a,k,j.opts))return r.isFunction(d.stop)&&(r._queueHooks(j.elem,j.opts.queue).stop=r.proxy(d.stop,d)),d;return r.map(k,hb,j),r.isFunction(j.opts.start)&&j.opts.start.call(a,j),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always),r.fx.timer(r.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j}r.Animation=r.extend(kb,{tweeners:{"*":[function(a,b){var c=this.createTween(a,b);return fa(c.elem,a,ba.exec(b),c),c}]},tweener:function(a,b){r.isFunction(a)?(b=a,a=["*"]):a=a.match(L);for(var c,d=0,e=a.length;d<e;d++)c=a[d],kb.tweeners[c]=kb.tweeners[c]||[],kb.tweeners[c].unshift(b)},prefilters:[ib],prefilter:function(a,b){b?kb.prefilters.unshift(a):kb.prefilters.push(a)}}),r.speed=function(a,b,c){var d=a&&"object"==typeof a?r.extend({},a):{complete:c||!c&&b||r.isFunction(a)&&a,duration:a,easing:c&&b||b&&!r.isFunction(b)&&b};return r.fx.off?d.duration=0:"number"!=typeof d.duration&&(d.duration in r.fx.speeds?d.duration=r.fx.speeds[d.duration]:d.duration=r.fx.speeds._default),null!=d.queue&&d.queue!==!0||(d.queue="fx"),d.old=d.complete,d.complete=function(){r.isFunction(d.old)&&d.old.call(this),d.queue&&r.dequeue(this,d.queue)},d},r.fn.extend({fadeTo:function(a,b,c,d){return this.filter(da).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=r.isEmptyObject(a),f=r.speed(b,c,d),g=function(){var b=kb(this,r.extend({},a),f);(e||W.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=r.timers,g=W.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&db.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));!b&&c||r.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=W.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=r.timers,g=d?d.length:0;for(c.finish=!0,r.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;b<g;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),r.each(["toggle","show","hide"],function(a,b){var c=r.fn[b];r.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gb(b,!0),a,d,e)}}),r.each({slideDown:gb("show"),slideUp:gb("hide"),slideToggle:gb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){r.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),r.timers=[],r.fx.tick=function(){var a,b=0,c=r.timers;for(ab=r.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||r.fx.stop(),ab=void 0},r.fx.timer=function(a){r.timers.push(a),r.fx.start()},r.fx.interval=13,r.fx.start=function(){bb||(bb=!0,eb())},r.fx.stop=function(){bb=null},r.fx.speeds={slow:600,fast:200,_default:400},r.fn.delay=function(b,c){return b=r.fx?r.fx.speeds[b]||b:b,c=c||"fx",this.queue(c,function(c,d){var e=a.setTimeout(c,b);d.stop=function(){a.clearTimeout(e)}})},function(){var a=d.createElement("input"),b=d.createElement("select"),c=b.appendChild(d.createElement("option"));a.type="checkbox",o.checkOn=""!==a.value,o.optSelected=c.selected,a=d.createElement("input"),a.value="t",a.type="radio",o.radioValue="t"===a.value}();var lb,mb=r.expr.attrHandle;r.fn.extend({attr:function(a,b){return T(this,r.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?lb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),
+null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&B(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(L);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),lb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=mb[b]||r.find.attr;mb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=mb[g],mb[g]=e,e=null!=c(a,b,d)?g:null,mb[g]=f),e}});var nb=/^(?:input|select|textarea|button)$/i,ob=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return T(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):nb.test(a.nodeName)||ob.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function pb(a){var b=a.match(L)||[];return b.join(" ")}function qb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,qb(this)))});if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,qb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,qb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(L)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=qb(this),b&&W.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":W.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+pb(qb(c))+" ").indexOf(b)>-1)return!0;return!1}});var rb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":Array.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:pb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d<i;d++)if(c=e[d],(c.selected||d===f)&&!c.disabled&&(!c.parentNode.disabled||!B(c.parentNode,"optgroup"))){if(b=r(c).val(),g)return b;h.push(b)}return h},set:function(a,b){var c,d,e=a.options,f=r.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=r.inArray(r.valHooks.option.get(d),f)>-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(Array.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var sb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!sb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,sb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(W.get(h,"events")||{})[b.type]&&W.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&U(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!U(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=W.access(d,b);e||d.addEventListener(a,c,!0),W.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=W.access(d,b)-1;e?W.access(d,b,e):(d.removeEventListener(a,c,!0),W.remove(d,b))}}});var tb=a.location,ub=r.now(),vb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(Array.isArray(b))r.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(Array.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!ja.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:Array.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}});var Bb=/%20/g,Cb=/#.*$/,Db=/([?&])_=[^&]*/,Eb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Fb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Gb=/^(?:GET|HEAD)$/,Hb=/^\/\//,Ib={},Jb={},Kb="*/".concat("*"),Lb=d.createElement("a");Lb.href=tb.href;function Mb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(L)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Nb(a,b,c,d){var e={},f=a===Jb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Ob(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Pb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Qb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:tb.href,type:"GET",isLocal:Fb.test(tb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Ob(Ob(a,r.ajaxSettings),b):Ob(r.ajaxSettings,a)},ajaxPrefilter:Mb(Ib),ajaxTransport:Mb(Jb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Eb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||tb.href)+"").replace(Hb,tb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(L)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Lb.protocol+"//"+Lb.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Nb(Ib,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Gb.test(o.type),f=o.url.replace(Cb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(Bb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(vb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Db,"$1"),n=(vb.test(f)?"&":"?")+"_="+ub++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Kb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Nb(Jb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Pb(o,y,d)),v=Qb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Rb={0:200,1223:204},Sb=r.ajaxSettings.xhr();o.cors=!!Sb&&"withCredentials"in Sb,o.ajax=Sb=!!Sb,r.ajaxTransport(function(b){var c,d;if(o.cors||Sb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Rb[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("<script>").prop({charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&f("error"===a.type?404:200,a.type)}),d.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Tb=[],Ub=/(=)\?(?=&|$)|\?\?/;r.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Tb.pop()||r.expando+"_"+ub++;return this[a]=!0,a}}),r.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Ub.test(b.url)?"url":"string"==typeof b.data&&0===(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ub.test(b.data)&&"data");if(h||"jsonp"===b.dataTypes[0])return e=b.jsonpCallback=r.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Ub,"$1"+e):b.jsonp!==!1&&(b.url+=(vb.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||r.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){void 0===f?r(a).removeProp(e):a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Tb.push(e)),g&&r.isFunction(f)&&f(g[0]),g=f=void 0}),"script"}),o.createHTMLDocument=function(){var a=d.implementation.createHTMLDocument("").body;return a.innerHTML="<form></form><form></form>",2===a.childNodes.length}(),r.parseHTML=function(a,b,c){if("string"!=typeof a)return[];"boolean"==typeof b&&(c=b,b=!1);var e,f,g;return b||(o.createHTMLDocument?(b=d.implementation.createHTMLDocument(""),e=b.createElement("base"),e.href=d.location.href,b.head.appendChild(e)):b=d),f=C.exec(a),g=!c&&[],f?[b.createElement(f[1])]:(f=qa([a],b,g),g&&g.length&&r(g).remove(),r.merge([],f.childNodes))},r.fn.load=function(a,b,c){var d,e,f,g=this,h=a.indexOf(" ");return h>-1&&(d=pb(a.slice(h)),a=a.slice(0,h)),r.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&r.ajax({url:a,type:e||"GET",dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?r("<div>").append(r.parseHTML(a)).find(d):a)}).always(c&&function(a,b){g.each(function(){c.apply(this,f||[a.responseText,b,a])})}),this},r.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){r.fn[b]=function(a){return this.on(b,a)}}),r.expr.pseudos.animated=function(a){return r.grep(r.timers,function(b){return a===b.elem}).length},r.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=r.css(a,"position"),l=r(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=r.css(a,"top"),i=r.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),r.isFunction(b)&&(b=b.call(a,c,r.extend({},h))),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},r.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){r.offset.setOffset(this,a,b)});var b,c,d,e,f=this[0];if(f)return f.getClientRects().length?(d=f.getBoundingClientRect(),b=f.ownerDocument,c=b.documentElement,e=b.defaultView,{top:d.top+e.pageYOffset-c.clientTop,left:d.left+e.pageXOffset-c.clientLeft}):{top:0,left:0}},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===r.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),B(a[0],"html")||(d=a.offset()),d={top:d.top+r.css(a[0],"borderTopWidth",!0),left:d.left+r.css(a[0],"borderLeftWidth",!0)}),{top:b.top-d.top-r.css(c,"marginTop",!0),left:b.left-d.left-r.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent;while(a&&"static"===r.css(a,"position"))a=a.offsetParent;return a||ra})}}),r.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c="pageYOffset"===b;r.fn[a]=function(d){return T(this,function(a,d,e){var f;return r.isWindow(a)?f=a:9===a.nodeType&&(f=a.defaultView),void 0===e?f?f[b]:a[d]:void(f?f.scrollTo(c?f.pageXOffset:e,c?e:f.pageYOffset):a[d]=e)},a,d,arguments.length)}}),r.each(["top","left"],function(a,b){r.cssHooks[b]=Pa(o.pixelPosition,function(a,c){if(c)return c=Oa(a,b),Ma.test(c)?r(a).position()[b]+"px":c})}),r.each({Height:"height",Width:"width"},function(a,b){r.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){r.fn[d]=function(e,f){var g=arguments.length&&(c||"boolean"!=typeof e),h=c||(e===!0||f===!0?"margin":"border");return T(this,function(b,c,e){var f;return r.isWindow(b)?0===d.indexOf("outer")?b["inner"+a]:b.document.documentElement["client"+a]:9===b.nodeType?(f=b.documentElement,Math.max(b.body["scroll"+a],f["scroll"+a],b.body["offset"+a],f["offset"+a],f["client"+a])):void 0===e?r.css(b,c,h):r.style(b,c,e,h)},b,g?e:void 0,g)}})}),r.fn.extend({bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}}),r.holdReady=function(a){a?r.readyWait++:r.ready(!0)},r.isArray=Array.isArray,r.parseJSON=JSON.parse,r.nodeName=B,"function"==typeof define&&define.amd&&define("jquery",[],function(){return r});var Vb=a.jQuery,Wb=a.$;return r.noConflict=function(b){return a.$===r&&(a.$=Wb),b&&a.jQuery===r&&(a.jQuery=Vb),r},b||(a.jQuery=a.$=r),r});
diff -urN cahier-de-prepa6.2.0/js/jquery.min.js cahier-de-prepa8.0.1/js/jquery.min.js
--- cahier-de-prepa6.2.0/js/jquery.min.js	2016-07-21 11:28:38.874197523 +0200
+++ cahier-de-prepa8.0.1/js/jquery.min.js	2018-01-20 18:26:44.000000000 +0100
@@ -1,4 +1,2 @@
-/*! jQuery v3.1.0 | (c) jQuery Foundation | jquery.org/license */
-!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.0",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null!=a?a<0?this[a+this.length]:this[a]:f.call(this)},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c<b?[this[c]]:[])},end:function(){return this.prevObject||this.constructor()},push:h,sort:c.sort,splice:c.splice},r.extend=r.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||r.isFunction(g)||(g={}),h===i&&(g=this,h--);h<i;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(r.isPlainObject(d)||(e=r.isArray(d)))?(e?(e=!1,f=c&&r.isArray(c)?c:[]):f=c&&r.isPlainObject(c)?c:{},g[b]=r.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},r.extend({expando:"jQuery"+(q+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===r.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){var b=r.type(a);return("number"===b||"string"===b)&&!isNaN(a-parseFloat(a))},isPlainObject:function(a){var b,c;return!(!a||"[object Object]"!==k.call(a))&&(!(b=e(a))||(c=l.call(b,"constructor")&&b.constructor,"function"==typeof c&&m.call(c)===n))},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?j[k.call(a)]||"object":typeof a},globalEval:function(a){p(a)},camelCase:function(a){return a.replace(t,"ms-").replace(u,v)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b){var c,d=0;if(w(a)){for(c=a.length;d<c;d++)if(b.call(a[d],d,a[d])===!1)break}else for(d in a)if(b.call(a[d],d,a[d])===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(s,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(w(Object(a))?r.merge(c,"string"==typeof a?[a]:a):h.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:i.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;d<c;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;f<g;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,e,f=0,h=[];if(w(a))for(d=a.length;f<d;f++)e=b(a[f],f,c),null!=e&&h.push(e);else for(f in a)e=b(a[f],f,c),null!=e&&h.push(e);return g.apply([],h)},guid:1,proxy:function(a,b){var c,d,e;if("string"==typeof b&&(c=a[b],b=a,a=c),r.isFunction(a))return d=f.call(arguments,2),e=function(){return a.apply(b||this,d.concat(f.call(arguments)))},e.guid=a.guid=a.guid||r.guid++,e},now:Date.now,support:o}),"function"==typeof Symbol&&(r.fn[Symbol.iterator]=c[Symbol.iterator]),r.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(a,b){j["[object "+b+"]"]=b.toLowerCase()});function w(a){var b=!!a&&"length"in a&&a.length,c=r.type(a);return"function"!==c&&!r.isWindow(a)&&("array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},J="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",K="[\\x20\\t\\r\\n\\f]",L="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",M="\\["+K+"*("+L+")(?:"+K+"*([*^$|!~]?=)"+K+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+L+"))|)"+K+"*\\]",N=":("+L+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+M+")*)|.*)\\)|)",O=new RegExp(K+"+","g"),P=new RegExp("^"+K+"+|((?:^|[^\\\\])(?:\\\\.)*)"+K+"+$","g"),Q=new RegExp("^"+K+"*,"+K+"*"),R=new RegExp("^"+K+"*([>+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"label"in b&&b.disabled===a||"form"in b&&b.disabled===a||"form"in b&&b.disabled===!1&&(b.isDisabled===a||b.isDisabled!==!a&&("label"in b||!ea(b))!==a)}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}},d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="<a id='"+u+"'></a><select id='"+u+"-\r\\' msallowcapture=''><option selected=''></option></select>",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c<b;c+=2)a.push(c);return a}),odd:pa(function(a,b){for(var c=1;c<b;c+=2)a.push(c);return a}),lt:pa(function(a,b,c){for(var d=c<0?c+b:c;--d>=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d<b;)a.push(d);return a})}},d.pseudos.nth=d.pseudos.eq;for(b in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})d.pseudos[b]=ma(b);for(b in{submit:!0,reset:!0})d.pseudos[b]=na(b);function ra(){}ra.prototype=d.filters=d.pseudos,d.setFilters=new ra,g=ga.tokenize=function(a,b){var c,e,f,g,h,i,j,k=z[a+" "];if(k)return b?0:k.slice(0);h=a,i=[],j=d.preFilter;while(h){c&&!(e=Q.exec(h))||(e&&(h=h.slice(e[0].length)||h),i.push(f=[])),c=!1,(e=R.exec(h))&&(c=e.shift(),f.push({value:c,type:e[0].replace(P," ")}),h=h.slice(c.length));for(g in d.filter)!(e=V[g].exec(h))||j[g]&&!(e=j[g](e))||(c=e.shift(),f.push({value:c,type:g,matches:e}),h=h.slice(c.length));if(!c)break}return b?h.length:h?ga.error(a):z(a,i).slice(0)};function sa(a){for(var b=0,c=a.length,d="";b<c;b++)d+=a[b].value;return d}function ta(a,b,c){var d=b.dir,e=b.next,f=e||d,g=c&&"parentNode"===f,h=x++;return b.first?function(b,c,e){while(b=b[d])if(1===b.nodeType||g)return a(b,c,e)}:function(b,c,i){var j,k,l,m=[w,h];if(i){while(b=b[d])if((1===b.nodeType||g)&&a(b,c,i))return!0}else while(b=b[d])if(1===b.nodeType||g)if(l=b[u]||(b[u]={}),k=l[b.uniqueID]||(l[b.uniqueID]={}),e&&e===b.nodeName.toLowerCase())b=b[d]||b;else{if((j=k[f])&&j[0]===w&&j[1]===h)return m[2]=j[2];if(k[f]=m,m[2]=a(b,c,i))return!0}}}function ua(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d<e;d++)ga(a,b[d],c);return c}function wa(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;h<i;h++)(f=a[h])&&(c&&!c(f,d,e)||(g.push(f),j&&b.push(h)));return g}function xa(a,b,c,d,e,f){return d&&!d[u]&&(d=xa(d)),e&&!e[u]&&(e=xa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||va(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:wa(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=wa(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?I(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i<f;i++)if(c=d.relative[a[i].type])m=[ta(ua(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;e<f;e++)if(d.relative[a[e].type])break;return xa(i>1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i<e&&ya(a.slice(i,e)),e<f&&ya(a=a.slice(e)),e<f&&sa(a))}m.push(c)}return ua(m)}function za(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(_,aa),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=V.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(_,aa),$.test(j[0].type)&&qa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&sa(j),!a)return G.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,!b||$.test(a)&&qa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="<a href='#'></a>","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="<input/>",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){if(r.isFunction(b))return r.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return r.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(C.test(b))return r.filter(b,a,c);b=r.filter(b,a)}return r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType})}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b<d;b++)if(r.contains(e[b],this))return!0}));for(c=this.pushStack([]),b=0;b<d;b++)r.find(a,e[b],c);return d>1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a<c;a++)if(r.contains(this,b[a]))return!0})},closest:function(a,b){var c,d=0,e=this.length,f=[],g="string"!=typeof a&&r(a);if(!A.test(a))for(;d<e;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/\S+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h<f.length)f[h].apply(c[0],c[1])===!1&&a.stopOnFalse&&(h=f.length,c=!1)}a.memory||(c=!1),b=!1,e&&(f=c?[]:"")},j={add:function(){return f&&(c&&!b&&(h=f.length-1,g.push(c)),function d(b){r.each(b,function(b,c){r.isFunction(c)?a.unique&&j.has(c)||f.push(c):c&&c.length&&"string"!==r.type(c)&&d(c)})}(arguments),c&&!b&&i()),this},remove:function(){return r.each(arguments,function(a,b){var c;while((c=r.inArray(b,f,c))>-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b<f)){if(a=d.apply(h,i),a===c.promise())throw new TypeError("Thenable self-resolution");j=a&&("object"==typeof a||"function"==typeof a)&&a.then,r.isFunction(j)?e?j.call(a,g(f,c,M,e),g(f,c,N,e)):(f++,j.call(a,g(f,c,M,e),g(f,c,N,e),g(f,c,M,c.notifyWith))):(d!==M&&(h=void 0,i=[a]),(e||c.resolveWith)(h,i))}},k=e?j:function(){try{j()}catch(a){r.Deferred.exceptionHook&&r.Deferred.exceptionHook(a,k.stackTrace),b+1>=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R),a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,
-r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h<i;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},T=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function U(){this.expando=r.expando+U.uid++}U.uid=1,U.prototype={cache:function(a){var b=a[this.expando];return b||(b={},T(a)&&(a.nodeType?a[this.expando]=b:Object.defineProperty(a,this.expando,{value:b,configurable:!0}))),b},set:function(a,b,c){var d,e=this.cache(a);if("string"==typeof b)e[r.camelCase(b)]=c;else for(d in b)e[r.camelCase(d)]=b[d];return e},get:function(a,b){return void 0===b?this.cache(a):a[this.expando]&&a[this.expando][r.camelCase(b)]},access:function(a,b,c){return void 0===b||b&&"string"==typeof b&&void 0===c?this.get(a,b):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d=a[this.expando];if(void 0!==d){if(void 0!==b){r.isArray(b)?b=b.map(r.camelCase):(b=r.camelCase(b),b=b in d?[b]:b.match(K)||[]),c=b.length;while(c--)delete d[b[c]]}(void 0===b||r.isEmptyObject(d))&&(a.nodeType?a[this.expando]=void 0:delete a[this.expando])}},hasData:function(a){var b=a[this.expando];return void 0!==b&&!r.isEmptyObject(b)}};var V=new U,W=new U,X=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,Y=/[A-Z]/g;function Z(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(Y,"-$&").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c||"false"!==c&&("null"===c?null:+c+""===c?+c:X.test(c)?JSON.parse(c):c)}catch(e){}W.set(a,b,c)}else c=void 0;return c}r.extend({hasData:function(a){return W.hasData(a)||V.hasData(a)},data:function(a,b,c){return W.access(a,b,c)},removeData:function(a,b){W.remove(a,b)},_data:function(a,b,c){return V.access(a,b,c)},_removeData:function(a,b){V.remove(a,b)}}),r.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=W.get(f),1===f.nodeType&&!V.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=r.camelCase(d.slice(5)),Z(f,d,e[d])));V.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){W.set(this,a)}):S(this,function(b){var c;if(f&&void 0===b){if(c=W.get(f,a),void 0!==c)return c;if(c=Z(f,a),void 0!==c)return c}else this.each(function(){W.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?r.queue(this[0],a):void 0===b?this:this.each(function(){var c=r.queue(this,a,b);r._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&r.dequeue(this,a)})},dequeue:function(a){return this.each(function(){r.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=r.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=V.get(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var $=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,_=new RegExp("^(?:([+-])=|)("+$+")([a-z%]*)$","i"),aa=["Top","Right","Bottom","Left"],ba=function(a,b){return a=b||a,"none"===a.style.display||""===a.style.display&&r.contains(a.ownerDocument,a)&&"none"===r.css(a,"display")},ca=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};function da(a,b,c,d){var e,f=1,g=20,h=d?function(){return d.cur()}:function(){return r.css(a,b,"")},i=h(),j=c&&c[3]||(r.cssNumber[b]?"":"px"),k=(r.cssNumber[b]||"px"!==j&&+i)&&_.exec(r.css(a,b));if(k&&k[3]!==j){j=j||k[3],c=c||[],k=+i||1;do f=f||".5",k/=f,r.style(a,b,k+j);while(f!==(f=h()/i)&&1!==f&&--g)}return c&&(k=+k||+i||0,e=c[1]?k+(c[1]+1)*c[2]:+c[2],d&&(d.unit=j,d.start=k,d.end=e)),e}var ea={};function fa(a){var b,c=a.ownerDocument,d=a.nodeName,e=ea[d];return e?e:(b=c.body.appendChild(c.createElement(d)),e=r.css(b,"display"),b.parentNode.removeChild(b),"none"===e&&(e="block"),ea[d]=e,e)}function ga(a,b){for(var c,d,e=[],f=0,g=a.length;f<g;f++)d=a[f],d.style&&(c=d.style.display,b?("none"===c&&(e[f]=V.get(d,"display")||null,e[f]||(d.style.display="")),""===d.style.display&&ba(d)&&(e[f]=fa(d))):"none"!==c&&(e[f]="none",V.set(d,"display",c)));for(f=0;f<g;f++)null!=e[f]&&(a[f].style.display=e[f]);return a}r.fn.extend({show:function(){return ga(this,!0)},hide:function(){return ga(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){ba(this)?r(this).show():r(this).hide()})}});var ha=/^(?:checkbox|radio)$/i,ia=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,ja=/^$|\/(?:java|ecma)script/i,ka={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ka.optgroup=ka.option,ka.tbody=ka.tfoot=ka.colgroup=ka.caption=ka.thead,ka.th=ka.td;function la(a,b){var c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function ma(a,b){for(var c=0,d=a.length;c<d;c++)V.set(a[c],"globalEval",!b||V.get(b[c],"globalEval"))}var na=/<|&#?\w+;/;function oa(a,b,c,d,e){for(var f,g,h,i,j,k,l=b.createDocumentFragment(),m=[],n=0,o=a.length;n<o;n++)if(f=a[n],f||0===f)if("object"===r.type(f))r.merge(m,f.nodeType?[f]:f);else if(na.test(f)){g=g||l.appendChild(b.createElement("div")),h=(ia.exec(f)||["",""])[1].toLowerCase(),i=ka[h]||ka._default,g.innerHTML=i[1]+r.htmlPrefilter(f)+i[2],k=i[0];while(k--)g=g.lastChild;r.merge(m,g.childNodes),g=l.firstChild,g.textContent=""}else m.push(b.createTextNode(f));l.textContent="",n=0;while(f=m[n++])if(d&&r.inArray(f,d)>-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=la(l.appendChild(f),"script"),j&&ma(g),c){k=0;while(f=g[k++])ja.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="<textarea>x</textarea>",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var pa=d.documentElement,qa=/^key/,ra=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,sa=/^([^.]*)(?:\.(.+)|)/;function ta(){return!0}function ua(){return!1}function va(){try{return d.activeElement}catch(a){}}function wa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)wa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=ua;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(pa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=sa.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c<arguments.length;c++)i[c]=arguments[c];if(b.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,b)!==!1){h=r.event.handlers.call(this,b,j),c=0;while((f=h[c++])&&!b.isPropagationStopped()){b.currentTarget=f.elem,d=0;while((g=f.handlers[d++])&&!b.isImmediatePropagationStopped())b.rnamespace&&!b.rnamespace.test(g.namespace)||(b.handleObj=g,b.data=g.data,e=((r.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(b.result=e)===!1&&(b.preventDefault(),b.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,b),b.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&("click"!==a.type||isNaN(a.button)||a.button<1))for(;i!==this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(d=[],c=0;c<h;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?r(e,this).index(i)>-1:r.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h<b.length&&g.push({elem:this,handlers:b.slice(h)}),g},addProp:function(a,b){Object.defineProperty(r.Event.prototype,a,{enumerable:!0,configurable:!0,get:r.isFunction(b)?function(){if(this.originalEvent)return b(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[a]},set:function(b){Object.defineProperty(this,a,{enumerable:!0,configurable:!0,writable:!0,value:b})}})},fix:function(a){return a[r.expando]?a:new r.Event(a)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==va()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===va()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&r.nodeName(this,"input"))return this.click(),!1},_default:function(a){return r.nodeName(a.target,"a")}},beforeunload:{postDispatch:function(a){void 0!==a.result&&a.originalEvent&&(a.originalEvent.returnValue=a.result)}}}},r.removeEvent=function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c)},r.Event=function(a,b){return this instanceof r.Event?(a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||void 0===a.defaultPrevented&&a.returnValue===!1?ta:ua,this.target=a.target&&3===a.target.nodeType?a.target.parentNode:a.target,this.currentTarget=a.currentTarget,this.relatedTarget=a.relatedTarget):this.type=a,b&&r.extend(this,b),this.timeStamp=a&&a.timeStamp||r.now(),void(this[r.expando]=!0)):new r.Event(a,b)},r.Event.prototype={constructor:r.Event,isDefaultPrevented:ua,isPropagationStopped:ua,isImmediatePropagationStopped:ua,isSimulated:!1,preventDefault:function(){var a=this.originalEvent;this.isDefaultPrevented=ta,a&&!this.isSimulated&&a.preventDefault()},stopPropagation:function(){var a=this.originalEvent;this.isPropagationStopped=ta,a&&!this.isSimulated&&a.stopPropagation()},stopImmediatePropagation:function(){var a=this.originalEvent;this.isImmediatePropagationStopped=ta,a&&!this.isSimulated&&a.stopImmediatePropagation(),this.stopPropagation()}},r.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(a){var b=a.button;return null==a.which&&qa.test(a.type)?null!=a.charCode?a.charCode:a.keyCode:!a.which&&void 0!==b&&ra.test(a.type)?1&b?1:2&b?3:4&b?2:0:a.which}},r.event.addProp),r.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(a,b){r.event.special[a]={delegateType:b,bindType:b,handle:function(a){var c,d=this,e=a.relatedTarget,f=a.handleObj;return e&&(e===d||r.contains(d,e))||(a.type=f.origType,c=f.handler.apply(this,arguments),a.type=b),c}}}),r.fn.extend({on:function(a,b,c,d){return wa(this,a,b,c,d)},one:function(a,b,c,d){return wa(this,a,b,c,d,1)},off:function(a,b,c){var d,e;if(a&&a.preventDefault&&a.handleObj)return d=a.handleObj,r(a.delegateTarget).off(d.namespace?d.origType+"."+d.namespace:d.origType,d.selector,d.handler),this;if("object"==typeof a){for(e in a)this.off(e,b,a[e]);return this}return b!==!1&&"function"!=typeof b||(c=b,b=void 0),c===!1&&(c=ua),this.each(function(){r.event.remove(this,a,c,b)})}});var xa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,ya=/<script|<style|<link/i,za=/checked\s*(?:[^=]|=\s*.checked.)/i,Aa=/^true\/(.*)/,Ba=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Ca(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Da(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ea(a){var b=Aa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Fa(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c<d;c++)r.event.add(b,e,j[e][c])}W.hasData(a)&&(h=W.access(a),i=r.extend({},h),W.set(b,i))}}function Ga(a,b){var c=b.nodeName.toLowerCase();"input"===c&&ha.test(a.type)?b.checked=a.checked:"input"!==c&&"textarea"!==c||(b.defaultValue=a.defaultValue)}function Ha(a,b,c,d){b=g.apply([],b);var e,f,h,i,j,k,l=0,m=a.length,n=m-1,q=b[0],s=r.isFunction(q);if(s||m>1&&"string"==typeof q&&!o.checkClone&&za.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ha(f,b,c,d)});if(m&&(e=oa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(la(e,"script"),Da),i=h.length;l<m;l++)j=e,l!==n&&(j=r.clone(j,!0,!0),i&&r.merge(h,la(j,"script"))),c.call(a[l],j,l);if(i)for(k=h[h.length-1].ownerDocument,r.map(h,Ea),l=0;l<i;l++)j=h[l],ja.test(j.type||"")&&!V.access(j,"globalEval")&&r.contains(k,j)&&(j.src?r._evalUrl&&r._evalUrl(j.src):p(j.textContent.replace(Ba,""),k))}return a}function Ia(a,b,c){for(var d,e=b?r.filter(b,a):a,f=0;null!=(d=e[f]);f++)c||1!==d.nodeType||r.cleanData(la(d)),d.parentNode&&(c&&r.contains(d.ownerDocument,d)&&ma(la(d,"script")),d.parentNode.removeChild(d));return a}r.extend({htmlPrefilter:function(a){return a.replace(xa,"<$1></$2>")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=la(h),f=la(a),d=0,e=f.length;d<e;d++)Ga(f[d],g[d]);if(b)if(c)for(f=f||la(a),g=g||la(h),d=0,e=f.length;d<e;d++)Fa(f[d],g[d]);else Fa(a,h);return g=la(h,"script"),g.length>0&&ma(g,!i&&la(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ia(this,a,!0)},remove:function(a){return Ia(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.appendChild(a)}})},prepend:function(){return Ha(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ca(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ha(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(la(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!ya.test(a)&&!ka[(ia.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c<d;c++)b=this[c]||{},1===b.nodeType&&(r.cleanData(la(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=[];return Ha(this,arguments,function(b){var c=this.parentNode;r.inArray(this,a)<0&&(r.cleanData(la(this)),c&&c.replaceChild(b,this))},a)}}),r.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){r.fn[a]=function(a){for(var c,d=[],e=r(a),f=e.length-1,g=0;g<=f;g++)c=g===f?this:this.clone(!0),r(e[g])[b](c),h.apply(d,c.get());return this.pushStack(d)}});var Ja=/^margin/,Ka=new RegExp("^("+$+")(?!px)[a-z%]+$","i"),La=function(b){var c=b.ownerDocument.defaultView;return c&&c.opener||(c=a),c.getComputedStyle(b)};!function(){function b(){if(i){i.style.cssText="box-sizing:border-box;position:relative;display:block;margin:auto;border:1px;padding:1px;top:1%;width:50%",i.innerHTML="",pa.appendChild(h);var b=a.getComputedStyle(i);c="1%"!==b.top,g="2px"===b.marginLeft,e="4px"===b.width,i.style.marginRight="50%",f="4px"===b.marginRight,pa.removeChild(h),i=null}}var c,e,f,g,h=d.createElement("div"),i=d.createElement("div");i.style&&(i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",o.clearCloneStyle="content-box"===i.style.backgroundClip,h.style.cssText="border:0;width:8px;height:0;top:0;left:-9999px;padding:0;margin-top:1px;position:absolute",h.appendChild(i),r.extend(o,{pixelPosition:function(){return b(),c},boxSizingReliable:function(){return b(),e},pixelMarginRight:function(){return b(),f},reliableMarginLeft:function(){return b(),g}}))}();function Ma(a,b,c){var d,e,f,g,h=a.style;return c=c||La(a),c&&(g=c.getPropertyValue(b)||c[b],""!==g||r.contains(a.ownerDocument,a)||(g=r.style(a,b)),!o.pixelMarginRight()&&Ka.test(g)&&Ja.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0!==g?g+"":g}function Na(a,b){return{get:function(){return a()?void delete this.get:(this.get=b).apply(this,arguments)}}}var Oa=/^(none|table(?!-c[ea]).+)/,Pa={position:"absolute",visibility:"hidden",display:"block"},Qa={letterSpacing:"0",fontWeight:"400"},Ra=["Webkit","Moz","ms"],Sa=d.createElement("div").style;function Ta(a){if(a in Sa)return a;var b=a[0].toUpperCase()+a.slice(1),c=Ra.length;while(c--)if(a=Ra[c]+b,a in Sa)return a}function Ua(a,b,c){var d=_.exec(b);return d?Math.max(0,d[2]-(c||0))+(d[3]||"px"):b}function Va(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;f<4;f+=2)"margin"===c&&(g+=r.css(a,c+aa[f],!0,e)),d?("content"===c&&(g-=r.css(a,"padding"+aa[f],!0,e)),"margin"!==c&&(g-=r.css(a,"border"+aa[f]+"Width",!0,e))):(g+=r.css(a,"padding"+aa[f],!0,e),"padding"!==c&&(g+=r.css(a,"border"+aa[f]+"Width",!0,e)));return g}function Wa(a,b,c){var d,e=!0,f=La(a),g="border-box"===r.css(a,"boxSizing",!1,f);if(a.getClientRects().length&&(d=a.getBoundingClientRect()[b]),d<=0||null==d){if(d=Ma(a,b,f),(d<0||null==d)&&(d=a.style[b]),Ka.test(d))return d;e=g&&(o.boxSizingReliable()||d===a.style[b]),d=parseFloat(d)||0}return d+Va(a,b,c||(g?"border":"content"),e,f)+"px"}r.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Ma(a,"opacity");return""===c?"1":c}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=r.camelCase(b),i=a.style;return b=r.cssProps[h]||(r.cssProps[h]=Ta(h)||h),g=r.cssHooks[b]||r.cssHooks[h],void 0===c?g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b]:(f=typeof c,"string"===f&&(e=_.exec(c))&&e[1]&&(c=da(a,b,e),f="number"),null!=c&&c===c&&("number"===f&&(c+=e&&e[3]||(r.cssNumber[h]?"":"px")),o.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),g&&"set"in g&&void 0===(c=g.set(a,c,d))||(i[b]=c)),void 0)}},css:function(a,b,c,d){var e,f,g,h=r.camelCase(b);return b=r.cssProps[h]||(r.cssProps[h]=Ta(h)||h),g=r.cssHooks[b]||r.cssHooks[h],g&&"get"in g&&(e=g.get(a,!0,c)),void 0===e&&(e=Ma(a,b,d)),"normal"===e&&b in Qa&&(e=Qa[b]),""===c||c?(f=parseFloat(e),c===!0||isFinite(f)?f||0:e):e}}),r.each(["height","width"],function(a,b){r.cssHooks[b]={get:function(a,c,d){if(c)return!Oa.test(r.css(a,"display"))||a.getClientRects().length&&a.getBoundingClientRect().width?Wa(a,b,d):ca(a,Pa,function(){return Wa(a,b,d)})},set:function(a,c,d){var e,f=d&&La(a),g=d&&Va(a,b,d,"border-box"===r.css(a,"boxSizing",!1,f),f);return g&&(e=_.exec(c))&&"px"!==(e[3]||"px")&&(a.style[b]=c,c=r.css(a,b)),Ua(a,c,g)}}}),r.cssHooks.marginLeft=Na(o.reliableMarginLeft,function(a,b){if(b)return(parseFloat(Ma(a,"marginLeft"))||a.getBoundingClientRect().left-ca(a,{marginLeft:0},function(){return a.getBoundingClientRect().left}))+"px"}),r.each({margin:"",padding:"",border:"Width"},function(a,b){r.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];d<4;d++)e[a+aa[d]+b]=f[d]||f[d-2]||f[0];return e}},Ja.test(a)||(r.cssHooks[a+b].set=Ua)}),r.fn.extend({css:function(a,b){return S(this,function(a,b,c){var d,e,f={},g=0;if(r.isArray(b)){for(d=La(a),e=b.length;g<e;g++)f[b[g]]=r.css(a,b[g],!1,d);return f}return void 0!==c?r.style(a,b,c):r.css(a,b)},a,b,arguments.length>1)}});function Xa(a,b,c,d,e){return new Xa.prototype.init(a,b,c,d,e)}r.Tween=Xa,Xa.prototype={constructor:Xa,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Xa.propHooks[this.prop];return a&&a.get?a.get(this):Xa.propHooks._default.get(this)},run:function(a){var b,c=Xa.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Xa.propHooks._default.set(this),this}},Xa.prototype.init.prototype=Xa.prototype,Xa.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Xa.propHooks.scrollTop=Xa.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Xa.prototype.init,r.fx.step={};var Ya,Za,$a=/^(?:toggle|show|hide)$/,_a=/queueHooks$/;function ab(){Za&&(a.requestAnimationFrame(ab),r.fx.tick())}function bb(){return a.setTimeout(function(){Ya=void 0}),Ya=r.now()}function cb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=aa[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function db(a,b,c){for(var d,e=(gb.tweeners[b]||[]).concat(gb.tweeners["*"]),f=0,g=e.length;f<g;f++)if(d=e[f].call(c,b,a))return d}function eb(a,b,c){var d,e,f,g,h,i,j,k,l="width"in b||"height"in b,m=this,n={},o=a.style,p=a.nodeType&&ba(a),q=V.get(a,"fxshow");c.queue||(g=r._queueHooks(a,"fx"),null==g.unqueued&&(g.unqueued=0,h=g.empty.fire,g.empty.fire=function(){g.unqueued||h()}),g.unqueued++,m.always(function(){m.always(function(){g.unqueued--,r.queue(a,"fx").length||g.empty.fire()})}));for(d in b)if(e=b[d],$a.test(e)){if(delete b[d],f=f||"toggle"===e,e===(p?"hide":"show")){if("show"!==e||!q||void 0===q[d])continue;p=!0}n[d]=q&&q[d]||r.style(a,d)}if(i=!r.isEmptyObject(b),i||!r.isEmptyObject(n)){l&&1===a.nodeType&&(c.overflow=[o.overflow,o.overflowX,o.overflowY],j=q&&q.display,null==j&&(j=V.get(a,"display")),k=r.css(a,"display"),"none"===k&&(j?k=j:(ga([a],!0),j=a.style.display||j,k=r.css(a,"display"),ga([a]))),("inline"===k||"inline-block"===k&&null!=j)&&"none"===r.css(a,"float")&&(i||(m.done(function(){o.display=j}),null==j&&(k=o.display,j="none"===k?"":k)),o.display="inline-block")),c.overflow&&(o.overflow="hidden",m.always(function(){o.overflow=c.overflow[0],o.overflowX=c.overflow[1],o.overflowY=c.overflow[2]})),i=!1;for(d in n)i||(q?"hidden"in q&&(p=q.hidden):q=V.access(a,"fxshow",{display:j}),f&&(q.hidden=!p),p&&ga([a],!0),m.done(function(){p||ga([a]),V.remove(a,"fxshow");for(d in n)r.style(a,d,n[d])})),i=db(p?q[d]:0,d,m),d in q||(q[d]=i.start,p&&(i.end=i.start,i.start=0))}}function fb(a,b){var c,d,e,f,g;for(c in a)if(d=r.camelCase(c),e=b[d],f=a[c],r.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=r.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function gb(a,b,c){var d,e,f=0,g=gb.prefilters.length,h=r.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=Ya||bb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;g<i;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),f<1&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:r.extend({},b),opts:r.extend(!0,{specialEasing:{},easing:r.easing._default},c),originalProperties:b,originalOptions:c,startTime:Ya||bb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=r.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;c<d;c++)j.tweens[c].run(1);return b?(h.notifyWith(a,[j,1,0]),h.resolveWith(a,[j,b])):h.rejectWith(a,[j,b]),this}}),k=j.props;for(fb(k,j.opts.specialEasing);f<g;f++)if(d=gb.prefilters[f].call(j,a,k,j.opts))return r.isFunction(d.stop)&&(r._queueHooks(j.elem,j.opts.queue).stop=r.proxy(d.stop,d)),d;return r.map(k,db,j),r.isFunction(j.opts.start)&&j.opts.start.call(a,j),r.fx.timer(r.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}r.Animation=r.extend(gb,{tweeners:{"*":[function(a,b){var c=this.createTween(a,b);return da(c.elem,a,_.exec(b),c),c}]},tweener:function(a,b){r.isFunction(a)?(b=a,a=["*"]):a=a.match(K);for(var c,d=0,e=a.length;d<e;d++)c=a[d],gb.tweeners[c]=gb.tweeners[c]||[],gb.tweeners[c].unshift(b)},prefilters:[eb],prefilter:function(a,b){b?gb.prefilters.unshift(a):gb.prefilters.push(a)}}),r.speed=function(a,b,c){var e=a&&"object"==typeof a?r.extend({},a):{complete:c||!c&&b||r.isFunction(a)&&a,duration:a,easing:c&&b||b&&!r.isFunction(b)&&b};return r.fx.off||d.hidden?e.duration=0:e.duration="number"==typeof e.duration?e.duration:e.duration in r.fx.speeds?r.fx.speeds[e.duration]:r.fx.speeds._default,null!=e.queue&&e.queue!==!0||(e.queue="fx"),e.old=e.complete,e.complete=function(){r.isFunction(e.old)&&e.old.call(this),e.queue&&r.dequeue(this,e.queue)},e},r.fn.extend({fadeTo:function(a,b,c,d){return this.filter(ba).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=r.isEmptyObject(a),f=r.speed(b,c,d),g=function(){var b=gb(this,r.extend({},a),f);(e||V.get(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=r.timers,g=V.get(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&_a.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));!b&&c||r.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=V.get(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=r.timers,g=d?d.length:0;for(c.finish=!0,r.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;b<g;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),r.each(["toggle","show","hide"],function(a,b){var c=r.fn[b];r.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(cb(b,!0),a,d,e)}}),r.each({slideDown:cb("show"),slideUp:cb("hide"),slideToggle:cb("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){r.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),r.timers=[],r.fx.tick=function(){var a,b=0,c=r.timers;for(Ya=r.now();b<c.length;b++)a=c[b],a()||c[b]!==a||c.splice(b--,1);c.length||r.fx.stop(),Ya=void 0},r.fx.timer=function(a){r.timers.push(a),a()?r.fx.start():r.timers.pop()},r.fx.interval=13,r.fx.start=function(){Za||(Za=a.requestAnimationFrame?a.requestAnimationFrame(ab):a.setInterval(r.fx.tick,r.fx.interval))},r.fx.stop=function(){a.cancelAnimationFrame?a.cancelAnimationFrame(Za):a.clearInterval(Za),Za=null},r.fx.speeds={slow:600,fast:200,_default:400},r.fn.delay=function(b,c){return b=r.fx?r.fx.speeds[b]||b:b,c=c||"fx",this.queue(c,function(c,d){var e=a.setTimeout(c,b);d.stop=function(){a.clearTimeout(e)}})},function(){var a=d.createElement("input"),b=d.createElement("select"),c=b.appendChild(d.createElement("option"));a.type="checkbox",o.checkOn=""!==a.value,o.optSelected=c.selected,a=d.createElement("input"),a.value="t",a.type="radio",o.radioValue="t"===a.value}();var hb,ib=r.expr.attrHandle;r.fn.extend({attr:function(a,b){return S(this,r.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?hb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);
-if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),hb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=ib[b]||r.find.attr;ib[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=ib[g],ib[g]=e,e=null!=c(a,b,d)?g:null,ib[g]=f),e}});var jb=/^(?:input|select|textarea|button)$/i,kb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):jb.test(a.nodeName)||kb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});var lb=/[\t\r\n\f]/g;function mb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,mb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=mb(c),d=1===c.nodeType&&(" "+e+" ").replace(lb," ")){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=r.trim(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,mb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=mb(c),d=1===c.nodeType&&(" "+e+" ").replace(lb," ")){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=r.trim(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,mb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=mb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(c)+" ").replace(lb," ").indexOf(b)>-1)return!0;return!1}});var nb=/\r/g,ob=/[\x20\t\r\n\f]+/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(nb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:r.trim(r.text(a)).replace(ob," ")}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type,g=f?null:[],h=f?e+1:d.length,i=e<0?h:f?e:0;i<h;i++)if(c=d[i],(c.selected||i===e)&&!c.disabled&&(!c.parentNode.disabled||!r.nodeName(c.parentNode,"optgroup"))){if(b=r(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=r.makeArray(b),g=e.length;while(g--)d=e[g],(d.selected=r.inArray(r.valHooks.option.get(d),f)>-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ha.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,""),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r("<script>").prop({charset:a.scriptCharset,src:a.url}).on("load error",c=function(a){b.remove(),c=null,a&&f("error"===a.type?404:200,a.type)}),d.head.appendChild(b[0])},abort:function(){c&&c()}}}});var Qb=[],Rb=/(=)\?(?=&|$)|\?\?/;r.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=Qb.pop()||r.expando+"_"+rb++;return this[a]=!0,a}}),r.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(Rb.test(b.url)?"url":"string"==typeof b.data&&0===(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&Rb.test(b.data)&&"data");if(h||"jsonp"===b.dataTypes[0])return e=b.jsonpCallback=r.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(Rb,"$1"+e):b.jsonp!==!1&&(b.url+=(sb.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||r.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){void 0===f?r(a).removeProp(e):a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,Qb.push(e)),g&&r.isFunction(f)&&f(g[0]),g=f=void 0}),"script"}),o.createHTMLDocument=function(){var a=d.implementation.createHTMLDocument("").body;return a.innerHTML="<form></form><form></form>",2===a.childNodes.length}(),r.parseHTML=function(a,b,c){if("string"!=typeof a)return[];"boolean"==typeof b&&(c=b,b=!1);var e,f,g;return b||(o.createHTMLDocument?(b=d.implementation.createHTMLDocument(""),e=b.createElement("base"),e.href=d.location.href,b.head.appendChild(e)):b=d),f=B.exec(a),g=!c&&[],f?[b.createElement(f[1])]:(f=oa([a],b,g),g&&g.length&&r(g).remove(),r.merge([],f.childNodes))},r.fn.load=function(a,b,c){var d,e,f,g=this,h=a.indexOf(" ");return h>-1&&(d=r.trim(a.slice(h)),a=a.slice(0,h)),r.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(e="POST"),g.length>0&&r.ajax({url:a,type:e||"GET",dataType:"html",data:b}).done(function(a){f=arguments,g.html(d?r("<div>").append(r.parseHTML(a)).find(d):a)}).always(c&&function(a,b){g.each(function(){c.apply(this,f||[a.responseText,b,a])})}),this},r.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){r.fn[b]=function(a){return this.on(b,a)}}),r.expr.pseudos.animated=function(a){return r.grep(r.timers,function(b){return a===b.elem}).length};function Sb(a){return r.isWindow(a)?a:9===a.nodeType&&a.defaultView}r.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=r.css(a,"position"),l=r(a),m={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=r.css(a,"top"),i=r.css(a,"left"),j=("absolute"===k||"fixed"===k)&&(f+i).indexOf("auto")>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),r.isFunction(b)&&(b=b.call(a,c,r.extend({},h))),null!=b.top&&(m.top=b.top-h.top+g),null!=b.left&&(m.left=b.left-h.left+e),"using"in b?b.using.call(a,m):l.css(m)}},r.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){r.offset.setOffset(this,a,b)});var b,c,d,e,f=this[0];if(f)return f.getClientRects().length?(d=f.getBoundingClientRect(),d.width||d.height?(e=f.ownerDocument,c=Sb(e),b=e.documentElement,{top:d.top+c.pageYOffset-b.clientTop,left:d.left+c.pageXOffset-b.clientLeft}):d):{top:0,left:0}},position:function(){if(this[0]){var a,b,c=this[0],d={top:0,left:0};return"fixed"===r.css(c,"position")?b=c.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),r.nodeName(a[0],"html")||(d=a.offset()),d={top:d.top+r.css(a[0],"borderTopWidth",!0),left:d.left+r.css(a[0],"borderLeftWidth",!0)}),{top:b.top-d.top-r.css(c,"marginTop",!0),left:b.left-d.left-r.css(c,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent;while(a&&"static"===r.css(a,"position"))a=a.offsetParent;return a||pa})}}),r.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c="pageYOffset"===b;r.fn[a]=function(d){return S(this,function(a,d,e){var f=Sb(a);return void 0===e?f?f[b]:a[d]:void(f?f.scrollTo(c?f.pageXOffset:e,c?e:f.pageYOffset):a[d]=e)},a,d,arguments.length)}}),r.each(["top","left"],function(a,b){r.cssHooks[b]=Na(o.pixelPosition,function(a,c){if(c)return c=Ma(a,b),Ka.test(c)?r(a).position()[b]+"px":c})}),r.each({Height:"height",Width:"width"},function(a,b){r.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){r.fn[d]=function(e,f){var g=arguments.length&&(c||"boolean"!=typeof e),h=c||(e===!0||f===!0?"margin":"border");return S(this,function(b,c,e){var f;return r.isWindow(b)?0===d.indexOf("outer")?b["inner"+a]:b.document.documentElement["client"+a]:9===b.nodeType?(f=b.documentElement,Math.max(b.body["scroll"+a],f["scroll"+a],b.body["offset"+a],f["offset"+a],f["client"+a])):void 0===e?r.css(b,c,h):r.style(b,c,e,h)},b,g?e:void 0,g)}})}),r.fn.extend({bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}}),r.parseJSON=JSON.parse,"function"==typeof define&&define.amd&&define("jquery",[],function(){return r});var Tb=a.jQuery,Ub=a.$;return r.noConflict=function(b){return a.$===r&&(a.$=Ub),b&&a.jQuery===r&&(a.jQuery=Tb),r},b||(a.jQuery=a.$=r),r});
+/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */
+!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n<t?[this[n]]:[])},end:function(){return this.prevObject||this.constructor()},push:s,sort:n.sort,splice:n.splice},w.extend=w.fn.extend=function(){var e,t,n,r,i,o,a=arguments[0]||{},s=1,u=arguments.length,l=!1;for("boolean"==typeof a&&(l=a,a=arguments[s]||{},s++),"object"==typeof a||g(a)||(a={}),s===u&&(a=this,s--);s<u;s++)if(null!=(e=arguments[s]))for(t in e)n=a[t],a!==(r=e[t])&&(l&&r&&(w.isPlainObject(r)||(i=Array.isArray(r)))?(i?(i=!1,o=n&&Array.isArray(n)?n:[]):o=n&&w.isPlainObject(n)?n:{},a[t]=w.extend(l,o,r)):void 0!==r&&(a[t]=r));return a},w.extend({expando:"jQuery"+("3.3.1"+Math.random()).replace(/\D/g,""),isReady:!0,error:function(e){throw new Error(e)},noop:function(){},isPlainObject:function(e){var t,n;return!(!e||"[object Object]"!==c.call(e))&&(!(t=i(e))||"function"==typeof(n=f.call(t,"constructor")&&t.constructor)&&p.call(n)===d)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},globalEval:function(e){m(e)},each:function(e,t){var n,r=0;if(C(e)){for(n=e.length;r<n;r++)if(!1===t.call(e[r],r,e[r]))break}else for(r in e)if(!1===t.call(e[r],r,e[r]))break;return e},trim:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(C(Object(e))?w.merge(n,"string"==typeof e?[e]:e):s.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:u.call(t,e,n)},merge:function(e,t){for(var n=+t.length,r=0,i=e.length;r<n;r++)e[i++]=t[r];return e.length=i,e},grep:function(e,t,n){for(var r,i=[],o=0,a=e.length,s=!n;o<a;o++)(r=!t(e[o],o))!==s&&i.push(e[o]);return i},map:function(e,t,n){var r,i,o=0,s=[];if(C(e))for(r=e.length;o<r;o++)null!=(i=t(e[o],o,n))&&s.push(i);else for(o in e)null!=(i=t(e[o],o,n))&&s.push(i);return a.apply([],s)},guid:1,support:h}),"function"==typeof Symbol&&(w.fn[Symbol.iterator]=n[Symbol.iterator]),w.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function C(e){var t=!!e&&"length"in e&&e.length,n=x(e);return!g(e)&&!y(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n<r;n++)if(e[n]===t)return n;return-1},P="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\0-\\xa0])+",I="\\["+M+"*("+R+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+R+"))|)"+M+"*\\]",W=":("+R+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+I+")*)|.*)\\)|)",$=new RegExp(M+"+","g"),B=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),F=new RegExp("^"+M+"*,"+M+"*"),_=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="<a id='"+b+"'></a><select id='"+b+"-\r\\' msallowcapture=''><option selected=''></option></select>",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="<a href='' disabled='disabled'></a><select disabled='disabled'><option/></select>";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n<t;n+=2)e.push(n);return e}),odd:he(function(e,t){for(var n=1;n<t;n+=2)e.push(n);return e}),lt:he(function(e,t,n){for(var r=n<0?n+t:n;--r>=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r<t;)e.push(r);return e})}}).pseudos.nth=r.pseudos.eq;for(t in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})r.pseudos[t]=fe(t);for(t in{submit:!0,reset:!0})r.pseudos[t]=pe(t);function ye(){}ye.prototype=r.filters=r.pseudos,r.setFilters=new ye,a=oe.tokenize=function(e,t){var n,i,o,a,s,u,l,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=r.preFilter;while(s){n&&!(i=F.exec(s))||(i&&(s=s.slice(i[0].length)||s),u.push(o=[])),n=!1,(i=_.exec(s))&&(n=i.shift(),o.push({value:n,type:i[0].replace(B," ")}),s=s.slice(n.length));for(a in r.filter)!(i=V[a].exec(s))||l[a]&&!(i=l[a](i))||(n=i.shift(),o.push({value:n,type:a,matches:i}),s=s.slice(n.length));if(!n)break}return t?s.length:s?oe.error(e):k(e,u).slice(0)};function ve(e){for(var t=0,n=e.length,r="";t<n;t++)r+=e[t].value;return r}function me(e,t,n){var r=t.dir,i=t.next,o=i||r,a=n&&"parentNode"===o,s=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||a)return e(t,n,i);return!1}:function(t,n,u){var l,c,f,p=[T,s];if(u){while(t=t[r])if((1===t.nodeType||a)&&e(t,n,u))return!0}else while(t=t[r])if(1===t.nodeType||a)if(f=t[b]||(t[b]={}),c=f[t.uniqueID]||(f[t.uniqueID]={}),i&&i===t.nodeName.toLowerCase())t=t[r]||t;else{if((l=c[o])&&l[0]===T&&l[1]===s)return p[2]=l[2];if(c[o]=p,p[2]=e(t,n,u))return!0}return!1}}function xe(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r<i;r++)oe(e,t[r],n);return n}function we(e,t,n,r,i){for(var o,a=[],s=0,u=e.length,l=null!=t;s<u;s++)(o=e[s])&&(n&&!n(o,r,i)||(a.push(o),l&&t.push(s)));return a}function Te(e,t,n,r,i,o){return r&&!r[b]&&(r=Te(r)),i&&!i[b]&&(i=Te(i,o)),se(function(o,a,s,u){var l,c,f,p=[],d=[],h=a.length,g=o||be(t||"*",s.nodeType?[s]:s,[]),y=!e||!o&&t?g:we(g,p,e,s,u),v=n?i||(o?e:h||r)?[]:a:y;if(n&&n(y,v,s,u),r){l=we(v,d),r(l,[],s,u),c=l.length;while(c--)(f=l[c])&&(v[d[c]]=!(y[d[c]]=f))}if(o){if(i||e){if(i){l=[],c=v.length;while(c--)(f=v[c])&&l.push(y[c]=f);i(null,v=[],l,u)}c=v.length;while(c--)(f=v[c])&&(l=i?O(o,f):p[c])>-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u<o;u++)if(n=r.relative[e[u].type])p=[me(xe(p),n)];else{if((n=r.filter[e[u].type].apply(null,e[u].matches))[b]){for(i=++u;i<o;i++)if(r.relative[e[i].type])break;return Te(u>1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u<i&&Ce(e.slice(u,i)),i<o&&Ce(e=e.slice(i)),i<o&&ve(e))}p.push(n)}return xe(p)}function Ee(e,t){var n=t.length>0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="<a href='#'></a>","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="<input/>",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t<r;t++)if(w.contains(i[t],this))return!0}));for(n=this.pushStack([]),t=0;t<r;t++)w.find(e,i[t],n);return r>1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e<n;e++)if(w.contains(this,t[e]))return!0})},closest:function(e,t){var n,r=0,i=this.length,o=[],a="string"!=typeof e&&w(e);if(!D.test(e))for(;r<i;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(n.nodeType<11&&(a?a.index(n)>-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s<o.length)!1===o[s].apply(n[0],n[1])&&e.stopOnFalse&&(s=o.length,n=!1)}e.memory||(n=!1),t=!1,i&&(o=n?[]:"")},l={add:function(){return o&&(n&&!t&&(s=o.length-1,a.push(n)),function t(n){w.each(n,function(n,r){g(r)?e.unique&&l.has(r)||o.push(r):r&&r.length&&"string"!==x(r)&&t(r)})}(arguments),n&&!t&&u()),this},remove:function(){return w.each(arguments,function(e,t){var n;while((n=w.inArray(t,o,n))>-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t<o)){if((e=r.apply(s,u))===n.promise())throw new TypeError("Thenable self-resolution");l=e&&("object"==typeof e||"function"==typeof e)&&e.then,g(l)?i?l.call(e,a(o,n,I,i),a(o,n,W,i)):(o++,l.call(e,a(o,n,I,i),a(o,n,W,i),a(o,n,I,n.notifyWith))):(r!==I&&(s=void 0,u=[e]),(i||n.resolveWith)(s,u))}},c=i?l:function(){try{l()}catch(e){w.Deferred.exceptionHook&&w.Deferred.exceptionHook(e,c.stackTrace),t+1>=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s<u;s++)t(e[s],n,a?r:r.call(e[s],s,t(e[s],n)));return i?e:l?t.call(e):u?t(e[0],n):o},X=/^-ms-/,U=/-([a-z])/g;function V(e,t){return t.toUpperCase()}function G(e){return e.replace(X,"ms-").replace(U,V)}var Y=function(e){return 1===e.nodeType||9===e.nodeType||!+e.nodeType};function Q(){this.expando=w.expando+Q.uid++}Q.uid=1,Q.prototype={cache:function(e){var t=e[this.expando];return t||(t={},Y(e)&&(e.nodeType?e[this.expando]=t:Object.defineProperty(e,this.expando,{value:t,configurable:!0}))),t},set:function(e,t,n){var r,i=this.cache(e);if("string"==typeof t)i[G(t)]=n;else for(r in t)i[G(r)]=t[r];return i},get:function(e,t){return void 0===t?this.cache(e):e[this.expando]&&e[this.expando][G(t)]},access:function(e,t,n){return void 0===t||t&&"string"==typeof t&&void 0===n?this.get(e,t):(this.set(e,t,n),void 0!==n?n:t)},remove:function(e,t){var n,r=e[this.expando];if(void 0!==r){if(void 0!==t){n=(t=Array.isArray(t)?t.map(G):(t=G(t))in r?[t]:t.match(M)||[]).length;while(n--)delete r[t[n]]}(void 0===t||w.isEmptyObject(r))&&(e.nodeType?e[this.expando]=void 0:delete e[this.expando])}},hasData:function(e){var t=e[this.expando];return void 0!==t&&!w.isEmptyObject(t)}};var J=new Q,K=new Q,Z=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,ee=/[A-Z]/g;function te(e){return"true"===e||"false"!==e&&("null"===e?null:e===+e+""?+e:Z.test(e)?JSON.parse(e):e)}function ne(e,t,n){var r;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(ee,"-$&").toLowerCase(),"string"==typeof(n=e.getAttribute(r))){try{n=te(n)}catch(e){}K.set(e,t,n)}else n=void 0;return n}w.extend({hasData:function(e){return K.hasData(e)||J.hasData(e)},data:function(e,t,n){return K.access(e,t,n)},removeData:function(e,t){K.remove(e,t)},_data:function(e,t,n){return J.access(e,t,n)},_removeData:function(e,t){J.remove(e,t)}}),w.fn.extend({data:function(e,t){var n,r,i,o=this[0],a=o&&o.attributes;if(void 0===e){if(this.length&&(i=K.get(o),1===o.nodeType&&!J.get(o,"hasDataAttrs"))){n=a.length;while(n--)a[n]&&0===(r=a[n].name).indexOf("data-")&&(r=G(r.slice(5)),ne(o,r,i[r]));J.set(o,"hasDataAttrs",!0)}return i}return"object"==typeof e?this.each(function(){K.set(this,e)}):z(this,function(t){var n;if(o&&void 0===t){if(void 0!==(n=K.get(o,e)))return n;if(void 0!==(n=ne(o,e)))return n}else this.each(function(){K.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length<n?w.queue(this[0],e):void 0===t?this:this.each(function(){var n=w.queue(this,e,t);w._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&w.dequeue(this,e)})},dequeue:function(e){return this.each(function(){w.dequeue(this,e)})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=w.Deferred(),o=this,a=this.length,s=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=void 0),e=e||"fx";while(a--)(n=J.get(o[a],e+"queueHooks"))&&n.empty&&(r++,n.empty.add(s));return s(),i.promise(t)}});var re=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,ie=new RegExp("^(?:([+-])=|)("+re+")([a-z%]*)$","i"),oe=["Top","Right","Bottom","Left"],ae=function(e,t){return"none"===(e=t||e).style.display||""===e.style.display&&w.contains(e.ownerDocument,e)&&"none"===w.css(e,"display")},se=function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i};function ue(e,t,n,r){var i,o,a=20,s=r?function(){return r.cur()}:function(){return w.css(e,t,"")},u=s(),l=n&&n[3]||(w.cssNumber[t]?"":"px"),c=(w.cssNumber[t]||"px"!==l&&+u)&&ie.exec(w.css(e,t));if(c&&c[3]!==l){u/=2,l=l||c[3],c=+u||1;while(a--)w.style(e,t,c+l),(1-o)*(1-(o=s()/u||.5))<=0&&(a=0),c/=o;c*=2,w.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,i=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=i)),i}var le={};function ce(e){var t,n=e.ownerDocument,r=e.nodeName,i=le[r];return i||(t=n.body.appendChild(n.createElement(r)),i=w.css(t,"display"),t.parentNode.removeChild(t),"none"===i&&(i="block"),le[r]=i,i)}function fe(e,t){for(var n,r,i=[],o=0,a=e.length;o<a;o++)(r=e[o]).style&&(n=r.style.display,t?("none"===n&&(i[o]=J.get(r,"display")||null,i[o]||(r.style.display="")),""===r.style.display&&ae(r)&&(i[o]=ce(r))):"none"!==n&&(i[o]="none",J.set(r,"display",n)));for(o=0;o<a;o++)null!=i[o]&&(e[o].style.display=i[o]);return e}w.fn.extend({show:function(){return fe(this,!0)},hide:function(){return fe(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){ae(this)?w(this).show():w(this).hide()})}});var pe=/^(?:checkbox|radio)$/i,de=/<([a-z][^\/\0>\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,"<select multiple='multiple'>","</select>"],thead:[1,"<table>","</table>"],col:[2,"<table><colgroup>","</colgroup></table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n<r;n++)J.set(e[n],"globalEval",!t||J.get(t[n],"globalEval"))}var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d<h;d++)if((o=e[d])||0===o)if("object"===x(o))w.merge(p,o.nodeType?[o]:o);else if(me.test(o)){a=a||f.appendChild(t.createElement("div")),s=(de.exec(o)||["",""])[1].toLowerCase(),u=ge[s]||ge._default,a.innerHTML=u[1]+w.htmlPrefilter(o)+u[2],c=u[0];while(c--)a=a.lastChild;w.merge(p,a.childNodes),(a=f.firstChild).textContent=""}else p.push(t.createTextNode(o));f.textContent="",d=0;while(o=p[d++])if(r&&w.inArray(o,r)>-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="<textarea>x</textarea>",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n<arguments.length;n++)u[n]=arguments[n];if(t.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,t)){s=w.event.handlers.call(this,t,l),n=0;while((o=s[n++])&&!t.isPropagationStopped()){t.currentTarget=o.elem,r=0;while((a=o.handlers[r++])&&!t.isImmediatePropagationStopped())t.rnamespace&&!t.rnamespace.test(a.namespace)||(t.handleObj=a,t.data=a.data,void 0!==(i=((w.event.special[a.origType]||{}).handle||a.handler).apply(o.elem,u))&&!1===(t.result=i)&&(t.preventDefault(),t.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,t),t.result}},handlers:function(e,t){var n,r,i,o,a,s=[],u=t.delegateCount,l=e.target;if(u&&l.nodeType&&!("click"===e.type&&e.button>=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n<u;n++)void 0===a[i=(r=t[n]).selector+" "]&&(a[i]=r.needsContext?w(i,this).index(l)>-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u<t.length&&s.push({elem:l,handlers:t.slice(u)}),s},addProp:function(e,t){Object.defineProperty(w.Event.prototype,e,{enumerable:!0,configurable:!0,get:g(t)?function(){if(this.originalEvent)return t(this.originalEvent)}:function(){if(this.originalEvent)return this.originalEvent[e]},set:function(t){Object.defineProperty(this,e,{enumerable:!0,configurable:!0,writable:!0,value:t})}})},fix:function(e){return e[w.expando]?e:new w.Event(e)},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==Se()&&this.focus)return this.focus(),!1},delegateType:"focusin"},blur:{trigger:function(){if(this===Se()&&this.blur)return this.blur(),!1},delegateType:"focusout"},click:{trigger:function(){if("checkbox"===this.type&&this.click&&N(this,"input"))return this.click(),!1},_default:function(e){return N(e.target,"a")}},beforeunload:{postDispatch:function(e){void 0!==e.result&&e.originalEvent&&(e.originalEvent.returnValue=e.result)}}}},w.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n)},w.Event=function(e,t){if(!(this instanceof w.Event))return new w.Event(e,t);e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||void 0===e.defaultPrevented&&!1===e.returnValue?Ee:ke,this.target=e.target&&3===e.target.nodeType?e.target.parentNode:e.target,this.currentTarget=e.currentTarget,this.relatedTarget=e.relatedTarget):this.type=e,t&&w.extend(this,t),this.timeStamp=e&&e.timeStamp||Date.now(),this[w.expando]=!0},w.Event.prototype={constructor:w.Event,isDefaultPrevented:ke,isPropagationStopped:ke,isImmediatePropagationStopped:ke,isSimulated:!1,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=Ee,e&&!this.isSimulated&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=Ee,e&&!this.isSimulated&&e.stopPropagation()},stopImmediatePropagation:function(){var e=this.originalEvent;this.isImmediatePropagationStopped=Ee,e&&!this.isSimulated&&e.stopImmediatePropagation(),this.stopPropagation()}},w.each({altKey:!0,bubbles:!0,cancelable:!0,changedTouches:!0,ctrlKey:!0,detail:!0,eventPhase:!0,metaKey:!0,pageX:!0,pageY:!0,shiftKey:!0,view:!0,"char":!0,charCode:!0,key:!0,keyCode:!0,button:!0,buttons:!0,clientX:!0,clientY:!0,offsetX:!0,offsetY:!0,pointerId:!0,pointerType:!0,screenX:!0,screenY:!0,targetTouches:!0,toElement:!0,touches:!0,which:function(e){var t=e.button;return null==e.which&&we.test(e.type)?null!=e.charCode?e.charCode:e.keyCode:!e.which&&void 0!==t&&Te.test(e.type)?1&t?1:2&t?3:4&t?2:0:e.which}},w.event.addProp),w.each({mouseenter:"mouseover",mouseleave:"mouseout",pointerenter:"pointerover",pointerleave:"pointerout"},function(e,t){w.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return i&&(i===r||w.contains(r,i))||(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),w.fn.extend({on:function(e,t,n,r){return De(this,e,t,n,r)},one:function(e,t,n,r){return De(this,e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,w(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return!1!==t&&"function"!=typeof t||(n=t,t=void 0),!1===n&&(n=ke),this.each(function(){w.event.remove(this,e,n,t)})}});var Ne=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/<script|<style|<link/i,je=/checked\s*(?:[^=]|=\s*.checked.)/i,qe=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n<r;n++)w.event.add(t,i,l[i][n])}K.hasData(e)&&(s=K.access(e),u=w.extend({},s),K.set(t,u))}}function Me(e,t){var n=t.nodeName.toLowerCase();"input"===n&&pe.test(e.type)?t.checked=e.checked:"input"!==n&&"textarea"!==n||(t.defaultValue=e.defaultValue)}function Re(e,t,n,r){t=a.apply([],t);var i,o,s,u,l,c,f=0,p=e.length,d=p-1,y=t[0],v=g(y);if(v||p>1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f<p;f++)l=i,f!==d&&(l=w.clone(l,!0,!0),u&&w.merge(s,ye(l,"script"))),n.call(e[f],l,f);if(u)for(c=s[s.length-1].ownerDocument,w.map(s,Oe),f=0;f<u;f++)l=s[f],he.test(l.type||"")&&!J.access(l,"globalEval")&&w.contains(c,l)&&(l.src&&"module"!==(l.type||"").toLowerCase()?w._evalUrl&&w._evalUrl(l.src):m(l.textContent.replace(qe,""),c,l))}return e}function Ie(e,t,n){for(var r,i=t?w.filter(t,e):e,o=0;null!=(r=i[o]);o++)n||1!==r.nodeType||w.cleanData(ye(r)),r.parentNode&&(n&&w.contains(r.ownerDocument,r)&&ve(ye(r,"script")),r.parentNode.removeChild(r));return e}w.extend({htmlPrefilter:function(e){return e.replace(Ne,"<$1></$2>")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r<i;r++)Me(o[r],a[r]);if(t)if(n)for(o=o||ye(e),a=a||ye(s),r=0,i=o.length;r<i;r++)Pe(o[r],a[r]);else Pe(e,s);return(a=ye(s,"script")).length>0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n<r;n++)1===(t=this[n]||{}).nodeType&&(w.cleanData(ye(t,!1)),t.innerHTML=e);t=0}catch(e){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=[];return Re(this,arguments,function(t){var n=this.parentNode;w.inArray(this,e)<0&&(w.cleanData(ye(this)),n&&n.replaceChild(t,this))},e)}}),w.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){w.fn[e]=function(e){for(var n,r=[],i=w(e),o=i.length-1,a=0;a<=o;a++)n=a===o?this:this.clone(!0),w(i[a])[t](n),s.apply(r,n.get());return this.pushStack(r)}});var We=new RegExp("^("+re+")(?!px)[a-z%]+$","i"),$e=function(t){var n=t.ownerDocument.defaultView;return n&&n.opener||(n=e),n.getComputedStyle(t)},Be=new RegExp(oe.join("|"),"i");!function(){function t(){if(c){l.style.cssText="position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0",c.style.cssText="position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%",be.appendChild(l).appendChild(c);var t=e.getComputedStyle(c);i="1%"!==t.top,u=12===n(t.marginLeft),c.style.right="60%",s=36===n(t.right),o=36===n(t.width),c.style.position="absolute",a=36===c.offsetWidth||"absolute",be.removeChild(l),c=null}}function n(e){return Math.round(parseFloat(e))}var i,o,a,s,u,l=r.createElement("div"),c=r.createElement("div");c.style&&(c.style.backgroundClip="content-box",c.cloneNode(!0).style.backgroundClip="",h.clearCloneStyle="content-box"===c.style.backgroundClip,w.extend(h,{boxSizingReliable:function(){return t(),o},pixelBoxStyles:function(){return t(),s},pixelPosition:function(){return t(),i},reliableMarginLeft:function(){return t(),u},scrollboxSize:function(){return t(),a}}))}();function Fe(e,t,n){var r,i,o,a,s=e.style;return(n=n||$e(e))&&(""!==(a=n.getPropertyValue(t)||n[t])||w.contains(e.ownerDocument,e)||(a=w.style(e,t)),!h.pixelBoxStyles()&&We.test(a)&&Be.test(t)&&(r=s.width,i=s.minWidth,o=s.maxWidth,s.minWidth=s.maxWidth=s.width=a,a=n.width,s.width=r,s.minWidth=i,s.maxWidth=o)),void 0!==a?a+"":a}function _e(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}var ze=/^(none|table(?!-c[ea]).+)/,Xe=/^--/,Ue={position:"absolute",visibility:"hidden",display:"block"},Ve={letterSpacing:"0",fontWeight:"400"},Ge=["Webkit","Moz","ms"],Ye=r.createElement("div").style;function Qe(e){if(e in Ye)return e;var t=e[0].toUpperCase()+e.slice(1),n=Ge.length;while(n--)if((e=Ge[n]+t)in Ye)return e}function Je(e){var t=w.cssProps[e];return t||(t=w.cssProps[e]=Qe(e)||e),t}function Ke(e,t,n){var r=ie.exec(t);return r?Math.max(0,r[2]-(n||0))+(r[3]||"px"):t}function Ze(e,t,n,r,i,o){var a="width"===t?1:0,s=0,u=0;if(n===(r?"border":"content"))return 0;for(;a<4;a+=2)"margin"===n&&(u+=w.css(e,n+oe[a],!0,i)),r?("content"===n&&(u-=w.css(e,"padding"+oe[a],!0,i)),"margin"!==n&&(u-=w.css(e,"border"+oe[a]+"Width",!0,i))):(u+=w.css(e,"padding"+oe[a],!0,i),"padding"!==n?u+=w.css(e,"border"+oe[a]+"Width",!0,i):s+=w.css(e,"border"+oe[a]+"Width",!0,i));return!r&&o>=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a<i;a++)o[t[a]]=w.css(e,t[a],!1,r);return o}return void 0!==n?w.style(e,t,n):w.css(e,t)},e,t,arguments.length>1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o<a;o++)if(r=i[o].call(n,t,e))return r}function ct(e,t,n){var r,i,o,a,s,u,l,c,f="width"in t||"height"in t,p=this,d={},h=e.style,g=e.nodeType&&ae(e),y=J.get(e,"fxshow");n.queue||(null==(a=w._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,s=a.empty.fire,a.empty.fire=function(){a.unqueued||s()}),a.unqueued++,p.always(function(){p.always(function(){a.unqueued--,w.queue(e,"fx").length||a.empty.fire()})}));for(r in t)if(i=t[r],it.test(i)){if(delete t[r],o=o||"toggle"===i,i===(g?"hide":"show")){if("show"!==i||!y||void 0===y[r])continue;g=!0}d[r]=y&&y[r]||w.style(e,r)}if((u=!w.isEmptyObject(t))||!w.isEmptyObject(d)){f&&1===e.nodeType&&(n.overflow=[h.overflow,h.overflowX,h.overflowY],null==(l=y&&y.display)&&(l=J.get(e,"display")),"none"===(c=w.css(e,"display"))&&(l?c=l:(fe([e],!0),l=e.style.display||l,c=w.css(e,"display"),fe([e]))),("inline"===c||"inline-block"===c&&null!=l)&&"none"===w.css(e,"float")&&(u||(p.done(function(){h.display=l}),null==l&&(c=h.display,l="none"===c?"":c)),h.display="inline-block")),n.overflow&&(h.overflow="hidden",p.always(function(){h.overflow=n.overflow[0],h.overflowX=n.overflow[1],h.overflowY=n.overflow[2]})),u=!1;for(r in d)u||(y?"hidden"in y&&(g=y.hidden):y=J.access(e,"fxshow",{display:l}),o&&(y.hidden=!g),g&&fe([e],!0),p.done(function(){g||fe([e]),J.remove(e,"fxshow");for(r in d)w.style(e,r,d[r])})),u=lt(g?y[r]:0,r,p),r in y||(y[r]=u.start,g&&(u.end=u.start,u.start=0))}}function ft(e,t){var n,r,i,o,a;for(n in e)if(r=G(n),i=t[r],o=e[n],Array.isArray(o)&&(i=o[1],o=e[n]=o[0]),n!==r&&(e[r]=o,delete e[n]),(a=w.cssHooks[r])&&"expand"in a){o=a.expand(o),delete e[r];for(n in o)n in e||(e[n]=o[n],t[n]=i)}else t[r]=i}function pt(e,t,n){var r,i,o=0,a=pt.prefilters.length,s=w.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;for(var t=nt||st(),n=Math.max(0,l.startTime+l.duration-t),r=1-(n/l.duration||0),o=0,a=l.tweens.length;o<a;o++)l.tweens[o].run(r);return s.notifyWith(e,[l,r,n]),r<1&&a?n:(a||s.notifyWith(e,[l,1,0]),s.resolveWith(e,[l]),!1)},l=s.promise({elem:e,props:w.extend({},t),opts:w.extend(!0,{specialEasing:{},easing:w.easing._default},n),originalProperties:t,originalOptions:n,startTime:nt||st(),duration:n.duration,tweens:[],createTween:function(t,n){var r=w.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;n<r;n++)l.tweens[n].run(1);return t?(s.notifyWith(e,[l,1,0]),s.resolveWith(e,[l,t])):s.rejectWith(e,[l,t]),this}}),c=l.props;for(ft(c,l.opts.specialEasing);o<a;o++)if(r=pt.prefilters[o].call(l,e,c,l.opts))return g(r.stop)&&(w._queueHooks(l.elem,l.opts.queue).stop=r.stop.bind(r)),r;return w.map(c,lt,l),g(l.opts.start)&&l.opts.start.call(e,l),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always),w.fx.timer(w.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l}w.Animation=w.extend(pt,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return ue(n.elem,e,ie.exec(t),n),n}]},tweener:function(e,t){g(e)?(t=e,e=["*"]):e=e.match(M);for(var n,r=0,i=e.length;r<i;r++)n=e[r],pt.tweeners[n]=pt.tweeners[n]||[],pt.tweeners[n].unshift(t)},prefilters:[ct],prefilter:function(e,t){t?pt.prefilters.unshift(e):pt.prefilters.push(e)}}),w.speed=function(e,t,n){var r=e&&"object"==typeof e?w.extend({},e):{complete:n||!n&&t||g(e)&&e,duration:e,easing:n&&t||t&&!g(t)&&t};return w.fx.off?r.duration=0:"number"!=typeof r.duration&&(r.duration in w.fx.speeds?r.duration=w.fx.speeds[r.duration]:r.duration=w.fx.speeds._default),null!=r.queue&&!0!==r.queue||(r.queue="fx"),r.old=r.complete,r.complete=function(){g(r.old)&&r.old.call(this),r.queue&&w.dequeue(this,r.queue)},r},w.fn.extend({fadeTo:function(e,t,n,r){return this.filter(ae).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=w.isEmptyObject(e),o=w.speed(t,n,r),a=function(){var t=pt(this,w.extend({},e),o);(i||J.get(this,"finish"))&&t.stop(!0)};return a.finish=a,i||!1===o.queue?this.each(a):this.queue(o.queue,a)},stop:function(e,t,n){var r=function(e){var t=e.stop;delete e.stop,t(n)};return"string"!=typeof e&&(n=t,t=e,e=void 0),t&&!1!==e&&this.queue(e||"fx",[]),this.each(function(){var t=!0,i=null!=e&&e+"queueHooks",o=w.timers,a=J.get(this);if(i)a[i]&&a[i].stop&&r(a[i]);else for(i in a)a[i]&&a[i].stop&&ot.test(i)&&r(a[i]);for(i=o.length;i--;)o[i].elem!==this||null!=e&&o[i].queue!==e||(o[i].anim.stop(n),t=!1,o.splice(i,1));!t&&n||w.dequeue(this,e)})},finish:function(e){return!1!==e&&(e=e||"fx"),this.each(function(){var t,n=J.get(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=w.timers,a=r?r.length:0;for(n.finish=!0,w.queue(this,e,[]),i&&i.stop&&i.stop.call(this,!0),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;t<a;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}}),w.each(["toggle","show","hide"],function(e,t){var n=w.fn[t];w.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ut(t,!0),e,r,i)}}),w.each({slideDown:ut("show"),slideUp:ut("hide"),slideToggle:ut("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){w.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),w.timers=[],w.fx.tick=function(){var e,t=0,n=w.timers;for(nt=Date.now();t<n.length;t++)(e=n[t])()||n[t]!==e||n.splice(t--,1);n.length||w.fx.stop(),nt=void 0},w.fx.timer=function(e){w.timers.push(e),w.fx.start()},w.fx.interval=13,w.fx.start=function(){rt||(rt=!0,at())},w.fx.stop=function(){rt=null},w.fx.speeds={slow:600,fast:200,_default:400},w.fn.delay=function(t,n){return t=w.fx?w.fx.speeds[t]||t:t,n=n||"fx",this.queue(n,function(n,r){var i=e.setTimeout(n,t);r.stop=function(){e.clearTimeout(i)}})},function(){var e=r.createElement("input"),t=r.createElement("select").appendChild(r.createElement("option"));e.type="checkbox",h.checkOn=""!==e.value,h.optSelected=t.selected,(e=r.createElement("input")).value="t",e.type="radio",h.radioValue="t"===e.value}();var dt,ht=w.expr.attrHandle;w.fn.extend({attr:function(e,t){return z(this,w.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r<u;r++)if(((n=i[r]).selected||r===o)&&!n.disabled&&(!n.parentNode.disabled||!N(n.parentNode,"optgroup"))){if(t=w(n).val(),a)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=w.makeArray(t),a=i.length;while(a--)((r=i[a]).selected=w.inArray(w.valHooks.option.get(r),o)>-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w("<script>").prop({charset:e.scriptCharset,src:e.url}).on("load error",n=function(e){t.remove(),n=null,e&&o("error"===e.type?404:200,e.type)}),r.head.appendChild(t[0])},abort:function(){n&&n()}}}});var Yt=[],Qt=/(=)\?(?=&|$)|\?\?/;w.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Yt.pop()||w.expando+"_"+Et++;return this[e]=!0,e}}),w.ajaxPrefilter("json jsonp",function(t,n,r){var i,o,a,s=!1!==t.jsonp&&(Qt.test(t.url)?"url":"string"==typeof t.data&&0===(t.contentType||"").indexOf("application/x-www-form-urlencoded")&&Qt.test(t.data)&&"data");if(s||"jsonp"===t.dataTypes[0])return i=t.jsonpCallback=g(t.jsonpCallback)?t.jsonpCallback():t.jsonpCallback,s?t[s]=t[s].replace(Qt,"$1"+i):!1!==t.jsonp&&(t.url+=(kt.test(t.url)?"&":"?")+t.jsonp+"="+i),t.converters["script json"]=function(){return a||w.error(i+" was not called"),a[0]},t.dataTypes[0]="json",o=e[i],e[i]=function(){a=arguments},r.always(function(){void 0===o?w(e).removeProp(i):e[i]=o,t[i]&&(t.jsonpCallback=n.jsonpCallback,Yt.push(i)),a&&g(o)&&o(a[0]),a=o=void 0}),"script"}),h.createHTMLDocument=function(){var e=r.implementation.createHTMLDocument("").body;return e.innerHTML="<form></form><form></form>",2===e.childNodes.length}(),w.parseHTML=function(e,t,n){if("string"!=typeof e)return[];"boolean"==typeof t&&(n=t,t=!1);var i,o,a;return t||(h.createHTMLDocument?((i=(t=r.implementation.createHTMLDocument("")).createElement("base")).href=r.location.href,t.head.appendChild(i)):t=r),o=A.exec(e),a=!n&&[],o?[t.createElement(o[1])]:(o=xe([e],t,a),a&&a.length&&w(a).remove(),w.merge([],o.childNodes))},w.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return s>-1&&(r=vt(e.slice(s)),e=e.slice(0,s)),g(t)?(n=t,t=void 0):t&&"object"==typeof t&&(i="POST"),a.length>0&&w.ajax({url:e,type:i||"GET",dataType:"html",data:t}).done(function(e){o=arguments,a.html(r?w("<div>").append(w.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},w.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){w.fn[t]=function(e){return this.on(t,e)}}),w.expr.pseudos.animated=function(e){return w.grep(w.timers,function(t){return e===t.elem}).length},w.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l,c=w.css(e,"position"),f=w(e),p={};"static"===c&&(e.style.position="relative"),s=f.offset(),o=w.css(e,"top"),u=w.css(e,"left"),(l=("absolute"===c||"fixed"===c)&&(o+u).indexOf("auto")>-1)?(a=(r=f.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),g(t)&&(t=t.call(e,n,w.extend({},s))),null!=t.top&&(p.top=t.top-s.top+a),null!=t.left&&(p.left=t.left-s.left+i),"using"in t?t.using.call(e,p):f.css(p)}},w.fn.extend({offset:function(e){if(arguments.length)return void 0===e?this:this.each(function(t){w.offset.setOffset(this,e,t)});var t,n,r=this[0];if(r)return r.getClientRects().length?(t=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:t.top+n.pageYOffset,left:t.left+n.pageXOffset}):{top:0,left:0}},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===w.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===w.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=w(e).offset()).top+=w.css(e,"borderTopWidth",!0),i.left+=w.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-w.css(r,"marginTop",!0),left:t.left-i.left-w.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===w.css(e,"position"))e=e.offsetParent;return e||be})}}),w.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,t){var n="pageYOffset"===t;w.fn[e]=function(r){return z(this,function(e,r,i){var o;if(y(e)?o=e:9===e.nodeType&&(o=e.defaultView),void 0===i)return o?o[t]:e[r];o?o.scrollTo(n?o.pageXOffset:i,n?i:o.pageYOffset):e[r]=i},e,r,arguments.length)}}),w.each(["top","left"],function(e,t){w.cssHooks[t]=_e(h.pixelPosition,function(e,n){if(n)return n=Fe(e,t),We.test(n)?w(e).position()[t]+"px":n})}),w.each({Height:"height",Width:"width"},function(e,t){w.each({padding:"inner"+e,content:t,"":"outer"+e},function(n,r){w.fn[r]=function(i,o){var a=arguments.length&&(n||"boolean"!=typeof i),s=n||(!0===i||!0===o?"margin":"border");return z(this,function(t,n,i){var o;return y(t)?0===r.indexOf("outer")?t["inner"+e]:t.document.documentElement["client"+e]:9===t.nodeType?(o=t.documentElement,Math.max(t.body["scroll"+e],o["scroll"+e],t.body["offset"+e],o["offset"+e],o["client"+e])):void 0===i?w.css(t,n,s):w.style(t,n,i,s)},t,a?i:void 0,a)}})}),w.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,t){w.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),w.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),w.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}}),w.proxy=function(e,t){var n,r,i;if("string"==typeof t&&(n=e[t],t=e,e=n),g(e))return r=o.call(arguments,2),i=function(){return e.apply(t||this,r.concat(o.call(arguments)))},i.guid=e.guid=e.guid||w.guid++,i},w.holdReady=function(e){e?w.readyWait++:w.ready(!0)},w.isArray=Array.isArray,w.parseJSON=JSON.parse,w.nodeName=N,w.isFunction=g,w.isWindow=y,w.camelCase=G,w.type=x,w.now=Date.now,w.isNumeric=function(e){var t=w.type(e);return("number"===t||"string"===t)&&!isNaN(e-parseFloat(e))},"function"==typeof define&&define.amd&&define("jquery",[],function(){return w});var Jt=e.jQuery,Kt=e.$;return w.noConflict=function(t){return e.$===w&&(e.$=Kt),t&&e.jQuery===w&&(e.jQuery=Jt),w},t||(e.jQuery=e.$=w),w});
diff -urN cahier-de-prepa6.2.0/js/respond.min.js cahier-de-prepa8.0.1/js/respond.min.js
--- cahier-de-prepa6.2.0/js/respond.min.js	2015-09-19 09:27:00.707560002 +0200
+++ cahier-de-prepa8.0.1/js/respond.min.js	1970-01-01 01:00:00.000000000 +0100
@@ -1,6 +0,0 @@
-/*! Respond.js v1.4.2: min/max-width media query polyfill
- * Copyright 2014 Scott Jehl
- * Licensed under MIT
- * http://j.mp/respondjs */
-
-!function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='&shy;<style media="'+a+'"> #mq-test-1 { width: 42px; }</style>',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){v(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},g=function(a){return a.replace(c.regex.minmaxwh,"").match(c.regex.other)};if(c.ajax=f,c.queue=d,c.unsupportedmq=g,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,comments:/\/\*[^*]*\*+([^/][^*]*\*+)*\//gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,maxw:/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/,minmaxwh:/\(\s*m(in|ax)\-(height|width)\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/gi,other:/\([^\)]*\)/g},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var h,i,j,k=a.document,l=k.documentElement,m=[],n=[],o=[],p={},q=30,r=k.getElementsByTagName("head")[0]||l,s=k.getElementsByTagName("base")[0],t=r.getElementsByTagName("link"),u=function(){var a,b=k.createElement("div"),c=k.body,d=l.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=k.createElement("body"),c.style.background="none"),l.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&l.insertBefore(c,l.firstChild),a=b.offsetWidth,f?l.removeChild(c):c.removeChild(b),l.style.fontSize=d,e&&(c.style.fontSize=e),a=j=parseFloat(a)},v=function(b){var c="clientWidth",d=l[c],e="CSS1Compat"===k.compatMode&&d||k.body[c]||d,f={},g=t[t.length-1],p=(new Date).getTime();if(b&&h&&q>p-h)return a.clearTimeout(i),i=a.setTimeout(v,q),void 0;h=p;for(var s in m)if(m.hasOwnProperty(s)){var w=m[s],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?j||u():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?j||u():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(n[w.rules]))}for(var C in o)o.hasOwnProperty(C)&&o[C]&&o[C].parentNode===r&&r.removeChild(o[C]);o.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=k.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,r.insertBefore(E,g.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(k.createTextNode(F)),o.push(E)}},w=function(a,b,d){var e=a.replace(c.regex.comments,"").replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},i=!f&&d;b.length&&(b+="/"),i&&(f=1);for(var j=0;f>j;j++){var k,l,o,p;i?(k=d,n.push(h(a))):(k=e[j].match(c.regex.findStyles)&&RegExp.$1,n.push(RegExp.$2&&h(RegExp.$2))),o=k.split(","),p=o.length;for(var q=0;p>q;q++)l=o[q],g(l)||m.push({media:l.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:n.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}v()},x=function(){if(d.length){var b=d.shift();f(b.href,function(c){w(c,b.href,b.media),p[b.href]=!0,a.setTimeout(function(){x()},0)})}},y=function(){for(var b=0;b<t.length;b++){var c=t[b],e=c.href,f=c.media,g=c.rel&&"stylesheet"===c.rel.toLowerCase();e&&g&&!p[e]&&(c.styleSheet&&c.styleSheet.rawCssText?(w(c.styleSheet.rawCssText,e,f),p[e]=!0):(!/^([a-zA-Z:]*\/\/)/.test(e)&&!s||e.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&("//"===e.substring(0,2)&&(e=a.location.protocol+e),d.push({href:e,media:f})))}x()};y(),c.update=y,c.getEmValue=u,a.addEventListener?a.addEventListener("resize",b,!1):a.attachEvent&&a.attachEvent("onresize",b)}}(this);
\ No newline at end of file
diff -urN cahier-de-prepa6.2.0/login.php cahier-de-prepa8.0.1/login.php
--- cahier-de-prepa6.2.0/login.php	2016-07-21 23:09:57.483370561 +0200
+++ cahier-de-prepa8.0.1/login.php	2018-10-05 10:28:38.477907081 +0200
@@ -8,43 +8,66 @@
 /// HTML ///
 ////////////
 $lien = ( $actuel ) ? '' : '<br><a href=".">Retour à la page d\'accueil</a>';
-debut($mysqli,$titre,'Ce contenu est protégé. Vous devez vous connecter pour l\'afficher.'.$lien,$autorisation,$actuel);
+debut($mysqli,$titre,'',$autorisation,$actuel);
 $mysqli->close();
-echo <<<FIN
+if ( isset($_SESSION['light']) && $_SESSION['light'] )
+  echo <<<FIN
+  
+  <article>
+    <a class="icon-ok" title="Valider"></a>
+    <h3>Vérification de mot de passe</h3>
+    <form>
+      <p>Cette page contient des données sensibles. Vous êtes déjà connecté, mais vous devez fournir à nouveau votre mot de passe pour y accéder.</p>
+      <input class="ligne" type="password" name="motdepasse" autofocus placeholder="Mot de passe">
+    </form>
+  </article>
+FIN;
+else  
+  echo <<<FIN
 
-  <article id="login">
+  <div class="warning">Ce contenu est protégé. Vous devez vous connecter pour l'afficher.$lien</div>
+  
+  <article>
     <a class="icon-ok" title="Valider"></a>
     <h3>Se connecter</h3>
     <form>
       <p>Veuillez entrer votre identifiant et votre mot de passe&nbsp;:</p>
-      <input class="ligne" type="text" name="login" data-placeholder="Identifiant">
-      <input class="ligne" type="password" name="motdepasse" data-placeholder="Mot de passe">
-      <input type="hidden" name="connexion" value=1>
-      <p class="oubli"><a href="connexion?oublimdp">Identifiant ou mot de passe oublié&nbsp;?</a></p>
-      <p class="oubli"><a href="connexion?creationcompte">Créer un compte</a></p>
+      <input class="ligne" type="text" name="login" autofocus placeholder="Identifiant">
+      <input class="ligne" type="password" name="motdepasse" placeholder="Mot de passe">
+      <p class="oubli"><label for="permconn">Se souvenir de moi</label><input type="checkbox" name="permconn" id="permconn" value="1">
+      <p class="oubli"><a href="gestioncompte?oublimdp">Identifiant ou mot de passe oublié&nbsp;?</a></p>
+      <p class="oubli"><a href="gestioncompte?creation">Créer un compte</a></p>
     </form>
   </article>
+FIN;
+
+echo <<<FIN
 
   <script type="text/javascript">
 $( function() {
-  // Placeholders
-  $('#login input').placeholder();
   // Envoi
-  $('#login a.icon-ok').on("click",function () {
-    $.ajax({url: 'ajax.php', method: "post", data: $('form').serialize(), dataType: 'json', el: '', fonction: function(el) { location.reload(true); } })
-    .done( function(data) {
-      // Si erreur d'identification, on reste bloqué là
-      if ( data['etat'] == 'login_nok' )
-        $('form p:first').html(data['message']).addClass('warning');
-    });
+  $('a.icon-ok').on("click",function () {
+    $.ajax({url: 'ajax.php',
+            method: "post",
+            data: $('form').serialize()+'&connexion=1', 
+            dataType: 'json', 
+            el: '', 
+            beforeSend: function() { $('#load').show(200); },
+            complete: function() { $('#load').hide(200); },
+            fonction: function(el) { location.reload(true); } 
+          }).done( function(data) {
+            // Si erreur d'identification, on reste bloqué là
+            if ( data['etat'] == 'nok' )
+              $('form p:first').html(data['message']).addClass('warning');
+          });
   });
   // Envoi par appui sur Entrée
-  $('#login input').on('keypress',function (e) {
-    if ( e.which == 13 )
-      $('#login a.icon-ok').click();
+  $('input').on('keypress',function (e) {
+    if ( e.which == 13 ) {
+      $('a.icon-ok').click();
+      return false;
+    }
   });
-  // Focus avec du retard pour éviter un bug d'affichage du placeholder
-  setTimeout(function() { $('#login input:first').focus() },500);
 });
   </script>
 
@@ -52,5 +75,4 @@
     
 FIN;
 fin();
-
 ?>
diff -urN cahier-de-prepa6.2.0/mail.php cahier-de-prepa8.0.1/mail.php
--- cahier-de-prepa6.2.0/mail.php	2016-08-12 10:59:35.138775138 +0200
+++ cahier-de-prepa8.0.1/mail.php	2018-10-13 01:21:06.436259701 +0200
@@ -10,14 +10,22 @@
 // Autorisation //
 //////////////////
 
-// Accès aux professeurs et colleurs connectés uniquement
+// Accès défini selon le champ mailenvoi dans la table utilisateurs
+// Possibilité d'afficher cette page seulement si mailenvoi vaut 1.
 $mysqli = connectsql();
 if ( !$autorisation )  {
   $titre = 'Envoi de courriel';
   $actuel = false;
   include('login.php');
 }
-elseif ( $autorisation < 3 )  {
+/*/ Récupération de la valeur si non déjà stockée dans la session - a priori inutile
+elseif ( !isset($_SESSION['mailenvoi']) )  {
+  $resultat = $mysqli->query("SELECT mailenvoi FROM utilisateurs WHERE id = ${_SESSION['id']}");
+  $r = $resultat->fetch_row();
+  $resultat->free();
+  $_SESSION['mailenvoi'] = $r[0];
+}*/
+if ( $_SESSION['mailenvoi'] == 0 )  {
   debut($mysqli,'Envoi de courriel','Vous n\'avez pas accès à cette page.',$autorisation,' ');
   $mysqli->close();
   fin();
@@ -28,32 +36,35 @@
 //////////////
 debut($mysqli,'Envoi de courriel',$message,$autorisation,'mail');
 
-// Récupération des préférences d'envoi (mailexp, mailcopy) de l'utilisateur
-$resultat = $mysqli->query("SELECT mail, TRIM(mailexp) AS mailexp, IF(mailcopy,' checked','') AS mailcopy
+// Récupération des préférences d'envoi (mail, mailexp, mailcopie) de l'utilisateur
+$resultat = $mysqli->query("SELECT mail, mailexp, IF(mailcopie,' checked','') AS mailcopie
                             FROM utilisateurs WHERE id = ${_SESSION['id']}");
 $u = $resultat->fetch_assoc();
 $resultat->free();
 echo <<<FIN
 
-  <a class="icon-aide general" data-id="page" title="Aide pour l'envoi de courriel"></a>
-  <a class="icon-ok general" title="Envoyer le courriel"></a>
+  <div id="icones">
+    <a class="icon-aide" data-id="page" title="Aide pour l'envoi de courriel"></a>
+  </div>
 
   <article>
-    <p><strong>Nom de l'expéditeur&nbsp;: </strong>&nbsp;<span class="editable" data-id="mailprefs|mailexp|0" data-placeholder="Nom">${u['mailexp']}</span>
-    <p><strong>Adresse électronique de l'expéditeur&nbsp;: </strong>&nbsp;<span class="editable" data-id="mailprefs|mail|0" data-placeholder="Adresse électronique">${u['mail']}</span></p>
-    <p><strong>Destinataires&nbsp;: </strong>&nbsp;<span class="editabledest"><span>[Personne]</span><a class="icon-edite"></a></span></p>
+    <a class="icon-mailenvoi" title="Envoyer le courriel"></a>
+    <p><strong>Nom de l'expéditeur&nbsp;: </strong>&nbsp;${u['mailexp']}</p>
+    <p><strong>Adresse électronique&nbsp;: </strong>&nbsp;${u['mail']}</p>
+    <p><strong>Destinataires&nbsp;: </strong>&nbsp;<span id="maildest">[Personne]</span>&nbsp;<a class="icon-edite"></a></p>
     <p><strong>Sujet&nbsp;:</strong></p>
     <form id="mail">
-      <input class="ligne" name="sujet"></input>
+      <input class="ligne" type="text" name="sujet">
       <p><strong>Texte du message&nbsp;:</strong></p>
-      <textarea name="texte" rows="20" cols="100">\n\n\n\n-- \n${u['mailexp']}\nhttps://$site/</textarea>
+      <textarea name="texte" rows="20" cols="100">\n\n\n\n-- \n${u['mailexp']}\nMail envoyé depuis <https://$site/></textarea>
       <p class="ligne"><label for="copie">Recevoir le courriel en copie&nbsp;: </label>
-        <input type="checkbox" id="copie" name="copie" value="1"${u['mailcopy']}>
+        <input type="checkbox" id="copie" name="copie" value="1"${u['mailcopie']}>
       </p>
       <input type="hidden" name="id-copie" value="">
       <input type="hidden" name="id-bcc" value="">
-      <input type="hidden" name="envoimail" value="1">
+      <input type="hidden" name="action" value="courriel">
     </form>
+    <p>Le nom d'expéditeur, l'adresse électronique et le réglage par défaut de la mise en copie des courriels sont modifiables sur la page de <a href="prefs">vos préférences personnelles</a> (attention avant de cliquer sur ce lien, il n'y a pas d'enregistrement des brouillons).</p>
   </article>
 
   <p id="log"></p>
@@ -61,81 +72,133 @@
 FIN;
 
 // Récupération de tous les utilisateurs
-// Utilisateurs non validés : mdp précédé d'une étoile
-// Utilisateur vide pour marquer l'arrêt des créations de compte : mdp vide
-$resultat = $mysqli->query("SELECT id, autorisation, IF(LENGTH(nom),CONCAT(nom,' ',prenom),CONCAT(login,' (identifiant)')) AS nom, IF(LENGTH(mail),'',' disabled') AS mail
-                            FROM utilisateurs WHERE LENGTH(mdp)=40 AND id != ${_SESSION['id']} ORDER BY autorisation DESC, nom");
+// Requête différente pour un élève ou pour un autre type de compte
+if ( $autorisation == 2 )
+  $resultat = $mysqli->query("SELECT id, autorisation, '' AS pasmail,
+                              IF(autorisation=2,
+                                CONCAT(nom,' ',prenom),
+                                IF(LENGTH(mailexp),mailexp,CONCAT(nom,' ',prenom))) AS nomcomplet
+                              FROM utilisateurs WHERE mdp > '0' AND mailliste = 1 AND id != ${_SESSION['id']}
+                              ORDER BY autorisation DESC, nom, prenom, login");
+else
+  $resultat = $mysqli->query("SELECT id, autorisation, IF(LENGTH(mail),'',' disabled') AS pasmail,
+                              IF(LENGTH(nom),CONCAT(nom,' ',prenom),CONCAT('<em>',login,'</em>')) AS nomcomplet
+                              FROM utilisateurs WHERE mdp > '0' AND autorisation > 1 AND id != ${_SESSION['id']}
+                              ORDER BY autorisation DESC, nom, prenom, login");
 $a = 0;
 $table = '';
 while ( $r = $resultat->fetch_assoc() )  {
   if ( $a != $r['autorisation'] )  {
     $a = $r['autorisation'];
     switch ( $a )  {
-      case 1 : $t = 'Invités'; break;
       case 2 : $t = 'Élèves'; break;
       case 3 : $t = 'Colleurs'; break;
-      case 4 : $t = 'Professeurs'; break;
+      case 4 : $t = 'Administratifs'; break;
+      case 5 : $t = 'Professeurs'; break;
     }
     $table .= <<<FIN
-        <tr><th>$t</th>
-            <th><a class="icon-cocher" data-classe="dest_c$a" title="Cocher tous les $t en copie"></a><a class="icon-decocher" data-classe="dest_c$a" title="Décocher tous les $t en copie"></a></th>
-            <th><a class="icon-cocher" data-classe="dest_bcc$a" title="Cocher tous les $t en copie cachée"></a><a class="icon-decocher" data-classe="dest_bcc$a" title="Décocher tous les $t en copie cachée"></a></th></tr>
+        <tr class="categorie">
+          <th>$t</th>
+          <th><a class="icon-cocher dest" title="Cocher tous les $t en copie"></a></th>
+          <th><a class="icon-cocher bcc" title="Cocher tous les $t en copie cachée"></a></th>
+        </tr>
 
 FIN;
   }
-  if ( strlen($r['mail']) )
-    $r['nom'] .= ' (pas d\'adresse)';
-  // Le label sert à cocher automatiquement la case "copie", mais l'identifiant
-  // de cette case doit être ajouter au chargement javascript : voir edition.js,
-  // dans la fonction utilisateursmail
-  $table .= "        <tr><td><label for=\"u${r['id']}\">${r['nom']}</label></td><td><input type=\"checkbox\" class=\"dest_c$a\" name=\"dest[]\" value=\"${r['id']}\"${r['mail']}></td><td><input type=\"checkbox\" class=\"dest_bcc$a\" name=\"dest_bcc[]\" value=\"${r['id']}\"${r['mail']}></td></tr>\n";
-}
-
-// Récupération des groupes d'élèves
-$resultat = $mysqli->query('SELECT g.id, g.nom, g.eleves AS eid,
-                            GROUP_CONCAT( IF(LENGTH(e.nom),CONCAT(e.prenom," ",e.nom),CONCAT(e.login," (identifiant)")) ORDER BY IF(LENGTH(e.nom),e.nom,e.login) SEPARATOR \', \') AS eleves
-                            FROM groupes AS g JOIN utilisateurs AS e ON FIND_IN_SET(e.id,g.eleves) WHERE g.mailnotes%2 GROUP BY g.id ORDER BY g.nom_nat');
-if ( $resultat->num_rows )  {
-  $table .= "        <tr><th>Groupes d'élèves</th><th></th><th></th></tr>\n";
-  while ( $r = $resultat->fetch_assoc() )
-    $table .= "        <tr><td>Groupe ${r['nom']}&nbsp;: ${r['eleves']}</td><td><input type=\"checkbox\" class=\"dest\" value=\"${r['eid']}\"></td><td><input type=\"checkbox\" class=\"dest_bcc\" value=\"${r['eid']}\"></td></tr>\n";
-  $resultat->free();
+  if ( strlen($r['pasmail']) )
+    $r['nomcomplet'] .= ' (pas d\'adresse)';
+  $table .= "        <tr><td>${r['nomcomplet']}</td><td><input type=\"checkbox\" class=\"dest\" value=\"${r['id']}\"${r['pasmail']}></td><td><input type=\"checkbox\" class=\"bcc\" value=\"${r['id']}\"${r['pasmail']}></td></tr>\n";
+}
+$resultat->free();
+
+// Récupération des groupes d'utilisateurs, pour les comptes non élèves seulement
+if ( $autorisation > 2 )  {
+  $resultat = $mysqli->query('SELECT g.id, g.nom, g.utilisateurs AS uid,
+                              GROUP_CONCAT( IF(LENGTH(u.nom),CONCAT(u.prenom," ",u.nom),u.login) ORDER BY u.nom SEPARATOR \', \') AS noms
+                              FROM groupes AS g JOIN utilisateurs AS u ON FIND_IN_SET(u.id,g.utilisateurs)
+                              WHERE g.mails AND u.mdp > \'0\' GROUP BY g.id ORDER BY g.nom_nat');
+  if ( $resultat->num_rows )  {
+    $table .= "        <tr class=\"categorie\"><th>Groupes d'élèves</th><th></th><th></th></tr>\n";
+    while ( $r = $resultat->fetch_assoc() )
+      $table .= "        <tr class=\"gr\"><td>Groupe ${r['nom']}&nbsp;: ${r['noms']}</td><td><input type=\"checkbox\" class=\"dest\" value=\"${r['uid']}\"></td><td><input type=\"checkbox\" class=\"bcc\" value=\"${r['uid']}\"></td></tr>\n";
+    $resultat->free();
+  }
 }
+$mysqli->close();
 
 // Aide et formulaire d'ajout
 ?>
 
-  <div id="form-utilisateurs">
-    <a class="icon-ok" title="Valider ces utilisateurs"></a>
+  <div id="form-destinataires">
+    <a class="icon-ok" title="Valider ces destinataires"></a>
     <h3>Choix des destinataires</h3>
     <form>
-    <table class="destinataires">
-      <tbody>
+    <table>
+      <thead>
         <tr><th></th><th>Copie</th><th>Copie cachée</th></tr>
+      </thead>
+      <tbody>
 <?php echo $table; ?>
       </tbody>
     </table>
     </form>
   </div>
   
+<?php
+// Aide spécifique aux élèves
+if ( $autorisation == 2 )  {
+?>
+
+  <div id="aide-page">
+    <h3>Aide et explications</h3>
+    <p>Il est possible ici d'envoyer un courriel aux camarades de la classe, aux colleurs ou aux professeurs. L'envoi du courriel est réalisé lors du clic sur le bouton <span class="icon-mailenvoi" style="font-size: 1em;"></span>.</p>
+    <h4>Expéditeur</h4>
+    <p>Le <em>nom d'expéditeur</em>, l'<em>adresse électronique</em> et le réglage par défaut de la <em>mise en copie des courriels</em> sont modifiables sur la page de <a href="prefs">vos préférences personnelles</a>.</p>
+    <p>Le courriel sera envoyé en votre nom mais avec une adresse électronique différente de la vôtre pour éviter d'être considéré comme du spam. Le retour du mail sera positionné sur votre adresse électronique&nbsp;: si vos correspondants répondent à ce mail, ils devraient automatiquement vous répondre.</p>
+    <h4>Destinataires</h4>
+    <p>La liste des destinataires est éditable à tout moment en cliquant sur le bouton <span class="icon-edite"></span>. Cela ouvre une fenêtre dans laquelle on peut cocher les destinataires. Le bouton <span class="icon-ok"></span> de cette fenêtre valide la liste des destinataires uniquement, n'envoie pas le courriel. Seuls les colleurs et professeurs ayant accepté d'être contactés se trouvent dans cette liste.</p>
+    <p>Les cases à cocher <em>Copie</em> permettent d'ajouter les utilisateurs cochés dans la liste des destinataires visibles. Il est obligatoire qu'au moins un utilisateur soit destinataire.</p>
+    <p>Les cases à cocher <em>Copie cachée</em> permettent d'ajouter les utilisateurs cochés dans la liste des destinataires en copie cachée&nbsp;: les autres destinataires ne verront pas que ce courriel a aussi été envoyé à ceux en copie cachée.</p>
+    <p>Un utilisateur ne peut pas être à la fois en copie et en copie cachée.</p>
+    <p>Un clic sur le nom d'un utilisateur est équivalent à un clic sur la case <em>Copie</em> associée.</p>
+    <h4>Sujet et contenu du courriel</h4>
+    <p>Le <em>sujet</em> est le titre du courriel. Tous les caractères sont autorisés. Vous devez envoyer vos courriels avec un sujet correspondant explicitement au contenu...</p>
+    <p>Le <em>contenu</em> est le corps du courriel, en texte brut. Le courriel envoyé ne sera pas formaté en HTML&nbsp;: il n'est pas possible de réaliser un formattage particulier (changer une taille d'écriture, une police, mettre de la couleur...). Par convention classique,</p>
+    <ul>
+      <li>écrire un mot entre astérisques (*) signifie le mettre en gras et appuyer sur ce mot.</li>
+      <li>écrire un mot entre slashes (/) signifie le mettre en italique pour indiquer qu'il faut y faire attention.</li>
+      <li>écrire en majuscules signifie que l'on en train de crier. :-)</li>
+    </ul>
+    <h4>Réception en copie</h4>
+    <p>Il est aussi possible de recevoir en copie ce mail en cochant la case située en bas de ce formulaire. Si la case est cochée, votre adresse électronique sera placée dans les destinataires en copie cachée.</p>
+    <p>Les messages d'erreurs (de type &laquo;&nbsp;destinataire non valide&nbsp;&raquo;, &laquo;&nbsp;boîte pleine&nbsp;&raquo;...) seront envoyés automatiquement sur votre adresse électronique.</p>
+  </div>
+
+<?php
+}
+// Pour les autres utilisateurs
+else  {
+?>  
+
   <div id="aide-page">
     <h3>Aide et explications</h3>
-    <p>Il est possible ici d'envoyer un courriel aux utilisateurs ayant renseigné leur adresse électronique, qu'il s'agisse d'élèves, de colleurs ou de professeurs. L'envoi du courriel est réalisé lors du clic sur le bouton <span class="icon-ok"></span> situé en haut de cette page.</p>
-    <p>Seuls les professeurs et les colleurs ont accès à cette fonctionnalité.</p>
+    <p>Il est possible ici d'envoyer un courriel aux utilisateurs ayant renseigné leur adresse électronique, qu'il s'agisse d'élèves, de colleurs ou de professeurs. L'envoi du courriel est réalisé lors du clic sur le bouton <span class="icon-mailenvoi" style="font-size: 1em;"></span>.</p>
+    <p>Les élèves peuvent être autorisés à envoyer aussi des courriels par cette interface. Dans ce cas, ils verront une liste contenant tous les élèves et uniquement les utilisateurs colleurs/administration/professeurs ayant validé leur présence sur cette liste dans leurs préférences.</p>
     <h4>Expéditeur</h4>
-    <p>Le <em>nom de l'expéditeur</em> et l'<em>adresse électronique de l'expéditeur</em> sont ceux qui apparaîtront comme expéditeur du courriel. Ils sont modifiables directement en cliquant sur les boutons <span class="icon-edite"></span>, et immédiatement enregistrés dans votre compte&nbsp;: ils seront identiques lors de vos prochaines connexions.</p>
-    <p>Le <em>nom de l'expéditeur</em> par défaut est de la forme <em>Prénom Nom</em>, mais vous pouvez tout-à-fait mettre par exemple <em>M. Bidule</em> ou <em>Mme Bidule</em>. Cela ne change pas vos nom et prénom enregistrés.</p>
+    <p>Le <em>nom d'expéditeur</em>, l'<em>adresse électronique</em> et le réglage par défaut de la <em>mise en copie des courriels</em> sont modifiables sur la page de <a href="prefs">vos préférences personnelles</a>.</p>
+    <p>Le courriel sera envoyé en votre nom mais avec une adresse électronique différente de la vôtre pour éviter d'être considéré comme du spam. Le retour du mail sera positionné sur votre adresse électronique&nbsp;: si vos correspondants répondent à ce mail, ils devraient automatiquement vous répondre.</p>
     <h4>Destinataires</h4>
     <p>La liste des destinataires est éditable à tout moment en cliquant sur le bouton <span class="icon-edite"></span>. Cela ouvre une fenêtre dans laquelle on peut cocher les destinataires. Le bouton <span class="icon-ok"></span> de cette fenêtre valide la liste des destinataires uniquement, n'envoie pas le courriel.</p>
+    <p>Un utilisateur ne peut pas être à la fois en copie et en copie cachée.</p>
     <p>Les cases à cocher <em>Copie</em> permettent d'ajouter les utilisateurs cochés dans la liste des destinataires. Il est obligatoire qu'au moins un utilisateur soit destinataire.</p>
     <p>Les cases à cocher <em>Copie cachée</em> permettent d'ajouter les utilisateurs cochés dans la liste des destinataires en copie cachée&nbsp;: les autres destinataires ne verront pas que ce courriel a aussi été envoyé à ceux en copie cachée.</p>
-    <p>Un utilisateur ne peut pas être à la fois en copie et en copie cachée.</p>
     <p>Les cases à cocher sont grisées si l'utilisateur n'a pas renseigné son adresse électronique.</p>
     <p>Un clic sur le nom d'un utilisateur est équivalent à un clic sur la case <em>Copie</em> associée.</p>
     <p>Les boutons <span class="icon-cocher"></span> permettent de cocher l'ensemble des utilisateurs du type correspondant.</p>
-    <p>Si des groupes d'élèves ont été définis, vous pouvez cocher les cases correspondantes pour ajouter les élèves de ces groupes-là à la sélection.<?php if ( $autorisation == 4 ) echo ' Les groupes d\'élèves sont à définir ou modifier sur la page de <a href="groupes">gestion des groupes</a>.'; ?></p>
+    <h4>Groupes d'utilisateurs</h4>
+    <p>Si des groupes d'utilisateurs ont été établis, ils apparaissent en bas de la liste des destinataires. Cliquer sur les cases correspondantes ou le nom d'un groupe permet de sélectionner automatiquement les utilisateurs concernés.</p><?php if ( $autorisation == 5 ) echo '<p>Les groupes d\'utilisateurs peuvent être définis ou modifiés à la <a href="groupes">gestion des groupes</a>.</p>'; ?>
     <h4>Sujet et contenu du courriel</h4>
-    <p>Le <em>sujet</em> est le sujet du courriel. Tous les caractères sont autorisés. Pensez à envoyer des courriels avec un sujet correspondant explicitement au contenu...</p>
+    <p>Le <em>sujet</em> est le titre du courriel. Tous les caractères sont autorisés. Pensez à envoyer des courriels avec un sujet correspondant explicitement au contenu...</p>
     <p>Le <em>contenu</em> est le corps du courriel, en texte brut. Le courriel envoyé ne sera pas formaté en HTML&nbsp;: il n'est pas possible de réaliser un formattage particulier (changer une taille d'écriture, une police, mettre de la couleur...). Par convention classique,</p>
     <ul>
       <li>écrire un mot entre astérisques (*) signifie le mettre en gras et appuyer sur ce mot.</li>
@@ -143,10 +206,14 @@
       <li>écrire en majuscules signifie que l'on en train d'hurler. :-)</li>
     </ul>
     <h4>Réception en copie</h4>
-    <p>Il est aussi possible de recevoir en copie ce mail en cochant la case située en bas de ce formulaire. Cocher ou décocher cette case enregistre votre choix. Si la case est cochée, votre adresse électronique sera placée dans les destinataires en copie cachée.</p>
-    <p>Les messages d'erreurs (de type &laquo;&nbsp;destinataire non valide&nbsp;&raquo;, &laquo;&nbsp;boîte pleine&nbsp;&raquo;...) vous seront envoyés.</p>
+    <p>Il est aussi possible de recevoir en copie ce mail en cochant la case située en bas de ce formulaire. Si la case est cochée, votre adresse électronique sera placée dans les destinataires en copie cachée.</p>
+    <p>Les messages d'erreurs (de type &laquo;&nbsp;destinataire non valide&nbsp;&raquo;, &laquo;&nbsp;boîte pleine&nbsp;&raquo;...) seront envoyés automatiquement sur votre adresse électronique.</p>
   </div>
-  
+
+<?php
+}
+?>  
+
   <script type="text/javascript">
 $( function() {
   // Envoi par appui sur Entrée dans le sujet
@@ -159,6 +226,5 @@
 });
   </script>
 <?php
-$mysqli->close();
 fin(true);
 ?>
diff -urN cahier-de-prepa6.2.0/MAJSQL.sql cahier-de-prepa8.0.1/MAJSQL.sql
--- cahier-de-prepa6.2.0/MAJSQL.sql	2018-10-14 22:30:44.317976074 +0200
+++ cahier-de-prepa8.0.1/MAJSQL.sql	2018-10-16 10:25:21.920408874 +0200
@@ -349,7 +349,7 @@
   ('2018-06-04'),('2018-06-11'),('2018-06-18'),('2018-06-25'),('2018-07-02');
 INSERT INTO agenda (id,matiere,debut,fin,type,texte)
   VALUES 
-         (1, 0, '2017-09-04 00:00:00', '2017-09-04 00:00:00', 1, '<div class="annonce">C''est la rentrée ! Bon courage pour cette nouvelle année&nbsp;!</div>'),
+         (1, 0, '2017-09-04 00:00:00', '2017-09-04 00:00:00', 3, '<div class="annonce">C''est la rentrée ! Bon courage pour cette nouvelle année&nbsp;!</div>'),
          (2, 0, '2017-08-15 00:00:00', '2017-08-15 00:00:00', 7, '<p>Assomption</p>'),
          (3, 0, '2017-11-01 00:00:00', '2017-11-01 00:00:00', 7, '<p>Toussaint</p>'),
          (4, 0, '2017-11-11 00:00:00', '2017-11-11 00:00:00', 7, '<p>Armistice 1918</p>'),
@@ -416,3 +416,76 @@
          (22, 0, '2019-07-07 00:00:00', '2019-09-01 00:00:00', 8, '<p>Vacances d''été</p>');
 UPDATE semaines SET colle = 1;
 
+--
+-- Voilà les modifications à effectuer sur chaque base pour passer de Cahier de
+-- Prépa 6.2.0 à Cahier de Prépa 8.0.0
+--
+-- Nouvelles colonnes et tables
+ALTER TABLE groupes
+  CHANGE nom nom VARCHAR( 50 ) NOT NULL,
+  CHANGE nom_nat nom_nat VARCHAR( 50 ) NOT NULL,
+  CHANGE eleves utilisateurs VARCHAR( 250 ) NOT NULL,
+  CHANGE mailnotes mails TINYINT( 1 ) UNSIGNED NOT NULL,
+  ADD notes TINYINT( 1 ) UNSIGNED NOT NULL AFTER mails;
+UPDATE groupes SET notes = mails>1, mails = mails%2;
+ALTER TABLE matieres
+  ADD docs_protection TINYINT( 1 ) UNSIGNED NOT NULL,
+  ADD dureecolle TINYINT(2) UNSIGNED NOT NULL;
+UPDATE matieres SET docs_protection = (SELECT protection FROM reps WHERE parent=0 AND matiere=matieres.id);
+ALTER TABLE utilisateurs
+  CHANGE mailcopy mailcopie TINYINT( 1 ) UNSIGNED NOT NULL,
+  ADD mailenvoi TINYINT( 1 ) UNSIGNED NOT NULL,
+  ADD mailliste TINYINT( 1 ) UNSIGNED NOT NULL,
+  ADD permconn VARCHAR( 10 ) NOT NULL,
+  ADD lastconn DATETIME NOT NULL;
+ALTER TABLE infos
+  ADD protection TINYINT( 1 ) UNSIGNED NOT NULL;
+ALTER TABLE reps
+  DROP nbrep,
+  DROP nbrep_v,
+  DROP nbdoc,
+  DROP nbdoc_v;
+ALTER TABLE `cdt-types`
+  DROP nb_v;
+-- Nouvelle gestion des notes
+ALTER TABLE notes 
+  CHANGE eleve eleve SMALLINT( 3 ) UNSIGNED NOT NULL,
+  CHANGE colleur colleur SMALLINT( 3 ) UNSIGNED NOT NULL,
+  ADD heure SMALLINT( 1 ) UNSIGNED NOT NULL AFTER semaine,
+  ADD KEY `semaine` (`semaine`),
+  ADD KEY `heure` (`heure`),
+  ADD KEY `eleve` (`eleve`),
+  ADD KEY `colleur` (`colleur`),
+  ADD KEY `matiere` (`matiere`);
+CREATE TABLE heurescolles (
+  `id` smallint(5) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+  `colleur` smallint(3) unsigned NOT NULL,
+  `matiere` tinyint(2) unsigned NOT NULL,
+  `jour` date NOT NULL,
+  `heure` time NOT NULL,
+  `duree` smallint(3) unsigned NOT NULL,
+  `releve` date NOT NULL,
+  KEY `colleur` (`colleur`),
+  KEY `matiere` (`matiere`)
+) ENGINE=MyISAM  DEFAULT CHARSET=utf8;
+INSERT INTO heurescolles (colleur, matiere, jour, duree) 
+  SELECT colleur,matiere,debut,count(eleve)*20
+  FROM notes JOIN semaines ON notes.semaine=semaines.id
+  GROUP BY semaine,colleur,matiere;
+UPDATE notes AS n, heurescolles AS h, semaines AS s SET n.heure=h.id
+  WHERE n.semaine = s.id AND s.debut = h.jour AND n.colleur = h.colleur AND n.matiere = h.matiere;
+UPDATE matieres SET dureecolle = 20;
+-- Modifications sur la gestion des utilisateurs et des protections
+UPDATE utilisateurs SET autorisation = 5 WHERE autorisation = 4;
+UPDATE matieres SET colles_protection = POW(2,colles_protection-IF(colles_protection<4,1,0)) WHERE colles_protection > 0;
+UPDATE matieres SET cdt_protection = POW(2,cdt_protection-IF(cdt_protection<4,1,0)) WHERE cdt_protection > 0;
+UPDATE pages SET protection = POW(2,protection-IF(protection<4,1,0)) WHERE protection > 0;
+UPDATE reps SET protection = POW(2,protection-IF(protection<4,1,0)) WHERE protection > 0;
+UPDATE docs SET protection = POW(2,protection-IF(protection<4,1,0)) WHERE protection > 0;
+UPDATE recents SET protection = POW(2,protection-IF(protection<4,1,0)) WHERE protection > 0;
+UPDATE prefs SET val = POW(2,val-IF(val<4,1,0)) WHERE nom='protection_agenda' AND val > 0;
+DELETE FROM utilisateurs WHERE mdp = '';
+UPDATE utilisateurs set mdp = "?" WHERE mdp = "*" ; 
+UPDATE utilisateurs SET mailenvoi = 1 WHERE autorisation > 2 AND mail > '';
+UPDATE utilisateurs SET mailliste = 1 WHERE autorisation > 1 AND mail > '';
+
diff -urN cahier-de-prepa6.2.0/matieres.php cahier-de-prepa8.0.1/matieres.php
--- cahier-de-prepa6.2.0/matieres.php	2016-08-11 00:35:28.274946646 +0200
+++ cahier-de-prepa8.0.1/matieres.php	2018-10-14 03:00:44.288099342 +0200
@@ -10,255 +10,239 @@
 // Autorisation //
 //////////////////
 
-// Accès aux professeurs connectés uniquement
+// Accès aux professeurs connectés uniquement. Redirection pour les autres.
+if ( $autorisation < 5 )  {
+  header("Location: https://$site");
+  exit();
+}
 $mysqli = connectsql();
-if ( !$autorisation )  {
+// Si connexion light : on doit répéter son mot de passe pour aller plus loin
+// login.php contient fin()
+if ( $_SESSION['light'] )  {
   $titre = 'Modification des matières';
-  $actuel = false;
+  $actuel = 'matieres';
   include('login.php');
 }
-elseif ( $autorisation < 4 )  {
-  debut($mysqli,'Modification des matières','Vous n\'avez pas accès à cette page.',$autorisation,' ');
-  $mysqli->close();
-  fin();
-}
 
 //////////////
 //// HTML ////
 //////////////
-debut($mysqli,'Modification des matières',$message,4,'matieres');
+debut($mysqli,'Modification des matières',$message,5,'matieres');
 echo <<<FIN
 
-  <a class="icon-aide general" data-id="page" title="Aide pour les modifications des matières"></a>
-  <a class="icon-ajoute general" data-id="ajoute" title="Ajouter une nouvelle matière"></a>
+  <div id="icones">
+    <a class="icon-ajoute formulaire" title="Ajouter une nouvelle matière"></a>
+    <a class="icon-aide" title="Aide pour les modifications des matières"></a>
+  </div>
 
 FIN;
 $select_protection = '
-          <option value="0">Visible de tous</option>
-          <option value="1">Visible pour les connectés</option>
-          <option value="2">Visible pour les élèves, colleurs, profs</option>
-          <option value="3">Visible pour les colleurs et les profs</option>
-          <option value="4">Visible pour les profs uniquement</option>';
+          <option value="0">Accès public</option>
+          <option value="6">Utilisateurs identifiés</option>
+          <option value="1">Invités</option>
+          <option value="2">Élèves</option>
+          <option value="3">Colleurs</option>
+          <option value="4">Adminstratifs</option>
+          <option value="5">Professeurs</option>
+          <option value="32">Fonction désactivée</option>';
 
 // Récupération
-$resultat = $mysqli->query("SELECT m.id, ordre, cle, m.nom, colles, cdt, docs, notes, colles_protection, cdt_protection, FIND_IN_SET(m.id,'${_SESSION['matieres']}') AS modifiable,
-                            COUNT(IF(u.autorisation=4,1,NULL)) AS np, COUNT(IF(u.autorisation=3,1,NULL)) AS nc, COUNT(IF(u.autorisation=2,1,NULL)) AS ne, COUNT(IF(u.autorisation=1,1,NULL)) AS ni,
-                            GROUP_CONCAT(u.id) AS uid
-                            FROM matieres AS m LEFT JOIN utilisateurs AS u ON ( FIND_IN_SET(m.id,u.matieres) OR u.matieres='0') AND LENGTH(u.mdp)=40 GROUP BY m.id ORDER BY ordre");
+$resultat = $mysqli->query("SELECT m.id, ordre, cle, m.nom, colles, cdt, docs, notes, dureecolle, colles_protection, cdt_protection, docs_protection, 
+                            FIND_IN_SET(m.id,'${_SESSION['matieres']}') AS modifiable, COUNT(u.id) AS nbeleves
+                            FROM matieres AS m LEFT JOIN utilisateurs AS u ON FIND_IN_SET(m.id,u.matieres) AND autorisation = 2 AND mdp > '0' GROUP BY m.id ORDER BY ordre");
+$mysqli->close();
 $max = $resultat->num_rows;
-function s($n)  { return ( $n > 1 ) ? 's' : ''; }
 while ( $r = $resultat->fetch_assoc() )  {
   $id = $r['id'];
   $monte = ( $r['ordre'] == 1 ) ? ' style="display:none;"' : '';
   $descend = ( $r['ordre'] == $max ) ? ' style="display:none;"' : '';
-  $sel_colles = str_replace("\"${r['colles_protection']}\"","\"${r['colles_protection']}\" selected",$select_protection);
-  $sel_cdt = str_replace("\"${r['cdt_protection']}\"","\"${r['cdt_protection']}\" selected",$select_protection);
-  $disabled = $boutons = '';
+  $selects_protection = array();
+  foreach ( array('colles','cdt','docs') as $fonction )  {
+    $p = $r[$fonction.'_protection'];
+    if ( ( $p == 0 ) || ( $p == 32 ) )
+      $selects_protection[$fonction] = str_replace("\"$p\"","\"$p\" selected",$select_protection);
+    else  {
+      $selects_protection[$fonction] = str_replace('"6"','"6" selected',$select_protection);
+      for ( $a=1; $a<6; $a++ )
+        if ( ( ($p-1)>>($a-1) & 1 ) == 0 )
+          $selects_protection[$fonction] = str_replace("\"$a\"","\"$a\" selected",$selects_protection[$fonction]);
+    }
+  }
+  $notes = str_replace( ($r['notes']<2)?'"1"':'"2"', ($r['notes']<2)?'"1" selected':'"2" selected', "\n          <option value=\"1\">Fonction activée</option>\n          <option value=\"2\">Fonction désactivée</option>");
+  $boutons = '';
   if ( $r['modifiable'] )  {
     $suppr = "\n    <a class=\"icon-supprime\" title=\"Supprimer cette matière\"></a>";
-    $ok = "\n      <a class=\"icon-ok\" title=\"Valider les modifications\" data-noreload=\"\"></a>";
-    $indication = '';
-    if ( $r['colles'] )
-      $boutons .= "\n    <input type=\"button\" class=\"ligne supprmultiple\" data-id=\"matieres|$id|colles\" value=\"Supprimer tous les programmes de colles\">";
-    if ( $r['cdt'] )
-      $boutons .= "\n    <input type=\"button\" class=\"ligne supprmultiple\" data-id=\"matieres|$id|cdt\" value=\"Supprimer tout le cahier de texte\">";
-    if ( $r['docs'] )
-      $boutons .= "\n    <input type=\"button\" class=\"ligne supprmultiple\" data-id=\"matieres|$id|docs\" value=\"Supprimer tous les répertoires et documents\">";
-    if ( $r['notes'] )
-      $boutons .= "\n    <input type=\"button\" class=\"ligne supprmultiple\" data-id=\"matieres|$id|notes\" value=\"Supprimer toutes les notes\">";
+    $ok = "\n      <a class=\"icon-ok noreload\" title=\"Valider les modifications\"></a>";
+    $indication = $disabled = '';
+    if ( $r['colles'] )    $boutons .= "\n    <input type=\"button\" class=\"ligne supprmultiple\" data-id=\"matieres|$id|colles\" value=\"Supprimer tous les programmes de colles\">";
+    if ( $r['cdt'] )       $boutons .= "\n    <input type=\"button\" class=\"ligne supprmultiple\" data-id=\"matieres|$id|cdt\" value=\"Supprimer tout le cahier de texte\">";
+    if ( $r['docs'] )      $boutons .= "\n    <input type=\"button\" class=\"ligne supprmultiple\" data-id=\"matieres|$id|docs\" value=\"Supprimer tous les répertoires et documents\">";
+    if ( $r['notes'] ==1 ) $boutons .= "\n    <input type=\"button\" class=\"ligne supprmultiple\" data-id=\"matieres|$id|notes\" value=\"Supprimer toutes les notes\">";
   }
   else  {
     $suppr = $ok = '';
-    $indication = "\n      <p>Vous ne pouvez pas modifier cette matière tant qu'elle ne vous est pas associée.</p>";
+    $indication = "\n      <p>Vous ne pouvez pas modifier cette matière tant qu'<a href=\"utilisateurs-matieres\">elle ne vous est pas associée</a>.</p>";
     $disabled = ' disabled';
   }
   if ( $max == 1 )  {
     $suppr = '';
     $indication = '';
   }
-  $np = $r['np']; $nc = $r['nc']; $ne = $r['ne']; $ni = $r['ni'];
-  if ( $n = $np+$nc+$ne+$ni )  {
-    $utilisateurs = '';
-    if ( $np )  $utilisateurs .= ", $np professeur".s($np);
-    if ( $nc )  $utilisateurs .= ", $nc colleur".s($nc);
-    if ( $ne )  $utilisateurs .= ", $ne élève".s($ne);
-    if ( $ni )  $utilisateurs .= ", $ni invité".s($ni);
-    $utilisateurs = "concerne $n utilisateur".s($n).' dont'.substr($utilisateurs,1);
-  }
-  else
-    $utilisateurs = 'ne concerne aucun utilisateur'; 
+  $associationeleves = 'Cette matière '.( $r['nbeleves']>0 ? "concerne ${r['nbeleves']} élèves" : 'ne concerne aucun élève').'. Ceci est modifiable sur la page de <a href="utilisateurs-matieres">gestion des associations utilisateurs-matières</a>.';
   echo <<<FIN
 
   <article data-id="matieres|$id">
-    <a class="icon-aide" data-id="matiere" title="Aide pour l'édition de cette matière"></a>
-    <a class="icon-monte"$monte title="Déplacer cette matière vers le haut"></a>
-    <a class="icon-descend"$descend title="Déplacer cette matière vers le bas"></a>$suppr
-    <form>$ok
+    <a class="icon-aide" title="Aide pour l'édition de cette matière"></a>$ok
+    <a class="icon-descend"$descend title="Déplacer cette matière vers le bas"></a>
+    <a class="icon-monte"$monte title="Déplacer cette matière vers le haut"></a>$suppr
+    <form>
       <h3 class="edition">${r['nom']}</h3>$indication
-      <p class="ligne"><label for="nom$id">Nom complet&nbsp;: </label><input type="text" id="nom$id" name="nom" data-placeholder="Commence par une majuscule : Mathématiques, Physique..." value="${r['nom']}" size="50"$disabled></p>
-      <p class="ligne"><label for="cle$id">Clé dans l'adresse&nbsp;: </label><input type="text" id="cle$id" name="cle" data-placeholder="Diminutif en minuscules : maths, phys..." value="${r['cle']}" size="30"$disabled></p>
+      <p class="ligne"><label for="nom$id">Nom complet&nbsp;: </label><input type="text" id="nom$id" name="nom" placeholder="Commence par une majuscule : Mathématiques, Physique..." value="${r['nom']}" size="50"$disabled></p>
+      <p class="ligne"><label for="cle$id">Clé dans l'adresse&nbsp;: </label><input type="text" id="cle$id" name="cle" placeholder="Diminutif en minuscules : maths, phys..." value="${r['cle']}" size="30"$disabled></p>
       <p class="ligne"><label for="colles_protection$id">Accès aux programmes de colles&nbsp;: </label>
-        <select id="colles_protection$id" name="colles_protection"$disabled>$sel_colles
+        <select name="colles_protection[]" multiple$disabled>${selects_protection['colles']}
         </select>
       </p>
       <p class="ligne"><label for="cdt_protection$id">Accès au cahier de texte&nbsp;: </label>
-        <select id="cdt_protection$id" name="cdt_protection"$disabled>$sel_cdt
+        <select name="cdt_protection[]" multiple$disabled>${selects_protection['cdt']}
         </select>
       </p>
+      <p class="ligne"><label for="docs_protection$id">Accès aux documents&nbsp;: </label>
+        <select name="docs_protection[]" multiple$disabled>${selects_protection['docs']}
+        </select>
+      </p>
+      <p class="ligne"><label for="notes$id">Notes de colles&nbsp;: </label>
+        <select name="notes"$disabled>$notes
+        </select>
+      </p>
+      <p class="ligne"><label for="dureecolle$id">Durée des colles en minutes par élève&nbsp;: </label><input type="text" id="dureecolle$id" name="dureecolle" placeholder="Valeur par défaut. Typiquement 20 ou 30." value="${r['dureecolle']}" size="3"$disabled></p>
+      <p>$associationeleves</p>
     </form>$boutons
-    <p class="usermat"><a class="icon-edite" data-uid="${r['uid']}" data-matiere="${r['nom']}|${r['id']}"></a><span>Cette matière $utilisateurs.</span></p>
   </article>
 
 FIN;
 }
 $resultat->free();
 
-// Récupération de tous les utilisateurs
-// Utilisateurs non validés : mdp précédé d'une étoile
-// Utilisateur vide pour marquer l'arrêt des créations de compte : mdp vide
-$resultat = $mysqli->query('SELECT id, autorisation, IF(LENGTH(nom),CONCAT(nom," ",prenom),CONCAT(login," (identifiant)")) AS nom FROM utilisateurs WHERE LENGTH(mdp)=40 ORDER BY autorisation DESC, nom');
-$a = 0;
-$formulaire = '';
-while ( $r = $resultat->fetch_assoc() )  {
-  if ( $a != $r['autorisation'] )  {
-    $a = $r['autorisation'];
-    switch ( $a )  {
-      case 1 : $t = 'Invités'; break;
-      case 2 : $t = 'Élèves'; break;
-      case 3 : $t = 'Colleurs'; break;
-      case 4 : $t = 'Professeurs'; break;
-    }
-    $formulaire .= <<<FIN
-    <!--<a class="icon-ajoute" title="Ajouter tous les $t"></a>
-    <a class="icon-supprime" title="Retirer tous les $t"></a>-->
-    <h4>$t</h4>
-
-FIN;
-  }
-  // Pas d'identifiant sur les checkbox : ils seront rajoutés en javascript
-  // après chargement dans #fenetre (sinon double id)
-  $formulaire .= "    <p class=\"ligne\"><label for=\"u${r['id']}\">${r['nom']}</label> <input type=\"checkbox\" name=\"u${r['id']}\" class=\"a${r['autorisation']}\"></p>\n";
-}
 
 // Aide et formulaire d'ajout
 ?>
 
-  <div id="form-utilisateurs">
-    <h3>Modifier les utilisateurs en </h3>
-<?php echo $formulaire; ?>
-  </div>
-
-  <form id="form-ajoute">
+  <form id="form-ajoute" data-action="ajout-matiere">
     <h3 class="edition">Ajouter une nouvelle matière</h3>
     <div>
-      <input type="input" class="ligne" name="nom" value="" size="50" data-placeholder="Nom pour l'affichage (Commence par une majuscule : Mathématiques, Physique...)">
-      <input type="input" class="ligne" name="cle" value="" size="50" data-placeholder="Clé pour les adresses web (Diminutif en minuscules : maths, phys...)">
-      <p class="ligne"><label for="colles_protection0">Accès aux programmes de colles&nbsp;: </label>
-        <select id="colles_protection0" name="colles_protection"><?php echo $select_protection; ?>
+      <input type="input" class="ligne" name="nom" value="" size="50" placeholder="Nom pour l'affichage (Commence par une majuscule : Mathématiques, Physique...)">
+      <input type="input" class="ligne" name="cle" value="" size="50" placeholder="Clé pour les adresses web (Diminutif en minuscules : maths, phys...)">
+      <p class="ligne"><label for="colles_protection">Accès aux programmes de colles&nbsp;: </label>
+        <select name="colles_protection[]" multiple><?php echo $select_protection; ?>
+
+        </select>
+      </p>
+      <p class="ligne"><label for="cdt_protection">Accès au cahier de texte&nbsp;: </label>
+        <select name="cdt_protection[]" multiple><?php echo $select_protection; ?>
 
         </select>
       </p>
-      <p class="ligne"><label for="cdt_protection0">Accès au cahier de texte&nbsp;: </label>
-        <select id="cdt_protection0" name="cdt_protection"><?php echo $select_protection; ?>
+      <p class="ligne"><label for="docs_protection">Accès aux documents&nbsp;: </label>
+        <select name="docs_protection[]" multiple><?php echo $select_protection; ?>
 
         </select>
       </p>
+      <p class="ligne"><label for="notes">Notes de colles&nbsp;: </label>
+        <select name="notes">
+          <option value="1">Fonction activée</option>
+          <option value="2">Fonction désactivée</option>
+        </select>
+      </p>
+      <input class="ligne" type="text" name="dureecolle" value="" size="3" placeholder="Durée des colles en minutes par élève. Typiquement 20 ou 30.">
     </div>
-    <input type="hidden" name="table" value="matieres">
   </form>
 
   <div id="aide-page">
     <h3>Aide et explications</h3>
-    <p>Il est possible ici d'ajouter ou de modifier les matières utilisables sur le Cahier de Prépa.</p>
+    <p>Il est possible ici d'ajouter ou de modifier les matières enregistrées.</p>
     <p>Seules les matières qui proposent un contenu (cahier de texte, programmes de colles, documents, notes) sont visibles dans le menu général. Cela est automatique.</p>
     <p>L'ordre d'apparition des matières dans le menu général est modifiable grâce aux boutons <span class="icon-monte"></span> et <span class="icon-descend"></span>.</p>
-    <p>Vous pouvez modifier uniquement les matières associées à votre compte. Vous pouvez néanmoins modifier l'ordre de toutes les matières.</p>
+    <p>Excepté l'ordre des matières, vous ne pouvez modifier que les matières associées à votre compte. Ces associations sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</p>
     <h4>Propriétés de chaque matière</h4>
     <p>Pour chaque matière associée à votre compte, vous pouvez modifier&nbsp;:</p>
     <ul>
-      <li>le <em>nom complet</em> qui s'affiche dans le menu et dans les titres des pages. Mettez une majuscule au début.</li>
-      <li>la <em>clé dans l'adresse</em> qui est un mot-clé utilisé uniquement dans l'adresse des pages associées à la matière. Il vaut mieux que ce soit un mot unique, court et sans majuscule. Par exemple, «&nbsp;maths&nbsp;», «&nbsp;phys&nbsp;»...</li>
-    </ul>
-    <p>L'<em>accès au programmes de colles</em> et l'<em>accès au cahier de texte</em> peuvent être choisis parmi cinq possibilités&nbsp;:</p>
-    <ul>
-      <li><em>Visible de tous</em>&nbsp;: accessible de tout visiteur, sans identification.</li>
-      <li><em>Visible pour les connectés</em>&nbsp;: accessible de tout utilisateur mais uniquement après identification (utilisateurs de type invité, élève, colleur ou professeur).</li>
-      <li><em>Visible pour les élèves, colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type élève, colleur ou professeur.</li>
-      <li><em>Visible pour les colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type colleur ou professeur.</li>
-      <li><em>Visible pour les professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type professeur.</li>
-    </ul>
-    <p>Quel que soit ce choix, les liens dans le menu apparaîtront pour tout visiteur non connecté, dès qu'un contenu existe. Pour les utilisateurs connectés, n'apparaissent dans le menu que les liens vers les contenus visibles.</p>
-    <p>Les modifications de nom, d'accès et les suppressions ne sont possibles que pour les matières qui vous sont associées. Vous pouvez modifier votre association à une matière en cliquant sur le bouton <span class="icon-edite"></span> en bas de chaque matière, afin d'éditer la liste d'utilisateurs. Il est bien sûr possible d'être associé à plusieurs matières.</p>
+      <li>le <em>nom complet</em> qui s'affiche dans le menu général et dans les titres des pages propres à la matières (cahier de texte, programmes de colles, documents, notes).</li>
+      <li>la <em>clé dans l'adresse</em> qui est un mot-clé utilisé uniquement dans l'adresse des pages associées à la matière. Il vaut mieux que ce soit un mot unique, court (possiblement abrégé) et sans majuscule. Par exemple, «&nbsp;maths&nbsp;», «&nbsp;phys&nbsp;»...</li>
+      <li>la <em>durée des colles</em>, qui permet de précalculer automatiquement la durée des colles déclarées. Le colleur peut systématiquement modifier la durée calculée avant de valider sa colle.</li>
+    </ul>
+    <h4>Gestion des accès</h4>
+    <p>L'<em>accès au programmes de colles</em>, l'<em>accès au cahier de texte</em> et l'<em>accès aux documents</em> peuvent être choisis parmi trois possibilités&nbsp;:</p>
+    <ul>
+      <li><em>Accès public</em>&nbsp;: ressource accessible de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: ressource accessible uniquement par les utilisateurs identifiés, en fonction de leur type de compte et des matières auxquelles ils sont associés (seuls les utilisateurs du type autorisé et associés à la matière peuvent accéder à la ressource). Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Fonction désactivée</em>&nbsp;: ressource complètement indisponible pour cette matière. La fonction n'apparaît plus dans le menu, y compris pour vous.</li>
+    </ul>
+    <p>Vous pouvez aussi activer ou désactiver les <em>notes de colles</em>.</p>
+    <p>Lorsque vous désactivez une fonction, cela ne supprime pas les éléments qui auraient été déjà saisis&nbsp;: le choix est réversible.</p>
+    <p>Les liens dans le menu n'existent que lorsque du contenu est présent. Ils sont visibles de tout visiteur avant identification. Ils disparaissent après identification pour les utilisateurs n'ayant pas accès aux contenus.</p>
     <h4>Suppressions massives</h4>
-    <p>Pour les matières qui vous sont associées, il est possible de supprimer en un seul coup les programmes de colles, le cahier de texte ou les documents. Des boutons larges apparaissent alors pour ce faire. Une confirmation sera demandée. Seules les rubriques non vides correspondent à un bouton.</p>
+    <p>Pour les matières qui vous sont associées, il est possible de supprimer en un seul coup les programmes de colles, le cahier de texte, les documents ou les notes, en cliquant sur les boutons associés. Une confirmation sera demandée. Les boutons ne s'affichent pas s'il n'y a rien à supprimer.</p>
     <p>Il est possible de complètement supprimer une matière en cliquant sur le bouton <span class="icon-supprime"></span> (une confirmation sera demandée). Cela entraîne&nbsp;:</p>
     <ul>
       <li>la suppression définitive des programmes de colles de cette matière</li>
       <li>la suppression définitive du cahier de texte de cette matière</li>
-      <li>la suppression définitive des pages d'informations de cette matière</li>
       <li>la suppression définitive des notes de cette matière</li>
+      <li>le déplacement des pages d'informations éventuelles de cette matière vers les pages «&nbsp;générales&nbsp;», non associées à une matière</li>
       <li>le déplacement virtuel du répertoire racine de la matière et de tous les documents qu'il contient au sein du répertoire Général, non associé à une matière.</li>
     </ul>
-    <h4>Édition de la liste des utilisateurs</h4>
-    <p>Pour chaque matière, il est possible d'éditer la liste des utilisateurs associés en cliquant sur le bouton <span class="icon-edite"></span> situé après le décompte global d'utilisateurs associés à la matière. Pour les comptes invités/élèves/colleurs, cela a une conséquence uniquement pour les contenus dont l'accès est restreint. Si l'accès d'un contenu est réglé à &laquo;&nbsp;<em>Visible pour les élèves, colleurs, professeurs</em>&nbsp;&raquo;, seuls les élèves et colleurs associés à la matière pourront y accéder.</p>
-    <p>Les professeurs peuvent voir tous les contenus (même pour les matières qui ne leur sont pas associées) mais ne peuvent éditer que les matières qui leur sont associées.</p>
-    <p>Les invités/élèves/colleurs ne peuvent consulter que les matières qui leur sont associées.</p>
-    <p>Tout compte doit être associé au minimum à une matière.</p>
+    <h4>Associations utilisateurs-matières</h4>
+    <p>Les associations utilisateurs-matières se trouvent sur une <a href="utilisateurs-matieres">page séparée</a>.</p>
   </div>
 
-  <div id="aide-matiere">
+  <div id="aide-matieres">
     <h3>Aide et explications</h3>
     <h4>Édition des propriétés</h4>
-    <p>Les modifications de nom, d'accès et les suppressions ne sont possibles que pour les matières qui vous sont associées. Vous pouvez modifier cela en cliquant sur le bouton <span class="icon-edite"></span> en bas de chaque matière, afin d'éditer la liste d'utilisateurs. Il est bien sûr possible d'être associé à plusieurs matières.</p>
-    <p>Le <em>nom complet</em> est affiché dans le menu général et dans les titres des pages propres à la matières (cahier de texte, programmes de colles, documents, notes). Il doit commencer par une majuscule. Il peut être relativement long. Par exemple&nbsp;: «&nbsp;Mathématiques&nbsp;», «&nbsp;Sciences Physiques&nbsp;»...</p>
-    <p>Le <em>nom</em> est aussi utilisé pour le répertoire situé à la racine des documents et contenant tous les documents de la matière.</p>
-    <p>La <em>clé dans l'adresse</em> n'est lisible que dans les liens pointant vers ce Cahier de Prépa. Par convention, on préfère un mot tout en minuscules et court. Par exemple : «&nbsp;maths&nbsp;», «&nbsp;phys&nbsp;», «&nbsp;ph-ch&nbsp;», «&nbsp;frc&nbsp;»... La clé doit obligatoirement être unique (deux matières ne peuvent pas avoir la même clé).</p>
-    <p>L'<em>accès aux programmes de colles</em> et l'<em>accès au cahier de texte</em> peuvent être choisis parmi cinq possibilités&nbsp;:</p>
-    <ul>
-      <li><em>Visible de tous</em>&nbsp;: accessible de tout visiteur, sans identification.</li>
-      <li><em>Visible pour les connectés</em>&nbsp;: accessible de tout utilisateur mais uniquement après identification (utilisateurs de type invité, élève, colleur ou professeur).</li>
-      <li><em>Visible pour les élèves, colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de types élève, colleur ou professeur.</li>
-      <li><em>Visible pour les colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type colleur ou professeur.</li>
-      <li><em>Visible pour les professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type professeur.</li>
-    </ul>
-    <p>Si ce choix n'est pas <em>Visible de tous</em>, les utilisateurs qui ne sont pas associés à cette matière n'ont pas accès aux pages correspondantes. Par exemple, si le programme de colles n'est visible que par les colleurs ou professeurs, alors les colleurs d'une autre matière n'y auront pas accès. Les professeurs ont eux accès à tout, mais ne peuvent éditer que les pages des matières qui leur sont associées.</p>
-    <p>Quel que soit ce choix, les liens dans le menu apparaîtront pour tout visiteur non connecté, dès qu'un contenu existe. Pour les utilisateurs connectés, n'apparaissent dans le menu que les liens vers les contenus visibles.</p>
-    <p>Pour les documents, le réglage de l'accès est propre à chaque répertoire, et ne se fait donc pas ici.</p>
-    <h4>Déplacements et suppressions</h4>
-    <p>D'autres modifications sont possibles à l'aide des boutons disponibles pour chaque matière&nbsp;:</p>
+    <p>Excepté l'ordre des matières, vous ne pouvez modifier que les matières associées à votre compte. Ces associations sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</p>
+    <p>Le <em>nom complet</em> est affiché dans le menu général et dans les titres des pages propres à la matières (cahier de texte, programmes de colles, documents, notes). Il doit commencer par une majuscule. Il peut être relativement long. Par exemple&nbsp;: «&nbsp;Mathématiques&nbsp;», «&nbsp;Sciences Physiques&nbsp;»... Il est aussi utilisé pour le répertoire situé à la racine des documents et contenant tous les documents de la matière.</p>
+    <p>La <em>clé dans l'adresse</em> n'est lisible que dans les liens pointant vers les ressources associées à la matière. Par convention, on préfère un mot tout en minuscules et court. Par exemple : «&nbsp;maths&nbsp;», «&nbsp;phys&nbsp;», «&nbsp;ph-ch&nbsp;», «&nbsp;frc&nbsp;»... La clé doit obligatoirement être unique (deux matières ne peuvent pas avoir la même clé).</p>
+    <p>La <em>durée des colles</em> est une valeur indicative qui permet de précalculer automatiquement la durée des colles déclarées. Le colleur peut systématiquement modifier la durée calculée avant de valider sa colle.</li>
+    <h4>Gestion des accès</h4>
+    <p>L'<em>accès au programmes de colles</em>, l'<em>accès au cahier de texte</em> et l'<em>accès aux documents</em> peuvent être choisis parmi trois possibilités&nbsp;:</p>
+    <ul>
+      <li><em>Accès public</em>&nbsp;: ressource accessible de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: ressource accessible uniquement par les utilisateurs identifiés, en fonction de leur type de compte et des matières auxquelles ils sont associés (seuls les utilisateurs du type autorisé et associés à la matière peuvent accéder à la ressource). Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Fonction désactivée</em>&nbsp;: ressource complètement indisponible pour cette matière. La fonction n'apparaît plus dans le menu, y compris pour vous.</li>
+    </ul>
+    <p>Vous pouvez aussi activer ou désactiver les <em>notes de colles</em>.</p>
+    <p>Lorsque vous désactivez une fonction, cela ne supprime pas les éléments qui auraient été déjà saisis&nbsp;: le choix est réversible.</p>
+    <p>Les liens dans le menu n'existent que lorsque du contenu est présent. Ils sont visibles de tout visiteur avant identification. Ils disparaissent après identification pour les utilisateurs n'ayant pas accès aux contenus.</p>
+    <h4>Suppressions massives</h4>
+    <p>Pour les matières qui vous sont associées, il est possible de supprimer en un seul coup les programmes de colles, le cahier de texte, les documents ou les notes, en cliquant sur les boutons associés. Une confirmation sera demandée. Les boutons ne s'affichent pas s'il n'y a rien à supprimer.</p>
+    <p>Il est possible de complètement supprimer une matière en cliquant sur le bouton <span class="icon-supprime"></span> (une confirmation sera demandée). Cela entraîne&nbsp;:</p>
     <ul>
-      <li><span class="icon-supprime"></span>&nbsp;: supprimer la matière (une confirmation sera demandée)</li>
-      <li><span class="icon-monte"></span>&nbsp;: remonter la matière d'un cran dans le menu général</li>
-      <li><span class="icon-descend"></span>&nbsp;: descendre le matière d'un cran dans le menu général</li>
+      <li>la suppression définitive des programmes de colles de cette matière</li>
+      <li>la suppression définitive du cahier de texte de cette matière</li>
+      <li>la suppression définitive des notes de cette matière</li>
+      <li>le déplacement des pages d'informations éventuelles de cette matière vers les pages «&nbsp;générales&nbsp;», non associées à une matière</li>
+      <li>le déplacement virtuel du répertoire racine de la matière et de tous les documents qu'il contient au sein du répertoire Général, non associé à une matière.</li>
     </ul>
-    <p>Attention&nbsp;: supprimer une matière n'est pas sans conséquence&nbsp;: cela supprime aussi automatiquement le cahier de texte de la matière, tous les programmes de colles et toutes les notes de la matière, et les pages d'informations associées à la matière. Le répertoire contenant tous les documents n'est pas supprimé mais placé sans matière, comme simple sous-répertoire du répertoire Général.</p>
-    <h4>Suppressions massives</h4>
-    <p>Pour les matières qui vous sont associées et s'il y en a, il est possible de supprimer en un seul coup les programmes de colles, le cahier de texte ou les documents. Des boutons larges apparaissent alors pour ce faire. Une confirmation sera demandée.</p>
-    <h4>Édition de la liste des utilisateurs</h4>
-    <p>Pour chaque matière, il est possible d'éditer la liste des utilisateurs associés en cliquant sur le bouton <span class="icon-edite"></span> situé après le décompte global d'utilisateurs associés à la matière. Pour les comptes invités/élèves/colleurs, cela a une conséquence uniquement pour les contenus dont l'accès est restreint. Si l'accès d'un contenu est réglé à &laquo;&nbsp;<em>Visible pour les élèves, colleurs, professeurs</em>&nbsp;&raquo;, seuls les élèves et colleurs associés à la matière pourront y accéder.</p>
-    <p>Les professeurs peuvent voir tous les contenus (même pour les matières qui ne leur sont pas associées) mais ne peuvent éditer que les matières qui leur sont associées.</p>
-    <p>Les invités/élèves/colleurs ne peuvent pas changer seuls les matières qui leur sont associées.</p>
-    <p>Tout compte doit être associé au minimum à une matière.</p>
+    <h4>Associations utilisateurs-matières</h4>
+    <p>Les associations utilisateurs-matières se trouvent sur une <a href="utilisateurs-matieres">page séparée</a>.</p>
   </div>
 
   <div id="aide-ajoute">
     <h3>Aide et explications</h3>
     <p>Ce formulaire permet de créer une nouvelle matière. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
-    <p>Le <em>nom pour l'affichage</em> sera affiché dans le menu général et dans les titres des pages propres à la matières (cahier de texte, programmes de colles, documents, notes). Il doit commencer par une majuscule. Il peut être relativement long. Par exemple&nbsp;: «&nbsp;Mathématiques&nbsp;», «&nbsp;Sciences Physiques&nbsp;»...</p>
-    <p>Le <em>nom</em> est aussi utilisé pour le répertoire situé à la racine des documents et contenant tous les documents de la matière.</p>
-    <p>La <em>clé pour les adresses web</em> ne sera lisible que dans les liens pointant vers ce Cahier de Prépa. Par convention, on préfère un mot tout en minuscules et court. Par exemple : «&nbsp;maths&nbsp;», «&nbsp;phys&nbsp;», «&nbsp;ph-ch&nbsp;», «&nbsp;frc&nbsp;»... La clé doit obligatoirement être unique (deux matières ne peuvent pas avoir la même clé).</p>
-    <p>L'<em>accès aux programmes de colles</em> et l'<em>accès au cahier de texte</em> peuvent être choisis parmi cinq possibilités&nbsp;:</p>
-    <ul>
-      <li><em>Visible de tous</em>&nbsp;: accessible de tout visiteur, sans identification.</li>
-      <li><em>Visible pour les connectés</em>&nbsp;: accessible de tout utilisateur mais uniquement après identification (utilisateurs de type invité, élève, colleur ou professeur).</li>
-      <li><em>Visible pour les élèves, colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de types élève, colleur ou professeur.</li>
-      <li><em>Visible pour les colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type colleur ou professeur.</li>
-      <li><em>Visible pour les professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type professeur.</li>
-    </ul>
-    <p>Si ce choix n'est pas <em>Visible de tous</em>, les utilisateurs qui ne sont pas associés à cette matière n'ont pas accès aux pages correspondantes. Par exemple, si le programme de colles n'est visible que par les colleurs ou professeurs, alors les colleurs d'une autre matière n'y auront pas accès. Les professeurs ont eux accès à tout, mais ne peuvent éditer que les pages des matières qui leur sont associées.</p>
-    <p>Quel que soit ce choix, les liens dans le menu apparaîtront pour tout visiteur non connecté, dès qu'un contenu existe. Pour les utilisateurs connectés, n'apparaissent dans le menu que les liens vers les contenus visibles.</p>
-    <p>Pour les documents, le réglage de l'accès est propre à chaque répertoire, et ne se fait donc pas ici.</p>
-    <p>La nouvelle matière sera automatiquement associée à tous les comptes utilisateurs qui peuvent déjà avoir accès à toutes les matières (en général, les élèves). Pour les autres comptes, il faudra éditer la liste d'utilisateurs une fois la matière enregistrée.</p>
+    <p>Une nouvelle matière, ainsi que chaque rubrique associée, n'apparaît que si du contenu peut être affiché. Cet affichage est automatique.</p>
+    <p>Le <em>nom pour l'affichage</em> sera affiché dans le menu général et dans les titres des pages propres à la matières (cahier de texte, programmes de colles, documents, notes). Il doit commencer par une majuscule. Il peut être relativement long. Par exemple&nbsp;: «&nbsp;Mathématiques&nbsp;», «&nbsp;Sciences Physiques&nbsp;»... Il est aussi utilisé pour le répertoire situé à la racine des documents et contenant tous les documents de la matière.</p>
+    <p>La <em>clé pour les adresses web</em> ne sera lisible que dans les liens pointant vers les ressources associées à la matière. Par convention, on préfère un mot tout en minuscules et court. Par exemple : «&nbsp;maths&nbsp;», «&nbsp;phys&nbsp;», «&nbsp;ph-ch&nbsp;», «&nbsp;frc&nbsp;»... La clé doit obligatoirement être unique (deux matières ne peuvent pas avoir la même clé).</p>
+    <p>La <em>durée des colles</em> est une valeur indicative qui permet de précalculer automatiquement la durée des colles déclarées. Le colleur peut systématiquement modifier la durée calculée avant de valider sa colle.</li>
+    <h4>Gestion des accès</h4>
+    <p>L'<em>accès au programmes de colles</em>, l'<em>accès au cahier de texte</em> et l'<em>accès aux documents</em> peuvent être choisis parmi trois possibilités&nbsp;:</p>
+    <ul>
+      <li><em>Accès public</em>&nbsp;: ressource accessible de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: ressource accessible uniquement par les utilisateurs identifiés, en fonction de leur type de compte et des matières auxquelles ils sont associés (seuls les utilisateurs du type autorisé et associés à la matière peuvent accéder à la ressource). Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Fonction désactivée</em>&nbsp;: ressource complètement indisponible pour cette matière. La fonction n'apparaît plus dans le menu, y compris pour vous.</li>
+    </ul>
+    <p>Les liens dans le menu n'existeront que lorsque du contenu sera présent. Ils seront visibles de tout visiteur avant identification. Ils disparaitront après identification pour les utilisateurs n'ayant pas accès aux contenus.</p>
+    <h4>Association aux utilisateurs</h4>
+    <p>La nouvelle matière sera automatiquement associée à tous les comptes utilisateurs qui peuvent déjà avoir accès à toutes les matières (en général, les élèves). Après création, les associations avec les utilisateurs pourront être modifiées à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</p>
   </div>
 
   <p id="log"></p>
@@ -267,13 +251,13 @@
 $( function() {
   // Envoi par appui sur Entrée
   $('input,select').on('keypress',function (e) {
-    if ( e.which == 13 )
-      $(this).parent().parent().children('a.icon-ok').click();
+    if ( e.which == 13 ) {
+      $(this).parent().parent().siblings('a.icon-ok').click();
+      return false;
+    }
   });
 });
   </script>
 <?php
-
-$mysqli->close();
 fin(true);
 ?>
diff -urN cahier-de-prepa6.2.0/notes.php cahier-de-prepa8.0.1/notes.php
--- cahier-de-prepa6.2.0/notes.php	2016-08-25 01:15:52.973595536 +0200
+++ cahier-de-prepa8.0.1/notes.php	2018-10-17 01:11:56.925053433 +0200
@@ -11,17 +11,26 @@
 //////////////////
 
 // Accès aux professeurs, colleurs, élèves connectés uniquement
+// L'administration doit aller sur une autre page
 $mysqli = connectsql();
 if ( !$autorisation )  {
   $titre = 'Notes de colles';
   $actuel = false;
   include('login.php');
 }
-elseif ( $autorisation < 2 )  {
+elseif ( ( $autorisation < 2 ) || ( $autorisation == 4 ) )  {
   debut($mysqli,'Notes de colles','Vous n\'avez pas accès à cette page.',$autorisation,' ');
   $mysqli->close();
   fin();
 }
+// Pour colleurs et professeurs : connexion light insuffisante
+// Si connexion light : on doit répéter son mot de passe pour aller plus loin
+// login.php contient fin()
+if ( ( $autorisation > 2 ) && $_SESSION['light'] )  {
+  $titre = 'Notes de colles';
+  $actuel = 'colles';
+  include('login.php');
+}
 
 ////////////////////////////////////////
 // Validation de la requête : matière //
@@ -29,12 +38,16 @@
 
 // Recherche de la matière concernée, variable $matiere
 // Si $_REQUEST['cle'] existe, on la cherche dans les matières disponibles.
-$resultat = $mysqli->query('SELECT id, cle, nom FROM matieres WHERE '.( ( $autorisation == 2 ) ? 'notes AND ' : '' )."FIND_IN_SET(id,'${_SESSION['matieres']}')");
+// notes=0 : pas de note saisie
+// notes=1 : déjà des notes saisies
+// notes = 2 : fonction désactivée, pas d'affichage
+$resultat = $mysqli->query("SELECT id, cle, nom, dureecolle FROM matieres WHERE FIND_IN_SET(id,'${_SESSION['matieres']}') AND notes < 2");
 if ( $resultat->num_rows )  {
   if ( !empty($_REQUEST) )  {
     while ( $r = $resultat->fetch_assoc() )
       if ( isset($_REQUEST[$r['cle']]) )  {
         $matiere = $r;
+        $mid = $matiere['id'];
         break;
       }
   }
@@ -46,7 +59,7 @@
     fin();
   }
 }
-// Si aucune matière présentant son programme de colle n'est enregistrée
+// Si aucune matière avec des notes n'est enregistrée
 else  {
   debut($mysqli,'Notes de colles','Cette page ne contient aucune information.',$autorisation,' ');
   $mysqli->close();
@@ -57,12 +70,13 @@
 // Exportation des notes en xls //
 //////////////////////////////////
 // Exportation uniquement si aucun header déjà envoyé
-if ( ( $autorisation == 4 ) && isset($_REQUEST['xls']) && !headers_sent() )  {
+if ( ( $autorisation > 3 ) && isset($_REQUEST['xls']) && !headers_sent() )  {
   // Recherche des notes concernées
-  $resultat = $mysqli->query("SELECT nom, GROUP_CONCAT( IFNULL(note,'') ORDER BY sid SEPARATOR '|') AS notes
+  $resultat = $mysqli->query("SELECT nom, GROUP_CONCAT( IFNULL(note,'') ORDER BY sid SEPARATOR '|') AS notes,
+                              LEFT(REPLACE(AVG(IF(note='abs' OR note='nn',NULL,REPLACE(note,',','.'))),'.',','),4) AS moyenne
                               FROM ( SELECT s.id AS sid, u.id AS eid, IF(LENGTH(nom),CONCAT(nom,' ',prenom),CONCAT(login,' (identifiant)')) AS nom
-                                     FROM semaines AS s LEFT JOIN utilisateurs AS u ON 1 WHERE colle AND u.autorisation=2 AND FIND_IN_SET(${matiere['id']},u.matieres) ORDER BY IF(LENGTH(nom),nom,login)) AS t
-                              LEFT JOIN notes ON sid = semaine AND eid = eleve AND matiere = ${matiere['id']} GROUP BY eid ORDER BY nom,sid");
+                                     FROM semaines AS s LEFT JOIN utilisateurs AS u ON 1 WHERE colle AND u.autorisation=2 AND FIND_IN_SET($mid,u.matieres) ORDER BY IF(LENGTH(nom),nom,login)) AS t
+                              LEFT JOIN notes ON sid = semaine AND eid = eleve AND matiere = $mid GROUP BY eid ORDER BY nom,sid");
   if ( $resultat->num_rows )  {
     // Fonctions de saisie
     function saisie_nombre($l, $c, $v)  {
@@ -85,6 +99,7 @@
     while ( $r = $semaines->fetch_row() )
       saisie_chaine(0, ++$i, $r[0]);
     $semaines->free();
+    saisie_chaine(0, $colmoy = ++$i, 'Moyenne');
     $i = 0;
     while ( $r = $resultat->fetch_assoc() )  {
       saisie_chaine(++$i, 0, utf8_decode($r['nom']));
@@ -94,6 +109,7 @@
           saisie_nombre($i, $j+1, $n);
         elseif ( strlen($n) )
           saisie_chaine($i, $j+1, $n);
+      saisie_chaine($i, $colmoy, $r['moyenne']);
     }
     // Fin du fichier xls
     echo pack("ss", 0x0A, 0x00);
@@ -106,20 +122,19 @@
 ////////////
 /// HTML ///
 ////////////
-debut($mysqli,"Notes de colles - ${matiere['nom']}",$message,$autorisation,"notes?${matiere['cle']}");
+debut($mysqli,"Notes de colles - ${matiere['nom']}",$message,$autorisation,"notes?${matiere['cle']}",$mid,'datetimepicker');
 
 // Affichage pour les élèves
 if ( $autorisation == 2 )  {
   
-  // Récupération de l'ensemble des notes, semaines, colleurs
-  $resultat = $mysqli->query("SELECT DATE_FORMAT(s.debut,'%w%Y%m%e') AS debut, n.note, IF(LENGTH(u.nom),u.nom,u.login) AS colleur
-                              FROM notes AS n LEFT JOIN semaines AS s ON n.semaine=s.id LEFT JOIN utilisateurs AS u ON n.colleur=u.id
-                              WHERE n.eleve = ${_SESSION['id']} AND n.matiere = ${matiere['id']} ORDER BY s.id");
-
+  // Récupération de l'ensemble des notes, semaines, colleurs propres à l'élève
+  $resultat = $mysqli->query("SELECT DATE_FORMAT(jour,'%w%Y%m%e') AS jour, n.note, IF(LENGTH(c.nom),c.nom,c.login) AS colleur
+                              FROM notes AS n LEFT JOIN utilisateurs AS c ON n.colleur=c.id LEFT JOIN heurescolles AS h ON n.heure = h.id
+                              WHERE n.eleve = ${_SESSION['id']} AND n.matiere = $mid ORDER BY jour DESC");
   // Affichage des notes concernées
   if ( $resultat->num_rows )  {
     while ( $r = $resultat->fetch_assoc() )
-      echo "\n  <article>\n    <h3>Semaine du ".format_date($r['debut'])."</h3>\n    <p>${r['note']} (${r['colleur']})</p>\n  </article>\n";
+      echo "\n  <article>\n    <p><strong>".ucfirst(format_date($r['jour']))."</strong>&nbsp;: ${r['note']} (${r['colleur']})</p>\n  </article>\n";
     $resultat->free();
   }
   else
@@ -129,24 +144,37 @@
 
 // Affichage colleur et professeur
 else  {
+  
+  // Fonction pour l'affichage des durées en heures/minutes. Argument en minutes.
+  function format_duree($duree)  {
+    if ( $duree == 0 )
+      return '-';
+    if ( $duree >= 60 )
+      return intdiv($duree,60).'h'.( $duree%60 ?: '');
+    return ($duree%60).'m';
+  }
 
-  // Professeur : Visibilité du tableau global
-  if ( $autorisation == 4 )  {
+  // Pour les professeurs : affichage d'un récapitulatif pour tous les colleurs
+  // de la matière et possibilité d'affichage d'un tableau de notes global
+  if ( $autorisation == 5 )  {
     // Icônes d'actions générales
     echo <<<FIN
   
-  <a class="icon-aide general" data-id="page" title="Aide pour les modifications des programmes de colles"></a>
-  <a class="icon-voirtout general" href="?${matiere['cle']}&amp;tableau" title="Voir le tableau de notes"></a>
-  <a class="icon-download general" href="?${matiere['cle']}&amp;xls" title="Télécharger le tableau de notes en xls"></a>
-  <a class="icon-ajoute general" data-id="ajoute-notes" title="Ajouter des notes de colles"></a>
+  <div id="icones">
+    <a class="icon-ajoute formulaire" title="Ajouter des notes de colles"></a>
+    <a class="icon-prefs formulaire" title="Modifier les réglages des notes de colles"></a>
+    <a class="icon-voirtout" href="?${matiere['cle']}&amp;tableau" title="Voir le tableau de notes"></a>
+    <a class="icon-download" href="?${matiere['cle']}&amp;xls" title="Télécharger le tableau de notes en xls"></a>
+    <a class="icon-aide" title="Aide pour les modifications des notes de colles"></a>
+  </div>
 
 FIN;
 
     // Tableau récapitulatif
     if ( isset($_REQUEST['tableau']) )  {
       // Récupération des colleurs
-      $resultat = $mysqli->query("SELECT id, IF(LENGTH(nom),CONCAT(prenom,' ',nom),CONCAT(login,' (identifiant)')) AS nom
-                                  FROM utilisateurs WHERE autorisation > 2 AND FIND_IN_SET(${matiere['id']},matieres) ORDER BY IF(LENGTH(nom),nom,login)");
+      $resultat = $mysqli->query("SELECT id, IF(LENGTH(nom),CONCAT(prenom,' ',nom),login) AS nom
+                                  FROM utilisateurs WHERE autorisation > 2 AND FIND_IN_SET($mid,matieres) ORDER BY IF(LENGTH(nom),nom,login)");
       $select_colleurs = '';
       while ( $r = $resultat->fetch_assoc() )
         $select_colleurs .= "\n      <option value=\"${r['id']}\">${r['nom']}</option>";
@@ -163,230 +191,414 @@
 
 FIN;
       // Recherche des notes concernées
-      $resultat = $mysqli->query("SELECT nom, GROUP_CONCAT( IF(ISNULL(note),'<td></td>',CONCAT('<td data-colleur=\"',colleur,'\">',note,'</td>')) ORDER BY sid SEPARATOR '') AS notes
-                                  FROM ( SELECT s.id AS sid,  u.id AS eid, CONCAT('<td>',IF(LENGTH(nom),CONCAT(nom,' ',prenom),CONCAT(login,' (identifiant)')),'</td>') AS nom
-                                         FROM semaines AS s LEFT JOIN utilisateurs AS u ON 1 WHERE colle AND u.autorisation=2 AND FIND_IN_SET(${matiere['id']},u.matieres) ORDER BY IF(LENGTH(nom),nom,login)) AS t
-                                  LEFT JOIN notes ON sid = semaine AND eid = eleve AND matiere = ${matiere['id']} GROUP BY eid ORDER BY nom,sid");
+      $resultat = $mysqli->query("SELECT nom, GROUP_CONCAT( IF(ISNULL(note),'<td></td>',CONCAT('<td data-colleur=\"',colleur,'\">',note,'</td>')) ORDER BY sid SEPARATOR '') AS notes,
+                                  LEFT(REPLACE(AVG(IF(note='abs' OR note='nn',NULL,REPLACE(note,',','.'))),'.',','),4) AS moyenne
+                                  FROM ( SELECT s.id AS sid,  u.id AS eid, CONCAT('<td>',IF(LENGTH(nom),CONCAT(nom,' ',prenom),login),'</td>') AS nom
+                                         FROM semaines AS s LEFT JOIN utilisateurs AS u ON 1 WHERE colle AND u.autorisation=2 AND FIND_IN_SET($mid,u.matieres) ORDER BY IF(LENGTH(nom),nom,login)) AS t
+                                  LEFT JOIN notes ON sid = semaine AND eid = eleve AND matiere = $mid GROUP BY eid ORDER BY nom,sid");
       if ( $resultat->num_rows )  {
-        echo "\n  <table>\n    <thead>\n      <tr><th class=\"semaines\">Nom</th>";
+        echo "\n  <table>\n    <thead>\n      <tr><th></th>";
         $semaines = $mysqli->query('SELECT DATE_FORMAT(debut,\'%d/%m\') FROM semaines WHERE colle');
         $nb = $semaines->num_rows;
         while ( $r = $semaines->fetch_row() )
-          echo "<td class=\"semaines\"><span>${r[0]}</span></td>";
+          echo "<th class=\"semaines\"><span>${r[0]}</span></th>";
+        echo '<th class="semaines"><span>Moyenne</span></th>';
         $semaines->free();
         echo "</tr>\n    </thead>\n    <tbody>\n";
         while ( $r = $resultat->fetch_assoc() )
           if ( strlen(str_replace('<td></td>','',$r['notes'])) )
-            echo "      <tr>${r['nom']}${r['notes']}</tr>\n";
+            echo "      <tr>${r['nom']}${r['notes']}<td>${r['moyenne']}</td></tr>\n";
           else
-            echo "      <tr>${r['nom']}<td class=\"pasnote\" colspan=\"$nb\">Pas encore de note pour cet élève</td></tr>\n";
+            echo "      <tr>${r['nom']}<td class=\"pasnote\" colspan=\"$nb+1\">Pas encore de note pour cet élève</td></tr>\n";
         $resultat->free();
         echo "    </tbody>\n  </table>\n";
       }
       else
         echo "\n  <article>\n    <h2>Il n'y a encore aucune note de colle en ${matiere['nom']} cette année.</h2>\n  </article>\n";
     }
+    
+    // Récupération du décompte de la matière
+    $resultat = $mysqli->query("SELECT COUNT(*) FROM notes WHERE matiere = $mid");
+    $r = $resultat->fetch_row();
+    $resultat->free();
+    //~ $resultat = $mysqli->query("SELECT IF(LENGTH(nom),CONCAT(LEFT(prenom,1),'. ',nom),login), SUM(nb), SUM(duree*(releve>0)), SUM(duree*(releve=0)) 
+                                //~ FROM heurescolles AS h LEFT JOIN (SELECT COUNT(*) AS nb, heure FROM notes GROUP BY heure) AS n ON h.id = n.heure LEFT JOIN utilisateurs AS u ON h.colleur=u.id
+                                //~ WHERE h.matiere = $mid GROUP BY h.colleur");
+    $resultat = $mysqli->query("SELECT colleur, IF(LENGTH(nom),CONCAT(LEFT(prenom,1),'. ',nom),login), SUM(duree*(releve>0)), SUM(duree*(releve=0)) 
+                                FROM heurescolles AS h LEFT JOIN utilisateurs AS u ON h.colleur=u.id
+                                WHERE matiere = $mid GROUP BY colleur ORDER BY IF(LENGTH(nom),nom,login)");
+    if ( $resultat->num_rows )  {
+      echo  <<<FIN
+  <article>
+    <h3>Récapitulatif de la matière</h3>
+    <table id="notes">
+      <tbody>
+        <tr><th></th>
+
+FIN;
+      $ligne_eleves = $ligne_heures1 = $ligne_heures2 = $ligne_moyenne = '';
+      $total = array(0,0,0);
+      while ( $r = $resultat->fetch_row() )  {
+        $resultat2 = $mysqli->query("SELECT COUNT(*), LEFT(REPLACE(AVG(IF(note='abs' OR note='nn',NULL,REPLACE(note,',','.'))),'.',','),4) FROM notes WHERE colleur = ${r[0]} AND matiere = $mid");
+        $s = $resultat2->fetch_row();
+        $resultat2->free();
+        echo "          <th class=\"colleurs\"><span>${r[1]}</span></th>\n";
+        $ligne_eleves .= "<td>${s[0]}</td>";
+        $ligne_heures1 .= '<td>'.format_duree($r[2]).'</td>';
+        $ligne_heures2 .= '<td>'.format_duree($r[3]).'</td>';
+        $ligne_moyenne .= "<td>${s[1]}</td>";
+        $total[0] += $s[0];
+        $total[1] += $r[2];
+        $total[2] += $r[3];
+      }
+      $resultat->free();
+      // Totaux d'heures
+      $total[1] = format_duree($total[1]);
+      $total[2] = format_duree($total[2]);
+      // Moyenne globale
+      $resultat = $mysqli->query("SELECT LEFT(REPLACE(AVG(IF(note='abs' OR note='nn',NULL,REPLACE(note,',','.'))),'.',','),4) FROM notes WHERE matiere = $mid");
+      $r = $resultat->fetch_row();
+      $resultat->free();
+      echo <<<FIN
+          <th class="colleurs"><span>Total</span></th>
+        </tr>
+        <tr><th>Nombre d'élèves interrogés</th>$ligne_eleves<td>${total[0]}</td></tr>
+        <tr><th>Nombre d'heures relevées</th>$ligne_heures1<td>${total[1]}</td></tr>
+        <tr><th>Nombre d'heures non relevées</th>$ligne_heures2<td>${total[2]}</td></tr>
+        <tr><th>Moyenne</th>$ligne_moyenne<td>${r[0]}</td></tr>
+      </tbody>
+    </table>
+  </article>
+  
+FIN;
+    }
   }
-  else
+  // Pour les colleurs : affichage d'un récapitulatif personnel
+  else  {
     echo <<<FIN
   
-  <a class="icon-aide general" data-id="page" title="Aide pour les modifications des programmes de colles"></a>
-  <a class="icon-ajoute general" data-id="ajoute-notes" title="Ajouter des notes de colles"></a>
+  <div id="icones">
+    <a class="icon-ajoute formulaire" title="Ajouter des notes de colles"></a>
+    <a class="icon-aide" title="Aide pour les modifications des notes de colles"></a>
+  </div>
+
+FIN;
 
+    // Récupération du décompte personnel
+    $resultat = $mysqli->query("SELECT COUNT(*), LEFT(REPLACE(AVG(IF(note='abs' OR note='nn',NULL,REPLACE(note,',','.'))),'.',','),4) FROM notes WHERE colleur = ${_SESSION['id']} AND matiere = $mid");
+    $r = $resultat->fetch_row();
+    $resultat->free();
+    $resultat = $mysqli->query("SELECT SUM(duree*(releve>0)), SUM(duree*(releve=0)) FROM heurescolles WHERE colleur = ${_SESSION['id']} AND matiere = $mid");
+    $s = array_map('format_duree',$resultat->fetch_row());
+    $resultat->free();
+    echo <<<FIN
+  
+  <article>
+    <h3>Récapitulatif personnel</h3>
+    <p><strong>Nombre d'élèves interrogés</strong>&nbsp;:&nbsp;${r[0]}</p>
+    <p><strong>Nombre d'heures relevées</strong>&nbsp;:&nbsp;${s[0]}</p>
+    <p><strong>Nombre d'heures non relevées</strong>&nbsp;:&nbsp;${s[1]}</p>
+    <p><strong>Moyenne</strong>&nbsp;:&nbsp;${r[1]}/20</p>
+  </article>
+  
 FIN;
+  }
 
-  // Récupération de l'ensemble des élèves (associés à la matière)
-  $resultat = $mysqli->query("SELECT id, IF(LENGTH(nom),CONCAT(nom,' ',prenom),CONCAT(login,' (identifiant)')) AS nom
-                              FROM utilisateurs WHERE autorisation = 2 AND FIND_IN_SET(${matiere['id']},matieres) ORDER BY IF(LENGTH(nom),nom,login)");
+  // Récupération de l'ensemble des élèves associés à la matière
+  $resultat = $mysqli->query("SELECT id, IF(LENGTH(nom),CONCAT(nom,' ',prenom),login) AS nomcomplet, IF(LENGTH(nom),CONCAT(prenom,' ',nom),login) AS prenomnom,
+                              IF(LENGTH(nom),CONCAT(LEFT(prenom,1),'. ',nom),login) AS initiale
+                              FROM utilisateurs WHERE autorisation = 2 AND FIND_IN_SET($mid,matieres) ORDER BY IF(LENGTH(nom),nom,login)");
   $eleves = array();
   while ( $r = $resultat->fetch_assoc() )
-    $eleves[$r['id']] = $r['nom'];
-  $resultat->free();
-
-  // Récupération de l'ensemble des semaines
-  $resultat = $mysqli->query("SELECT s.id, DATE_FORMAT(debut,'%w%Y%m%e') AS debut, colle, vacances, IFNULL(GROUP_CONCAT(n.eleve),'') AS eleves, COUNT(IF(n.colleur=${_SESSION['id']},1,NULL)) AS n
-                              FROM semaines AS s LEFT JOIN (SELECT * FROM notes WHERE matiere = ${matiere['id']}) AS n ON s.id = n.semaine GROUP BY s.id");
-  $select_semaines = "\n        <option value=\"0\">Choisir une semaine</option>";
-  $semaines = $semaines_notes = array();
-  while ( $r = $resultat->fetch_assoc() )  {
-    switch ( $r['vacances'] )  {
-      case 0:
-        if ( $r['colle'] == 0 )
-          $select_semaines .= "\n        <option disabled>".format_date($r['debut']).' (pas de colle)</option>';
-        elseif ( $r['n'] )  {
-          $select_semaines .= "\n        <option disabled>".format_date($r['debut'])." (${r['n']} notes déjà entrées)</option>";
-          $semaines[$r['id']] = $r['debut']; 
-          $semaines_notes[$r['id']] = $r['eleves']; 
-        }
-        else  {
-          $select_semaines .= "\n        <option value=\"${r['id']}-${matiere['id']}\">".format_date($r['debut']).'</option>';
-          $semaines_notes[$r['id']] = $r['eleves']; 
-        }
-        break;
-      case 1:
-        $select_semaines .= "\n        <option disabled>Vacances de Toussaint</option>";
-        break;
-      case 2:
-        $select_semaines .= "\n        <option disabled>Vacances de Noël</option>";
-        break;
-      case 3:
-        $select_semaines .= "\n        <option disabled>Vacances d'hiver</option>";
-        break;
-      case 4:
-        $select_semaines .= "\n        <option disabled>Vacances de Pâques</option>";
-    }
-  }
+    $eleves[$r['id']] = $r;
   $resultat->free();
   
-  // Récupération de l'ensemble des notes mises, regroupées par semaine
-  $resultat = $mysqli->query("SELECT semaine, COUNT(id) AS nb,
-                              GROUP_CONCAT(eleve ORDER BY id SEPARATOR '|') AS eleves, GROUP_CONCAT(note ORDER BY id SEPARATOR '|') AS notes
-                              FROM notes WHERE colleur = ${_SESSION['id']} AND matiere = ${matiere['id']} GROUP BY semaine ORDER BY semaine DESC");
+  // Récupération de l'ensemble des notes mises, regroupées par heure
+  $resultat = $mysqli->query("SELECT h.id, n.semaine AS sid, DATE_FORMAT(jour,'%d/%m/%y') AS jour, TIME_FORMAT(h.heure,'%kh%i') AS heure, duree, DATE_FORMAT(releve,'%d/%m') AS releve,
+                              GROUP_CONCAT( eleve ORDER BY IF(LENGTH(nom),nom,login) SEPARATOR '|') AS eleves,
+                              GROUP_CONCAT( note  ORDER BY IF(LENGTH(nom),nom,login) SEPARATOR '|') AS notes
+                              FROM heurescolles AS h JOIN notes AS n ON n.heure = h.id JOIN utilisateurs AS e ON n.eleve = e.id
+                              WHERE h.colleur = ${_SESSION['id']} AND h.matiere = $mid
+                              GROUP BY h.id ORDER BY h.jour DESC, h.heure DESC");
   // Affichage de l'ensemble des notes de l'année
   if ( $resultat->num_rows )  {
+    echo  <<<FIN
+  <article>
+    <h3>Listes des colles réalisées</h3>
+    <table id="notes">
+      <tbody>
+        <tr><th>Jour</th><th>Heure</th><th>Élèves (notes)</th><th>Durée</th><th>Relève</th><th></th></tr>
+
+FIN;
     while ( $r = $resultat->fetch_assoc() )  {
-      $date = format_date($semaines[$r['semaine']]);
+      $heure = ( $r['heure'] == '0h00' ) ? '-' : $r['heure'];
+      $duree = format_duree($r['duree']);
+      if ( substr($r['releve'],0,2) == '00' )  {
+        $r['releve'] = '-';
+        $supprime = "\n            <a class=\"icon-supprime\" title=\"Supprimer cette colle\"></a>";
+      }
+      else  {
+        $supprime = '';
+      }
       $notes = array_combine(explode('|',$r['eleves']),explode('|',$r['notes']));
-      $n = count($notes);
       $texte = array();
       foreach( $notes as $e => $n ) {
-        $texte[] = "${eleves[$e]} ($n)";
+        $texte[] = "{$eleves[$e]['initiale']} ($n)";
       }
       $texte = implode(', ',$texte);
       echo <<<FIN
-
-  <article data-id="notes|${r['semaine']}-${matiere['id']}">
-    <h3 class="edition">Semaine du $date</h3>
-    <a class="icon-supprime" title="Supprimer ces notes"></a>
-    <a class="icon-edite" title="Modifier ces notes" data-id="notes" data-eleves="${r['eleves']}" data-notes="${r['notes']}" onclick="formulaire(this);"></a>
-    <p><strong>${r['nb']} notes&nbsp;:</strong> $texte</p>
-  </article>
+        <tr>
+          <td>${r['jour']}</td><td>$heure</td><td>$texte</td><td>$duree</td><td>${r['releve']}</td>
+          <td class="icones" data-id="notes|${r['id']}">
+            <a class="icon-edite formulaire" data-eleves="${r['eleves']}" data-notes="${r['notes']}" data-sid="${r['sid']}" title="Éditer cette colle"></a>$supprime
+          </td>
+        </tr>
 
 FIN;
     }
     $resultat->free();
+    echo <<<FIN
+      </tbody>
+    </table>
+  </article>
+
+FIN;
   }
   else
     echo "\n  <article>\n    <h2>Vous n'avez encore saisi aucune note en ${matiere['nom']} cette année.</h2>\n  </article>\n";
 
-  // Table contenant les élèves
+  // Table contenant les élèves et les groupes
   $table = '';
-  foreach ( $eleves as $id => $eleve )  {
-    $table .= "        <tr data-id=\"$id\"><td>$eleve</td><td></td></tr>\n";
-  }
+  foreach ( $eleves as $id => $eleve )
+    $table .= "        <tr data-id=\"$id\"><td>${eleve['nomcomplet']}</td></tr>\n";
   // Récupération des groupes de colles et préparation à l'affichage
   $resultat = $mysqli->query("SELECT g.id, g.nom, GROUP_CONCAT(e.id) AS eid,
-                              GROUP_CONCAT( IF(LENGTH(e.nom),CONCAT(e.prenom,' ',e.nom),CONCAT(e.login,' (identifiant)')) ORDER BY IF(LENGTH(e.nom),e.nom,e.login) SEPARATOR ', ') AS eleves
-                              FROM groupes AS g JOIN utilisateurs AS e ON FIND_IN_SET(e.id,g.eleves)
-                              WHERE g.mailnotes>1 AND FIND_IN_SET(${matiere['id']},e.matieres) GROUP BY g.id ORDER BY g.nom_nat");
+                              GROUP_CONCAT( IF(LENGTH(e.nom),CONCAT(e.prenom,' ',e.nom),e.login) ORDER BY IF(LENGTH(e.nom),e.nom,e.login) SEPARATOR ', ') AS eleves
+                              FROM groupes AS g JOIN utilisateurs AS e ON FIND_IN_SET(e.id,g.utilisateurs)
+                              WHERE g.notes=1 AND FIND_IN_SET($mid,e.matieres) AND e.autorisation = 2
+                              GROUP BY g.id ORDER BY g.nom_nat");
   if ( $resultat->num_rows )  {
     $table .= "        <tr><th>Groupes de colles</th><th></th></tr>\n";
     while ( $r = $resultat->fetch_assoc() )
       $table .= "        <tr><td>Groupe ${r['nom']}&nbsp;: ${r['eleves']}</td><td><input type=\"checkbox\" class=\"grpnote\" value=\"${r['eid']}\"></td></tr>\n";
     $resultat->free();
+    $table .= "        <tr><td><strong>Voir tous les élèves</strong></td><td><input type=\"checkbox\"></td></tr>\n";
+  }
+
+  // Récupération de l'ensemble des semaines
+  // notesperso : élèves déjà notés par le colleur concerné
+  // notesautres : élèves déjà notés par les autres colleurs
+  // n : nombre de notes du colleur concerné
+  $resultat = $mysqli->query("SELECT s.id, DATE_FORMAT(debut,'%w%Y%m%e') AS debut, DATE_FORMAT(debut,'%d/%m/%Y') AS datedebut, colle, vacances, 
+                              IFNULL(GROUP_CONCAT(IF(n.colleur=${_SESSION['id']},n.eleve,NULL)),'') AS notesperso,
+                              IFNULL(GROUP_CONCAT(IF(n.colleur=${_SESSION['id']},NULL,n.eleve)),'') AS notesautres,
+                              COUNT(IF(n.colleur=${_SESSION['id']},1,NULL)) AS n
+                              FROM semaines AS s LEFT JOIN (SELECT * FROM notes WHERE matiere = $mid) AS n ON s.id = n.semaine GROUP BY s.id");
+  $select_semaines = "\n        <option value=\"0\">Choisir une semaine</option>";
+  $notesperso = $notesautres = array();
+  while ( $r = $resultat->fetch_assoc() )  {
+    switch ( $r['vacances'] )  {
+      case 0:
+        if ( $r['colle'] == 0 )
+          $select_semaines .= "\n        <option disabled data-date=\"${r['datedebut']}\">".format_date($r['debut']).' (pas de colle)</option>';
+        else  {
+          $select_semaines .= "\n        <option value=\"${r['id']}\" data-date=\"${r['datedebut']}\">".format_date($r['debut']).( $r['n'] ? " (${r['n']} notes déjà saisies)" : '').'</option>';
+          $notesperso[$r['id']] = $r['notesperso'];
+          $notesautres[$r['id']] = $r['notesautres'];
+        }
+        break;
+      case 1:
+        $select_semaines .= "\n        <option data-date=\"${r['datedebut']}\" disabled>Vacances de Toussaint</option>";
+        break;
+      case 2:
+        $select_semaines .= "\n        <option data-date=\"${r['datedebut']}\" disabled>Vacances de Noël</option>";
+        break;
+      case 3:
+        $select_semaines .= "\n        <option data-date=\"${r['datedebut']}\" disabled>Vacances d'hiver</option>";
+        break;
+      case 4:
+        $select_semaines .= "\n        <option data-date=\"${r['datedebut']}\" disabled>Vacances de Pâques</option>";
+    }
   }
+  $resultat->free();
 
+  // Réglage à la semaine actuelle pour l'ajout de notes
+  $resultat = $mysqli->query('SELECT id, DATE_FORMAT(debut,\'%d/%m/%Y\') FROM semaines WHERE colle AND debut<CURDATE() ORDER BY DATEDIFF(CURDATE(),debut) LIMIT 1');
+  if ( $resultat->num_rows )  {
+    $r = $resultat->fetch_row();
+    $select_semaines = str_replace("\"${r[0]}\"","\"${r[0]}\" selected",$select_semaines);
+    $resultat->free();
+  }
+  $mysqli->close();
+  
   // Aide et formulaire d'ajout
 ?>
 
   <script type="text/javascript">
-    dejanotes = <?php echo json_encode($semaines_notes); ?>;
+    dejanotesperso = <?php echo json_encode($notesperso); ?>;
+    dejanotesautres = <?php echo json_encode($notesautres); ?>;
+    dureecolle = <?php echo $matiere['dureecolle']; ?>
   </script>
 
-  <form id="form-notes">
-    <h3 class="edition">Modifier des notes de colles</h3>
-    <h4></h4>
-    <table id="notes">
-      <tbody>
-<?php echo $table; ?>
-      </tbody>
-    </table>
-    <div><select><option value="x"></option><option value="7">7</option><option value="8">8</option><option value="9">9</option><option value="10">10</option><option value="11">11</option><option value="12">12</option><option value="13">13</option><option value="14">14</option><option value="15">15</option><option value="16">16</option><option value="17">17</option><option value="18">18</option><option value="19">19</option><option value="20">20</option><option value="abs">Absent</option><option value="0">0</option><option value="1">1</option><option value="2">2</option><option value="3">3</option><option value="4">4</option><option value="5">5</option><option value="6">6</option><option value="0,5">0,5</option><option value="1,5">1,5</option><option value="2,5">2,5</option><option value="3,5">3,5</option><option value="4,5">4,5</option><option value="5,5">5,5</option><option value="6,5">6,5</option><option value="7,5">7,5</option><option value="8,5">8,5</option><option value="9,5">9,5</option><option value="10,5">10,5</option><option value="11,5">11,5</option><option value="12,5">12,5</option><option value="13,5">13,5</option><option value="14,5">14,5</option><option value="15,5">15,5</option><option value="16,5">16,5</option><option value="17,5">17,5</option><option value="18,5">18,5</option><option value="19,5">19,5</option></select></div>
+  <form id="form-edite" data-action="notes">
     <input type="hidden" name="id" value="">
-    <input type="hidden" name="notes" value="">
+    <h3 class="edition">Modifier des notes</h3>
   </form>
-
-  <form id="form-ajoute-notes">
+   
+  <form id="form-ajoute" data-action="ajout-notes">
+    <input type="hidden" name="matiere" value="<?php echo $mid; ?>">
     <h3 class="edition">Ajouter des notes de colles</h3>
-    <p class="ligne"><label for="semaine">Semaine</label>
-      <select id="semaine" name="id"><?php echo $select_semaines; ?>
+    <p class="ligne"><label for="sid">Semaine</label>
+      <select name="sid"><?php echo $select_semaines; ?>
 
       </select>
     </p>
-    <table id="notes">
+  </form>
+  
+  <form id="form-notes">
+    <p class="ligne"><label for="jour">Jour&nbsp;: </label><input type="text" name="jour" value="<?php echo $r[1]; ?>" size="8"></p>
+    <p class="ligne"><label for="heure">Heure&nbsp;: </label><input type="text" name="heure" value="" size="8" placeholder="Heure de la colle (facultatif)"></p>
+    <p class="ligne"><label for="duree">Durée&nbsp;: </label><input type="text" name="duree" value="0h" size="8" placeholder="Durée de la colle (obligatoire)"></p>
+    <table class="notes">
       <tbody>
 <?php echo $table; ?>
       </tbody>
     </table>
-    <div><select><option value="x"></option><option value="7">7</option><option value="8">8</option><option value="9">9</option><option value="10">10</option><option value="11">11</option><option value="12">12</option><option value="13">13</option><option value="14">14</option><option value="15">15</option><option value="16">16</option><option value="17">17</option><option value="18">18</option><option value="19">19</option><option value="20">20</option><option value="abs">Absent</option><option value="0">0</option><option value="1">1</option><option value="2">2</option><option value="3">3</option><option value="4">4</option><option value="5">5</option><option value="6">6</option><option value="0,5">0,5</option><option value="1,5">1,5</option><option value="2,5">2,5</option><option value="3,5">3,5</option><option value="4,5">4,5</option><option value="5,5">5,5</option><option value="6,5">6,5</option><option value="7,5">7,5</option><option value="8,5">8,5</option><option value="9,5">9,5</option><option value="10,5">10,5</option><option value="11,5">11,5</option><option value="12,5">12,5</option><option value="13,5">13,5</option><option value="14,5">14,5</option><option value="15,5">15,5</option><option value="16,5">16,5</option><option value="17,5">17,5</option><option value="18,5">18,5</option><option value="19,5">19,5</option></select></div>
-    <input type="hidden" name="notes" value="">
+    <div><select><option value="x"></option><option value="10">10</option><option value="11">11</option><option value="12">12</option><option value="13">13</option><option value="14">14</option><option value="15">15</option><option value="9">9</option><option value="8">8</option><option value="7">7</option><option value="6">6</option><option value="abs">Absent</option><option value="nn">Non noté</option><option value="16">16</option><option value="17">17</option><option value="18">18</option><option value="19">19</option><option value="20">20</option><option value="5">5</option><option value="4">4</option><option value="3">3</option><option value="2">2</option><option value="1">1</option><option value="0">0</option><option value="0,5">0,5</option><option value="1,5">1,5</option><option value="2,5">2,5</option><option value="3,5">3,5</option><option value="4,5">4,5</option><option value="5,5">5,5</option><option value="6,5">6,5</option><option value="7,5">7,5</option><option value="8,5">8,5</option><option value="9,5">9,5</option><option value="10,5">10,5</option><option value="11,5">11,5</option><option value="12,5">12,5</option><option value="13,5">13,5</option><option value="14,5">14,5</option><option value="15,5">15,5</option><option value="16,5">16,5</option><option value="17,5">17,5</option><option value="18,5">18,5</option><option value="19,5">19,5</option></select></div>
+  </form>
+  
+  <form id="form-prefs" data-action="prefsmatiere">
+    <h3 class="edition">Réglages des notes de colles en <?php echo $matiere['nom']; ?></h3>
+    <p>Il n'est pas possible de désactiver cette fonction ici. Vous pouvez le faire depuis la page de <a href="matieres">gestion des matières</a>.</p>
+    <p>Pour modifier les semaines correspondant à des notes de colle, il faut vous rendre sur la page de <a href="planning">gestion du planning annuel</a>.</p>
+    <p>Pour modifier les utilisateurs (élèves comme colleurs) concernés par cette matière, il faut vous rendre sur la page de <a href="utilisateurs-matieres">gestion des associations utilisateurs-matières</a>.</p>
+    <p class="ligne"><label for="dureecolle">Durée des colles en minutes par élève&nbsp;: </label><input type="text" id="dureecolle" name="dureecolle" placeholder="Valeur par défaut. Typiquement 20 ou 30." value="<?php echo $matiere['dureecolle']; ?>" size="3"></p>
+    <input type="hidden" name="id" value="<?php echo $mid; ?>">
   </form>
 
 <?php if ( $autorisation == 3 ) { ?>
+
   <div id="aide-page">
     <h3>Aide et explications</h3>
     <p>Il est possible ici consulter les notes que vous avez mises, de les modifier en cliquant sur le bouton <span class="icon-edite"></span>, de les supprimer en cliquant sur le bouton <span class="icon-supprime"></span> (une confirmation sera demandée) et d'en ajouter en cliquant sur le bouton <span class="icon-ajoute"></span>.</p>
+    <h4>Saisie des notes</h4>
+    <p>Vous pouvez saisir les notes par heure de colle ou par jour. La durée est précalculée automatiquement, mais vous pouvez modifier la valeur calculée avant de valider votre saisie.</p>
     <p>Vous ne pouvez mettre des notes de colles que dans certaines matières. Les professeurs de la classe ont la possibilité de modifier les matières qui vous sont associées.</p>
-    <p>Un élève ne peut avoir qu'une seule note par matière et par semaine. Dans les formulaires d'ajout ou de modification des notes, les élèves ayant déjà eu une note par un autre colleur lors de la semaine concernée sont indiqués.</p>
-    <p>Vous ne devez faire qu'une saisie pour chaque semaine, qui doit donc contenir l'ensemble des notes de toutes vos heures de colles correspondantes.</p>
+    <p>Un élève ne peut avoir qu'une seule note par matière et par semaine. Dans les formulaires d'ajout ou de modification des notes, les élèves ayant déjà eu une note par un autre colleur lors de la semaine concernée sont indiqués et non notables.</p> 
+    <h4>Déclaration administrative</h4>   
+    <p>Si le lycée utilise ces saisies pour mettre au paiement vos heures, ce sont normalement les durées déclarées qui comptent. Il est ainsi possible de déclarer une heure pour un binôme. Attention, les textes officiels précisent que chaque heure de colle est indivisible, mais la question du budget global peut modifier cela. Il est nécessaire que l'ensemble des colleurs se comportent de façon identique à ce niveau&nbsp;: parlez-en au professeur référent.</p>
+    <p>Pour les colles relevées par l'administration, la date de la relève est inscrite dans le tableau et la colle n'est plus modifiable sur sa durée ni supprimable. Il est par contre toujours possible de modifier les notes saisies.</p>
+    <h4>Absences et retards</h4>
+    <p>Lorsqu'un élève est absent, il vaut mieux le noter dans la colle et le marquer absent. Il sera toujours possible de le noter plus tard. Si la colle est relevée par l'administration entre son absence et son rattrapage, la note reste modifiable.</p>
+    <p>Les élèves ne peuvent pas avoir deux notes par semaine dans la même matière&nbsp;: il est toujours préférable de noter le jour initialement prévu dans le colloscope plutôt que le jour de rattrapage le cas échéant.</p>
+    <h4>Paramétrage</h4>
+    <p>L'ensemble de cette fonctionnalité est paramétrable par le professeur de la classe. La durée de colle par élève est propre à chaque matière, mais la liste des semaines correspondant à des colles est globale à toutes les matières. Si vous voyez une anomalie, pensez à en parler au professeur référent.</p>
   </div>
 
-  <div id="aide-notes">
+  <div id="aide-edite">
     <h3>Aide et explications</h3>
     <p>Ce formulaire permet de modifier, supprimer ou ajouter des notes pendant la semaine choisie. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
-    <p>Si des groupes de colles ont été définis par les professeurs, vous pouvez cocher les cases correspondantes pour n'afficher que les notes déjà saisies et ces groupes-là. Les notes déjà saisies restent affichées lors du cochage/décochage des groupes, sauf si elles ont été modifiées.</p>
-    <p>Un élève ne peut avoir qu'une seule note par matière et par semaine. Les élèves ayant déjà eu une note par un autre colleur lors de la semaine concernée sont indiqués.</p>
-    <p>Vous ne devez faire qu'une saisie pour chaque semaine, qui doit donc contenir l'ensemble des notes de toutes vos heures de colles correspondantes.</p>
-    <p>Seules les notes visibles au moment où vous cliquez sur le bouton <span class="icon-ok"></span> sont effectivement envoyées pour la saisie. Les notes que vous avez remplies mais qui ne sont pas affichées parce que vous avez décoché le groupe de colles correspondant ne sont pas envoyées.</p>
-    <p>Les notes laissées vides ne sont pas enregistrées. Pour saisir une note non numérique, le seul choix possible est <em>Absent</em>.</p>
+    <p>La semaine déjà saisie n'est pas modifiable. Le jour de la colle est contraint dans cette semaine. L'heure est modifiable. Sa saisie n'est pas obligatoire.</p>
+    <p>La saisie peut contenir autant d'élèves que vous le souhaitez, ou correspondre à une heure unique. La durée de la colle s'adapte automatiquement à la hausse. Elle reste modifiable à tout instant.</p>
+    <p>Si des groupes de colles ont été définis par les professeurs, vous pouvez cocher les cases correspondantes pour n'afficher que les notes déjà saisies et ces groupes-là. Les notes déjà saisies restent affichées lors du cochage/décochage des groupes, sauf lorsque vous venez de les modifier.</p>
+    <p>Un élève ne peut avoir qu'une seule note par matière et par semaine. Les élèves ayant déjà eu une note par un autre colleur lors de la semaine concernée sont indiqués et non notables.</p>
+    <p>Seules les notes non vides et visibles au moment où vous cliquez sur le bouton <span class="icon-ok"></span> sont effectivement envoyées pour l'enregistrement. Les notes que vous avez saisies mais qui ne sont pas affichées parce que vous avez décoché le groupe de colles correspondant ne sont pas envoyées.</p>
+    <p>Les notes laissées vides ne sont pas enregistrées. En dehors des notes numériques, les choix possibles sont <em>Absent</em> et <em>Non noté</em>.</p>
+    <h4>Déclaration administrative</h4>   
+    <p>Si le lycée utilise ces saisies pour mettre au paiement vos heures, ce sont normalement les durées déclarées qui comptent. Il est ainsi possible de déclarer une heure pour un binôme. Attention, les textes officiels précisent que chaque heure de colle est indivisible, mais la question du budget global peut modifier cela. Il est nécessaire que l'ensemble des colleurs se comportent de façon identique à ce niveau&nbsp;: parlez-en au professeur référent.</p>
+    <p>Pour les colles relevées par l'administration, la date de la relève est inscrite dans le tableau et la colle n'est plus modifiable sur sa durée ni supprimable. Il est par contre toujours possible de modifier les notes saisies.</p>
+    <h4>Absences et retards</h4>
+    <p>Lorsqu'un élève est absent, il vaut mieux le noter dans la colle et le marquer absent. Il sera toujours possible de le noter plus tard. Si la colle est relevée par l'administration entre son absence et son rattrapage, la note reste modifiable.</p>
+    <p>Les élèves ne peuvent pas avoir deux notes par semaine dans la même matière&nbsp;: il est toujours préférable de noter le jour initialement prévu dans le colloscope plutôt que le jour de rattrapage le cas échéant.</p>
   </div>
 
   <div id="aide-ajoute">
     <h3>Aide et explications</h3>
-    <p>Ce formulaire permet d'ajouter des notes sur une semaine où vous n'en avez pas encore saisi. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
-    <p>Vous devez commencer par choisir la semaine correspondant aux notes que vous allez saisir. Vous ne pouvez faire qu'une saisie pour chaque semaine, qui doit donc contenir l'ensemble des notes de toutes vos heures de colles correspondantes. Pour ajouter des notes sur une semaine où vous avez déjà saisi des notes, vous devez l'éditer en cliquant sur le bouton <span class="icon-edite"></span> correspondant.</p>
+    <p>Ce formulaire permet d'ajouter des notes. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
+    <p>Vous devez commencer par choisir la semaine correspondant aux notes que vous allez saisir. Vous pouvez ensuite choisir le jour dans cette semaine. La saisie de l'heure n'est pas obligatoire.</p>
     <p>Si des groupes de colles ont été définis par les professeurs, vous pouvez cocher les cases correspondantes pour n'afficher que les élèves de ces groupes-là.</p>
-    <p>Un élève ne peut avoir qu'une seule note par matière et par semaine. Les élèves ayant déjà eu une note par un autre colleur lors de la semaine choisie sont indiqués.</p>
-    <p>Seules les notes visibles au moment où vous cliquez sur le bouton <span class="icon-ok"></span> sont effectivement envoyées pour la saisie. Les notes que vous avez remplies mais qui ne sont pas affichées parce que vous avez décoché le groupe de colles correspondant ne sont pas envoyées.</p>
+    <p>La saisie peut contenir autant d'élèves que vous le souhaitez, ou correspondre à une heure unique. La durée de la colle s'adapte automatiquement à la hausse à chaque nouvelle note saisie. Elle reste modifiable à tout instant.</p>
+    <p>Un élève ne peut avoir qu'une seule note par matière et par semaine. Les élèves ayant déjà eu une note par un autre colleur lors de la semaine choisie sont indiqués et non notables.</p>
+    <p>Seules les notes non vides et visibles au moment où vous cliquez sur le bouton <span class="icon-ok"></span> sont effectivement envoyées pour l'enregistrement. Les notes que vous avez saisies mais qui ne sont pas affichées parce que vous avez décoché le groupe de colles correspondant ne sont pas envoyées.</p>
+    <p>Les notes laissées vides ne sont pas enregistrées. En dehors des notes numériques, les choix possibles sont <em>Absent</em> et <em>Non noté</em>.</p>
+    <h4>Déclaration administrative</h4>   
+    <p>Si le lycée utilise ces saisies pour mettre au paiement vos heures, ce sont normalement les durées déclarées qui comptent. Il est ainsi possible de déclarer une heure pour un binôme. Attention, les textes officiels précisent que chaque heure de colle est indivisible, mais la question du budget global peut modifier cela. Il est nécessaire que l'ensemble des colleurs se comportent de façon identique à ce niveau&nbsp;: parlez-en au professeur référent.</p>
+    <p>Pour les colles relevées par l'administration, la date de la relève est inscrite dans le tableau et la colle n'est plus modifiable sur sa durée ni supprimable. Il est par contre toujours possible de modifier les notes saisies.</p>
+    <h4>Absences et retards</h4>
+    <p>Lorsqu'un élève est absent, il vaut mieux le noter dans la colle et le marquer absent. Il sera toujours possible de le noter plus tard. Si la colle est relevée par l'administration entre son absence et son rattrapage, la note reste modifiable.</p>
+    <p>Les élèves ne peuvent pas avoir deux notes par semaine dans la même matière&nbsp;: il est toujours préférable de noter le jour initialement prévu dans le colloscope plutôt que le jour de rattrapage le cas échéant.</p>
   </div>
+  
 <?php } else { ?>
+
   <div id="aide-page">
     <h3>Aide et explications</h3>
-    <h4>Saisie des notes</h4>
     <p>Il est possible ici consulter les notes que vous avez mises, de les modifier en cliquant sur le bouton <span class="icon-edite"></span>, de les supprimer en cliquant sur le bouton <span class="icon-supprime"></span> (une confirmation sera demandée) et d'en ajouter en cliquant sur le bouton <span class="icon-ajoute"></span>.</p>
-    <p>Vous ne pouvez mettre des notes de colles que dans certaines matières. Les professeurs de la classe ont la possibilité de modifier les matières qui vous sont associées.</p>
-    <p>Un élève ne peut avoir qu'une seule note par matière et par semaine. Dans les formulaires d'ajout ou de modification des notes, les élèves ayant déjà eu une note par un autre colleur lors de la semaine concernée sont indiqués.</p>
-    <p>Vous ne devez faire qu'une saisie pour chaque semaine, qui doit donc contenir l'ensemble des notes de toutes vos heures de colles correspondantes.</p>
+    <h4>Saisie des notes</h4>
+    <p>Vous pouvez saisir les notes par heure de colle ou par jour. La durée est précalculée automatiquement, mais vous pouvez modifier la valeur calculée avant de valider votre saisie. La durée de colle par élève, permettant le précalcul, est modifiable dans les préférences accessibles en cliquant sur le bouton <span class="icon-prefs"></span>.</p>
+    <p>Vous ne pouvez mettre des notes de colles que dans les matières qui vous sont associées. Ces associations sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matieres</a>.</p>
+    <p>Un élève ne peut avoir qu'une seule note par matière et par semaine. Dans les formulaires d'ajout ou de modification des notes, les élèves ayant déjà eu une note par un autre colleur lors de la semaine concernée sont indiqués et non notables.</p> 
     <h4>Consultation globale des notes</h4>
-    <p>En tant que professeur associé à cette matière, vous avez la possibilité de consulter l'ensemble des notes qui ont été saisies par l'ensemble des colleurs. Les utilisateurs de type colleurs n'ont pas droit à cette fonctionnalité.</p>
-    <p>Un clic sur le bouton <span class="icon-download"></span> permet de récupérer le tableau récapitulatif global en fichier de type <code>xls</code>, éditable par un logiciel tableur (Excel, LibreOffice Calc...).</p>
-    <p>Un clic sur le bouton <span class="icon-voirtout"></span> permet de voir en ligne ce tableau récapitulatif. Il est alors possible de faire ressortir les notes de chaque colleur séparément à l'aide d'un menu déroulant. Cet affichage peut être supprimé par un clic sur le bouton <span class="icon-ferme"></span>.</p>
+    <p>En tant que professeur associé à cette matière, vous avez la possibilité de consulter en ligne et en téléchargement l'ensemble des notes qui ont été saisies par vos colleurs. Les utilisateurs de type colleurs n'ont pas droit à cette fonctionnalité.</p>
+    <p>En tant que professeur, vous avez aussi la possibilité de consulter l'ensemble des heures déclarées. Vous pouvez grâce à cela savoir si vos colleurs sont en retard sur leurs déclarations et suivre les relevés de l'administration pour chaque colleur le cas échéant. Vous pouvez aussi vérifier les totaux du nombre d'élèves notés et du nombre d'heures déclarées réalisées.</p>
+    <p>Les boutons généraux permettent les actions suivantes&nbsp;:</p>
+    <ul>
+      <li>le bouton <span class="icon-prefs"></span> permet de modifier uniquement la durée de colle par élève.</li>
+      <li>le bouton <span class="icon-download"></span> permet de récupérer le tableau récapitulatif global des notes en fichier de type <code>xls</code>, éditable par un logiciel tableur (Excel, LibreOffice Calc...)</li>
+      <li>le bouton <span class="icon-voirtout"></span> permet de voir en ligne ce tableau récapitulatif. Il est alors possible de faire ressortir les notes de chaque colleur séparément à l'aide d'un menu déroulant. Cet affichage peut alors être supprimé par un clic sur le bouton <span class="icon-ferme"></span>.</li>
+    </ul>
+    <h4>Déclaration administrative</h4>
+    <p>Si le lycée utilise ces saisies pour mettre au paiement vos heures, ce sont normalement les durées déclarées qui comptent. Il est ainsi possible de déclarer une heure pour un binôme. Attention, les textes officiels précisent que chaque heure de colle est indivisible, mais la question du budget global peut modifier cela. Il est nécessaire que l'ensemble des colleurs se comportent de façon identique à ce niveau et que l'équipe pédagogique soit cohérente. Pensez à en parler avec vos collègues et votre administration.</p>
+    <p>Pour les colles relevées par l'administration, la date de la relève est inscrite dans le tableau et la colle n'est plus modifiable sur sa durée ni supprimable. Il est par contre toujours possible de modifier les notes saisies.</p>
+    <h4>Absences et retards</h4>
+    <p>Lorsqu'un élève est absent, il vaut mieux le noter dans la colle et le marquer absent. Il sera toujours possible de le noter plus tard. Si la colle est relevée par l'administration entre son absence et son rattrapage, la note reste modifiable.</p>
+    <p>Les élèves ne peuvent pas avoir deux notes par semaine dans la même matière&nbsp;: il est toujours préférable de noter le jour initialement prévu dans le colloscope plutôt que le jour de rattrapage le cas échéant.</p>
+    <h4>Autres paramétrage</h4>
+    <p>Toutes les semaines ne sont pas des semaines de colles et vous pouvez modifier quelles semaines sont associées ou non à des colles à la <a href="planning">gestion du planning</a>. Ce réglage est valable pour toutes les matières et est aussi utilisé pour afficher les programmes de colles.</p>
+    <p>La durée de colle par élève, qui permet de précalcul immédiat de la durée des colles, est propre à chaque matière. Vous pouvez la modifier dans les préférences accessibles en cliquant sur le bouton <span class="icon-prefs"></span>.</p>
   </div>
 
-  <div id="aide-notes">
+  <div id="aide-edite">
     <h3>Aide et explications</h3>
     <p>Ce formulaire permet de modifier, supprimer ou ajouter des notes pendant la semaine choisie. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
-    <p>Si des groupes de colles ont été définis, vous pouvez cocher les cases correspondantes pour n'afficher que les notes déjà saisies et ces groupes-là. Les notes déjà saisies restent affichées lors du cochage/décochage des groupes, sauf si elles ont été modifiées. Les groupes de colles sont définir ou modifier sur la page de <a href="groupes">gestion des groupes</a>.</p>
-    <p>Un élève ne peut avoir qu'une seule note par matière et par semaine. Les élèves ayant déjà eu une note par un autre colleur lors de la semaine concernée sont indiqués.</p>
-    <p>Vous ne devez faire qu'une saisie pour chaque semaine, qui doit donc contenir l'ensemble des notes de toutes vos heures de colles correspondantes.</p>
-    <p>Seules les notes visibles au moment où vous cliquez sur le bouton <span class="icon-ok"></span> sont effectivement envoyées pour la saisie. Les notes que vous avez remplies mais qui ne sont pas affichées parce que vous avez décoché le groupe de colles correspondant ne sont pas envoyées.</p>
-    <p>Les notes laissées vides ne sont pas enregistrées. Pour saisir une note non numérique, le seul choix possible est <em>Absent</em>.</p>
+    <p>La semaine déjà saisie n'est pas modifiable. Le jour de la colle est contraint dans cette semaine. L'heure est modifiable. Sa saisie n'est pas obligatoire.</p>
+    <p>La saisie peut contenir autant d'élèves que vous le souhaitez, ou correspondre à une heure unique. La durée de la colle s'adapte automatiquement à la hausse. Elle reste modifiable à tout instant.</p>
+    <p>Si des groupes de colles ont été définis, vous pouvez cocher les cases correspondantes pour n'afficher que les notes déjà saisies et ces groupes-là. Les notes déjà saisies restent affichées lors du cochage/décochage des groupes, sauf lorsque vous venez de les modifier. Vous pouvez définir et modifier les groupes de colles à la <a href="groupes">gestion des groupes</a>. Ces modifications sont valables pour toutes les matières auxquelles sont associés les élèves. Créer des groupes n'est pas obligatoire pour pouvoir saisir des notes.</p>
+    <p>Un élève ne peut avoir qu'une seule note par matière et par semaine. Les élèves ayant déjà eu une note par un autre colleur lors de la semaine concernée sont indiqués et non notables.</p>
+    <p>Seules les notes non vides et visibles au moment où vous cliquez sur le bouton <span class="icon-ok"></span> sont effectivement envoyées pour l'enregistrement. Les notes que vous avez saisies mais qui ne sont pas affichées parce que vous avez décoché le groupe de colles correspondant ne sont pas envoyées.</p>
+    <p>Les notes laissées vides ne sont pas enregistrées. En dehors des notes numériques, les choix possibles sont <em>Absent</em> et <em>Non noté</em>.</p>
+    <h4>Déclaration administrative</h4>
+    <p>Si le lycée utilise ces saisies pour mettre au paiement vos heures, ce sont normalement les durées déclarées qui comptent. Il est ainsi possible de déclarer une heure pour un binôme. Attention, les textes officiels précisent que chaque heure de colle est indivisible, mais la question du budget global peut modifier cela. Il est nécessaire que l'ensemble des colleurs se comportent de façon identique à ce niveau et que l'équipe pédagogique soit cohérente. Pensez à en parler avec vos collègues et votre administration.</p>
+    <p>Pour les colles relevées par l'administration, la date de la relève est inscrite dans le tableau et la colle n'est plus modifiable sur sa durée ni supprimable. Il est par contre toujours possible de modifier les notes saisies.</p>
+    <h4>Absences et retards</h4>
+    <p>Lorsqu'un élève est absent, il vaut mieux le noter dans la colle et le marquer absent. Il sera toujours possible de le noter plus tard. Si la colle est relevée par l'administration entre son absence et son rattrapage, la note reste modifiable.</p>
+    <p>Les élèves ne peuvent pas avoir deux notes par semaine dans la même matière&nbsp;: il est toujours préférable de noter le jour initialement prévu dans le colloscope plutôt que le jour de rattrapage le cas échéant.</p>
   </div>
 
   <div id="aide-ajoute">
     <h3>Aide et explications</h3>
-    <p>Ce formulaire permet d'ajouter des notes sur une semaine où vous n'en avez pas encore saisi. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
-    <p>Vous devez commencer par choisir la semaine correspondant aux notes que vous allez saisir. Vous ne pouvez faire qu'une saisie pour chaque semaine, qui doit donc contenir l'ensemble des notes de toutes vos heures de colles correspondantes. Pour ajouter des notes sur une semaine où vous avez déjà saisi des notes, vous devez l'éditer en cliquant sur le bouton <span class="icon-edite"></span> correspondant.</p>
-    <p>Si des groupes de colles ont été définis, vous pouvez cocher les cases correspondantes pour n'afficher que les élèves de ces groupes-là. Les groupes de colles sont à définir ou modifier sur la page de <a href="groupes">gestion des groupes</a>.</p>
-    <p>Un élève ne peut avoir qu'une seule note par matière et par semaine. Les élèves ayant déjà eu une note par un autre colleur lors de la semaine choisie sont indiqués.</p>
-    <p>Seules les notes visibles au moment où vous cliquez sur le bouton <span class="icon-ok"></span> sont effectivement envoyées pour la saisie. Les notes que vous avez remplies mais qui ne sont pas affichées parce que vous avez décoché le groupe de colles correspondant ne sont pas envoyées.</p>
+    <p>Ce formulaire permet d'ajouter des notes. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
+    <p>Vous devez commencer par choisir la semaine correspondant aux notes que vous allez saisir. Vous pouvez ensuite choisir le jour dans cette semaine. La saisie de l'heure n'est pas obligatoire.</p>
+    <p>Si des groupes de colles ont été définis, vous pouvez cocher les cases correspondantes pour n'afficher que les notes déjà saisies et ces groupes-là. Les notes déjà saisies restent affichées lors du cochage/décochage des groupes, sauf lorsque vous venez de les modifier. Vous pouvez définir et modifier les groupes de colles à la <a href="groupes">gestion des groupes</a>. Ces modifications sont valables pour toutes les matières auxquelles sont associés les élèves. Créer des groupes n'est pas obligatoire pour pouvoir saisir des notes.</p>
+    <p>La saisie peut contenir autant d'élèves que vous le souhaitez, ou correspondre à une heure unique. La durée de la colle s'adapte automatiquement à la hausse à chaque nouvelle note saisie. Elle reste modifiable à tout instant.</p>
+    <p>Un élève ne peut avoir qu'une seule note par matière et par semaine. Les élèves ayant déjà eu une note par un autre colleur lors de la semaine choisie sont indiqués et non notables.</p>
+    <p>Seules les notes non vides et visibles au moment où vous cliquez sur le bouton <span class="icon-ok"></span> sont effectivement envoyées pour l'enregistrement. Les notes que vous avez saisies mais qui ne sont pas affichées parce que vous avez décoché le groupe de colles correspondant ne sont pas envoyées.</p>
+    <p>Les notes laissées vides ne sont pas enregistrées. En dehors des notes numériques, les choix possibles sont <em>Absent</em> et <em>Non noté</em>.</p>
+    <h4>Déclaration administrative</h4>   
+    <p>Si le lycée utilise ces saisies pour mettre au paiement vos heures, ce sont normalement les durées déclarées qui comptent. Il est ainsi possible de déclarer une heure pour un binôme. Attention, les textes officiels précisent que chaque heure de colle est indivisible, mais la question du budget global peut modifier cela. Il est nécessaire que l'ensemble des colleurs se comportent de façon identique à ce niveau et que l'équipe pédagogique soit cohérente. Pensez à en parler avec vos collègues et votre administration.</p>
+    <p>Pour les colles relevées par l'administration, la date de la relève est inscrite dans le tableau et la colle n'est plus modifiable sur sa durée ni supprimable. Il est par contre toujours possible de modifier les notes saisies.</p>
+    <h4>Absences et retards</h4>
+    <p>Lorsqu'un élève est absent, il vaut mieux le noter dans la colle et le marquer absent. Il sera toujours possible de le noter plus tard. Si la colle est relevée par l'administration entre son absence et son rattrapage, la note reste modifiable.</p>
+    <p>Les élèves ne peuvent pas avoir deux notes par semaine dans la même matière&nbsp;: il est toujours préférable de noter le jour initialement prévu dans le colloscope plutôt que le jour de rattrapage le cas échéant.</p>
   </div>
+
 <?php } ?>
 
   <p id="log"></p>
+  
+  <script type="text/javascript" src="js/datetimepicker.min.js"></script>
 <?php
 }
 
-$mysqli->close();
-fin($autorisation>2);
+fin($autorisation==3 || $autorisation==5);
 ?>
diff -urN cahier-de-prepa6.2.0/pages.php cahier-de-prepa8.0.1/pages.php
--- cahier-de-prepa6.2.0/pages.php	2016-08-11 01:01:33.740896926 +0200
+++ cahier-de-prepa8.0.1/pages.php	2018-10-15 22:07:11.487476927 +0200
@@ -10,36 +10,41 @@
 // Autorisation //
 //////////////////
 
-// Accès aux professeurs connectés uniquement
+// Accès aux professeurs connectés uniquement. Redirection pour les autres.
+if ( $autorisation < 5 )  {
+  header("Location: https://$site");
+  exit();
+}
 $mysqli = connectsql();
-if ( !$autorisation )  {
-  $titre = 'Pages';
-  $actuel = false;
+// Si connexion light : on doit répéter son mot de passe pour aller plus loin
+// login.php contient fin()
+if ( $_SESSION['light'] )  {
+  $titre = 'Modification du planning';
+  $actuel = 'planning';
   include('login.php');
 }
-elseif ( $autorisation < 4 )  {
-  debut($mysqli,'Pages','Vous n\'avez pas accès à cette page.',$autorisation,' ');
-  $mysqli->close();
-  fin();
-}
 
 //////////////
 //// HTML ////
 //////////////
-debut($mysqli,'Modification des pages',$message,4,'pages');
+debut($mysqli,'Modification des pages',$message,5,'pages');
 echo <<<FIN
 
-  <a class="icon-aide general" data-id="page" title="Aide pour les modifications des pages"></a>
-  <a class="icon-ajoute general" data-id="ajoute" title="Ajouter une nouvelle page"></a>
+  <div id="icones">
+    <a class="icon-ajoute formulaire" title="Ajouter une nouvelle page"></a>
+    <a class="icon-aide" title="Aide pour les modifications des pages"></a>
+  </div>
 
 FIN;
 $select_protection = '
-          <option value="0">Visible de tous</option>
-          <option value="1">Visible pour les connectés</option>
-          <option value="2">Visible pour les élèves, colleurs, profs</option>
-          <option value="3">Visible pour les colleurs et les profs</option>
-          <option value="4">Visible pour les profs uniquement</option>
-          <option value="5">Non visible</option>';
+          <option value="0">Accès public</option>
+          <option value="6">Utilisateurs identifiés</option>
+          <option value="1">Invités</option>
+          <option value="2">Élèves</option>
+          <option value="3">Colleurs</option>
+          <option value="4">Administratifs</option>
+          <option value="5">Professeurs</option>
+          <option value="32">Page invisible</option>';
 
 
 // Récupération des matières
@@ -63,9 +68,19 @@
     $monte = ( ( $r['ordre'] == 1 ) || ( !$m['id'] && ( $r['ordre'] == 2 ) ) ) ? ' style="display:none;"' : '';
     $descend = ( ( $r['ordre'] == $m['max'] ) || ( $r['id'] == 1 ) ) ? ' style="display:none;"' : '';
     $sel_matiere = str_replace("\"${m['id']}\"","\"${m['id']}\" selected",$select_matieres);
-    $sel_protection = str_replace("\"${r['protection']}\"","\"${r['protection']}\" selected",$select_protection);
     $suppr = ( $r['id'] > 1 ) ? "\n    <a class=\"icon-supprime\" title=\"Supprimer cette page\"></a>" : '';
     $nom = ( $m['id'] ) ? "${m['nom']}/${r['nom']}" : $r['nom'];
+    // Protection
+    $p = $r['protection'];
+    if ( ( $p == 0 ) || ( $p == 32 ) )
+      $sel_protection = str_replace("\"$p\"","\"$p\" selected",$select_protection);
+    else  {
+      $sel_protection = str_replace('"6"','"6" selected',$select_protection);
+      for ( $a=1; $a<6; $a++ )
+        if ( ( ($p-1)>>($a-1) & 1 ) == 0 )
+          $sel_protection = str_replace("\"$a\"","\"$a\" selected",$sel_protection);
+    }
+    // Différence entre page d'accueil et les autres
     if ( $r['id'] > 1 )  {
       $suppr = "\n    <a class=\"icon-supprime\" title=\"Supprimer cette page\"></a>";
       str_replace("\"${m['id']}\"","\"${m['id']}\" selected",$select_matieres);
@@ -83,23 +98,26 @@
       $span = '<span></span>';
     }
     $supprinfos = ( $r['n'] ) ? "\n      <input type=\"button\" class=\"ligne supprmultiple\" data-id=\"pages|$id|infos\" value=\"Supprimer les ${r['n']} informations de la page\">" : '';
+    $propagationdisabled = ( $r['n'] ) ? '' : ' disabled';
     echo <<<FIN
 
   <article data-id="pages|$id">
-    <a class="icon-monte"$monte title="Déplacer cette matière vers le haut"></a>
-    <a class="icon-descend"$descend title="Déplacer cette matière vers le bas"></a>$suppr
+    <a class="icon-aide" title="Aide pour l'édition de cette page"></a>
+    <a class="icon-ok" title="Valider les modifications"></a>$suppr
+    <a class="icon-descend"$descend title="Déplacer cette page vers le bas"></a>
+    <a class="icon-monte"$monte title="Déplacer cette page vers le haut"></a>
     <form>
-      <a class="icon-ok" title="Valider les modifications" data-noreload=""></a>
       <h3 class="edition">$nom</h3>
-      <p class="ligne"><label for="titre$id">Titre&nbsp;: </label><input type="text" id="titre$id" name="titre" value="${r['titre']}" size="50" data-placeholder="Ex: «&nbsp;Informations en [matière]&nbsp;», «&nbsp;À propos du TIPE&nbsp;»"></p>
-      <p class="ligne"><label for="nom$id">Nom dans le menu&nbsp;: </label><input type="text" id="nom$id" name="nom" value="${r['nom']}" size="50" data-placeholder="Pas trop long. Ex: «&nbsp;Informations&nbsp;», «&nbsp;Informations TIPE&nbsp;»"></p>
-      <p class="ligne"><label for="cle$id">Clé dans l'adresse&nbsp;: </label><input type="text" id="cle$id" name="cle" value="${r['cle']}" size="30" data-placeholder="En minuscules et sans espace. Ex: «&nbsp;infos&nbsp;», «&nbsp;tipe&nbsp;»"></p>$sel_matiere
+      <p class="ligne"><label for="titre$id">Titre&nbsp;: </label><input type="text" id="titre$id" name="titre" value="${r['titre']}" size="50" placeholder="Ex: «&nbsp;Informations en [matière]&nbsp;», «&nbsp;À propos du TIPE&nbsp;»"></p>
+      <p class="ligne"><label for="nom$id">Nom dans le menu&nbsp;: </label><input type="text" id="nom$id" name="nom" value="${r['nom']}" size="50" placeholder="Pas trop long. Ex: «&nbsp;Informations&nbsp;», «&nbsp;Informations TIPE&nbsp;»"></p>
+      <p class="ligne"><label for="cle$id">Clé dans l'adresse&nbsp;: </label><input type="text" id="cle$id" name="cle" value="${r['cle']}" size="30" placeholder="En minuscules et sans espace. Ex: «&nbsp;infos&nbsp;», «&nbsp;tipe&nbsp;»"></p>$sel_matiere
       <p class="ligne"><label for="protection$id">Accès&nbsp;: </label>
-        <select id="protection$id" name="protection">$sel_protection
+        <select name="protection[]" multiple>$sel_protection
         </select>
       </p>
+      <p class="ligne"><label for="propagation$id">Propager l'accès à chaque information de la page&nbsp;: </label><input type="checkbox" id="propagation$id" name="propagation" value="1"$propagationdisabled></p>
       <p class="ligne"><label for="bandeau$id">Texte de début&nbsp;:</label></p>
-      <textarea id="bandeau$id" name="bandeau" rows="2" cols="100" data-placeholder="Texte qui s'affichera au début de la page">${r['bandeau']}</textarea>$supprinfos
+      <textarea id="bandeau$id" name="bandeau" rows="2" cols="100" placeholder="Texte qui s'affichera au début de la page">${r['bandeau']}</textarea>$supprinfos
     </form>
   </article>$span
 
@@ -108,60 +126,62 @@
   $resultat1->free();
 }
 $resultat->free();
+$mysqli->close();
 
 // Aide et formulaire d'ajout
 ?>
 
-  <form id="form-ajoute">
+  <form id="form-ajoute" data-action="ajout-page">
     <h3 class="edition">Ajouter une nouvelle page</h3>
-    <div>
-      <input type="text" class="ligne"  id="nom" name="nom" value="" size="50" data-placeholder="Nom pour le menu. Pas trop long. Ex: «&nbsp;Informations&nbsp;», «&nbsp;Informations TIPE&nbsp;»">
-      <input type="text" class="ligne" id="cle" name="cle" value="" size="30" data-placeholder="Clé pour l'adresse web. En minuscules et sans espace. Ex: «&nbsp;infos&nbsp;», «&nbsp;tipe&nbsp;»">
-      <input type="text" class="ligne" id="titre" name="titre" value="" size="50" data-placeholder="Titre. Ex: «&nbsp;Informations en [matière]&nbsp;», «&nbsp;À propos du TIPE&nbsp;»">
-      <p class="ligne"><label for="matiere$id">Matière&nbsp;: </label>
-        <select id="matiere" name="matiere">
-          <?php echo $select_matieres; ?>
-        </select>
-      </p>
-      <p class="ligne"><label for="protection">Accès&nbsp;: </label>
-        <select id="protection" name="protection"><?php echo $select_protection; ?>
-        </select>
-      </p>
-      <p class="ligne"><label for="bandeau">Texte de début&nbsp;:</label></p>
-      <textarea id="bandeau" name="bandeau" rows="2" cols="100" data-placeholder="Texte qui s'affichera au début de la page"></textarea>
-      <input type="hidden" name="table" value="pages">
-    </div>
+    <input type="text" class="ligne" name="nom" value="" size="50" placeholder="Nom pour le menu. Pas trop long. Ex: «&nbsp;Informations&nbsp;», «&nbsp;Informations TIPE&nbsp;»">
+    <input type="text" class="ligne" name="cle" value="" size="30" placeholder="Clé pour l'adresse web. En minuscules et sans espace. Ex: «&nbsp;infos&nbsp;», «&nbsp;tipe&nbsp;»">
+    <input type="text" class="ligne" name="titre" value="" size="50" placeholder="Titre. Ex: «&nbsp;Informations en [matière]&nbsp;», «&nbsp;À propos du TIPE&nbsp;»">
+    <p class="ligne"><label for="matiere$id">Matière&nbsp;: </label>
+      <select name="matiere">
+        <?php echo $select_matieres; ?>
+      </select>
+    </p>
+    <p class="ligne"><label for="protection">Accès&nbsp;: </label>
+      <select name="protection[]" multiple><?php echo $select_protection; ?>
+      </select>
+    </p>
+    <p class="ligne"><label for="bandeau">Texte de début&nbsp;:</label></p>
+    <textarea name="bandeau" rows="2" cols="100" placeholder="Texte qui s'affichera au début de la page"></textarea>
   </form>
 
   <div id="aide-page">
     <h3>Aide et explications</h3>
-    <p>Il est possible ici d'ajouter, de modifier et de supprimer les pages d'informations de ce Cahier de Prépa.</p>
-    <p>Chaque page peut être sans matière associée ou non. Sans matière associée, elle sera affichée tout en haut du menu, directement sous les icônes. Avec une matière associée, elle sera affichée dans le menu sous le titre de la matière. Une page apparaît toujours dans le menu des visiteurs connectés en tant que professeur, pour pouvoir être éditée. Pour les autres visiteurs (non connecté ou invité/élève/colleur), elle n'apparaît que si elle est contient au moins une information.</p>
+    <p>Il est possible ici d'ajouter avec le bouton <span class="icon-ajoute"></span>, de modifier et de supprimer les pages d'informations.</p>
+    <p>Chaque page peut être associée à une matière ou non. Sans matière associée, elle sera affichée tout en haut du menu, directement sous les icônes. Avec une matière associée, elle sera affichée dans le menu sous le titre de la matière. Une page apparaît toujours dans le menu des utilisateurs de type professeur, pour pouvoir être éditée. Pour les visiteurs non identifiés, elle n'apparaît que si elle est contient au moins une information. Pour les utilisateurs identifiés, il faut en plus que l'accès à la page leur soit autorisé.</p>
     <p>La première page sans matière associée est la page d'accueil du Cahier de Prépa&nbsp;: il est donc impossible de la supprimer ou de la déplacer.</p>
     <p>Pour toutes les autres pages, il est possible de les supprimer en cliquant sur le bouton <span class="icon-supprime"></span> (une confirmation sera demandée). Si cela est possible, les pages peuvent être déplacées les unes par rapport aux autres dans le menu, à l'aide des boutons <span class="icon-monte"></span> et <span class="icon-descend"></span>.</p>
-    <p>Seules les pages sans matière associée ou dont la matière associée est aussi associée à votre compte sont modifiables. Vous pouvez modifier les associations des matières à votre compte dans la page de <a href="matieres">gestion des matières</a>.</p>
-    <h4>Préférences associées à chaque page</h4>
+    <p>Seules les pages sans matière associée ou dont la matière est aussi associée à votre compte sont modifiables. Vous pouvez modifier les associations des matières à votre compte à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</p>
+    <p>Le titre de la première page a un statut spécial&nbsp;: c'est le titre du Cahier de Prépa. Il est donc repris à plusieurs endroits (titre dans la barre de titre du navigateur, titre dans le flux RSS). Le <em>nom dans le menu</em> de cette page est par contre peu important car affiché uniquement lorsque la souris survole l'icône <span class="icon-accueil"></span>.</p>
+  </div>
+  
+  <div id="aide-pages">
+    <h3>Aide et explications</h3>
     <p>Pour chaque page, vous pouvez modifier&nbsp;:</p>
     <ul>
       <li>le <em>titre</em> qui sera affiché en haut de page et dans la barre de titre du navigateur. Par exemple, «&nbsp;À propos du TIPE&nbsp;».</li>
       <li>le <em>nom dans le menu</em> qui est affiché dans le menu en tant que lien vers la page. Il est préférable qu'il rentre sur une ligne, il faut donc le choisir assez court. Par exemple, «&nbsp;Informations TIPE&nbsp;».</li>
       <li>la <em>clé dans l'adresse</em> qui est un mot-clé utilisé uniquement dans l'adresse de la page. Par convention, il vaut mieux que ce soit un mot unique, court et sans majuscule. Par exemple, «&nbsp;tipe&nbsp;». La clé doit obligatoirement être unique (deux pages ne peuvent pas avoir la même clé).</li>
-      <li>la <em>matière</em> associée qui conditionne la place du lien dans le menu de la partie publique. Vous ne pouvez associer à une nouvelle page qu'une matière déjà associée à votre compte. Vous pouvez modifier les associations des matières à votre compte dans la page de <a href="matieres">gestion des matières</a>.</li>
+      <li>la <em>matière</em> associée qui conditionne la place du lien dans le menu général.</li>
       <li>l'<em>accès</em> à la page (voir ci-dessous).</li>
       <li>le <em>texte de début</em> qui sera affiché au-dessus des informations de la page. Il s'agit d'une ou deux phrases maximum. Il n'est affiché que si la page contient des informations. Cette case peut être laissée vide.</li>
     </ul>
-    <p>L'<em>accès</em> à chaque page peut être choisi parmi six possibilités&nbsp;:</p>
+    <p>Une fois modifié, le formulaire est à validé par un clic sur le bouton <span class="icon-ok"></span>.</p>
+    <h4>Gestion des accès</h4>
+    <p>L'accès à chaque page et chaque information peut être protégé. Trois choix sont possibles&nbsp;:</p>
     <ul>
-      <li><em>Visible de tous</em>&nbsp;: accessible de tout visiteur, sans identification.</li>
-      <li><em>Visible pour les connectés</em>&nbsp;: accessible de tout utilisateur mais uniquement après identification (utilisateurs de type invité, élève, colleur ou professeur). Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les élèves, colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type élève, colleur ou professeur. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type colleur ou professeur. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type professeur. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Non visible</em>&nbsp;: entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont la page peut dépendre). La différence avec le choix précédent est que cette page n'apparaît plus du tout dans le menu pour ceux qui ne la voient pas.</li>
+      <li><em>Accès public</em>&nbsp;: page accessible de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: page accessible uniquement par les utilisateurs identifiés, en fonction de leur type de compte. Un cadenas <span class="icon-lock"></span> est alors affiché dans le titre de la page. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière les voient affichées dans le menu et peuvent y accéder. Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Page invisible</em>&nbsp;: page entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont la page peut dépendre). Un cadenas <span class="icon-locktotal"></span> est alors affiché dans le titre de la page.</li>
     </ul>
-    <p>Hormis le dernier choix, le lien dans le menu apparaîtra pour tout visiteur (identifié ou non). Le niveau de protection est indiqué dans le titre par une icône de type <span class="icon-lock1"></span> où le numéro correspond au niveau de protection à côté du titre, lorsque vous allez sur la page concernée connecté en tant que professeur.</p>
-    <p>Le titre de la première page a un statut spécial&nbsp;: c'est le titre du Cahier de Prépa. Il est donc repris à plusieurs endroits (titre dans la barre de titre du navigateur, titre dans le flux RSS). Le <em>nom dans le menu</em> de cette page est par contre peu important car affiché uniquement lorsque la souris survole l'icône <span class="icon-accueil"></span>.</p>
-    <h4>Suppression d'une page ou de ses informations</h4>
+    <p>Le lien de la page dans le menu est visible avant identification. Il disparaît après identification pour les utilisateurs n'ayant pas accès à la page.</p>
+    <h4>Protection des informations</h4>
+    <p>Chaque information sur une page peut être aussi protégée indépendamment de la page qui la contient (les possibilités sont identiques à celles citées ci-dessus). L'information dont l'accès est différent de celui de la page ne sera simplement pas affichée pour les utilisateurs non autorisés.</p>
+    <h4>Suppression de la page ou de ses informations</h4>
     <p>La suppression d'une page entraîne automatiquement la suppression de toutes les informations qui y étaient inscrites.</p>
     <p>Il est aussi possible de supprimer toutes les informations d'une page (sans supprimer la page elle-même) en cliquant sur le bouton correspondant. Celui-ci n'apparaît pas pour les pages vides.</p>
   </div>
@@ -174,38 +194,34 @@
       <li>le <em>titre</em> qui sera affiché en haut de page et dans la barre de titre du navigateur. Par exemple, «&nbsp;À propos du TIPE&nbsp;».</li>
       <li>le <em>nom dans le menu</em> qui est affiché dans le menu en tant que lien vers la page. Il est préférable qu'il rentre sur une ligne, il faut donc le choisir assez court. Par exemple, «&nbsp;Informations TIPE&nbsp;».</li>
       <li>la <em>clé dans l'adresse</em> qui est un mot-clé utilisé uniquement dans l'adresse de la page. Par convention, il vaut mieux que ce soit un mot unique, court et sans majuscule. Par exemple, «&nbsp;tipe&nbsp;». La clé doit obligatoirement être unique (deux pages ne peuvent pas avoir la même clé).</li>
-      <li>la <em>matière</em> associée qui conditionne la place du lien dans le menu de la partie publique. Vous ne pouvez associer à une nouvelle page qu'une matière déjà associée à votre compte. Vous pouvez modifier les associations des matières à votre compte dans la page de <a href="matieres">gestion des matières</a>.</li>
+      <li>la <em>matière</em> associée qui conditionne la place du lien dans le menu général.</li>
       <li>l'<em>accès</em> à la page (voir ci-dessous).</li>
       <li>le <em>texte de début</em> qui sera affiché au-dessus des informations de la page. Il s'agit d'une ou deux phrases maximum. Il n'est affiché que si la page contient des informations. Cette case peut être laissée vide.</li>
     </ul>
-    <p>L'<em>accès</em> à la page peut être choisi parmi six possibilités&nbsp;:</p>
+    <h4>Gestion des accès</h4>
+    <p>L'accès à chaque page et chaque information peut être protégé. Trois choix sont possibles&nbsp;:</p>
     <ul>
-      <li><em>Visible de tous</em>&nbsp;: accessible de tout visiteur, sans identification.</li>
-      <li><em>Visible pour les connectés</em>&nbsp;: accessible de tout utilisateur mais uniquement après identification (utilisateurs de type invité, élève, colleur ou professeur). Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les élèves, colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type élève, colleur ou professeur. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les colleurs, professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type colleur ou professeur. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Visible pour les professeurs</em>&nbsp;: accessible uniquement par les utilisateurs de type professeur. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière ou à toutes les matières peuvent y accéder.</li>
-      <li><em>Non visible</em>&nbsp;: entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont la page peut dépendre). La différence avec le choix précédent est que cette page n'apparaît plus du tout dans le menu pour ceux qui ne la voient pas.</li>
+      <li><em>Accès public</em>&nbsp;: page accessible de tout visiteur, sans identification.</li>
+      <li><em>Utilisateurs identifiés</em>&nbsp;: page accessible uniquement par les utilisateurs identifiés, en fonction de leur type de compte. Un cadenas <span class="icon-lock"></span> est alors affiché dans le titre de la page. Pour les pages associées à une matière, seuls les utilisateurs associés à cette matière les voient affichées dans le menu et peuvent y accéder. Les associations entre utilisateurs et matières sont modifiables à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</li>
+      <li><em>Page invisible</em>&nbsp;: page entièrement invisible pour les utilisateurs autres que les professeurs (éventuellement associés à la matière dont la page peut dépendre). Un cadenas <span class="icon-locktotal"></span> est alors affiché dans le titre de la page.</li>
     </ul>
-    <p>Hormis le dernier choix, le lien dans le menu apparaîtra pour tout visiteur (identifié ou non). Le niveau de protection est indiqué dans le titre par une icône de type <span class="icon-lock1"></span> où le numéro correspond au niveau de protection à côté du titre, lorsque vous allez sur la page concernée connecté en tant que professeur.</p>
-    <p>La page sera automatiquement positionnée en dernière place, éventuellement au sein de la matière choisie. Il est ensuite possible de la déplacer parmi les autres pages à l'aide des boutons <span class="icon-monte"></span> et <span class="icon-descend"></span>.</p>   
+    <p>Le lien de la page dans le menu est visible avant identification. Il disparaît après identification pour les utilisateurs n'ayant pas accès à la page.</p>
+    <p>La page sera automatiquement positionnée en dernière place, éventuellement au sein de la matière choisie. Il sera ensuite possible de la déplacer parmi les autres pages à l'aide des boutons <span class="icon-monte"></span> et <span class="icon-descend"></span>.</p>   
   </div>
 
   <p id="log"></p>
   
   <script type="text/javascript">
 $( function() {
-  // Défaut sur le bandeau
-  $('[name="bandeau"]').placeholder();
   // Envoi par appui sur Entrée
   $('input,select').on('keypress',function (e) {
-    if ( e.which == 13 )
+    if ( e.which == 13 ) {
       $(this).parent().parent().children('a.icon-ok').click();
+      return false;
+    }
   });
 });
   </script>
 <?php
-
-$mysqli->close();
 fin(true);
 ?>
diff -urN cahier-de-prepa6.2.0/planning.php cahier-de-prepa8.0.1/planning.php
--- cahier-de-prepa6.2.0/planning.php	2015-10-27 14:34:06.622602873 +0100
+++ cahier-de-prepa8.0.1/planning.php	2018-10-14 03:16:38.899206997 +0200
@@ -10,27 +10,30 @@
 // Autorisation //
 //////////////////
 
-// Accès aux professeurs connectés uniquement
+// Accès aux professeurs connectés uniquement. Redirection pour les autres.
+if ( $autorisation < 5 )  {
+  header("Location: https://$site");
+  exit();
+}
 $mysqli = connectsql();
-if ( !$autorisation )  {
-  $titre = 'Planning';
-  $actuel = false;
+// Si connexion light : on doit répéter son mot de passe pour aller plus loin
+// login.php contient fin()
+if ( $_SESSION['light'] )  {
+  $titre = 'Modification du planning';
+  $actuel = 'planning';
   include('login.php');
 }
-elseif ( $autorisation < 4 )  {
-  debut($mysqli,'Planning','Vous n\'avez pas accès à cette page.',$autorisation,' ');
-  $mysqli->close();
-  fin();
-}
 
 //////////////
 //// HTML ////
 //////////////
-debut($mysqli,'Modication du planning',$message,4,'planning');
+debut($mysqli,'Modication du planning',$message,5,'planning');
 echo <<<FIN
 
-  <a class="icon-aide general" data-id="page" title="Aide pour les modifications du planning"></a>
-  <a class="icon-ok general" title="Valider les modifications du planning" data-noreload=""></a>
+  <div id="icones">
+    <a class="icon-ok noreload" title="Valider les modifications du planning"></a>
+    <a class="icon-aide" title="Aide pour les modifications du planning"></a>
+  </div>
 
   <article>
     <h3>Liste des semaines</h3>
@@ -50,6 +53,7 @@
 // Récupération et affichage des matières
 $semaine = array('Dimanche','Lundi','Mardi','Mercredi','Jeudi','Vendredi','Samedi');
 $resultat = $mysqli->query('SELECT id, DATE_FORMAT(debut,\'%w\') AS jour, DATE_FORMAT(debut,\'%d/%m/%Y\') AS debut, IF(colle=1,\' checked\',\'\') AS colle, vacances FROM semaines');
+$mysqli->close();
 while ( $r = $resultat->fetch_assoc() )  {
   $select = str_replace("\"${r['vacances']}\"","\"${r['vacances']}\" selected",'<option value="0">Période scolaire</option><option value="1">Vacances de Toussaint</option><option value="2">Vacances de Noël</option><option value="3">Vacances d\'hiver</option><option value="4">Vacances de printemps</option>');
   $r['jour'] = $semaine[$r['jour']];
@@ -68,28 +72,9 @@
 ?>
         </tbody>
       </table>
-      <input type="hidden" name="table" value="semaines">
     </form>
   </article>
 
-  <script type="text/javascript">
-$( function() {
-  $('select').change(function () {
-    if ( $(this).val() == 0 )  {
-      $(this).parent().prev().children('input').prop('checked',true);
-    }
-    else  {
-      $(this).parent().prev().children('input').prop('checked',false);
-    }
-  });
-  $('input:checkbox').change(function () {
-    if ( $(this).is(':checked') )  {
-      $(this).parent().next().children('select').val(0);
-    }
-  });
-});
-  </script>
-
   <div id="aide-page">
     <h3>Aide et explications</h3>
     <p>Il est possible ici de modifier le planning annuel, c'est-à-dire pour chaque semaine de l'année, préciser s'il s'agit&nbsp;:
@@ -99,12 +84,10 @@
       <li>d'une semaine de vacances (colonne <em>Vacances</em>) qui ne pourra recevoir ni cahier de texte, ni programmes de colles, ni notes de colles.</li>
     </ul>
     <p>Les vacances de deux semaines (toutes en fait :-) ) sont donc à marquer deux fois.</p>
-    <p>Il est préférable de décocher la case <em>Colle ou non</em> lorsque l'on sait qu'il n'y aura pas de colle, comme souvent en début ou en fin d'année&nbsp;: cela modifie l'affichage des programmes de colles (&laquo;&nbsp;Il n'y a pas de colle cette semaine&nbsp;&raquo; au lieu de &laquo;&nbsp;Le programme de colles de cette semaine n'est pas défini.&nbsp;&raquo;), et évite les erreurs d'écriture des programmes.</p>
+    <p>Il est préférable de décocher la case <em>Colle ou non</em> lorsque l'on sait qu'il n'y aura pas de colle, comme souvent en début ou en fin d'année&nbsp;: cela modifie l'affichage des programmes de colles (&laquo;&nbsp;Il n'y a pas de colle cette semaine&nbsp;&raquo; au lieu de &laquo;&nbsp;Le programme de colles de cette semaine n'est pas défini.&nbsp;&raquo;), et évite les erreurs d'écriture des programmes de colles ou de saisie des notes.</p>
   </div>
 
   <p id="log"></p>
 <?php
-
-$mysqli->close();
 fin(true);
 ?>
diff -urN cahier-de-prepa6.2.0/prefs.php cahier-de-prepa8.0.1/prefs.php
--- cahier-de-prepa6.2.0/prefs.php	2015-10-22 01:08:20.385698306 +0200
+++ cahier-de-prepa8.0.1/prefs.php	2018-10-14 03:35:17.932343688 +0200
@@ -22,6 +22,13 @@
   $mysqli->close();
   fin();
 }
+// Si connexion light : on doit répéter son mot de passe pour aller plus loin
+// login.php contient fin()
+if ( $_SESSION['light'] )  {
+  $titre = 'Mon compte';
+  $actuel = 'prefs';
+  include('login.php');
+}
 
 //////////////
 //// HTML ////
@@ -29,56 +36,117 @@
 debut($mysqli,'Mon compte',$message,$autorisation,'prefs');
 
 // Récupération des données de l'utilisateur
-$resultat = $mysqli->query("SELECT login, nom, prenom, mail, mdp, matieres, timeout FROM utilisateurs WHERE id = ${_SESSION['id']}");
+$resultat = $mysqli->query("SELECT login, nom, prenom, mail, mdp, timeout, mailexp,
+                            IF(mailcopie,' checked','') AS mailcopie,
+                            IF(mailenvoi,' checked','') AS mailenvoi,
+                            IF(mailliste,' checked','') AS mailliste,
+                            IF(permconn > '',' checked','') AS permconn
+                            FROM utilisateurs WHERE id = ${_SESSION['id']}");
+$mysqli->close();
 $r = $resultat->fetch_assoc();
 $resultat->free();
-
 ?>
 
-  <a class="icon-aide general" data-id="page" title="Aide pour les modifications de vos préférences"></a>
+  <div id="icones">
+    <a class="icon-aide" title="Aide pour les modifications de vos préférences"></a>
+  </div>
 
-  <article data-id="aucune|0">
+  <article>
+    <a class="icon-ferme" title="Fermer ce cadre"></a>
+    <h3>Confidentialité des données</h3>
+    <p>Les données stockées dans ce Cahier de Prépa à propos de vous sont modifiables ici. Il ne s'agit que de données permettant le fonctionnement de ce site. Aucune de ces données ne sera jamais partagée avec une autre entité.</p>
+    <p>Votre mot de passe vous est complètement personnel. Il est chiffré avant son stockage dans la base de données et ne peut donc techniquement être divulgué à personne.</p>
+    <p>Ces données (excepté votre mot de passe) sont aussi accessibles et modifiables par l'ensemble des professeurs de la classe pour permettre le bon fonctionnement du site. Par votre inscription sur ce site, vous autorisez cela et l'administrateur du site à stocker ces informations. Les éventuelles notes de colles que vous pouvez avoir ne sont consultables que par les pages dédiées, pour chaque matière.</p>
+    <p>Pour supprimer votre compte, vous devez le demander aux professeurs de la classe.</p>
+    <p>Le mot de passe associé à votre compte doit être obligatoirement fourni pour toute modification sur cette page. Cela permet d'être sûr que la modification n'est pas réalisée par quelqu'un d'autre.</p>
+    <p>D'autres données de connexion sont stockées pour le service du site&nbsp;: les connexions aux comptes (avec identifiant et adresse IP) et les modifications de la base de données sont consignées pour réparer les éventuelles erreurs. Ces données sont supprimées en fin d'année scolaire. Ces données ne sont exploitées qu'en cas de besoin technique et ne sont pas partagées.</p>
+  </article>
+
+  <article data-id="prefsperso|1">
+    <h3 class="edition">Mon identité</h3>
+    <a class="icon-ok noreload" title="Valider les modifications"></a>
     <form>
-      <a class="icon-ok" title="Valider les modifications" data-noreload=""></a>
-      <h3 class="edition">Mon identité</h3>
       <p class="ligne"><label for="prenom">Prénom&nbsp;: </label><input type="text" id="prenom" name="prenom" value="<?php echo $r['prenom']; ?>" size="50"></p>
       <p class="ligne"><label for="nom">Nom&nbsp;: </label><input type="text" id="nom" name="nom" value="<?php echo $r['nom']; ?>" size="50"></p>
-      <p class="ligne"><label for="mail1">Adresse électronique&nbsp;: </label><input type="text" id="mail1" name="mail1" value="<?php echo $r['mail']; ?>" size="50"></p>
-      <p class="ligne"><label for="mail2">Confirmation&nbsp;: </label><input type="text" id="mail2" name="mail2" value="" size="50"></p>
-      <p class="ligne"><label for="mdp0">Mot de passe&nbsp;: </label><input type="password" id="mdp0" name="mdp" value=""></p>
+      <p class="ligne"><label for="mail1">Adresse électronique&nbsp;: </label><input type="email" id="mail1" name="mail1" value="<?php echo $r['mail']; ?>" size="50"></p>
+      <p class="ligne"><label for="mail2">Confirmation&nbsp;: </label><input type="email" id="mail2" name="mail2" value="" size="50"></p>
+      <p class="ligne"><label for="mdp_1">Mot de passe&nbsp;: </label><input type="password" id="mdp_1" name="mdp" value=""></p>
       <p>Le mot de passe actuel doit être obligatoirement fourni pour toute modification.</p>
-      <input type="hidden" name="prefs" value="">
     </form>
   </article>
 
-  <article data-id="prefs|moi">
+  <article data-id="prefsperso|2">
+    <h3 class="edition">Mon mot de passe</h3>
+    <a class="icon-ok noreload" title="Valider les modifications"></a>
     <form>
-      <a class="icon-ok" title="Valider les modifications" data-noreload=""></a>
-      <h3 class="edition">Ma connexion</h3>
-      <p class="ligne"><label for="login">Identifiant&nbsp;: </label><input type="text" id="login" name="login" value="<?php echo $r['login']; ?>" size="50"></p>
+      <p class="ligne"><label for="mdp_2">Mot de passe actuel&nbsp;: </label><input type="password" id="mdp_2" name="mdp" value=""></p>
       <p class="ligne"><label for="mdp1">Nouveau mot de passe&nbsp;: </label><input type="password" id="mdp1" name="mdp1" value=""></p>
       <p class="ligne"><label for="mdp2">Confirmation&nbsp;: </label><input type="password" id="mdp2" name="mdp2" value=""></p>
+    </form>
+  </article>
+
+  <article data-id="prefsperso|3">
+    <h3 class="edition">Ma connexion</h3>
+    <a class="icon-ok noreload" title="Valider les modifications"></a>
+    <form>
+      <p class="ligne"><label for="login">Identifiant&nbsp;: </label><input type="text" id="login" name="login" value="<?php echo $r['login']; ?>" size="50"></p>
+      <p class="ligne"><label for="permconn">Conserver ma connexion sur cette machine&nbsp;: </label><input type="checkbox" id="permconn" name="permconn" value="1"<?php echo $r['permconn']; ?>></p>
       <p class="ligne"><label for="timeout">Durée avant déconnexion&nbsp;: </label><input type="text" id="timeout" name="timeout" value="<?php echo $r['timeout']; ?>" size="3"></p>
-      <p class="ligne"><label for="mdp00">Mot de passe actuel&nbsp;: </label><input type="password" id="mdp00" name="mdp" value=""></p>
+      <p class="ligne"><label for="mdp_3">Mot de passe actuel&nbsp;: </label><input type="password" id="mdp_3" name="mdp" value=""></p>
       <p>Le mot de passe actuel doit être obligatoirement fourni pour toute modification.</p>
-      <input type="hidden" name="prefs" value="">
     </form>
   </article>
+<?php
+// Cas d'envoi de mail des élèves : uniquement si validé par un professeur
+if ( ( $autorisation == 2 ) && $_SESSION['mailenvoi'] )  {
+?>
+
+  <article data-id="prefsperso|4">
+    <h3 class="edition">Mes envois de courriel</h3>
+    <a class="icon-ok noreload" title="Valider les modifications"></a>
+    <form>
+      <p class="ligne"><label for="mailexp">Nom d'expéditeur&nbsp;: </label><input type="text" id="mailexp" name="mailexp" value="<?php echo $r['mailexp']; ?>" size="50"></p>
+      <p class="ligne"><label for="mailcopie">Recevoir une copie des courriels envoyés&nbsp;: </label><input type="checkbox" id="mailcopie" name="mailcopie" value="1"<?php echo $r['mailcopie']; ?>></p>
+      <p class="ligne"><label for="mdp_4">Mot de passe actuel&nbsp;: </label><input type="password" id="mdp_4" name="mdp" value=""></p>
+      <p>Le mot de passe actuel doit être obligatoirement fourni pour toute modification.</p>
+    </form>
+  </article>
+<?php
+}
+// Cas des comptes colleur/administration/professeur
+elseif ( $autorisation > 2 )  {
+?>
+
+  <article data-id="prefsperso|4">
+    <h3 class="edition">Mes envois de courriel</h3>
+    <a class="icon-ok noreload" title="Valider les modifications"></a>
+    <form>
+      <p class="ligne"><label for="mailenvoi">S'autoriser l'envoi de courriel&nbsp;: </label><input type="checkbox" id="mailenvoi" name="mailenvoi" value="1"<?php echo $r['mailenvoi']; ?>></p>
+      <p class="ligne"><label for="mailexp">Nom d'expéditeur&nbsp;: </label><input type="text" id="mailexp" name="mailexp" value="<?php echo $r['mailexp']; ?>" size="50"></p>
+      <p class="ligne"><label for="mailcopie">Recevoir une copie des courriels envoyés&nbsp;: </label><input type="checkbox" id="mailcopie" name="mailcopie" value="1"<?php echo $r['mailcopie']; ?>></p>
+      <p class="ligne"><label for="mailliste">Apparaître dans la liste visible des élèves&nbsp;: </label><input type="checkbox" id="mailliste" name="mailliste" value="1"<?php echo $r['mailliste']; ?>></p>
+      <p class="ligne"><label for="mdp_4">Mot de passe actuel&nbsp;: </label><input type="password" id="mdp_4" name="mdp" value=""></p>
+      <p>Le mot de passe actuel doit être obligatoirement fourni pour toute modification.</p>
+    </form>
+  </article>
+<?php
+}
+?>
 
   <div id="aide-page">
     <h3>Aide et explications</h3>
-    <p>Il est possible ici de modifier vos préférences. Une fois modifié, le formulaire est à validé par un clic sur le bouton <span class="icon-ok"></span>.</p>
+    <p>Il est possible ici de modifier vos préférences. Une fois modifié, un formulaire est à validé par un clic sur le bouton <span class="icon-ok"></span>.</p>
+    <p>Afin d'éviter les usurpations de compte, il est nécessaire de taper son mot de passe pour toute modification.</p>
     <h4>Modification de l'identité&nbsp;: nom, prénom, adresse électronique</h4>
     <p>Si l'on souhaite modifier l'adresse électronique, il faut la saisir deux fois afin d'éviter les fautes de frappes.</p>
     <p>Afin d'éviter les usurpations de compte, il est nécessaire de taper son mot de passe pour toute modification.</p>
     <p>L'adresse électronique ne sera pas modifiée si un autre compte contient déjà la nouvelle valeur saisie.</p>
     <p>Il est possible de ne modifier que le prénom ou le nom sans modifier l'adresse électronique&nbsp;: il n'est alors pas nécessaire de saisir une deuxième fois l'adresse électronique.</p>
-    <p>L'adresse électronique sert pour les élèves à recevoir des courriels de la part des professeurs et colleurs. Pour les colleurs et professeurs, cela permet de recevoir et d'envoyer des courriels via ce Cahier de Prépa.</p>
+    <p>L'adresse électronique peut servir pour envoyer ou recevoir des courriels, en fonction du réglage choisi par l'équipe pédagogique.</p>
     <h4>Modification des paramètres de connexion</h4>
     <p>Votre identifiant est initialement de la forme &laquo;&nbsp;jdupont&nbsp;&raquo;. Vous pouvez le modifier, à condition de ne pas demander un identifiant déjà existant dans la base.</p>
-    <p>Si l'on souhaite modifier le mot de passe, il faut le saisir deux fois afin d'éviter les fautes de frappes.</p>
-    <p>La <em>durée avant connexion</em> est la durée en secondes au bout de laquelle votre session sera effacée sur le serveur, et une reconnexion sera nécessaire. C'est une sécurité si vous oubliez de vous déconnecter alors que vous êtes sur un ordinateur publique. La valeur par défaut est 900s, soit 15 minutes.</p>
-    <p>Afin d'éviter les usurpations de compte, il est nécessaire de taper son mot de passe pour toute modification.</p>
+    <p>La <em>durée avant connexion</em> est la durée en secondes au bout de laquelle votre session sera effacée sur le serveur, et une reconnexion sera nécessaire. C'est une sécurité si vous oubliez de vous déconnecter alors que vous êtes sur un ordinateur ouvert au public. La valeur par défaut est 900s, soit 15 minutes.</p>
+    <p>La case à cocher <em>Conserver ma connexion sur cette machine</em> est au contraire utile lorsque vous êtes sur une machine personnelle, comme votre téléphone. Cocher cette case permet de ne pas avoir besoin de se connecter systématiquement. Le mot de passe ne sera demandé que pour l'affichage de données nomminatives ou de modifications importantes.</p>
   </div>
 
   <p id="log"></p>
@@ -87,13 +155,16 @@
 $( function() {
   // Envoi par appui sur Entrée
   $('input').on('keypress',function (e) {
-    if ( e.which == 13 )
-      $(this).parent().parent().children('a.icon-ok').click();
+    if ( e.which == 13 ) {
+      $(this).parents('article').children('a.icon-ok').click();
+      return false;
+    }
+  });
+  $('.icon-ferme').on('click',function() {
+    $(this).parent().remove();
   });
 });
   </script>
 <?php
-
-$mysqli->close();
 fin(true);
 ?>
diff -urN cahier-de-prepa6.2.0/recup.php cahier-de-prepa8.0.1/recup.php
--- cahier-de-prepa6.2.0/recup.php	1970-01-01 01:00:00.000000000 +0100
+++ cahier-de-prepa8.0.1/recup.php	2018-10-16 12:36:04.191711083 +0200
@@ -0,0 +1,88 @@
+<?php
+// Sécurité
+define('OK',1);
+// Configuration
+include('config.php');
+// Fonctions
+include('fonctions.php');
+
+// Vérification du token CSRF
+if ( !isset($_REQUEST['csrf-token']) || isset($_SESSION['csrf-token']) && ( $_REQUEST['csrf-token'] != $_SESSION['csrf-token'] ) )
+  exit('{"etat":"nok","message":"Accès non autorisé"}');
+
+// Récupération de l'action
+if ( !isset($_REQUEST['action']) || !in_array($action = $_REQUEST['action'],array('prefs','docs')) )
+  exit('{"etat":"nok","message":"Mauvais paramètrage"}');
+
+// Accès professeur uniquement
+if ( $autorisation < 5 )
+  exit( strlen($message) ? $message : '{"etat":"nok","message":"Accès interdit"}' );
+
+// Connexion à la base de données
+$mysqli = connectsql(false);
+
+///////////////////////////////////////////////
+// Récupération des répertoires et documents //
+///////////////////////////////////////////////
+if ( $action == 'docs' )  {
+
+  $mats = '<option value="-1">[Choisissez une matière]</option>';
+  $reps = array( -1 =>'<option value="-1">[Choisissez une matière]</option>');
+  $docs = array( -1 => '<option value="0">[Choisissez une matière]</option>', 0 => '<option value="0">[Choisissez un répertoire]</option>' );
+  // Récupération des répertoires, avec le chemin complet
+  $resultat = $mysqli->query("SELECT r.id, r.nom, r.matiere,
+                              CONCAT( ( SELECT GROUP_CONCAT(reps.nom SEPARATOR '/') FROM reps WHERE FIND_IN_SET(reps.id,r.parents) ) ,'/') AS parents
+                              FROM reps AS r LEFT JOIN matieres AS m ON m.id = r.matiere
+                              WHERE FIND_IN_SET(r.matiere,'${_SESSION['matieres']}') ORDER BY m.ordre, CONCAT(parents, r.nom)");
+  if ( $resultat->num_rows )  {
+    while ( $r = $resultat->fetch_assoc() )  {
+      if ( !isset($reps[$r['matiere']]) )  {
+        $reps[$r['matiere']] = '<option value="0">[Choisissez un répertoire]</option>';
+        $mats .= "<option value=\"${r['matiere']}\">".( ( strpos($r['parents'],'/') ) ? strstr($r['parents'],'/',true) : $r['nom'] ).'</option>';
+      }
+      $reps[$r['matiere']] .= "<option value=\"${r['id']}\">${r['parents']}${r['nom']}</option>";
+      // Récupération des documents
+      $docs[$r['id']] = '<option value="0">[Choisissez un document]</option>';
+      $res = $mysqli->query("SELECT id, nom, SUBSTRING(ext,2) AS ext FROM docs WHERE parent = ${r['id']} AND protection < 32");
+      while ( $d = $res->fetch_assoc() )  {
+        $docs[$r['id']] .= "<option value=\"${d['id']}\">${d['nom']} (${d['ext']})</option>";
+      }
+      $res->free();
+    }
+    $resultat->free();
+  }
+  $mysqli->close();
+  exit(json_encode(array('recupok'=>1,'mats'=>$mats,'reps'=>$reps,'docs'=>$docs)));
+
+}
+
+///////////////////////////////////////////////
+// Récupération des données d'un utilisateur //
+///////////////////////////////////////////////
+elseif ( ( $action == 'prefs' ) && connexionlight() )  {
+
+  // Vérification que l'identifiant est valide
+  if ( isset($_REQUEST['id']) && ctype_digit($id = $_REQUEST['id']) )  {
+    $resultat = $mysqli->query("SELECT nom, prenom, login, matieres, mail as mail1, (LENGTH(mdp)=40) AS valide, (LEFT(mdp,1)='*') AS demande, (LENGTH(mdp)=1) AS invitation, autorisation, mailenvoi, mailexp, mailcopie, mailliste FROM utilisateurs WHERE id = $id");
+    $mysqli->close();
+    if ( $resultat->num_rows )  {
+      $r = $resultat->fetch_assoc();
+      $resultat->free();
+      // Problème d'encodage des entiers, renvoyés en tant que chaîne.
+      // Semble dépendre du driver MySQL, ne prenons pas de risque
+      $r['valide'] = intval($r['valide']);
+      $r['demande'] = intval($r['demande']);
+      $r['invitation'] = intval($r['invitation']);
+      $r['autorisation'] = intval($r['autorisation']);
+      $r['mailenvoi'] = intval($r['mailenvoi']);
+      $r['mailcopie'] = intval($r['mailcopie']);
+      $r['mailliste'] = intval($r['mailliste']);
+      $r['mail2'] = '';
+      $r['etat'] = 'recupok';
+      exit(json_encode($r));
+    }
+  }
+  exit('{"etat":"nok","message":"Identifiant non valide"}');
+
+}
+?>
diff -urN cahier-de-prepa6.2.0/relevenotes.php cahier-de-prepa8.0.1/relevenotes.php
--- cahier-de-prepa6.2.0/relevenotes.php	1970-01-01 01:00:00.000000000 +0100
+++ cahier-de-prepa8.0.1/relevenotes.php	2018-10-14 04:06:19.126138269 +0200
@@ -0,0 +1,281 @@
+<?php
+// Sécurité
+define('OK',1);
+// Configuration
+include('config.php');
+// Fonctions
+include('fonctions.php');
+
+//////////////////
+// Autorisation //
+//////////////////
+
+// Accès aux comptes administratifs connectés uniquement. Redirection pour les autres.
+if ( $autorisation != 4 )  {
+  header("Location: https://$site");
+  exit();
+}
+$mysqli = connectsql();
+// Si connexion light : on doit répéter son mot de passe pour aller plus loin
+// login.php contient fin()
+if ( $_SESSION['light'] )  {
+  $titre = 'Relève des notes de colle';
+  $actuel = 'relevenotes';
+  include('login.php');
+}
+
+// Fonction pour l'affichage des durées en heures/minutes. Argument en minutes.
+function format_duree($duree)  {
+  if ( $duree == 0 )
+    return '-';
+  if ( $duree >= 60 )
+    return intdiv($duree,60).'h'.( $duree%60 ?: '');
+  return ($duree%60).'m';
+}
+
+//////////////////////////////////////
+// Exportation des décomptes en xls //
+//////////////////////////////////////
+// Exportation uniquement si aucun header déjà envoyé
+if ( isset($_REQUEST['decompte']) && ctype_digit($decompte = $_REQUEST['decompte']) && !headers_sent() )  {
+  // Récupération de la bonne date et vérification
+  $decompte = preg_replace('/(\d{2})(\d{2})(\d{2})/','20$1-$2-$3',$decompte);
+  $resultat = $mysqli->query("SELECT IF(LENGTH(c.nom),CONCAT(c.nom,' ',c.prenom),c.login) AS colleur, m.nom AS matiere, SUM(nb) AS nb, SUM(duree) AS duree
+                              FROM heurescolles AS h JOIN (SELECT COUNT(*) AS nb, heure FROM notes GROUP BY heure) AS n ON h.id = n.heure
+                              JOIN utilisateurs AS c ON h.colleur = c.id JOIN matieres AS m ON h.matiere = m.id
+                              WHERE h.releve = '$decompte' GROUP BY h.colleur ORDER BY c.nom, m.ordre");
+  $mysqli->close();
+  if ( $resultat->num_rows )  {
+    // Fonctions de saisie
+    function saisie_nombre($l, $c, $v)  {
+      echo pack("sssss", 0x203, 14, $l, $c, 0).pack("d", $v);
+      return;
+    }
+    function saisie_chaine($l, $c, $v)  {
+      echo pack("ssssss", 0x204, 8 + strlen($v), $l, $c, 0, strlen($v)).$v;
+      return;
+    }
+    // Envoi des headers
+    header("Content-Type: application/vnd.ms-excel");
+    header("Content-Disposition: attachment; filename=decompte-$decompte.xls");
+    header("Content-Transfer-Encoding: binary");
+    // Début du fichier xls
+    echo pack("sssss", 0x809, 6, 0, 0x10, 0);
+    // Remplissage
+    saisie_chaine(0, 0, utf8_decode('Relevé des heures de colles - '.preg_replace('/(\d{4})-(\d{2})-(\d{2})/','$3/$2/$1',$decompte)));
+    saisie_chaine(2, 0, 'Colleur');
+    saisie_chaine(2, 1, utf8_decode('Matière'));
+    saisie_chaine(2, 2, utf8_decode('Nombre d\'élèves'));
+    saisie_chaine(2, 3, utf8_decode('Durée déclarée'));
+    $i = 2;
+    $total_n = 0;
+    $total_duree = 0;
+    while ( $r = $resultat->fetch_assoc() )  {
+      saisie_chaine(++$i, 0, utf8_decode($r['colleur']));
+      saisie_chaine($i, 1, utf8_decode($r['matiere']));
+      saisie_nombre($i, 2, $r['nb']);
+      saisie_chaine($i, 3, format_duree($r['duree']));
+      $total_n += $r['nb'];
+      $total_duree += $r['duree'];
+    }
+    // Totaux
+    saisie_chaine($i = $i+2, 0, Total);
+    saisie_nombre($i, 2, $total_n);
+    saisie_chaine($i, 3, format_duree($total_duree));
+    // Fin du fichier xls
+    echo pack("ss", 0x0A, 0x00);
+    $resultat->free();
+    exit();
+  }
+}
+
+//////////////////////////////////
+// Exportation des notes en xls //
+//////////////////////////////////
+// Exportation uniquement si aucun header déjà envoyé
+if ( isset($_REQUEST['notes']) && ctype_digit($decompte = $_REQUEST['notes']) && !headers_sent() )  {
+  // Récupération de la bonne date et vérification
+  $decompte = preg_replace('/(\d{2})(\d{2})(\d{2})/','20$1-$2-$3',$decompte);
+  $resultat = $mysqli->query("SELECT h.id, IF(LENGTH(c.nom),CONCAT(c.nom,' ',c.prenom),c.login) AS colleur, IF(LENGTH(e.nom),CONCAT(e.nom,' ',e.prenom),e.login) AS eleve,
+                              m.nom AS matiere, DATE_FORMAT(jour,'%d/%m/%y') AS jour, TIME_FORMAT(h.heure,'%kh%i') AS heure, note, duree
+                              FROM notes AS n JOIN heurescolles AS h ON h.id = n.heure JOIN utilisateurs AS e ON n.eleve = e.id
+                              JOIN utilisateurs AS c ON n.colleur = c.id JOIN matieres AS m ON n.matiere = m.id
+                              WHERE h.releve = '$decompte' ORDER BY c.nom, h.jour, h.heure, e.nom");
+  $mysqli->close();
+  if ( $resultat->num_rows )  {
+    // Fonctions de saisie
+    function saisie_nombre($l, $c, $v)  {
+      echo pack("sssss", 0x203, 14, $l, $c, 0).pack("d", $v);
+      return;
+    }
+    function saisie_chaine($l, $c, $v)  {
+      echo pack("ssssss", 0x204, 8 + strlen($v), $l, $c, 0, strlen($v)).$v;
+      return;
+    }
+    // Envoi des headers
+    header("Content-Type: application/vnd.ms-excel");
+    header("Content-Disposition: attachment; filename=decompte-$decompte.xls");
+    header("Content-Transfer-Encoding: binary");
+    // Début du fichier xls
+    echo pack("sssss", 0x809, 6, 0, 0x10, 0);
+    // Remplissage
+    saisie_chaine(0, 0, utf8_decode('Relevé des heures de colles - Détails des notes '.preg_replace('/(\d{4})-(\d{2})-(\d{2})/','$3/$2/$1',$decompte)));
+    saisie_chaine(2, 0, 'Colleur');
+    saisie_chaine(2, 1, utf8_decode('Matière'));
+    saisie_chaine(2, 2, utf8_decode('Élève'));
+    saisie_chaine(2, 3, utf8_decode('Note'));
+    saisie_chaine(2, 4, utf8_decode('Date'));
+    saisie_chaine(2, 5, utf8_decode('Heure'));
+    saisie_chaine(2, 6, utf8_decode('Nb d\'élèves'));
+    saisie_chaine(2, 7, utf8_decode('Durée déclarée'));
+    $i = 2;
+    $n = $hid = 0;
+    while ( $r = $resultat->fetch_assoc() )  {
+      if ( $hid != $r['id'] )  {
+        if ( $n )  {
+          for ( $j = 0 ; $j < $n ; $j++)
+            saisie_nombre($i-$j, 6, $n);
+        }
+        $hid = $r['id'];
+        $n = 0;
+      }
+      saisie_chaine(++$i, 0, utf8_decode($r['colleur']));
+      saisie_chaine($i, 1, utf8_decode($r['matiere']));
+      saisie_chaine($i, 2, utf8_decode($r['eleve']));
+      saisie_chaine($i, 3, utf8_decode($r['note']));
+      saisie_chaine($i, 4, utf8_decode($r['jour']));
+      saisie_chaine($i, 5, utf8_decode($r['heure']));
+      saisie_chaine($i, 7, format_duree($r['duree']));
+      $n = $n+1;
+    }
+    for ( $j = 0 ; $j < $n ; $j++)
+      saisie_nombre($i-$j, 6, $n);
+    // Fin du fichier xls
+    echo pack("ss", 0x0A, 0x00);
+    $resultat->free();
+    exit();
+  }
+}
+
+//////////////
+//// HTML ////
+//////////////
+debut($mysqli,'Relève des notes de colles',$message,4,'relevenotes');
+echo <<<FIN
+
+  <div id="icones">
+    <a class="icon-voirtout" href="?tableau" title="Voir le tableau récapitulatif pour tous les colleurs"></a>
+    <a class="icon-aide" title="Aide pour les relèves de notes de colles"></a>
+  </div>
+
+FIN;
+
+// Si tableau récapitulatif total demandé
+if ( isset($_REQUEST['tableau']) )  {
+  $resultat = $mysqli->query('SELECT IF(LENGTH(c.nom),CONCAT(c.nom,\' \',c.prenom),c.login) AS colleur, m.nom AS matiere, 
+                                     SUM(nb*(releve=0)) AS nbe_nr, SUM(duree*(releve=0)) AS duree_nr, 
+                                     SUM(nb*(releve>0)) AS nbe_r, SUM(duree*(releve>0)) AS duree_r
+                              FROM heurescolles AS h JOIN (SELECT COUNT(*) AS nb, heure FROM notes GROUP BY heure) AS n ON h.id = n.heure
+                                     JOIN utilisateurs AS c ON h.colleur = c.id JOIN matieres AS m ON h.matiere = m.id
+                              GROUP BY h.colleur ORDER BY m.ordre, c.nom');
+  echo <<<FIN
+  <article>
+    <a class="icon-ferme" href="relevenotes" title="Fermer ce récapitulatif"></a>
+    <h3>Récapitulatif annuel des heures de colles réalisées</h3>
+
+FIN;
+  if ( $resultat->num_rows )  {
+    echo <<<FIN
+    <table class="centre">
+      <tbody>
+        <tr><th colspan="2"></th><th colspan="2">Heures relevées</th><th colspan="2">Heures non relevées</th></tr>
+        <tr><th>Matière</th><th>Colleur</th><th>Nombre d'élèves</th><th>Durée</th><th>Nombre d'élèves</th><th>Durée</th></tr>
+
+FIN;
+    while ( $r = $resultat->fetch_assoc() )
+      echo "        <tr><td>${r['matiere']}</td><td>${r['colleur']}</td><td>${r['nbe_nr']}</td><td>".format_duree($r['duree_nr'])."</td><td>${r['nbe_r']}</td><td>".format_duree($r['duree_r'])."</td></tr>\n";
+    echo "      </tbody>\n    </table>\n  </article>\n\n";
+    $resultat->free();
+  }
+  else
+    echo "<div class=\"annonce\">Il n'y a encore aucune heure de colle déclarée cette année.</div>\n  </article>\n\n";
+}
+
+// Récupération des heures à relever, matière par matière
+$resultat = $mysqli->query('SELECT m.nom AS matiere, SUM(nb) AS nb, SUM(duree) AS duree
+                            FROM heurescolles AS h JOIN (SELECT COUNT(*) AS nb, heure FROM notes GROUP BY heure) AS n ON h.id = n.heure JOIN matieres AS m ON h.matiere = m.id
+                            WHERE h.releve = 0 GROUP BY h.matiere ORDER BY m.ordre');
+echo "\n  <article>\n    <h3>Heures de colles à relever actuellement</h3>\n";
+if ( $resultat->num_rows )  {
+  echo <<<FIN
+    <table class="centre">
+      <tbody>
+        <tr><th>Matière</th><th>Nombre d'élèves</th><th>Durée</th></tr>
+
+FIN;
+  while ( $r = $resultat->fetch_assoc() )  {
+    echo "        <tr><td>${r['matiere']}</td><td>${r['nb']}</td><td>".format_duree($r['duree'])."</td></tr>\n";
+  }
+  echo <<<FIN
+      </tbody>
+    </table>
+    <input id="relevenotes" type="button" class="ligne" value="Relever les notes">
+  </article>
+
+FIN;
+  $resultat->free();
+}
+else
+  echo "<div class=\"annonce\">Il n'y a actuellement aucune nouvelle heure de colle à relever.</div>\n  </article>\n\n";
+
+// Récupération des relevés déjà réalisés
+$resultat = $mysqli->query('SELECT DATE_FORMAT(releve,\'%d/%m/%y\') AS date, DATE_FORMAT(releve,\'%y%m%d\') AS ref, SUM(duree) AS duree, SUM(nb) AS nb
+                            FROM heurescolles AS h JOIN (SELECT COUNT(*) AS nb, heure FROM notes GROUP BY heure) AS n ON h.id = n.heure
+                            WHERE releve > 0 GROUP BY releve ORDER BY releve DESC');
+echo "\n  <article>\n    <h3>Relevés déjà réalisés</h3>\n";
+if ( $resultat->num_rows )  {
+  echo <<<FIN
+    <table class="centre">
+      <tbody>
+        <tr><th>Date</th><th>Décomptes des heures</th><th>Détail des notes</th></tr>
+
+FIN;
+  while ( $r = $resultat->fetch_assoc() )  {
+    echo "        <tr><td>${r['date']}</td><td><a class=\"icon-download\" href=\"?decompte=${r['ref']}\"></a> ".format_duree($r['duree'])."</td><td><a class=\"icon-download\" href=\"?notes=${r['ref']}\"></a> ${r['nb']} notes</td></tr>\n";
+  }
+  echo "      </tbody>\n    </table>\n  </article>\n\n";
+  $resultat->free();
+}
+else
+  echo "<div class=\"annonce\">Vous n'avez pas encore réalisé de relevé cette année.</div>\n  </article>\n\n";
+$mysqli->close();
+?>
+
+  <div id="aide-page">
+    <h3>Aide et explications</h3>
+    <p>Cette page n'est disponible que pour les utilisateurs de type administratif. Elle permet la relève des notes de colles saisies par les colleurs et les professeurs.</p>
+    <p>Le bouton <em>Relever les notes</em> permet de réaliser une relève des notes de colles. Cela consiste à marquer comme relevées toutes les heures déclarées jusqu'à maintenant et non encore relevées. Une confirmation sera demandée. Toutes les colles non encore relevées le seront alors&nbsp;: il n'est pas possible de sélectionner seulement une période.</p>
+    <p>L'ensemble des relevés est consigné dans un tableau. Y sont téléchargeables&nbsp;:
+    <ul>
+      <li>le décompte des heures, suffisant pour mettre au paiement les heures de colles</li>
+      <li>le détail de l'ensemble des notes, si besoin de vérification</li>
+    </ul> 
+    </p>
+    <p>Le bouton général <span class="icon-voirtout"></span> permet de visualiser un tableau récapitulatif global des totaux dans toutes les matières, depuis le début de l'année.</p>
+    <h4>Du côté des colleurs</h4>
+    <p>Les colleurs et professeurs ne peuvent plus modifier la durée ni le nombre d'élèves notés pendant les colles marquées comme relevées. Ils peuvent cependant encore modifier les notes, y compris pour les élèves «&nbsp;non notés&nbsp;» ou «&nbsp;absents&nbsp;».</p>
+    <p>Les colleurs ont accès à la liste de leur colles et des dates de relèves. Ils sont donc au courant des heures qui doivent être mises au paiment.</p>
+    <p>Les professeurs ont accès aux totaux du nombre d'élèves et du nombre d'heures qui ont été déclarées, relevées et non relevées. Cela leur permet de voir si un colleur déclare un nombre d'heures suspect.</p>
+    <h4>Retards de déclaration</h4>
+    <p>Le retard de déclaration d'un colleur est sans conséquence&nbsp;: à chaque relève, les colles non encore relevées le sont, indépendemment des dates des relèves précédentes. Il n'y a pas de régularisation globale à prévoir en juin.</p>
+    <h4></h4>
+    <h4>Binômes, élèves absents</h4>
+    <p>Les textes officiels sont parfaitement clairs&nbsp;: l'heure de colle est indivisible, un binôme doit être payé une heure, au moins pour les matières où une heure est la durée réelle de la colle. C'est pour cela que la durée déclarée par les colleurs peut différer du simple produit nombre d'élèves par durée individuelle. Cela dépend cependant du budget global. Le plus sain est d'avoir débattu le sujet avec l'équipe pédagogique.</p>
+    <p>Les élèves absents doivent rattraper leur colle. À défaut, le colleur doit quand même être payé&nbsp;: c'est pour cela qu'il n'y a pas de différence entre un élève noté et un élève absent dans le décompte.</p>
+  </div>
+
+  <p id="log"></p>
+
+<?php
+fin(true);
+?>
diff -urN cahier-de-prepa6.2.0/utilisateurs-matieres.php cahier-de-prepa8.0.1/utilisateurs-matieres.php
--- cahier-de-prepa6.2.0/utilisateurs-matieres.php	1970-01-01 01:00:00.000000000 +0100
+++ cahier-de-prepa8.0.1/utilisateurs-matieres.php	2018-10-14 04:56:55.977792932 +0200
@@ -0,0 +1,144 @@
+<?php
+// Sécurité
+define('OK',1);
+// Configuration
+include('config.php');
+// Fonctions
+include('fonctions.php');
+
+// Comptes en attente de validation de l'équipe pédagogique :
+// * en début de mdp (donc 41 caractères)
+// Comptes en attente de réponse de l'utilisateur :
+// ? en début de mdp (non défini, donc 1 caractère)
+// Comptes désactivés :
+// ! en début de mdp (donc 41 caractères)
+// Comptes actifs :
+// mdp valant un sha1 (donc "mdp > '0'" dans les where)
+
+//////////////////
+// Autorisation //
+//////////////////
+
+// Accès aux professeurs connectés uniquement. Redirection pour les autres.
+if ( $autorisation < 5 )  {
+  header("Location: https://$site");
+  exit();
+}
+$mysqli = connectsql();
+// Si connexion light : on doit répéter son mot de passe pour aller plus loin
+// login.php contient fin()
+if ( $_SESSION['light'] )  {
+  $titre = 'Modification des associations utilisateurs-matières';
+  $actuel = 'utilisateurs-matieres';
+  include('login.php');
+}
+
+//////////////
+//// HTML ////
+//////////////
+debut($mysqli,'Modification des associations utilisateurs-matières',$message,5,'utilisateurs-matieres');
+?>
+
+  <div id="icones">
+    <a class="icon-aide" title="Aide pour les modifications des associations utilisateurs-matières"></a>
+  </div>
+
+  <article>
+    <h3>Liste des utilisateurs</h3>
+    <table id="utilisateurs-matieres">
+      <thead>
+        <tr>
+          <th></th>
+<?php
+// Récupération des matières
+$resultat = $mysqli->query('SELECT id, nom FROM matieres ORDER BY ordre');
+$matieres = array();
+$iconesmultiples = '';
+while ( $r = $resultat->fetch_assoc() )  {
+  $matieres[$r['id']] = 0;
+  echo "          <th class=\"matieres\"><span id=\"m${r['id']}\">${r['nom']}</span></th>\n";
+  $iconesmultiples .= "\n          <th><span class=\"icon-ok\" data-id=\"${r['id']}\"></span></th>";
+}
+$resultat->free();
+echo "          <th></th>\n        </tr>\n      </thead>\n      <tbody>\n";
+$iconesmultiples .= "\n          <th><a class=\"icon-cocher\"></a></th>";
+
+// Nombre de colonnes du tableau
+$nc = count($matieres)+2;
+
+// Variables utilisées pour tout le tableau
+$autorisations = array(5=>'Professeur',4=>'Administratif',3=>'Colleur',2=>'Élève',1=>'Invité');
+$requete = 'SELECT id, IF(LENGTH(nom),CONCAT(nom,\' \',prenom),CONCAT(\'<em>\',login,\'</em>\')) AS nomprenom, autorisation, SUBSTR(matieres,3) AS matieres FROM utilisateurs WHERE XXX ORDER BY autorisation DESC, nom, prenom, login';
+
+// Fonction de remplissage des lignes
+function ligne($r,$matieres,$autorisations = false)  {
+  if ( $autorisations )
+    $r['nomprenom'] .= " (${autorisations[$r['autorisation']]})";
+  echo "        <tr data-id=\"${r['id']}\">\n          <td>${r['nomprenom']}</td>\n          ";
+  foreach ( explode(',',$r['matieres']) as $mid )
+    $matieres[$mid] = 1;
+  foreach ( $matieres as $mid => $ok )
+    echo "<td>$mid|$ok</td>";
+  echo "<td><input type=\"checkbox\"></td>\n        </tr>\n";
+}
+
+// Récupération des demandes à valider
+$resultat = $mysqli->query(str_replace('XXX','mdp LIKE \'*%\'',$requete));
+if ( $n = $resultat->num_rows )  {
+  echo "        <tr class=\"categorie\">\n          <th>Demandes en attente de validation ($n)</th>$iconesmultiples\n        </tr>\n";
+  while ( $r = $resultat->fetch_assoc() )
+    ligne($r,$matieres,$autorisations);
+  $resultat->free();
+}
+
+// Récupération des invitations non répondues
+$resultat = $mysqli->query(str_replace('XXX','mdp = \'?\'',$requete));
+if ( $n = $resultat->num_rows )  {
+  echo "        <tr class=\"categorie\">\n          <th>Invitations envoyées en attente de réponse ($n)</th>$iconesmultiples\n        </tr>\n";
+  while ( $r = $resultat->fetch_assoc() )
+    ligne($r,$matieres,$autorisations);
+  $resultat->free();
+}
+
+// Décompte total des utilisateurs (comptes validés et non désactivés) en fonction de leur type
+foreach ( $autorisations as $a => $auto)  {
+  $resultat = $mysqli->query(str_replace('XXX',"mdp > '0' AND autorisation=$a",$requete));
+  if ( $n = $resultat->num_rows )  {
+    echo "        <tr class=\"categorie\">\n          <th>{$auto}s ($n)</th>$iconesmultiples\n        </tr>\n";
+    while ( $r = $resultat->fetch_assoc() )
+      ligne($r,$matieres);
+    $resultat->free();
+  }
+}
+
+// Récupération des comptes désactivés
+$resultat = $mysqli->query(str_replace('XXX','mdp LIKE \'!%\'',$requete));
+
+if ( $n = $resultat->num_rows )  {
+  echo "        <tr class=\"categorie\">\n          <th>Comptes désactivés ($n)</th>$iconesmultiples\n        </tr>\n";
+  while ( $r = $resultat->fetch_assoc() )
+    ligne($r,$matieres,$autorisations);
+  $resultat->free();
+}
+?>
+      </tbody>
+    </table>
+  </article>
+
+<?php
+
+
+// Aide et formulaire d'ajout
+?>
+  
+  <div id="aide-page">
+    <h3>Aide et explications</h3>
+    <p>Il est possible ici de modifier les associations entre utilisateurs et matières.</p>
+    <p>Chaque bouton <span class="icon-ok"></span> ou <span class="icon-nok"></span> à l'intersection d'un utilisateur et d'une matière permet de modifier immédiatement en cliquant l'association concernée.</p>
+    <p>Il est possible de traiter simultanément plusieurs utilisateurs en cochant les cases en bout de ligne et en cliquant sur les boutons d'action situés sur les lignes d'entêtes. Seuls des comptes de même types peuvent être traités simultanément. Les boutons <span class="icon-cocher"></span> permettent de cocher l'ensemble des comptes d'un type donné.</p>
+  </div>
+
+  <p id="log"></p>
+<?php
+fin(true);
+?>
diff -urN cahier-de-prepa6.2.0/utilisateurs.php cahier-de-prepa8.0.1/utilisateurs.php
--- cahier-de-prepa6.2.0/utilisateurs.php	2016-08-30 15:12:53.057498780 +0200
+++ cahier-de-prepa8.0.1/utilisateurs.php	2018-10-15 17:51:12.269180965 +0200
@@ -6,27 +6,32 @@
 // Fonctions
 include('fonctions.php');
 
-// Compte demandés en attente de validation de l'équipe pédagogique :
-// @ devant mail (pour empêcher la récupération de mdp par mail), * devant mdp
-// Compte proposés en attente de réponse de l'utilisateur : mdp = '*'
-// => Pour les différentier, utiliser la longueur du mdp (1, 40, 41).
+// Comptes en attente de validation de l'équipe pédagogique :
+// * en début de mdp (donc 41 caractères)
+// Comptes en attente de réponse de l'utilisateur :
+// ? en début de mdp (non défini, donc 1 caractère)
+// Comptes désactivés :
+// ! en début de mdp (donc 41 caractères)
+// Comptes actifs :
+// mdp valant un sha1 (donc "mdp > '0'" dans les where)
 
 //////////////////
 // Autorisation //
 //////////////////
 
-// Accès aux professeurs connectés uniquement
+// Accès aux professeurs connectés uniquement. Redirection pour les autres.
+if ( $autorisation < 5 )  {
+  header("Location: https://$site");
+  exit();
+}
 $mysqli = connectsql();
-if ( !$autorisation )  {
+// Si connexion light : on doit répéter son mot de passe pour aller plus loin
+// login.php contient fin()
+if ( $_SESSION['light'] )  {
   $titre = 'Modification des utilisateurs';
-  $actuel = false;
+  $actuel = 'utilisateurs';
   include('login.php');
 }
-elseif ( $autorisation < 4 )  {
-  debut($mysqli,'Modification des utilisateurs','Vous n\'avez pas accès à cette page.',$autorisation,' ');
-  $mysqli->close();
-  fin();
-}
 
 //////////////////////////////////
 // Exportation des notes en xls //
@@ -35,9 +40,12 @@
 if ( isset($_REQUEST['xls']) && !headers_sent() )  {
   // Recherche des utilisateurs concernés : comptes validés hors comptes invités
   // et hors comptes sans nom et sans mail (identifiant seul)
-  $resultat = $mysqli->query('SELECT nom, prenom, IF(LENGTH(mail),mail,"Pas d\'adresse") AS mail, autorisation
-                              FROM utilisateurs WHERE LENGTH(mdp)=40 AND autorisation > 1 AND LENGTH(CONCAT(nom,prenom,mail))
-                              ORDER BY autorisation DESC, IF(LENGTH(nom),CONCAT(nom,prenom),login)');
+  $resultat = $mysqli->query('SELECT u.nom, prenom, IF(LENGTH(mail),mail,"Pas d\'adresse") AS mail, autorisation,
+                              GROUP_CONCAT(m.nom ORDER BY m.ordre SEPARATOR \', \') AS mats 
+                              FROM utilisateurs AS u JOIN matieres AS m ON FIND_IN_SET(m.id,u.matieres)
+                              WHERE mdp > \'0\' AND autorisation > 1 AND u.nom > \'\' OR prenom > \'\' OR mail > \'\'
+                              GROUP BY u.id ORDER BY autorisation DESC, IF(LENGTH(u.nom),CONCAT(u.nom,prenom),login)');
+  $mysqli->close();
   if ( $resultat->num_rows )  {
     // Fonction de saisie
     function saisie_chaine($l, $c, $v)  {
@@ -45,7 +53,7 @@
       return;
     }
     // Correspondance autorisation-type de compte
-    $categories = array(2=>'Élève',3=>'Colleur',4=>'Professeur');
+    $categories = array(2=>'Élève',3=>'Colleur',4=>'Administration',5=>'Professeur');
     // Envoi des headers
     header("Content-Type: application/vnd.ms-excel");
     header("Content-Disposition: attachment; filename=utilisateurs.xls");
@@ -55,95 +63,245 @@
     // Remplissage
     saisie_chaine(0, 0, 'Nom');
     saisie_chaine(0, 1, utf8_decode('Prénom'));
-    saisie_chaine(0, 2, 'Mail');
+    saisie_chaine(0, 2, utf8_decode('Adresse électronique'));
     saisie_chaine(0, 3, utf8_decode('Catégorie'));
+    saisie_chaine(0, 4, utf8_decode('Matières'));
     $i = 0;
     while ( $r = $resultat->fetch_assoc() )  {
       saisie_chaine(++$i, 0, utf8_decode($r['nom']));
       saisie_chaine($i, 1, utf8_decode($r['prenom']));
       saisie_chaine($i, 2, utf8_decode($r['mail']));
       saisie_chaine($i, 3, utf8_decode($categories[$r['autorisation']]));
+      saisie_chaine($i, 4, utf8_decode($r['mats']));
     }
     // Fin du fichier xls
     echo pack("ss", 0x0A, 0x00);
     $resultat->free();
-    $mysqli->close();
-    exit();
   }
+  exit();
 }
 
 //////////////
 //// HTML ////
 //////////////
-debut($mysqli,'Modification des utilisateurs',$message,4,'utilisateurs');
-echo <<<FIN
+debut($mysqli,'Modification des utilisateurs',$message,5,'utilisateurs');
+?>
 
-  <a class="icon-aide general" data-id="page" title="Aide pour les modifications des utilisateurs"></a>
-  <a class="icon-prefs general" data-id="prefs" title="Modifier les réglages de la gestion des comptes"></a>
-  <a class="icon-download general" href="?xls" title="Télécharger la liste des mails en xls"></a>
-  <a class="icon-ajoute general" data-id="ajoute" title="Ajouter de nouveaux utilisateurs"></a>
+  <div id="icones">
+    <a class="icon-prefs formulaire" title="Modifier les réglages de la gestion des comptes"></a>
+    <a class="icon-ajoute formulaire" title="Ajouter de nouveaux utilisateurs"></a>
+    <a class="icon-download" href="?xls" title="Télécharger la liste des utilisateurs en xls"></a>
+    <a class="icon-aide" title="Aide pour les modifications des utilisateurs"></a>
+  </div>
 
   <article>
     <h3>Liste des utilisateurs</h3>
     <table id="utilisateurs">
       <tbody>
-
-FIN;
-
-$autorisations = array(1=>'Invité',2=>'Élève',3=>'Colleur',4=>'Professeur');
+<?php
+// Variables utilisées pour tout le tableau
+$autorisations = array(5=>'Professeur',4=>'Administratif',3=>'Colleur',2=>'Élève',1=>'Invité');
+$requete = 'SELECT id, nom, prenom, login, IF(LENGTH(mail),mail,"Pas d\'adresse") AS mail, autorisation, mailenvoi FROM utilisateurs WHERE XXX ORDER BY autorisation DESC, nom, prenom, login';
 
 // Récupération des demandes à valider
-$resultat = $mysqli->query('SELECT id, IF(LENGTH(mail),SUBSTR(mail,2),"Pas d\'adresse") AS mail, autorisation, CONCAT( nom, \' \', prenom ) AS nom
-                            FROM utilisateurs WHERE LENGTH(mdp)=41 ORDER BY IF(LENGTH(nom),CONCAT(nom,prenom),login)');
+$resultat = $mysqli->query(str_replace('XXX','mdp LIKE \'*%\'',$requete));
 if ( $n = $resultat->num_rows )  {
-  $n = ( $n > 1 ) ? "$n comptes" : "1 compte";
-  echo "        <tr><th colspan=\"5\">En attente de validation ($n)</th></tr>\n";
+  echo  <<<FIN
+        <tr class="categorie"><th colspan="7">Demandes en attente de validation ($n)</th></tr>
+        <tr>
+          <th>Nom Prénom</th><th>Identifiant</th><th>Adresse électronique</th><th>Type</th>
+          <th class="icones">
+            <a class="icon-mail" title="Autoriser l'envoi de courriel pour les demandes cochées"></a>
+          </th>
+          <th class="icones">
+            <a class="icon-validutilisateur" title="Valider l'ensemble des demandes cochées"></a>
+            <a class="icon-supprutilisateur" title="Supprimer l'ensemble des demandes cochées"></a>
+          </th>
+          <th class="icones"><a class="icon-cocher" title="Tout cocher"></a></th>
+        </tr>
+
+FIN;
   while ( $r = $resultat->fetch_assoc() )
-    echo "        <tr><td colspan=\"4\">${r['nom']} - ${r['mail']} - ".$autorisations[$r['autorisation']]."</td><td data-id=\"utilisateurs|${r['id']}\"><a class=\"icon-supprime\" title=\"Supprimer ce compte\"> <a class=\"icon-ajoute\" data-id=\"valide_utilisateur\" title=\"Valider ce compte\"></a></td></tr>\n";
+    echo <<<FIN
+        <tr data-id="${r['id']}">
+          <td>${r['nom']} ${r['prenom']}</td>
+          <td>${r['login']}</td>
+          <td>${r['mail']}</td>
+          <td>${autorisations[$r['autorisation']]}</td>
+          <td class="icones mailenvoi">${r['mailenvoi']}</td>
+          <td class="icones">
+            <a class="icon-edite" title="Éditer cette demande"></a>
+            <a class="icon-validutilisateur" title="Valider cette demande"></a>
+            <a class="icon-supprutilisateur" title="Supprimer cette demande"></a>
+          </td>
+          <td class="icones"><input type="checkbox"></td>
+        </tr>
+
+FIN;
   $resultat->free();
 }
 
 // Récupération des invitations non répondues
-$resultat = $mysqli->query('SELECT id, IF(LENGTH(mail),mail,"Pas d\'adresse") AS mail, autorisation, CONCAT( nom, \' \', prenom ) AS nom
-                            FROM utilisateurs WHERE LENGTH(mdp)=1 ORDER BY IF(LENGTH(nom),CONCAT(nom,prenom),login)');
+$resultat = $mysqli->query(str_replace('XXX','mdp = \'?\'',$requete));
 if ( $n = $resultat->num_rows )  {
-  $n = ( $n > 1 ) ? "$n comptes" : "1 compte";
-  echo "        <tr><th colspan=\"5\">Invitations envoyées ($n)</th></tr>\n";
+  echo <<<FIN
+        <tr class="categorie"><th colspan="7">Invitations envoyées en attente de réponse ($n)</th></tr>
+        <tr>
+          <th>Nom Prénom</th><th>Identifiant</th><th>Adresse électronique</th><th>Type</th>
+          <th class="icones">
+            <a class="icon-mail" title="Autoriser l'envoi de courriel pour les invitations cochées"></a>
+          </th>
+          <th class="icones">
+            <a class="icon-desactive" title="Désactiver tous les invitations cochées"></a>
+            <a class="icon-supprutilisateur" title="Supprimer l'ensemble des invitations cochées"></a>
+          </th>
+          <th class="icones"><a class="icon-cocher" title="Tout cocher"></a></th>
+        </tr>
+
+FIN;
   while ( $r = $resultat->fetch_assoc() )
-    echo "        <tr><td colspan=\"4\">${r['nom']} - ${r['mail']} - ".$autorisations[$r['autorisation']]."</td><td data-id=\"utilisateurs|${r['id']}\"><a class=\"icon-supprime\" title=\"Supprimer ce compte\"></a></td></tr>\n";
+    echo <<<FIN
+        <tr data-id="${r['id']}">
+          <td>${r['nom']} ${r['prenom']}</td>
+          <td>${r['login']}</td>
+          <td>${r['mail']}</td>
+          <td>${autorisations[$r['autorisation']]}</td>
+          <td class="icones mailenvoi">${r['mailenvoi']}</td>
+          <td class="icones">
+            <a class="icon-edite" title="Éditer cette invitation"></a>
+            <a class="icon-desactive" title="Désactiver cette invitation"></a>
+            <a class="icon-supprutilisateur" title="Supprimer cette invitation"></a>
+          </td>
+          <td class="icones"><input type="checkbox"></td>
+        </tr>
+
+FIN;
   $resultat->free();
 }
 
-// Décompte total des utilisateurs (comptes validés) en fonction de leur type
-$resultat = $mysqli->query('SELECT autorisation, COUNT(id) AS n FROM utilisateurs WHERE LENGTH(mdp)=40 GROUP BY autorisation ORDER BY autorisation DESC');
-$n = array();
-while ( $r = $resultat->fetch_assoc() )
-  $n[$r['autorisation']] = ( $r['n'] > 1 ) ? "${r['n']} comptes" : "${r['n']} compte";
-$resultat->free();
+// Décompte total des utilisateurs (comptes validés et non désactivés) en fonction de leur type
+foreach ( $autorisations as $a => $auto)  {
+  $resultat = $mysqli->query(str_replace('XXX',"mdp > '0' AND autorisation=$a",$requete));
+  if ( $n = $resultat->num_rows )  {
+    if ( $a > 1 )
+      echo <<<FIN
+        <tr class="categorie"><th colspan="7">{$auto}s ($n)</th></tr>
+        <tr>
+          <th>Nom Prénom</th><th>Identifiant</th><th colspan="2">Adresse électronique</th>
+          <th class="icones">
+            <a class="icon-mail" title="Autoriser/supprimer l'envoi de courriel pour les comptes cochés"></a>
+          </th>
+          <th class="icones">
+            <a class="icon-desactive" title="Désactiver les comptes cochés"></a>
+            <a class="icon-supprutilisateur" title="Supprimer l'ensemble des comptes cochés"></a>
+          </th>
+          <th class="icones"><a class="icon-cocher" title="Tout cocher"></a></th>
+        </tr>
 
-// Récupération des utilisateurs
-$resultat = $mysqli->query('SELECT id, nom, prenom, login, mail, autorisation
-                            FROM utilisateurs WHERE LENGTH(mdp)=40 ORDER BY autorisation DESC, IF(LENGTH(nom),CONCAT(nom,prenom),login)');
-$a = 0;
-while ( $r = $resultat->fetch_assoc() )  {
-  if ( $a != $r['autorisation'] )  {
-    $a = $r['autorisation'];
-    switch ( $a )  {
-      case 1 : $t = 'Invités'; break;
-      case 2 : $t = 'Élèves'; break;
-      case 3 : $t = 'Colleurs'; break;
-      case 4 : $t = 'Professeurs'; break;
-    }
-    echo <<<FIN
-        <tr><th colspan="5">$t (${n[$a]})</th></tr>
-        <tr><th>Nom</th><th>Prénom</th><th>Identifiant</th><th>Adresse électronique</th><th></th></tr>
+FIN;
+    else
+      echo <<<FIN
+        <tr class="categorie"><th colspan="7">Invités ($n)</th></tr>
+        <tr>
+          <th>Nom Prénom</th><th>Identifiant</th><th colspan="3">Adresse électronique</th>
+          <th class="icones">
+            <a class="icon-desactive" title="Désactiver les comptes cochés"></a>
+            <a class="icon-supprutilisateur" title="Supprimer l'ensemble des comptes cochés"></a>
+          </th>
+          <th class="icones"><a class="icon-cocher" title="Tout cocher"></a></th>
+        </tr>
+
+FIN;
+    while ( $r = $resultat->fetch_assoc() )
+      if ( $a > 1 )
+        echo <<<FIN
+        <tr data-id="${r['id']}">
+          <td>${r['nom']} ${r['prenom']}</td>
+          <td>${r['login']}</td>
+          <td colspan="2">${r['mail']}</td>
+          <td class="icones mailenvoi">${r['mailenvoi']}</td>
+          <td class="icones">
+            <a class="icon-edite" title="Éditer ce compte"></a>
+            <a class="icon-desactive" title="Désactiver ce compte"></a>
+            <a class="icon-supprutilisateur" title="Supprimer cet compte"></a>
+          </td>
+          <td class="icones"><input type="checkbox"></td>
+        </tr>
 
 FIN;
+      else
+        echo <<<FIN
+        <tr data-id="${r['id']}">
+          <td></td>
+          <td>${r['login']}</td>
+          <td colspan="3"></td>
+          <td class="icones">
+            <a class="icon-edite" title="Éditer ce compte"></a>
+            <a class="icon-desactive" title="Désactiver ce compte"></a>
+            <a class="icon-supprutilisateur" title="Supprimer cet compte"></a>
+          </td>
+          <td class="icones"><input type="checkbox"></td>
+        </tr>
+
+FIN;
+    $resultat->free();
   }
-  if ( $a > 1 )
-    echo "        <tr><td class=\"editable\" data-id=\"utilisateurs|nom|${r['id']}\">${r['nom']}</td><td class=\"editable\" data-id=\"utilisateurs|prenom|${r['id']}\">${r['prenom']}</td><td>${r['login']}</td><td class=\"editable\" data-id=\"utilisateurs|mail|${r['id']}\">${r['mail']}</td><td data-id=\"utilisateurs|${r['id']}\"><a class=\"icon-supprime\" title=\"Supprimer ce compte\"></a></td></tr>\n";
-  else
-    echo "        <tr><td></td><td></td><td>${r['login']}</td><td></td><td data-id=\"utilisateurs|${r['id']}\"><a class=\"icon-supprime\" title=\"Supprimer ce compte\"></a></td></tr>\n";
+}
+
+// Récupération des comptes désactivés
+$resultat = $mysqli->query(str_replace('XXX','mdp LIKE \'!%\'',$requete));
+if ( $n = $resultat->num_rows )  {
+  echo <<<FIN
+        <tr class="categorie"><th colspan="7">Comptes désactivés ($n)</th></tr>
+        <tr>
+          <th>Nom Prénom</th><th>Identifiant</th><th>Adresse électronique</th><th>Type</th>
+          <th class="icones">
+            <a class="icon-mail" title="Autoriser l'envoi de courriel pour les comptes cochés"></a>
+          </th>
+          <th class="icones">
+            <a class="icon-active" title="Activer tous les comptes cochés"></a>
+            <a class="icon-supprutilisateur" title="Supprimer toutes les comptes cochés"></a>
+          </th>
+          <th class="icones"><a class="icon-cocher" title="Tout cocher"></a></th>
+        </tr>
+
+FIN;
+  while ( $r = $resultat->fetch_assoc() )
+    if ( $r['autorisation'] > 1 )
+      echo <<<FIN
+        <tr data-id="${r['id']}">
+          <td>${r['nom']} ${r['prenom']}</td>
+          <td>${r['login']}</td>
+          <td>${r['mail']}</td>
+          <td>${autorisations[$r['autorisation']]}</td>
+          <td class="icones mailenvoi">${r['mailenvoi']}</td>
+          <td class="icones">
+            <a class="icon-edite" title="Éditer ce compte"></a>
+            <a class="icon-active" title="Activer ce compte"></a>
+            <a class="icon-supprutilisateur" title="Supprimer cet compte"></a>
+          </td>
+          <td class="icones"><input type="checkbox"></td>
+        </tr>
+
+FIN;
+    else
+      echo <<<FIN
+        <tr data-id="${r['id']}">
+          <td></td>
+          <td>${r['login']}</td>
+          <td></td>
+          <td>${autorisations[$r['autorisation']]}</td>
+          <td></td>
+          <td class="icones">
+            <a class="icon-edite" title="Éditer ce compte"></a>
+            <a class="icon-active" title="Activer ce compte"></a>
+            <a class="icon-supprutilisateur" title="Supprimer cet compte"></a>
+          </td>
+          <td class="icones"><input type="checkbox"></td>
+        </tr>
+
+FIN;
+  $resultat->free();
 }
 ?>
       </tbody>
@@ -152,68 +310,91 @@
 
 <?php
 // Récupération des matières
-$resultat = $mysqli->query('SELECT id, nom FROM matieres');
+$resultat = $mysqli->query('SELECT id, nom FROM matieres ORDER BY ordre');
 $select_matieres = '';
 while ( $r = $resultat->fetch_assoc() )
-  $select_matieres .= "\n          <option value=\"${r['id']}\">${r['nom']}</option>";
+  $select_matieres .= "          <option value=\"${r['id']}\">${r['nom']}</option>\n";
 $resultat->free();
 
-// Récupération du réglage "création impossible"
-$resultat = $mysqli->query('SELECT id FROM utilisateurs WHERE LENGTH(mdp) = 0');
-if ( $creationinterdite = $resultat->num_rows )
-  $resultat->free();
+// Récupération de la préférence de création de compte
+$resultat = $mysqli->query('SELECT IF(val,\' checked\',\'\') FROM prefs WHERE nom = "creation_compte"');
+$creation_compte = $resultat->fetch_row()[0];
+$resultat->free();
+$mysqli->close();
 
 // Aide et formulaire d'ajout
 ?>
-  <form id="form-ajoute">
+  <form id="form-ajoute" data-action="ajout-utilisateurs">
     <h3 class="edition">Ajouter de nouveaux utilisateurs</h3>
     <p class="ligne"><label for="autorisation">Type de comptes&nbsp;:</label>
-      <select name="autorisation" id="autorisation">
+      <select name="autorisation">
+        <option selected hidden value="0">Choisir ...</option>
         <option value="1">Invités</option>
-        <option value="2" selected>Élèves</option>
+        <option value="2">Élèves</option>
         <option value="3">Colleurs</option>
-        <option value="4">Professeurs</option>
+        <option value="4">Administratifs</option>
+        <option value="5">Professeurs</option>
       </select>
     </p>
-    <p class="ligne"><label for="matiere">Matière&nbsp;:</label>
-      <select name="matiere" id="matiere">
-        <option value="0">Toutes matières</option><?php echo $select_matieres; ?>
-
+    <p class="ligne"><label for="matieres">Matières&nbsp;:</label>
+      <select multiple name="matieres[]">
+<?php echo $select_matieres; ?>
+      </select>
+    </p>
+    <p class="ligne"><label for="saisie">Type de saisie&nbsp;:</label>
+      <select name="saisie">
+        <option selected value="1">Invitation électronique</option>
+        <option value="2">Saisie du mot de passe</option>
       </select>
     </p>
-    <p class="ligne"><label for="utilisateurs">Comptes à créer&nbsp;:</label></p>
+    <p class="ligne"><strong>Comptes à créer&nbsp;:</strong></p>
     <p>Écrire ci-dessous les nouveaux utilisateurs&nbsp;:</p>
     <ul>
       <li>Un utilisateur par ligne</li>
-      <li>Uniquement des utilisateurs de même type à chaque envoi</li>
-      <li>Pour chaque utilisateur de type élève, colleur ou professeur, deux possibilités&nbsp;:
-        <ul style="margin: 0;">
-          <li>3 valeurs séparées par des virgules&nbsp;: nom, prénom, adresse électronique</li>
-          <li>3 valeurs séparées par des points-virgules&nbsp;: nom, prénom, mot de passe</li>
-        </ul>
-        Dans le premier cas, un courriel sera envoyé à chaque utilisateur avec un lien pour générer le mot de passe. Dans le second cas, ce sera à vous de communiquer les mots de passe.
-      </li>
-      <li>Pour chaque compte invité, 2 valeurs séparées par des virgules&nbsp;: identifiant, mot de passe</li>
+      <li>Uniquement des utilisateurs de même type associés aux mêmes matières</li>
     </ul>
-    <textarea id="utilisateurs" name="utilisateurs" rows="10" cols="100" data-placeholder="Comptes élèves ou colleurs ou professeurs :<br>Nom1,Prénom1,Mail1<br>Nom2,Prénom2,Mail2<br>...<br>Comptes invités :<br>Identifiant1,Motdepasse1<br>Identifiant2,Motdepasse2<br>..."></textarea>
-    <input type="hidden" name="table" value="utilisateurs">
+    <p class="affichesiinvitation">Sur chaque ligne, vous devez écrire nom, prénom, adresse électronique (séparés par des virgules). Un courriel sera envoyé à chaque utilisateur avec un lien à validité permanente pour saisir le mot de passe. Ces comptes apparaîtront dans la catégorie «&nbsp;invitation&nbsp;» tant qu'ils n'auront pas finalisé leur inscription.</p>
+    <p class="affichesimotdepasse">Sur chaque ligne, vous devez écrire nom, prénom et mot de passe (séparés par des virgules). Les utilisateurs ne seront pas prévenus automatiquement de cette création de compte, ce sera à vous de le faire. Ils pourront modifier leur mot de passe s'ils le souhaitent. Ils ne pourront envoyer des courriels que s'ils saisissent une adresse électronique.</p>
+    <p class="affichesiinvite">Sur chaque ligne, vous devez écrire l'identifiant du compte et le mot de passe, séparés par une virgule. Vous pourrez ensuite communiquer ces coordonnées aux personnes concernées. Elles ne pourront pas modifier le mot de passe que vous avez choisi.</p>
+    <textarea name="listeutilisateurs" rows="10" cols="100"></textarea>
+    <p class="ligne mailenvoi"><label for="mailenvoi">Autorisation à l'envoi de courriel&nbsp;: </label><input type="checkbox" name="mailenvoi" value="1"></p>
+    <p class="ligne mailliste"><label for="mailliste">Apparition dans la liste visible des élèves&nbsp;: </label><input type="checkbox" name="mailliste" value="1"></p>
   </form>
 
-  <form id="form-prefs">
+  <form id="form-prefs" data-action="prefsglobales">
     <h3 class="edition">Réglages de la gestion des comptes</h3>
     <p class="ligne"><label for="autoriser">Autoriser les demandes de création de comptes&nbsp;: </label>
-      <input type="checkbox" id="autoriser" name="autoriser"<?php echo ( $creationinterdite ) ? '' : ' checked'; ?>>
+      <input type="checkbox" name="autoriser"<?php echo $creation_compte; ?>>
     </p>
-    <input type="hidden" name="creation" value="1">
-    <input type="hidden" name="table" value="utilisateurs">
-    <p class="ligne">Pour modifier les associations entre utilisateurs et matières, il faut vous rendre sur la page de <a href="matieres">gestion des matières</a>.</p>
-    <span id="noreload"></span>
+    <input type="hidden" name="creation_compte" value="1">
+    <p class="ligne">Pour modifier les associations entre utilisateurs et matières, il faut vous rendre sur la page de <a href="utilisateurs-matieres">gestion des associations utilisateurs-matières</a>.</p>
   </form>
+
+  <div id="form-edite">
+    <a class="icon-ok" title="Valider ces modifications"></a>
+    <h3 class="edition">Modifier un utilisateur</h3>
+    <form>
+      <p id="compteactif">Vous pouvez ici modifier le compte XXX, de type YYY. Ce compte est actif. L'utilisateur du compte ne sera pas automatiquement prévenu de vos modifications.</p>
+      <p id="comptedesactive">Vous pouvez ici modifier le compte XXX, de type YYY. Ce compte a été désactivé&nbsp;: la connexion à ce Cahier de Prépa par ce compte n'est pas possible.</p>
+      <p id="demande">Vous pouvez ici modifier la demande XXX, de type YYY. Cette demande n'a pas encore été validée, vous pourrez la valider après modification.</p>
+      <p id="invitation">Vous pouvez ici modifier l'invitation XXX, de type YYY. L'utilisateur de ce compte ne sera pas automatiquement prévenu de vos modifications. Attention, la modification de l'identifiant ou de l'adresse électronique rendra impossible la validation de l'invitation par l'utilisateur concerné.</p>
+      <p>Seules les valeurs modifiées seront prises en compte. Pour modifier l'adresse électronique, il est nécessaire de la saisir deux fois.</p>
+      <p class="ligne"><label for="prenom">Prénom&nbsp;: </label><input type="text" name="prenom" value="" size="50"></p>
+      <p class="ligne"><label for="nom">Nom&nbsp;: </label><input type="text" name="nom" value="" size="50"></p>
+      <p class="ligne"><label for="login">Identifiant&nbsp;: </label><input type="text" name="login" value="" size="50"></p>
+      <p class="ligne"><label for="mail1">Adresse électronique&nbsp;: </label><input type="email" name="mail1" value="" size="50"></p>
+      <p class="ligne"><label for="mail2">Confirmation (si modification)&nbsp;: </label><input type="email" name="mail2" value="" size="50"></p>
+      <p class="ligne"><label for="mailenvoi">Autorisation à l'envoi de courriel&nbsp;: </label><input type="checkbox" name="mailenvoi" value="1"></p>
+      <p class="ligne"><label for="mailexp">Nom d'expéditeur&nbsp;: </label><input type="text" name="mailexp" value="" size="50"></p>
+      <p class="ligne"><label for="mailcopie">Recevoir une copie des courriels envoyés&nbsp;: </label><input type="checkbox" name="mailcopie" value="1"></p>
+      <p class="ligne"><label for="mailliste">Apparition dans la liste visible des élèves&nbsp;: </label><input type="checkbox" name="mailliste" value="1"></p>
+    </form>
+  </div>
   
   <div id="aide-page">
     <h3>Aide et explications</h3>
     <p>Il est possible ici d'ajouter, de modifier et de supprimer des utilisateurs pouvant se connecter à ce Cahier de Prépa.</p>
-    <p>Les associations entre les utilisateurs et les matières sont à régler dans la page de <a href="matieres">gestion des matières</a>.</p>
+    <p>Les associations entre les utilisateurs et les matières sont à régler à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</p>
     <p>Les trois boutons généraux permettent de&nbsp;:</p>
     <ul>
       <li><span class="icon-ajoute"></span>&nbsp;: ouvrir un formulaire pour ajouter de nouveaux utilisateurs.</li>
@@ -221,43 +402,51 @@
       <li><span class="icon-prefs"></span>&nbsp;: ouvrir un formulaire pour autoriser ou interdire les demandes de création de compte.</li>
     </ul>
     <h4>Tableau récapitulatif</h4>
-    <p>Le tableau récapitulatif ci-dessous présente tous les utilisateurs existants, ordonnés par type puis par ordre alphabétique.</p>
-    <p>Les noms, prénoms et adresses électroniques des utilisateurs existants sont directement modifiables en cliquant sur le bouton <span class="icon-edite"></span> et en validant avec le bouton <span class="icon-ok"></span> qui apparaît alors.</p>
-    <p>La modification d'une adresse électronique entraîne l'envoi automatique d'un courriel, à l'adresse modifiée uniquement (pas à l'ancienne adresse). Vous ne serez pas en copie de ce courriel.</p>
-    <p>Les identifiants ne sont pas modifiables ici&nbsp;: l'utilisateur concerné ne pourrait plus se connecter. Chaque utilisateur (y compris vous) peut modifier son propre identifiant sur la page de ses <a href="prefs">préférences</a>.</p>
-    <p>Chaque utilisateur peut être supprimé à l'aide du bouton <span class="icon-supprime"></span> (une confirmation sera demandée). Attention, si vous supprimez votre compte, vous serez rapidement déconnecté et ne pourrez plus vous connecter à nouveau.</p>
-    <p>Attention&nbsp;: lors de la suppression d'un utilisateur (élève, colleur, professeur), les colles le concernant sont automatiquement supprimées. Ne supprimez pas un compte pour le recréer, modifiez-le directement.</p>
-    <p>Il n'est pas possible de changer le type d'un utilisateur (transformer un élève en colleur, etc.).</p>
+    <p>Le tableau général présente tous les utilisateurs existants, ordonnés par type puis par ordre alphabétique.</p>
+    <p>Les données particulières des utilisateurs (nom, prénom, identifiant, adresse électronique) sont modifiables en cliquant sur le bouton <span class="icon-edite"></span> qui ouvrira un formulaire.</p>
+    <p>Les autres boutons du tableau permettent une action directe&nbsp;: autoriser ou non l'envoi de courriel, désactiver un compte, supprimer un compte, valider une demande.</p>
+    <p>Les identifiants sont modifiables ici&nbsp;: n'oubliez pas alors de prévenir l'utilisateur, qui ne pourra plus se connecter tant que vous ne lui aurez pas signifié son nouvel identifiant.</p>
+    <h4>Modifications multiples</h4>
+    <p>Il est possible de réaliser une modification identique sur un certain nombre de comptes en cochant les cases en bout de ligne et en cliquant sur les boutons d'action situés sur les lignes d'entêtes. Seuls des comptes de même types peuvent être traités simultanément. Les boutons <span class="icon-cocher"></span> permettent de cocher l'ensemble des comptes d'un type donné.</p>
+    <h4>Suppression et désactivation</h4>
+    <p>Chaque utilisateur peut être supprimé à l'aide du bouton <span class="icon-supprime"></span> (une confirmation sera demandée). Attention, lors de la suppression d'un utilisateur (élève, colleur, professeur), les colles le concernant sont automatiquement supprimées. Ne supprimez surtout pas un compte pour le recréer, modifiez-le directement.</p>
+    <p>La désactivation d'un compte permet de supprimer la possibilité de l'utilisateur de se connecter tout en conservant ses données comme son adresse électronique et les colles réalisées. C'est donc l'opération à réaliser pour un élève parti en cours d'année, dont on veut conserver les déclaration de colles jusqu'en fin d'année.</p>
     <h4>Types d'utilisateurs</h4>
-    <p>Il existe quatre types d'utilisateurs&nbsp;:</p>
+    <p>Il existe cinq types d'utilisateurs&nbsp;:</p>
     <ul>
-      <li>Les <em>professeurs</em> peuvent modifier tout ce qui est réglable dans ce Cahier de Prépa&nbsp;: pages d'informations et informations générales, utilisateurs, groupes d'élèves, matières, planning annuel. Tous les professeurs ont les mêmes droits sur ces catégories (il n'y a pas d'&laquo;&nbsp;administrateur&nbsp;&raquo;). Ils peuvent être associés ou non à une ou plusieurs matières, et pouvoir alors modifier ce qui concerne spécifiquement ces matières&nbsp;: programmes de colles, cahier de texte, documents, notes de colles. Ils peuvent voir l'ensemble des notes de colles mises dans les matières associées, et les récupérer sous forme de fichier xls. Les professeurs peuvent aussi envoyer des courriels à tous les autres utilisateurs.</li>
-      <li>Les <em>colleurs</em> peuvent être associés ou non à une ou plusieurs matières (ils ne peuvent pas modifier leur liste des matières associées). Ils peuvent voir les contenus associés à ces matières et les contenus généraux, sauf les contenus protégés visibles seulement par les professeurs. Ils peuvent mettre des notes dans ces matières et voir leurs notes uniquement. Ils peuvent modifier leur identité. Ils peuvent aussi envoyer des courriels à tous les autres utilisateurs.</li>
-      <li>Les <em>élèves</em> peuvent être associés ou non à une ou plusieurs matières (ils ne peuvent pas modifier leur liste des matières associées). Ils peuvent voir les contenus associés à ces matières et les contenus généraux, sauf les contenus protégés visibles seulement par les colleurs ou les professeurs. Ils peuvent voir leurs notes de colles. Ils peuvent modifier leur identité. Ils ne peuvent pas envoyer de courriels.</li>
-      <li>Les <em>invités</em> sont des comptes prévus pour être éventuellement partagés entre plusieurs personnes. Une fois connecté, il est impossible de changer les paramètres du compte (identifiant, mot de passe, matières associées). Les invités peuvent voir les contenus associés à leurs matières et les contenus généraux marqués comme visibles sans autorisation ou pour tous les connectés.</li>
+      <li>Les <em>professeurs</em> peuvent modifier tout ce qui est réglable dans ce Cahier de Prépa&nbsp;: pages d'informations et informations générales, utilisateurs, groupes d'élèves, matières, planning annuel. Tous les professeurs ont les mêmes droits sur ces catégories (il n'y a pas d'&laquo;&nbsp;administrateur&nbsp;&raquo;). Ils peuvent être associés ou non à une ou plusieurs matières, et pouvoir alors modifier ce qui concerne spécifiquement ces matières&nbsp;: programmes de colles, cahier de texte, documents, notes de colles. Ils peuvent voir l'ensemble des notes de colles mises dans les matières associées, et les récupérer sous forme de fichier xls. </li>
+      <li>Les <em>administratifs</em> peuvent relever les notes de colles via une interface spécifique. Ils peuvent envoyer des courriels si les professeurs le décident.</li>
+      <li>Les <em>colleurs</em> peuvent être associés ou non à une ou plusieurs matières (ils ne peuvent pas modifier leur liste des matières associées). Ils peuvent voir les contenus associés à ces matières et les contenus généraux, si l'accès de ces contenus est autorisé. Ils peuvent mettre des notes dans ces matières et voir leurs notes uniquement. Ils peuvent envoyer des courriels si les professeurs le décident.</li>
+      <li>Les <em>élèves</em> peuvent être associés ou non à une ou plusieurs matières (ils ne peuvent pas modifier leur liste des matières associées). Ils peuvent voir les contenus associés à ces matières et les contenus généraux,  si l'accès de ces contenus est autorisé. Ils peuvent voir leurs notes de colles. Ils peuvent modifier leur identité et leur mot de passe. Ils peuvent envoyer des courriels si les professeurs le décident.</li>
+      <li>Les <em>invités</em> sont des comptes prévus pour être éventuellement partagés entre plusieurs personnes. Une fois connecté, il est impossible de changer les paramètres du compte (identifiant, mot de passe, matières associées). Les invités peuvent voir les contenus associés à leurs matières et les contenus généraux, si l'accès de ces contenus est autorisé.</li>
     </ul>
+    <p>Il n'est pas possible de changer le type d'un utilisateur (transformer un élève en colleur, etc.).</p>
   </div>
 
   <div id="aide-ajoute">
     <h3>Aide et explications</h3>
     <p>Ce formulaire permet de créer de nouveaux comptes utilisateurs. Il sera validé par un clic sur <span class="icon-ok"></span>, et abandonné (donc supprimé) par un clic sur <span class="icon-ferme"></span>.</p>
-    <p>Il est possible de créer simultanément autant de comptes que l'on le souhaite, mais tous les comptes créés simultanément doivent correspondre à un même type d'utilisateurs (invités, élèves, colleurs, professeurs).</p>
-    <p>Il est possible de restreindre les utilisateurs à une seule matière&nbsp;: ils ne pourront alors consulter que les contenus généraux et ceux de cette matière&nbsp;; pour les colleurs, ils pourront aussi mettre des notes, uniquement dans cette matière&nbsp;; pour les professeurs, ils pourront aussi modifier les propriétés générales du Cahier de Prépa et les contenus de cette matière.</p>
-    <p>L'association à une matière n'est pas définitive&nbsp;: il est possible de modifier (ajouter ou supprimer) les associations entre matières et utilisateurs à tout moment et pour tous les utilisateurs, sur la page de <a href="matieres">gestion des matières</a>. On peut alors notamment associer une deuxième matière à un utilisateur créé et associé à une seule.</p>
-    <p>Les comptes utilisateurs à créer doivent être écrits à raison d'un compte par ligne.</p>
-    <p>À chaque ligne, si les utilisateurs sont de type élèves, colleurs ou professeurs, il y a deux syntaxes possibles&nbsp;:</p>
+    <p>Il est possible de créer simultanément autant de comptes que l'on le souhaite, mais tous les comptes créés simultanément doivent correspondre à un même type d'utilisateurs (invités, élèves, colleurs, administratifs, professeurs).</p>
+    <h4>Matières</h4>
+    <p>Il est nécessaire de spécifier à quelle matières seront associés les utilisateurs (les mêmes pour les comptes créés en même temps). Toutes les matières peuvent être sélectionnées. Les ressources associées aux matières non associées seront interdites sans autre condition à ces nouveaux utilisateurs. Pour les colleurs et professeurs, les actions possibles seront restreintes aux matières associées. Pour les administratifs, la relève des colles est indépendante et se fait simultanément sur toutes les matières.</p>
+    <p>L'association à une matière n'est pas définitive, il est possible de modifier (ajouter ou supprimer) les associations entre matières et utilisateurs à tout moment à la <a href="utilisateurs-matieres">gestion utilisateurs-matières</a>.</p>
+    <p>Il est plus facile de <a href="matieres">créer les matières</a> avant de créer les comptes utilisateurs.</p>
+    <h4>Invitation ou saisie du mot de passe</h4>
+    <p>Deux méthodes d'inscription sont possibles&nbsp;:</p>
     <ul>
-      <li>3 valeurs séparées par des virgules&nbsp;: nom, prénom, adresse électronique. Un courriel d'invitation sera alors envoyé à l'adresse indiquée, avec un lien permettant de générer le mot de passe. L'utilisateur apparaîtra dans une case &laquo;&nbsp;invitations&nbsp;&raquo; dans le tableau récapitulatif. Attention, il ne sera pas possible de modifier cet utilisateur, de lui envoyer un mail ou de lui attribuer une note tant qu'il n'aura pas répondu à l'invitation. Il sera cependant possible de le supprimer.</li>
-      <li>3 valeurs séparées par des points-virgules&nbsp;: nom, prénom, mot de passe. Le compte est alors immédiatement créé et directement utilisable (pour lui envoyer un mail ou de lui attribuer une note par exemple). Il faudra par contre communiquer séparément à la personne concernée son identifiant et son mot de passe.</li>
+      <li>l'envoi d'invitation&nbsp;: il faut pour cela renseigner sur chaque ligne de la case de saisie nom, prénom et adresse électronique. Les utilisateurs recevront chacun un courriel avec un lien à usage unique et validité illimitée permettant de finaliser l'inscription en saisissant un mot de passe.</li>
+      <li>la saisie directe du mot de passe&nbsp;: il faut pour cela renseigner sur chaque ligne de la case de saisie nom, prénom et mot de passe. Vous devrez alors contacter et donner le mot de passe choisi à chaque nouvel utilisateur. Cette méthode est moins bonne sur le plan de la sécurité.</li>
     </ul>
-    <p>À chaque ligne, si les utilisateurs sont de type invités, il faut écrire l'identifiant et le mot de passe séparés par une virgule. À vous de communiquer ces données aux personnes à qui vous souhaiter donner l'accès.</p>
+    <p>Les données à saisir doivent séparées par des virgules, sans espaces, et à un compte par ligne.</p>
+    <p>Les élèves peuvent aussi s'inscrire seule en faisant une demande, disponible au niveau de l'identification.</p>
     <h4>Types d'utilisateurs</h4>
-    <p>Il existe quatre types d'utilisateurs&nbsp;:</p>
+    <p>Il existe cinq types d'utilisateurs&nbsp;:</p>
     <ul>
-      <li>Les <em>professeurs</em> peuvent modifier tout ce qui est réglable dans ce Cahier de Prépa&nbsp;: pages d'informations et informations générales, utilisateurs, groupes d'élèves, matières, planning annuel. Tous les professeurs ont les mêmes droits sur ces catégories (il n'y a pas d'&laquo;&nbsp;administrateur&nbsp;&raquo;). Ils peuvent être associés ou non à une ou plusieurs matières, et pouvoir alors modifier ce qui concerne spécifiquement ces matières&nbsp;: programmes de colles, cahier de texte, documents, notes de colles. Ils peuvent voir l'ensemble des notes de colles mises dans les matières associées, et les récupérer sous forme de fichier xls. Les professeurs peuvent aussi envoyer des courriels à tous les autres utilisateurs.</li>
-      <li>Les <em>colleurs</em> peuvent être associés ou non à une ou plusieurs matières (ils ne peuvent pas modifier leur liste des matières associées). Ils peuvent voir les contenus associés à ces matières et les contenus généraux, sauf les contenus protégés visibles seulement par les professeurs. Ils peuvent mettre des notes dans ces matières et voir leurs notes uniquement. Ils peuvent modifier leur identité. Ils peuvent aussi envoyer des courriels à tous les autres utilisateurs.</li>
-      <li>Les <em>élèves</em> peuvent être associés ou non à une ou plusieurs matières (ils ne peuvent pas modifier leur liste des matières associées). Ils peuvent voir les contenus associés à ces matières et les contenus généraux, sauf les contenus protégés visibles seulement par les colleurs ou les professeurs. Ils peuvent voir leurs notes de colles. Ils peuvent modifier leur identité. Ils ne peuvent pas envoyer de courriels.</li>
-      <li>Les <em>invités</em> sont des comptes prévus pour être éventuellement partagés entre plusieurs personnes. Une fois connecté, il est impossible de changer les paramètres du compte (identifiant, mot de passe, matières associées). Les invités peuvent voir les contenus associés à leurs matières et les contenus généraux marqués comme visibles sans autorisation ou pour tous les connectés.</li>
+      <li>Les <em>professeurs</em> peuvent modifier tout ce qui est réglable dans ce Cahier de Prépa&nbsp;: pages d'informations et informations générales, utilisateurs, groupes d'élèves, matières, planning annuel. Tous les professeurs ont les mêmes droits sur ces catégories (il n'y a pas d'&laquo;&nbsp;administrateur&nbsp;&raquo;). Ils peuvent être associés ou non à une ou plusieurs matières, et pouvoir alors modifier ce qui concerne spécifiquement ces matières&nbsp;: programmes de colles, cahier de texte, documents, notes de colles. Ils peuvent voir l'ensemble des notes de colles mises dans les matières associées, et les récupérer sous forme de fichier xls. </li>
+      <li>Les <em>administratifs</em> peuvent relever les notes de colles via une interface spécifique. Ils peuvent envoyer des courriels si les professeurs le décident.</li>
+      <li>Les <em>colleurs</em> peuvent être associés ou non à une ou plusieurs matières (ils ne peuvent pas modifier leur liste des matières associées). Ils peuvent voir les contenus associés à ces matières et les contenus généraux, si l'accès de ces contenus est autorisé. Ils peuvent mettre des notes dans ces matières et voir leurs notes uniquement. Ils peuvent envoyer des courriels si les professeurs le décident.</li>
+      <li>Les <em>élèves</em> peuvent être associés ou non à une ou plusieurs matières (ils ne peuvent pas modifier leur liste des matières associées). Ils peuvent voir les contenus associés à ces matières et les contenus généraux,  si l'accès de ces contenus est autorisé. Ils peuvent voir leurs notes de colles. Ils peuvent modifier leur identité et leur mot de passe. Ils peuvent envoyer des courriels si les professeurs le décident.</li>
+      <li>Les <em>invités</em> sont des comptes prévus pour être éventuellement partagés entre plusieurs personnes. Une fois connecté, il est impossible de changer les paramètres du compte (identifiant, mot de passe, matières associées). Les invités peuvent voir les contenus associés à leurs matières et les contenus généraux, si l'accès de ces contenus est autorisé.</li>
     </ul>
   </div>
   
@@ -269,7 +458,5 @@
 
   <p id="log"></p>
 <?php
-
-$mysqli->close();
 fin(true);
 ?>
