diff --git a/a_propos.html b/a_propos.html
index 0307e8c798fd7b613bf8de23fdcef29277418292..153b2cd66d5d6b3ab47cbdcd898902bac5b15877 100644
--- a/a_propos.html
+++ b/a_propos.html
@@ -41,14 +41,19 @@
       <h2>Cadre</h2>
 
       <p class=text-justify>
-        Cette page a été créée par Vincent Nivoliers (l'auteur de ces mots),
-        doctorant en informatique au LORIA (Nancy) à l'époque. Elle a été
-        utilisée dans le cadre d'un atelier <a
+        Cette page a été créée par Vincent Nivoliers,
+        doctorant en informatique au <a href="http://www.loria.fr">LORIA</a> 
+        (Nancy) à l'époque. Elle a été utilisée dans le cadre d'un atelier <a
         href="http://www.mathenjeans.fr">MAth.en.JEANS</a> avec des élèves de la
         sixième à la quatrième du <a
         href="http://www.ac-nancy-metz.fr/pres-etab/collchepfervillers/">
         Collège George Chepfer</a> encadrés par Louisette Hiriart et Christelle
-        Kunc pendant l'année scolaire 2011-2012. Le but de ces ateliers consiste
+        Kunc pendant l'année scolaire 2011-2012. Elle a ensuite été mise à jour 
+        depuis le <a href="http://liris.cnrs.fr">LIRIS</a> et <a 
+        href="http://www.univ-lyon1.fr">l'université de Lyon</a> en 2016 et dotée 
+        d'outils de programmation pour un second atelier au
+        <a href="http://www.lamartinierediderot.fr/">Lycée Lamartinière Diderot
+        </a> à Lyon encadré par Éric Koubi.  Le but de ces ateliers consiste
         à faire découvrir les mathématiques (et l'informatique) différemment, en
         mettant les élèves en situation de recherche. Le sujet que j'ai proposé
         aux élèves est fourni sur la page d'accueil. Les élèves et leurs
@@ -56,7 +61,7 @@
         href=http://www.mathenjeans.fr/content/article-explosurf-collège-chepfer-villers-lès-nancy
         >sur le site de l'association</a>. La liste des élèves impliqués dans le
         projet est fournie dans l'article. Je tiens à les remercier, ainsi que
-        leurs enseignantes, pour m'avoir invité dans leur classe, et pour leur
+        leurs enseignant.e.s, pour m'avoir invité dans leur classe, et pour leur
         motivation sans faille tout au long de l'année.
       </p>
 
@@ -75,7 +80,7 @@
           <li>une première case ;</li>
           <li>quel bord de la première case ;</li>
           <li>une seconde case ;</li>
-          <li>quel bords de la seconde case ;</li>
+          <li>quel bord de la seconde case ;</li>
           <li>orientation de la connexion.</li>
         </ul>
         L'orientation de la connexion indique en quelque sorte si la boussole
@@ -111,7 +116,7 @@
         utiliser pour la description de la planète correspondante. Ce fichier
         est nommé «&nbsp;explorer.php&nbsp;» dans l'<a
         href=explosurf_ressources.tar.gz>archive</a> fournie.  Dans le dossier
-        de la planète, un fichier «&nbsp;extra.html&nbsp;» peut être ajouté pour
+        de la planète, un fichier «&nbsp;extra.php&nbsp;» peut être ajouté pour
         rajouter une information supplémentaire en dessous du bloc de navigation
         de la planète. C'est par ce biais que la mission sur la planète Dédale a
         été ajoutée.
@@ -119,21 +124,55 @@
 
       <p class=text-justify>
         Pour ne pas avoir à gérer de base de données, de comptes utilisateur, et
-        pour pouvoir laisser sans risque le site ouvert à tous, le parcours de
-        la planète et les notes sont entièrement gérées côté client via du
-        javascript. Le code réalisant ce parcours est regroupé dans le fichier
-        «&nbsp;explorer.js&nbsp;». Ce choix de tout déléguer côté client a des
-        désavantages, en particulier celui de ne stocker les notes que via le
-        navigateur, il est donc presque impossible de transférer ses notes d'une
-        machine à l'autre et, selon la politique de vie privée du navigateur,
-        les notes peuvent être perdues lorsque le site est fermé. Ce choix
-        signifie également que le fichier XML décrivant la planète est lisible
-        par l'utilisateur et qu'il peut y accéder directement plutôt que de
-        parcourir la planète «&nbsp;à pieds&nbsp;». Il est également possible de
-        sauter à la case voulue en exécutant du code javascript supplémentaire.
-        Ces deux derniers défauts n'en sont cependant pas réellement à mon sens,
-        car je serais ravi de voir les enfants acquérir les connaissances
-        nécessaires à leur exploitation.
+        pour pouvoir laisser sans risque le site ouvert à tous, les données
+        d'utilisation (notes, programme) sont entièrement gérées côté client via
+        du javascript et le stockage local. Il est également possible de
+        sauvegardes ses données dans un fichier pour les transférer d'un poste à
+        un autre. Ces choix signifient également que le fichier XML décrivant la
+        planète est lisible par l'utilisateur et qu'il peut y accéder
+        directement plutôt que de parcourir la planète «&nbsp;à pieds&nbsp;». Il
+        est également possible de sauter à la case voulue en exécutant du code
+        javascript supplémentaire.  Ces deux derniers défauts n'en sont
+        cependant pas réellement à mon sens, car je serais ravi de voir les
+        enfants acquérir les connaissances nécessaires à leur exploitation. Le
+        code réalisé est séparé en quatre fichiers :
+        <ul>
+          <li>
+            <code>explorer.js</code> 
+            pour la gestion des planètes et la navigation
+          </li>
+          <li>
+            <code>notes.js</code> 
+            pour la gestion des notes et des variables du programme
+          </li>
+          <li>
+            <code>pgm_construction.js</code> 
+            pour l'interface de programmation
+          </li>
+          <li>
+            <code>pgm_compile.js</code> 
+            pour l'interprétation des programmes et leur exécution
+          </li>
+        </ul>
+      </p>
+
+      <p class=text-justify>
+        La conception du site a bénéficié d'outils ouverts pour la mise en page
+        et la gestion de certains aspects compliqués :
+        <ul>
+          <li>
+            <a href="http://getbootstrap.com">bootstrap</a> 
+            pour la mise en page
+          </li>
+          <li>
+            <a href="http://thednp.github.io/bootstrap.native">bootstrap-native</a> 
+            également pour la mise en page
+          </li>
+          <li>
+            <a href="http://rubaxa.github.io/Sortable">sortable</a>
+            pour la gestion du glisser / déposer dans le programme
+          </li>
+        </ul>
       </p>
 
       <h3>Description XML des planètes</h3>
diff --git a/explorer.php b/explorer.php
index 6f737735a846ae325e65bac62256b5e51c207bdf..5963eafd6720b3cb9facb00b22de90c657ed4626 100644
--- a/explorer.php
+++ b/explorer.php
@@ -250,6 +250,17 @@
             coller&nbsp;<span class="glyphicon glyphicon-paste"></span>.
             </p>
 
+            <h4>Sauvegarde</h4>
+            <p>
+            Les boutons 
+            sauvegarde&nbsp;<span class="glyphicon glyphicon-floppy-save"></span>
+            et
+            chargement&nbsp;<span class="glyphicon glyphicon-floppy-open"></span>
+            du panneau programme vous permettent de sauver vos programmes sous
+            différents noms et de les charger. Les programmes sauvegardés sont
+            disponibles sur toutes les planètes.
+            </p>
+
             <h4>Bibliothèque</h4>
 
             <p>
@@ -275,10 +286,63 @@
                 globales. Vous pouvez écrire&nbsp;&larr;, lire&nbsp;<span class="
                 glyphicon glyphicon-search"></span> ou supprimer&nbsp;<span class="
                 glyphicon glyphicon-trash"></span> des variables. Une variable
-                apparaîtra sous la forme <code>[ nom = valeur ]</code> dans les notes.
+                apparaîtra sous la forme <code>[ nom = valeur ]</code> dans les
+                notes. L'action de lire une variable peut être placée quasiment
+                partout, si la variable contient le bon type de données. Si une
+                variable contient une direction ou une couleur, la lire permet
+                de réaliser le déplacement. Si elle contient un nombre ou un
+                test, vous pouvez lire le contenu d'une variable dans un
+                emplacement réclamant un entier ou un test.
+              </li>
+              <li>
+                <span class="glyphicon glyphicon-random"></span> 
+                La catégorie <strong>contrôle</strong> vous permet de modifier
+                le comportement de votre programme en fonction de tests. Vous
+                pouvez également trouver des <em>boucles</em> qui vous
+                permettent de répéter plusieurs fois une partie des
+                instructions.
+              </li>
+              <li>
+                <span class="glyphicon glyphicon-ok"></span> 
+                La catégorie <strong>tests</strong> vous permet de réaliser des
+                tests : vérifier si deux valeurs sont identiques, les comparer,
+                vérifier l'existence de variables, ou le fait qu'une case est au
+                bord de la planète. Ces tests peuvent être fournis aux blocs de
+                la catégorie contrôle, ou enregistrés dans des variables.
+              </li>
+              <li>
+                <span class="glyphicon glyphicon-plus"></span> 
+                La catégorie <strong>arithmétique</strong> permet de réaliser
+                des opérations sur des nombres entiers : addition, soustraction,
+                multiplication et division. Attention, la division est entière :
+                elle vous donne le quotient de la division euclidienne.
+              </li>
+              <li>
+                <span class="glyphicon glyphicon-font"></span> 
+                La catégorie <strong>texte</strong> permet de manipuler du texte. 
+                Le trombone&nbsp;<span class="glyphicon glyphicon-paperclip"></span>
+                vous permet d'accoler deux mots en un seul. Vous pouvez en
+                particulier vous en servir pour créer des noms de variables du
+                type <code>voisin_droite</code> en accolant la chaîne
+                <code>voisin_</code> et la valeur d'une variable contenant une
+                direction. 
+                Le haut-parleur&nbsp;<span class="glyphicon glyphicon-bullhorn"></span>
+                permet au programme de vous afficher des messages d'information,
+                utiles pour étudier le comportement de votre programme.
               </li>
             </ul>
             </p>
+            
+            <h3>Sauvegarde de toutes les données</h3>
+
+            <p>
+            Si vous utilisez toujours le même navigateur sur le même appareil
+            pour accéder à Explosurf, vous retrouverez vos données. Vous pouvez
+            également sauvegarder et recharger l'ensemble de vos données (les
+            programmes et les notes en cours sur toutes les planètes ainsi que
+            les programmes sauvegardés) en utilisant les boutons correspondants
+            sur la barre de menu tout en haut.
+            </p>
 
           </div>
         </div>
@@ -431,14 +495,24 @@
             </div>
           </div>
         </div>
+        <div class="row hidden" id="log-panel">
+          <div class="col-xs-12">
+            <div class="alert alert-info" role="alert">
+              <button type="button" class="close" id="log-close">
+                <span aria-hidden="true">&times;</span>
+              </button>
+              <div id="log-msg"></div>
+            </div>
+          </div>
+        </div>
         <div class="row hidden" id="error-panel">
           <div class="col-xs-12">
-            <div class="alert alert-danger">
+            <div class="alert alert-danger alert-dismissible" role='alert'>
               <span class="glyphicon glyphicon-exclamation-sign"></span>
-              <span id="error-msg"></span>
               <button type="button" class="close" id="error-close">
                 <span aria-hidden="true">&times;</span>
               </button>
+              <span id="error-msg"></span>
             </div>
           </div>
         </div>
@@ -810,6 +884,12 @@
                       <li class="field"><input size=8 placeholder="fin" disabled></input> </li>
                     </ul>
                   </li>
+                  <li class="list-group-item pgm-command pgm-type-void cmd-str-log">
+                    <span class="glyphicon glyphicon-bullhorn"></span>
+                    <ul class="pgm-recv pgm-recv-msg">
+                      <li class="field"><input size=20 placeholder="message" disabled></input> </li>
+                    </ul>
+                  </li>
                 </ul>
                 <!--}}}-->
               </div>
diff --git a/js/EventHelpers.js b/js/EventHelpers.js
deleted file mode 100644
index 7198df0580e5aa74d9270c7f25c30b5a2e75f63f..0000000000000000000000000000000000000000
--- a/js/EventHelpers.js
+++ /dev/null
@@ -1,474 +0,0 @@
-/*******************************************************************************
- * This notice must be untouched at all times.
- *
- * This javascript library contains helper routines to assist with event 
- * handling consinstently among browsers
- *
- * EventHelpers.js v.1.3 available at http://www.useragentman.com/
- *
- * released under the MIT License:
- *   http://www.opensource.org/licenses/mit-license.php
- *
- *******************************************************************************/
-var EventHelpers = new function(){
-    var me = this;
-    
-    var safariTimer;
-    var isSafari = /WebKit/i.test(navigator.userAgent);
-    var globalEvent;
-	
-	me.init = function () {
-		if (me.hasPageLoadHappened(arguments)) {
-			return;	
-		}
-		
-		if (document.createEventObject){
-	        // dispatch for IE
-	        globalEvent = document.createEventObject();
-	    } else 	if (document.createEvent) {
-			globalEvent = document.createEvent("HTMLEvents");
-		} 
-		
-		me.docIsLoaded = true;
-	}
-	
-    /**
-     * Adds an event to the document.  Examples of usage:
-     * me.addEvent(window, "load", myFunction);
-     * me.addEvent(docunent, "keydown", keyPressedFunc);
-     * me.addEvent(document, "keyup", keyPressFunc);
-     *
-     * @author Scott Andrew - http://www.scottandrew.com/weblog/articles/cbs-events
-     * @author John Resig - http://ejohn.org/projects/flexible-javascript-events/
-     * @param {Object} obj - a javascript object.
-     * @param {String} evType - an event to attach to the object.
-     * @param {Function} fn - the function that is attached to the event.
-     */
-    me.addEvent = function(obj, evType, fn){
-    
-        if (obj.addEventListener) {
-            obj.addEventListener(evType, fn, false);
-        } else if (obj.attachEvent) {
-            obj['e' + evType + fn] = fn;
-            obj[evType + fn] = function(){
-                obj["e" + evType + fn](self.event);
-            }
-            obj.attachEvent("on" + evType, obj[evType + fn]);
-        }
-    }
-    
-    
-    /**
-     * Removes an event that is attached to a javascript object.
-     *
-     * @author Scott Andrew - http://www.scottandrew.com/weblog/articles/cbs-events
-     * @author John Resig - http://ejohn.org/projects/flexible-javascript-events/	 
-     * @param {Object} obj - a javascript object.
-     * @param {String} evType - an event attached to the object.
-     * @param {Function} fn - the function that is called when the event fires.
-     */
-    me.removeEvent = function(obj, evType, fn){
-    
-        if (obj.removeEventListener) {
-            obj.removeEventListener(evType, fn, false);
-        } else if (obj.detachEvent) {
-            try {
-                obj.detachEvent("on" + evType, obj[evType + fn]);
-                obj[evType + fn] = null;
-                obj["e" + evType + fn] = null;
-            } 
-            catch (ex) {
-                // do nothing;
-            }
-        }
-    }
-    
-    function removeEventAttribute(obj, beginName){
-        var attributes = obj.attributes;
-        for (var i = 0; i < attributes.length; i++) {
-            var attribute = attributes[i]
-            var name = attribute.name
-            if (name.indexOf(beginName) == 0) {
-                //obj.removeAttributeNode(attribute);
-                attribute.specified = false;
-            }
-        }
-    }
-    
-    me.addScrollWheelEvent = function(obj, fn){
-        if (obj.addEventListener) {
-            /** DOMMouseScroll is for mozilla. */
-            obj.addEventListener('DOMMouseScroll', fn, true);
-        }
-        
-        /** IE/Opera. */
-        if (obj.attachEvent) {
-            obj.attachEvent("onmousewheel", fn);
-        }
-        
-    }
-    
-    me.removeScrollWheelEvent = function(obj, fn){
-        if (obj.removeEventListener) {
-            /** DOMMouseScroll is for mozilla. */
-            obj.removeEventListener('DOMMouseScroll', fn, true);
-        }
-        
-        /** IE/Opera. */
-        if (obj.detachEvent) {
-            obj.detatchEvent("onmousewheel", fn);
-        }
-        
-    }
-    
-	me.getMouseCoords = function (e) {
-		if (!e) {
-			return;
-		}
-		// IE
-		if (e.clientX != null) {
-			return {
-				x: e.clientX,
-				y: e.clientY
-			}
-		
-		}
-		// NS4
-		else if (e.pageX != null) {
-			return {
-				x: e.pageX,
-				y: e.pageY
-			}
-		// W3C
-		}  else if (window.event != null && window.event.clientX != null 
-				&& document.body != null && 
-				document.body.scrollLeft != null) {
-			return {
-				x: window.event.clientX + document.body.scrollLeft,
-				y: window.event.clientY + document.body.scrollTop
-			}
-					
-		} else { 
-			return null;
-		}
-	}
-	
-    /**
-     * Given a mouse event, get the mouse pointer's x-coordinate.
-     *
-     * @param {Object} e - a DOM Event object.
-     * @return {int} - the mouse pointer's x-coordinate.
-     */
-    me.getMouseX = function(e){
-        if (!e) {
-            return;
-        }
-        // NS4
-        if (e.pageX != null) {
-            return e.pageX;
-            // IE
-        } else if (window.event != null && window.event.clientX != null &&
-        document.body != null &&
-        document.body.scrollLeft != null) 
-            return window.event.clientX + document.body.scrollLeft;
-        // W3C
-        else if (e.clientX != null) 
-            return e.clientX;
-        else 
-            return null;
-    }
-    
-    /**
-     * Given a mouse event, get the mouse pointer's y-coordinate.
-     * @param {Object} e - a DOM Event Object.
-     * @return {int} - the mouse pointer's y-coordinate.
-     */
-    me.getMouseY = function(e){
-        // NS4
-        if (e.pageY != null) 
-            return e.pageY;
-        // IE
-        else if (window.event != null && window.event.clientY != null &&
-        document.body != null &&
-        document.body.scrollTop != null) 
-            return window.event.clientY + document.body.scrollTop;
-        // W3C
-        else if (e.clientY != null) {
-            return e.clientY;
-        }
-    }
-    /**
-     * Given a mouse scroll wheel event, get the "delta" of how fast it moved.
-     * @param {Object} e - a DOM Event Object.
-     * @return {int} - the mouse wheel's delta.  It is greater than 0, the
-     * scroll wheel was spun upwards; if less than 0, downwards.
-     */
-    me.getScrollWheelDelta = function(e){
-        var delta = 0;
-        if (!e) /* For IE. */ 
-            e = window.event;
-        if (e.wheelDelta) { /* IE/Opera. */
-            delta = e.wheelDelta / 120;
-            /** In Opera 9, delta differs in sign as compared to IE.
-             */
-            if (window.opera) {
-                delta = -delta;
-            }
-        } else if (e.detail) { /** Mozilla case. */
-            /** In Mozilla, sign of delta is different than in IE.
-             * Also, delta is multiple of 3.
-             */
-            delta = -e.detail / 3;
-        }
-        return delta
-    }
-    
-    /**
-     * Sets a mouse move event of a document.
-     *
-     * @deprecated - use only if compatibility with IE4 and NS4 is necessary.  Otherwise, just
-     * 		use EventHelpers.addEvent(window, 'mousemove', func) instead. Cannot be used to add
-     * 		multiple mouse move event handlers.
-     *
-     * @param {Function} func - the function that you want a mouse event to fire.
-     */
-    me.addMouseEvent = function(func){
-    
-        if (document.captureEvents) {
-            document.captureEvents(Event.MOUSEMOVE);
-        }
-        
-        document.onmousemove = func;
-        window.onmousemove = func;
-        window.onmouseover = func;
-        
-    }
-    
-    
-    
-    /** 
-     * Find the HTML object that fired an Event.
-     *
-     * @param {Object} e - an HTML object
-     * @return {Object} - the HTML object that fired the event.
-     */
-    me.getEventTarget = function(e){
-        // first, IE method for mouse events(also supported by Safari and Opera)
-        if (e.toElement) {
-            return e.toElement;
-            // W3C
-        } else if (e.currentTarget) {
-            return e.currentTarget;
-            
-            // MS way
-        } else if (e.srcElement) {
-            return e.srcElement;
-        } else {
-            return null;
-        }
-    }
-    
-    
-    
-    
-    /**
-     * Given an event fired by the keyboard, find the key associated with that event.
-     *
-     * @param {Object} e - an event object.
-     * @return {String} - the ASCII character code representing the key associated with the event.
-     */
-    me.getKey = function(e){
-        if (e.keyCode) {
-            return e.keyCode;
-        } else if (e.event && e.event.keyCode) {
-            return window.event.keyCode;
-        } else if (e.which) {
-            return e.which;
-        }
-    }
-    
-    
-    /** 
-     *  Will execute a function when the page's DOM has fully loaded (and before all attached images, iframes,
-     *  etc., are).
-     *
-     *  Usage:
-     *
-     *  EventHelpers.addPageLoadEvent('init');
-     *
-     *  where the function init() has this code at the beginning:
-     *
-     *  function init() {
-     *
-     *  if (EventHelpers.hasPageLoadHappened(arguments)) return;
-     *
-     *	// rest of code
-     *   ....
-     *  }
-     *
-     * @author This code is based off of code from http://dean.edwards.name/weblog/2005/09/busted/ by Dean
-     * Edwards, with a modification by me.
-     *
-     * @param {String} funcName - a string containing the function to be called.
-     */
-    me.addPageLoadEvent = function(funcName){
-    
-        var func = eval(funcName);
-        
-        // for Internet Explorer (using conditional comments)
-        /*@cc_on @*/
-        /*@if (@_win32)
-         pageLoadEventArray.push(func);
-         return;
-         /*@end @*/
-        if (isSafari) { // sniff
-            pageLoadEventArray.push(func);
-            
-            if (!safariTimer) {
-            
-                safariTimer = setInterval(function(){
-                    if (/loaded|complete/.test(document.readyState)) {
-                        clearInterval(safariTimer);
-                        
-                        /*
-                         * call the onload handler
-                         * func();
-                         */
-                        me.runPageLoadEvents();
-                        return;
-                    }
-                    set = true;
-                }, 10);
-            }
-            /* for Mozilla */
-        } else if (document.addEventListener) {
-            var x = document.addEventListener("DOMContentLoaded", func, null);
-            
-            /* Others */
-        } else {
-            me.addEvent(window, 'load', func);
-        }
-    }
-    
-    var pageLoadEventArray = new Array();
-    
-    me.runPageLoadEvents = function(e){
-        if (isSafari || e.srcElement.readyState == "complete") {
-        
-            for (var i = 0; i < pageLoadEventArray.length; i++) {
-                pageLoadEventArray[i]();
-            }
-        }
-    }
-    /**
-     * Determines if either addPageLoadEvent('funcName') or addEvent(window, 'load', funcName)
-     * has been executed.
-     *
-     * @see addPageLoadEvent
-     * @param {Function} funcArgs - the arguments of the containing. function
-     */
-    me.hasPageLoadHappened = function(funcArgs){
-        // If the function already been called, return true;
-        if (funcArgs.callee.done) 
-            return true;
-        
-        // flag this function so we don't do the same thing twice
-        funcArgs.callee.done = true;
-    }
-    
-    
-    
-    /**
-     * Used in an event method/function to indicate that the default behaviour of the event
-     * should *not* happen.
-     *
-     * @param {Object} e - an event object.
-     * @return {Boolean} - always false
-     */
-    me.preventDefault = function(e){
-    
-        if (e.preventDefault) {
-            e.preventDefault();
-        }
-        
-        try {
-            e.returnValue = false;
-        } 
-        catch (ex) {
-            // do nothing
-        }
-        
-    }
-    
-    me.cancelBubble = function(e){
-        if (e.stopPropagation) {
-            e.stopPropagation();
-        }
-        
-        try {
-            e.cancelBubble = true;
-        } 
-        catch (ex) {
-            // do nothing
-        }
-    }
-	
-	/* 
-	 * Fires an event manually.
-	 * @author Scott Andrew - http://www.scottandrew.com/weblog/articles/cbs-events
-	 * @author John Resig - http://ejohn.org/projects/flexible-javascript-events/	 * @param {Object} obj - a javascript object.
-	 * @param {String} evType - an event attached to the object.
-	 * @param {Function} fn - the function that is called when the event fires.
-	 * 
-	 */
-	me.fireEvent = function (element,event, options){
-		
-		if(!element) {
-			return;
-		}
-		
-	    if (document.createEventObject){
-	        /* 
-			var stack = DebugHelpers.getStackTrace();
-			var s = stack.toString();
-			jslog.debug(s);
-			if (s.indexOf('fireEvent') >= 0) {
-				return;
-			}
-			*/
-			return element.fireEvent('on' + event, globalEvent)
-			//jslog.debug('ss');
-			
-	    }
-	    //else{
-	    //    // dispatch for firefox + others
-	    //    globalEvent.initEvent(event, true, true); // event type,bubbling,cancelable
-	    //    return !element.dispatchEvent(globalEvent);
-	    //}
-}
-    
-    /* EventHelpers.init () */
-    function init(){
-        // Conditional comment alert: Do not remove comments.  Leave intact.
-        // The detection if the page is secure or not is important. If 
-        // this logic is removed, Internet Explorer will give security
-        // alerts.
-        /*@cc_on @*/
-        /*@if (@_win32)
-        
-         document.write('<script id="__ie_onload" defer src="' +
-        
-         ((location.protocol == 'https:') ? '//0' : 'javascript:void(0)') + '"><\/script>');
-        
-         var script = document.getElementById("__ie_onload");
-        
-         me.addEvent(script, 'readystatechange', me.runPageLoadEvents);
-        
-         /*@end @*/
-        
-    }
-    
-    init();
-}
-
-EventHelpers.addPageLoadEvent('EventHelpers.init');
diff --git a/js/fakeLocalStorage.js b/js/fakeLocalStorage.js
deleted file mode 100644
index 1661226c8df227a60fdb41b413144676a773ce1a..0000000000000000000000000000000000000000
--- a/js/fakeLocalStorage.js
+++ /dev/null
@@ -1,24 +0,0 @@
-if (!window.localStorage) {
-  window.localStorage = {
-    getItem: function (sKey) {
-      if (!sKey || !this.hasOwnProperty(sKey)) { return null; }
-      return unescape(document.cookie.replace(new RegExp("(?:^|.*;\\s*)" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*((?:[^;](?!;))*[^;]?).*"), "$1"));
-    },
-    key: function (nKeyId) { return unescape(document.cookie.replace(/\s*\=(?:.(?!;))*$/, "").split(/\s*\=(?:[^;](?!;))*[^;]?;\s*/)[nKeyId]); },
-    setItem: function (sKey, sValue) {
-      if(!sKey) { return; }
-      document.cookie = escape(sKey) + "=" + escape(sValue) + "; path=/";
-      this.length = document.cookie.match(/\=/g).length;
-    },
-    length: 0,
-    removeItem: function (sKey) {
-      if (!sKey || !this.hasOwnProperty(sKey)) { return; }
-      var sExpDate = new Date();
-      sExpDate.setDate(sExpDate.getDate() - 1);
-      document.cookie = escape(sKey) + "=; expires=" + sExpDate.toGMTString() + "; path=/";
-      this.length--;
-    },
-    hasOwnProperty: function (sKey) { return (new RegExp("(?:^|;\\s*)" + escape(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie); }
-  };
-  window.localStorage.length = (document.cookie.match(/\=/g) || window.localStorage).length;
-}
diff --git a/js/logger.js b/js/logger.js
deleted file mode 100644
index 45c0cdbe3c3b33c0ce53b87103f5fb1bc5571f7e..0000000000000000000000000000000000000000
--- a/js/logger.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * @licstart
- * This program is meant to write a log file in the element with "log" id
- *
- * Copyright (C) 2011 Vincent Nivoliers
- *
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.  If not, see http://www.gnu.org/licenses/gpl.html.
- * @licend
-*/
-
-var Logger = new function () {
-  var me = this ;
-  var muted = false ;
-  var buffer = ""
-  
-  me.clear_log = function() {
-    var logger = document.getElementById("log") ;
-    logger.value = "" ;
-  }
-
-  me.mute = function() {
-    muted = true ;
-  }
-
-  me.unmute = function() {
-    muted = false ;
-  }
-
-  me.flush = function() {
-    var logger = document.getElementById("log") ;
-    if(logger) {
-      logger.value += buffer ;
-      buffer = "" ;
-    }
-  }
-  
-  me.log = function(txt) {
-    if(!muted) {
-      buffer += txt ;
-      buffer += '\n'
-    }
-  }
-}
diff --git a/js/pgm_compiler.js b/js/pgm_compiler.js
index d843c8e0d2bd9be83eef910aae718977a50a98d6..4c7afe1f1bb03e77250f97bc918de55378bf253c 100644
--- a/js/pgm_compiler.js
+++ b/js/pgm_compiler.js
@@ -70,6 +70,18 @@ function get_str(data, error_target) {
   }
 }
 
+function get_msg(data, error_target) {
+  var str = typeof data === 'string' ? data : '' + data ;
+  if(str.match(/^[\w\u00C0-\u02B8\- ]+$/)) {
+    return str ;
+  } else {
+    return pgm_selector_error(
+        error_target,
+        "seules les lettre, les chiffres, l'espace et _ sont autorisés : " + str 
+        );
+  }
+}
+
 function receiver_compile(el) {
   //type of the receiver
   var type = el.className.match(/pgm-recv-(\S+)/)[1] ;
@@ -95,7 +107,11 @@ function receiver_compile(el) {
   var rec_compile = "" ;
   if(target.className.match(/field/)) {
     var err_target = selector_path_to_root(target.children[0]) ;
-    rec_compile = '"' + get_str(target.children[0].value, err_target) + '"' ;
+    if(type === 'str') {
+      rec_compile = '"' + get_str(target.children[0].value, err_target) + '"' ;
+    } else {
+      rec_compile = '"' + get_msg(target.children[0].value, err_target) + '"' ;
+    }
   }
   else {
     rec_compile = cmd_compile(target) ;
@@ -106,7 +122,7 @@ function receiver_compile(el) {
     return 'get_integer(' + rec_compile + ',' + err_target + ')' ;
   } else if (type === 'bool') {
     return 'get_boolean(' + rec_compile + ',' + err_target + ')' ;
-  } else if (type === 'str') {
+  } else if (type === 'str' || type === 'msg') {
     return rec_compile ;
   } else if (type === 'direction') {
     return 'check_direction(' + rec_compile + ',' + err_target + ')'
@@ -163,6 +179,23 @@ document.getElementById('error-close').addEventListener(
   pgm_clear_error
 ) ;
 
+var log_elt = document.getElementById('log-msg') ;
+var log_panel = document.getElementById('log-panel') ;
+
+function pgm_log(msg) {
+  log_elt.innerHTML = log_elt.innerHTML + msg + '<br/>' ;
+  Pgm.removeClass(log_panel, 'hidden') ;
+}
+
+function pgm_clear_log() {
+  log_elt.innerHTML = '' ;
+  Pgm.addClass(log_panel, 'hidden') ;
+}
+document.getElementById('log-close').addEventListener(
+  'click',
+  pgm_clear_log
+) ;
+
 /*}}}*/
 
 /* {{{ Color and direction ================================================== */
@@ -657,6 +690,13 @@ function command_concat(el) {
 
 pgm_elements['cmd-str-concat'] = command_concat ;
 
+function command_log(el) {
+  var msg = receiver_compile(el.children[1]) ;
+  return 'pgm_log(' + msg + ')' ;
+}
+
+pgm_elements['cmd-str-log'] = command_log ;
+
 /*}}}*/
 
 /* {{{ Compilation ========================================================== */
@@ -673,6 +713,7 @@ function hide_progress() {
 
 function run() {
   pgm_clear_error() ;
+  pgm_clear_log() ;
   var el = document.getElementById("pgm-main") ;
   show_progress() ;
   //timeout to allow the browser to update and show the bar
diff --git a/js/pgm_construction.js b/js/pgm_construction.js
index d17e521b108b74a686b20cac8201b33f5e054e36..182391b886c061eecfb6ffc139e573b308b39ce8 100644
--- a/js/pgm_construction.js
+++ b/js/pgm_construction.js
@@ -467,19 +467,19 @@ function deactivate_cloning(el) {
 
 function activate_copy_mode() {
   //list blocks and activate them
-  var pgm_blocks = main.getElementsByClassName('pgm-recv-void') ;
+  var pgm_blocks = pgm.getElementsByClassName('pgm-recv-void') ;
   [].forEach.call(pgm_blocks, activate_cloning) ;
   //list receivers and activate them
-  var pgm_receivers = main.getElementsByClassName('pgm-recv') ;
+  var pgm_receivers = pgm.getElementsByClassName('pgm-recv') ;
   [].forEach.call(pgm_receivers, activate_cloning) ;
 }
 
 function deactivate_copy_mode() {
   //list blocks and activate them
-  var pgm_blocks = main.getElementsByClassName('pgm-recv-void') ;
+  var pgm_blocks = pgm.getElementsByClassName('pgm-recv-void') ;
   [].forEach.call(pgm_blocks, deactivate_cloning) ;
   //list receivers and activate them
-  var pgm_receivers = main.getElementsByClassName('pgm-recv') ;
+  var pgm_receivers = pgm.getElementsByClassName('pgm-recv') ;
   [].forEach.call(pgm_receivers, deactivate_cloning) ;
 }
 
diff --git a/js/ui.js b/js/ui.js
deleted file mode 100644
index 6ff0e817fb334df3ca4d1fed4ece33c060434b4d..0000000000000000000000000000000000000000
--- a/js/ui.js
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * @licstart
- * This program provides some utilities to hide and show div elements
- * 
- * Copyright (C) 2011 Vincent Nivoliers
- *
- * This program is free software: you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation, either version 3 of the License, or (at your option) any later
- * version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.  If not, see http://www.gnu.org/licenses/gpl.html.
- * @licend
-*/
-
-/* {{{ tools*/
-
-function addClass(el,c) {
-  var re = new RegExp("(?:^|\\s)" + c + "(?!\\S)") ;
-  if(!el.className.match(re)) { 
-    el.className += ' ' + c ; 
-  }
-}
-
-function removeClass(el,c) {
-  var re = new RegExp("(?:^|\\s)" + c + "(?!\\S)") ;
-  el.className = el.className.replace( re , '' ) ;
-}
-
-function topOffset(el) {
-  return el.getBoundingClientRect().top 
-    + window.pageYOffset 
-    - el.ownerDocument.documentElement.clientTop ;
-}
-
-/*}}}*/
-
-/* {{{ copy mode button */
-
-function toggle_copy() {
-  var copy_switch = document.getElementById("desktop-ui-copy") ;
-  if(copy_switch.className.match(/active/)) {
-    removeClass(copy_switch, "active") ;
-    deactivate_copy_mode() ;
-  } else {
-    addClass(copy_switch, "active") ;
-    activate_copy_mode() ;
-  }
-}
-
-/*}}}*/
-
-/* {{{ mobile ui */
-
-function show_mobile_ui() {
-  var phone_toolbar = document.getElementById("phone-toolbar") ;
-  removeClass(phone_toolbar, "hidden") ;
-  var library_close = document.getElementById("btn-close-library") ;
-  removeClass(library_close, "hidden") ;
-  var block_adders_collection = document.getElementsByClassName('block-add') ;
-  //necessary, since it seems like appendChild messes with HTMLCollection
-  var block_adders = [].slice.call(block_adders_collection) ;
-  for(var i = 0; i < block_adders.length; ++i) {
-    var el = block_adders[i] ;
-    var el_parent = el.parentElement ;
-    removeClass(el, "hidden") ;
-    //replace adder at the end of the list
-    el_parent.appendChild(el) ;
-  } ;
-}
-
-function hide_mobile_ui() {
-  var phone_toolbar = document.getElementById("phone-toolbar") ;
-  addClass(phone_toolbar, "hidden") ;
-  var library_close = document.getElementById("btn-close-library") ;
-  addClass(library_close, "hidden") ;
-  var block_adders = document.getElementsByClassName('block-add') ;
-  [].forEach.call(block_adders, function(el) {
-    addClass(el, "hidden") ;
-  }) ;
-}
-
-function toggle_mobile() {
-  var mobile_switch = document.getElementById("desktop-ui-phone") ;
-  if(mobile_switch.className.match(/active/)) {
-    removeClass(mobile_switch, "active") ;
-    update_pgm_ui() ;
-  } else {
-    addClass(mobile_switch, "active") ;
-    update_pgm_ui() ;
-  }
-}
-
-function is_mobile() {
-  var mobile_switch = document.getElementById("desktop-ui-phone") ;
-  if(mobile_switch.className.match(/active/)) {
-    return true ;
-  }
-  var test_el = document.getElementById("mobile-tester") ;
-  return getComputedStyle(test_el ,null).display !== "none" ;
-}
-
-function phone_tools_set(id) {
-  var phone_toolbar = document.getElementById("phone-toolbar") ;
-  var btns = phone_toolbar.getElementsByTagName("button") ;
-  for(var i = 0; i < btns.length; ++i) {
-    if(btns[i].id == id) {
-      addClass(btns[i], "active") ;
-    } else {
-      removeClass(btns[i], "active") ;
-    }
-  }
-}
-
-function phone_tools_get() {
-  var phone_toolbar = document.getElementById("phone-toolbar") ;
-  var btns = phone_toolbar.getElementsByTagName("button") ;
-  for(var i = 0; i < btns.length; ++i) {
-    if(btns[i].className.match(/active/)) {
-      return btns[i].id ;
-    }
-  }
-}
-
-/*}}}*/
-
-/* {{{ library */
-
-function open_library() {
-  var elem = document.getElementById("program") ;
-  addClass(elem, "active") ;
-  /*
-  var t = topOffset(document.getElementById("library-open-btn")) + "px" ;
-  console.log(t) ;
-  document.getElementById("library").style.top = t ;
-  */
-}
-
-function close_library() {
-  var elem = document.getElementById("program") ;
-  removeClass(elem, "active") ;
-}
-
-/*}}}*/
-
-/* {{{ keyboard */
-
-function copy_detect(evt){
-  if(document.getElementsByClassName("sortable-ghost").length == 0) {
-    var evtobj=window.event? event : evt
-    if(evtobj.key === "Control") {
-      toggle_copy() ;
-    }
-  }
-}
-
-document.addEventListener("keydown", copy_detect) ;
-document.addEventListener("keyup", copy_detect) ;
-
-/*}}}*/