tite fractale

Modif cfcd4e1e

Date:   Tue Sep 23 22:04:49 2014 +0200

    MAJ MicroAlg (gros boulot à venir encore…).

diff --git a/input/microalg.l b/input/microalg.l
index c9f6e2a..e1628ae 100644
--- a/input/microalg.l
+++ b/input/microalg.l
@@ -1,75 +1,560 @@
 (put 'version 'text "MicroAlg version 0.1.0")
 (setq version "0.1.0")
-(setq apropos (pack
-"L’idée de MicroAlg est née lors d’une formation ISN, de la frustration due à "
-"l’abscence d’un langage vraiment axé sur la pédagogie.<br />"
-"À l’origine pensé comme DSL au sein de Tcl, il est finalement embarqué dans "
-"PicoLisp.<br />"
-"Voir <a href=\"http://microalg.info\">microalg.info</a>."))
+(setq apropos
+"L’idée de MicroAlg est née lors d’une formation ISN, de la frustration due à
+l’abscence d’un langage vraiment axé sur la pédagogie.  
+À l’origine pensé comme DSL au sein de Tcl, il est finalement embarqué dans
+PicoLisp.  
+Voir <http://microalg.info>.")
 (put 'apropos 'doc "Symbole contenant des informations sur MicroAlg.")

+(setq symboles '(
+"+" "-" "*" "/" "%"
+"!!!" "=" "=/" "<" "<=" ">" ">="
+"Affecter_a" "Afficher" "Aide" "Alors"
+"Booleen?"
+"Concatener"
+"Decimal@"
+"Definir" "Demander"
+"Entier@" "Et"
+"Faire"
+"Faux" "Faux?"
+"Initialiser"
+"Longueur"
+"Nieme" "Nombre" "Nombre?" "Non"
+"Ou"
+"Queue"
+"Retourner" "Rien"
+"Si" "Sinon"
+"Tant_que" "Tete" "Texte" "Texte?" "Type"
+"Vide?" "Vrai" "Vrai?"
+))
+
+# picolisp.l shimz
+(ifn caadr
+  (de caadr (arg)
+     (car (car (cdr arg)))
+  )
+)
+(ifn glue
+  (de glue (c l)
+    (cond
+      ((= 0 (length l)) "")
+      ((= 1 (length l)) (car l))
+      (T                (pack (car l) c  (glue c (cdr l))))
+    )
+  )
+)
+(ifn member
+  (de member (elt lst)
+    (let (result lst)
+         (while (not (or (not (<> elt (car result)))
+                         (not (n0 (length result)))))
+                (pop 'result))
+    result)
+  )
+)
+
+# Vérif et conversions de booléens MicroAlg <-> PicoLisp:
+(de !boolM2P (x) (if (<> Faux x) T NIL))
+(de !boolP2M (x) (if x Vrai Faux))
+(de !boolCheck (x) (if (member x '(Vrai Faux)) T))
+(de !boolCheckM2P (x msg)
+    (ifn (!boolCheck x) (quit msg x))
+    (!boolM2P x) )
+
+# Doc pour les fonctions PicoLisp
+(put '+ 'doc
+"Commande ajoute tous ses paramètres.")
+(put '- 'doc
+"Commande qui soustrait son second paramètre à son premier paramètre.")
+(put '* 'doc
+"Commande multiplie tous ses paramètres.")
+(put '/ 'doc
+"Commande qui divise son premier paramètre par son second paramètre.")
+(put '% 'doc
+"Commande qui donne le reste de la division euclidienne de son premier paramètre par son second paramètre.")
+
+# Commentaires
+(put '!!! 'doc
+"Commande qui ne fait rien et sert pour les commentaires.")
+(de !!! () Rien)
+
 # Aide
-(put 'Aide 'doc (pack
-"Commande qui permet d’obtenir de l’aide."))
-(de Aide syms (let truc (car syms)
-              (let doc (get truc 'doc)
-                  (cond
+(put 'Aide 'doc
+"Commande qui permet d’obtenir de l’aide.")
+(de Aide syms (let (truc (car syms)
+                    doc (get truc 'doc))
+                   (cond
                     ((== truc NIL) (get 'Aide 'text))
-                    (doc (pack "Aide pour `" truc "’:<br />" doc))
+                    ((== truc 'symboles) (glue " " symboles))
+                    (doc (pack "Aide pour `" truc "` :  ^J" doc))
                     ((eval truc) (pack "Pas d’aide pour " truc "."))
                     (T (pack "`" truc "’ n’existe pas."))
-                  ))))
-(put 'Aide 'text (pack
-"MicroAlg permet de s’initier à l’algorithmique et à la programmation.<br />"
-"Pour valider une commande, taper <code>Ctrl</code>+<code>Entrée</code>.<br />"
-"<ul>"
-"<li><code>(Aide commande)</code> pour de l’aide sur une commande,</li>"
-"<li><code>version</code> ou <code>apropos</code> pour des informations sur"
-"    MicroAlg.</li>"
-"<ul>"))
-
-# L’affichage se fait au travers de la variable globale *StdOut.
-(put 'Affecter_a 'doc (pack
-"Commande qui évalue son premier argument et l’affiche.<br />"
-"Attention, certains symboles particuliers comme `aide’ ou `version’ "
-"ont des comportements différents suivant qu’on les évalue, qu’on les "
-"affiche ou qu’on les appelle."))
-(setq *StdOut "")
+                   )))
+(put 'Aide 'text
+"MicroAlg permet de s’initier à l’algorithmique et à la programmation.  
+Si vous ne l’avez pas déjà fait, commencez par le
+[tutoriel](http://microalg.info/tuto.html).  
+
+* `(Aide symboles)` pour une liste des symboles prédéfinis,
+* `(Aide «quoi»)` (sans «») pour de l’aide sur quelque chose en particulier,
+* `version` ou `apropos` pour des informations sur MicroAlg.")
+
+# Afficher.
+# Avec EmuLisp, l’affichage se fait au travers de la fonction _stdPrint,
+# surchargée par stdPrint dans piljs ou ide_injection.js
+# On garde une trace du dernier affichage dans la variable globale *LastStdOut.
+(put 'Afficher 'doc
+"Commande qui évalue son premier argument et l’affiche.  
+Attention, certains symboles particuliers comme `aide` ou `version` ont des
+comportements différents suivant qu’on les évalue, qu’on les affiche ou qu’on
+les appelle.")
+(put 'Afficher 'text "Commande `Afficher`.")
+(setq *LastStdOut "?")
 (de Afficher args (let (first (car args)
-                        text (get first 'text)
+                        text (if (num? first) first (get first 'text))
                         a_afficher (if text text (eval first)))
-                       (prog
-                          (setq *StdOut (pack *StdOut a_afficher))
-                          (if text NIL a_afficher)
-                        )))
-(put 'Afficher 'text "Commande 'Afficher'.")
+                       (setq *LastStdOut (if a_afficher a_afficher "?"))
+                       (ifn !testing
+                            (println (ifn a_afficher
+                                          (if (<> *EMUENV "browser")
+                                              " "
+                                              " ")
+                                          a_afficher)))
+                       Rien  # Pas de valeur de retour
+                  )
+)
+
+# Concatener
+(put 'Concatener 'doc
+"Commande qui concatène les textes passés en paramètre.")
+(put 'Concatener 'text "Commande `Concatener`.")
+(de Concatener @ (let (
+                       result (pack (rest))
+                      )
+                      (if result
+                          result
+                          ""
+                      )))

 # Gestion des variables
-(put 'Affecter_a 'doc (pack
-"Commande qui permet d’affecter une valeur à une variable."))
-(de Affecter_a arg_lst (set (car arg_lst) (cadr arg_lst)))
+(put 'Initialiser 'doc
+"Commande qui permet d’initialiser une variable avec une valeur.")
+(put 'Initialiser 'text "Commande `Initialiser`.")
+(de Initialiser arg_lst (let (nbr_args (length arg_lst))
+                             (if (<> 2 nbr_args)
+                                 (quit (pack "Initialiser prend 2 paramètres. "
+                                             "Vous en donnez " nbr_args ".") )
+                             )  # Du coup un deuxième arg NIL ne peut être que "".
+                        )
+                        (let (var (car arg_lst)
+                              val (eval (cadr arg_lst))
+                              type_var (get var 'type)
+                              type_val (Type val))
+                             (if (and type_var (<> type_val type_var))
+                                 (quit (pack "Valeur de type " type_val ", ne peut initialiser "
+                                             var " qui est de type " type_var "."))
+                             )
+                             (set var val)
+                             (put var 'type (ifn val "texte" type_val))
+                             Rien  # Pas de valeur de retour
+                        ))
+(put 'Affecter_a 'doc
+"Commande qui permet d’affecter une valeur à une variable.")
+(put 'Affecter_a 'text "Commande `Affecter_a`.")
+(de Affecter_a arg_lst
+    (let (preums (car arg_lst)
+          deuz   (eval (cadr arg_lst))
+          troiz  (eval (caddr arg_lst))
+          var    preums
+          val    (ifn troiz deuz troiz)
+          idx    troiz)
+         (ifn idx
+              (prog
+                 # Affectation au symbole dans `var`.
+                 (ifn (get var 'type)
+                      (quit "Variable non initialisée." var)
+                 )
+                 (if (or (not val)  # Pour accepter "" qui est NIL.
+                         (<> Faux (= (get var 'type) (Type val))))
+                     (set var val)
+                     (quit "Le type ne correspond pas."
+                           (list var 'est 'un (get var 'type) '; val 'un (Type val))
+                     )
+                 )
+              )
+              (prog
+                 # Affectation à l’élément `idx` du symbole dans `var`.
+              )
+         )
+         Rien  # Pas de valeur de retour
+    )
+)
+
+# Tirages pseudo-aléatoires.
+(put 'Decimal@ 'doc
+"Retourne un nombre décimal pseudo-aléatoire dans [0;1[.
+
+Le suffixe `@` indique le caractère pseudo-aléatoire de cette commande.")
+(put 'Decimal@ 'text "Commande `Decimal@`.")
+(de Decimal@ () (/ (+ 2147483648 (rand)) 4294967296))
+(put 'Entier@ 'doc
+"Retourne un nombre entier pseudo-aléatoire dans [`min`;`max`].
+
+Le suffixe `@` indique le caractère pseudo-aléatoire de cette commande.")
+(put 'Entier@ 'text "Commande `Entier@`.")
+(de Entier@ arg_lst (let (nbr_args (length arg_lst))
+                         (if (<> 2 nbr_args)
+                             (quit (pack "`Entier@` prend 2 paramètres. "
+                                         "Vous en donnez " nbr_args ".") )
+                         )
+                    )
+                    (let (min (eval (car arg_lst))
+                          max (eval (cadr arg_lst)))
+                         (ifn (num? min) (quit "Le premier paramètre de `Entier@` doit être un nombre."))
+                         (ifn (num? max) (quit "Le second paramètre de `Entier@` doit être un nombre."))
+                         (rand min max)
+                    )
+)

 # Demander.
-(put 'Demander 'doc (pack
-"Commande qui permet de demander une valeur à l’utilisateur."))
+# Avec EmuLisp, les entrées utilisateur se font au travers de la fonction
+# _stdPrompt, surchargée dans ide_injection.js. Dans un navigateur, la dernière
+# ligne ayant été affichée précédemment est réutilisée lors de l’appel à
+# window.prompt.
+(put 'Demander 'doc
+"Commande qui permet de demander une valeur à l’utilisateur.")
+(put 'Demander 'text "Commande `Demander`.")
 (de Demander () (in NIL (read " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")))
 # Références:
 # https://fr.wikipedia.org/wiki/American_Standard_Code_for_Information_Interchange#Table_des_128_caract.C3.A8res_ASCII
 # https://en.wikipedia.org/wiki/ASCII#ASCII_printable_character_code_chart
 # >>> ''.join([chr(i) for i in range(32, 127) if not chr(i).isalnum()])
-# ' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
+# ' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'                                       "
+
+# Manipulations de texte
+(put 'Longueur 'doc
+"Commande qui retourne la longueur d’une liste.")
+(put 'Longueur 'text "Commande `Longueur`.")
+(de Longueur (obj)
+    (length obj))
+(put 'Nieme 'doc
+"Permet d’accéder en lecture à un des éléments d’une liste (se lit « énième »).
+
+La numérotation commence à 1.")
+(put 'Nieme 'text "Commande `Nieme`.")
+(de Nieme (obj idx)
+    # Quelques vérifications
+    (let (len (length obj))
+      (cond
+        ((not idx) (quit "Il manque le deuxième paramètre : l’indice."))
+        ((<> idx (format (round idx 0))) (quit "Indice non entier." idx))
+        ((le0 idx) (quit "Indice négatif ou nul." idx))
+        ((le0 (- (length obj) idx)) (quit "Indice trop grand." (list idx '> len)))
+      )
+    )
+    # Le calcul
+    (cond
+      ((str? obj) (car (nth (chop obj) idx)))
+      #((lst? obj) (nth obj idx))
+      (T Rien)
+    ))
+(put 'Tete 'doc
+"Commande qui retourne le premier élément d’une liste.")
+(put 'Tete 'text "Commande `Tete`.")
+(de Tete (obj)
+    # Quelques vérifications
+    (if (!boolM2P (Vide? obj))
+        (quit "Un objet vide n’a pas de tête.")
+    )
+    # Le calcul
+    (cond
+      ((str? obj) (car (chop obj)))
+      #((lst? obj) (car obj))
+      (T Rien)
+    ))
+(put 'Queue 'doc
+"Commande qui retourne une copie de la liste en paramètre, mais sans son premier élément.")
+(put 'Queue 'text "Commande `Queue`.")
+(de Queue (obj)
+    # Quelques vérifications
+    (if (!boolM2P (Vide? obj))
+        (quit "Un objet vide n’a pas de queue.")
+    )
+    # Le calcul
+    (cond
+      ((str? obj) (pack (cdr (chop obj))))
+      #((lst? obj) (cdr obj))
+      (T Rien)
+    ))
+
+# Types et conversions
+(put 'Type 'doc
+"Commande qui retourne le type de son paramètre (sous forme de texte et
+en minuscule).")
+(put 'Type 'text "Commande `Type`.")
+(de Type arg_lst
+  (let (!x (car arg_lst)
+        !evaled_x (eval !x))
+       (cond
+         ((<> Faux (Texte?   !evaled_x)) "texte")
+         ((<> Faux (Booleen? !evaled_x)) "booleen")
+         ((<> Faux (Nombre?  !evaled_x)) "nombre")
+         ((<> Faux (== Rien  !evaled_x)) "rien")
+         ((sym? x) (get !x 'type))
+         (T Rien)
+       ) ) )
+(put 'Texte 'doc
+"Commande qui retourne le texte correspondant à son paramètre.")
+(put 'Texte 'text "Commande `Texte`.")
+(de Texte (x) (cond
+                ((str? x) x)
+                ((num? x) (format x))
+                (T NIL)
+              ))
+(put 'Nombre 'doc
+"Commande qui retourne le nombre correspondant à son paramètre textuel.")
+(put 'Nombre 'text "Commande `Nombre`.")
+(de Nombre (x) (cond
+                ((num? x) x)
+                ((str? x) (or (format x) Rien))
+                (T NIL)
+              ))

 # Structure conditionnelle
-(put 'Vrai 'doc (pack "Booléen valant Vrai."))
+(put 'Vrai 'doc "Booléen valant Vrai.")
 (setq Vrai 'Vrai)
-(put 'Vrai 'doc (pack "Booléen valant Faux."))
+(put 'Faux 'doc "Booléen valant Faux.")
 (setq Faux 'Faux)
+(put 'Alors 'doc "Mot-clef intermédiaire pour la commande `Si`.")
+(setq Alors 'Alors)
+(put 'Sinon 'doc "Mot-clef intermédiaire pour la commande `Si`.")
+(setq Sinon 'Sinon)
+(put 'Si 'doc
+"Structure conditionnelle, de la forme `Si ... Alors ... Sinon ...` où le
+`Sinon` est facultatif.")
+(put 'Si 'text "Commande `Si`.")
 (de Si arg_lst (let (condition (car arg_lst)
-                     bloc_vrai (cadr arg_lst)
-                     bloc_faux (caddr arg_lst))
-                    (if (= Vrai (eval condition))
-                        (eval bloc_vrai) (eval bloc_faux))))
+                     kw_alors  (cadr arg_lst)
+                     splitted  (split (cddr arg_lst) 'Sinon)
+                     bloc_vrai (car splitted)
+                     bloc_faux (cadr splitted))
+                    (if (<> Alors kw_alors) (quit "Mot-clef `Alors` manquant." arg_lst))
+                    (if (!boolCheckM2P (eval condition) "La condition n’est pas un booléen.")
+                        (run bloc_vrai)
+                        (if (== NIL bloc_faux)
+                            Rien
+                            (run bloc_faux)))))
+
+# Opérations logiques
+(put 'Non 'doc
+"Non logique.
+
+Voir <http://fr.wikipedia.org/wiki/Fonction_NON>.")
+(put 'Non 'text "Commande `Non`.")
+(de Non (P)
+    (ifn (!boolCheck P) (quit "`Non` prend un paramètre booléen."))
+    (!boolP2M (not (!boolM2P P))) )
+(put 'Et 'doc
+"Et logique.
+
+Voir <http://fr.wikipedia.org/wiki/Fonction_ET>.")
+(put 'Et 'text "Commande `Et`.")
+(de Et (P Q)
+    (ifn (!boolCheck P) (quit "Le premier paramètre de `Et` doit être un booléen."))
+    (ifn (!boolCheck Q) (quit "Le second paramètre de `Et` doit être un booléen."))
+    (!boolP2M (and (!boolM2P P) (!boolM2P Q))) )
+(put 'Ou 'doc
+"Ou logique.
+
+Voir <http://fr.wikipedia.org/wiki/Fonction_OU>.")
+(put 'Ou 'text "Commande `Ou`.")
+(de Ou (P Q)
+    (ifn (!boolCheck P) (quit "Le premier paramètre de `Ou` doit être un booléen."))
+    (ifn (!boolCheck Q) (quit "Le second paramètre de `Ou` doit être un booléen."))
+    (!boolP2M (or (!boolM2P P) (!boolM2P Q))) )
+
+# Structures itératives
+(put 'Tant_que 'doc
+"Structure itérative de type « tant que ... faire ... ».")
+(put 'Tant_que 'text "Commande `Tant_que`.")
+(de Tant_que arg_lst (let (Bool (car arg_lst)
+                           Mot-Clef (cadr arg_lst)
+                           Corps (cddr arg_lst))
+                          (if (<> 'Faire Mot-Clef) (quit "Commande Tant_que sans mot-clé Faire."))
+                          (while (!boolCheckM2P (eval Bool) "La condition n’est pas un booléen.")
+                                 (run Corps))
+                          Rien
+                     )
+)
+(put 'Faire 'doc
+"Structure itérative de type « faire ... tant que ... ».")
+(put 'Faire 'text "Commande `Faire`.")
+(de Faire arg_lst (let (Splitted (split arg_lst 'Tant_que)
+                        Corps (car Splitted)
+                        Bool (caadr Splitted))
+                       (run Corps)
+                       (if (!boolCheckM2P (eval Bool) "La condition n’est pas un booléen.")
+                           (while (!boolCheckM2P (eval Bool) "La condition n’est pas un booléen.")
+                                  (run Corps)))
+                       Rien
+                  )
+)
+
+# Procédures utilisateur
+(put 'Definir 'doc
+"Permet de définir une nouvelle commande (fonction mathématique ou procédure).
+
+La syntaxe de cette commande est :
+
+    (Definir (Nom_de_la_nouvelle_commande arg1 arg2 ...)
+        \"Une phrase expliquant le rôle de cette commande.\"
+        \"L’auteur de cette commande.\"
+        (...    ici les     ...)
+        (...  instructions  ...)
+        (... de la commande ...)
+        (Retourner la_valeur_de_retour)
+    )
+
+Il peut être intéressant de signer ses créations avec le pseudo utilisé sur
+[les questions/réponses](http://qr.microalg.info).
+
+Exemple 1 :
+
+    (Definir (Double x)
+        \"Retourne le double du nombre (paramètre `x`).\"
+        \"anonyme\"
+        (Retourner (* 2 x))
+    )
+
+Exemple 2 :
+
+    (Definir (Crier texte)
+        \"Affiche le texte (paramètre `texte`) avec un point d’exclamation.\"
+        \"ProfGra\"
+        (Afficher (Concatener texte \" !\"))
+        (Retourner Rien)
+    )
+")
+(put 'Definir 'text "Commande `Definir`.")
+(de Definir arg_lst
+  (let (signature (car arg_lst)
+        nom       (car signature)
+        params    (cdr signature)
+        corps     (cdr arg_lst)
+        doc       (car corps)
+        auteur    (cadr corps))
+       (ifn (str? doc)
+            (put nom 'doc Rien)
+            (put nom 'doc (pack "`(" (str signature) ")` : " doc)))
+       (ifn (str? auteur) (setq auteur "un auteur anonyme"))
+       (put nom 'text (pack "Commande `"
+                            (str nom)
+                            "`, définie par *"
+                            auteur
+                            "*."))
+       (set nom (list params (list 'run (cons 'quote corps))))
+       Rien  # Pas de valeur de retour
+  )
+)
+(put 'Retourner 'doc
+"Dernière instruction du corps d’une commande définie avec `Definir`.
+Indique la valeur que cette commande va retourner.
+
+Utiliser `(Retourner Rien)` pour ne rien retourner.
+
+**Attention !** Cette instruction doit être la dernière de la commande créée car
+`Retourner` n’en interrompt pas l’exécution.")
+(put 'Retourner 'text "Commande `Retourner`.")
+(de Retourner (val) val)

 # Prédicats
-(de Vrai? (x) (if (= x Vrai) Vrai Faux))
-(de Faux? (x) (if (= x Faux) Vrai Faux))
+(put 'Vrai? 'doc
+"Prédicat retournant `Vrai` si la valeur de son argument est `Vrai`,
+et `Faux` sinon.")
+(put 'Vrai? 'text "Prédicat `Vrai?`.")
+(de Vrai? (x) (if (<> x Faux) Vrai Faux))
+(put 'Faux? 'doc
+"Prédicat retournant `Vrai` si la valeur de son argument est `Faux`,
+et `Faux` sinon.")
+(put 'Faux? 'text "Prédicat `Faux?`.")
+(de Faux? (x) (if (<> x Vrai) Vrai Faux))
+(put 'Texte? 'doc
+"Prédicat retournant `Vrai` si la valeur de son argument est du texte,
+et `Faux` sinon.")
+(put 'Texte? 'text "Prédicat `Texte?`.")
+(de Texte? (x) (if (str? x) Vrai Faux))
+(put 'Nombre? 'doc
+"Prédicat retournant `Vrai` si la valeur de son argument est un nombre,
+et `Faux` sinon.")
+(put 'Nombre? 'text "Prédicat `Nombre?`.")
+(de Nombre? (x) (if (num? x) Vrai Faux))
+(put 'Booleen? 'doc
+"Prédicat retournant `Vrai` si la valeur de son argument est un booleen,
+et `Faux` sinon.")
+(put 'Booleen? 'text "Prédicat `Booleen?`.")
+(de Booleen? (x) (if (or (== Vrai x) (== Faux x)) Vrai Faux))
+# Sans le suffixe '?'
+# Éviter le message « redefined »
+(setq = NIL)
+(setq < NIL)
+(setq <= NIL)
+(setq > NIL)
+(setq >= NIL)
+(put '= 'doc
+"Prédicat retournant `Vrai` si les valeurs de ses deux arguments sont égales,
+et `Faux` sinon.")
+(put '= 'text "Prédicat `=`.")
+(de = (x y) (ifn (<> x y) Vrai Faux))
+(put '=/ 'doc
+"Prédicat retournant `Vrai` si les valeurs de ses deux arguments sont
+différentes, et `Faux` sinon.")
+(put '=/ 'text "Prédicat `=/`.")
+(de =/ (x y) (if (<> x y) Vrai Faux))
+(put '< 'doc
+"Prédicat retournant `Vrai` si les valeurs de ses deux arguments sont
+dans l’ordre croissant (strictement), et `Faux` sinon.")
+(put '< 'text "Prédicat `<`.")
+(de < (x y) (ifn (ge0 (- x y)) Vrai Faux))
+(put '<= 'doc
+"Prédicat retournant `Vrai` si les valeurs de ses deux arguments sont
+dans l’ordre croissant.")
+(put '<= 'text "Prédicat `<=`.")
+(de <= (x y) (if (le0 (- x y)) Vrai Faux))
+(put '> 'doc
+"Prédicat retournant `Vrai` si les valeurs de ses deux arguments sont
+dans l’ordre décroissant (strictement), et `Faux` sinon.")
+(put '> 'text "Prédicat `>`.")
+(de > (x y) (ifn (le0 (- x y)) Vrai Faux))
+(put '>= 'doc
+"Prédicat retournant `Vrai` si les valeurs de ses deux arguments sont
+dans l’ordre décroissant.")
+(put '>= 'text "Prédicat `>=`.")
+(de >= (x y) (if (ge0 (- x y)) Vrai Faux))
+(put 'Vide? 'doc
+"Prédicat retournant `Vrai` si l’argument est considéré comme vide.
+
+* **Textes** : le seul texte vide est `\"\"`.
+")
+(put 'Vide? 'text "Prédicat `Vide?`.")
+(de Vide? (obj)
+    (if (<> 0 (length obj)) Faux Vrai))
+
+# Rien
+(put 'Rien 'doc
+"Valeur de contenant aucune information, représentant l’absence de valeur.")
+(setq Rien 'Rien)
+
+# Ignorer les balises de la galerie (http://galerie.microalg.info) si utilisées
+# par inadvertance, ou une fois de trop dans la galerie.
+(de MicroAlg arg_lst
+    (Afficher
+        (pack "L’instruction `(MicroAlg ...)` est une instruction fantôme.  ^J"
+              "Merci de la supprimer, elle ne sert que pour la galerie  ^J"
+              "(<http://galerie.microalg.info>).") ) )
+(de /MicroAlg arg_lst
+    (Afficher
+        (pack "L’instruction `(/MicroAlg)` est une instruction fantôme.  ^J"
+              "Merci de la supprimer, elle ne sert que pour la galerie  ^J"
+              "(<http://galerie.microalg.info>).") ) )
diff --git a/input/microalg_export.l b/input/microalg_export.l
new file mode 100644
index 0000000..5f869d5
--- /dev/null
+++ b/input/microalg_export.l
@@ -0,0 +1,9 @@
+(de proteger_litteraux src (proteger_litteraux_aux src))
+(de proteger_litteraux_aux (src)
+   (cond
+      ((num? src) (list 'Litteral src))
+      ((str? src) (list 'Litteral src))
+      ((sym? src) src)
+      (T          (mapcar 'proteger_litteraux_aux src))
+    )
+)
\ No newline at end of file
diff --git a/input/microalg_export_blockly.l b/input/microalg_export_blockly.l
new file mode 100644
index 0000000..c91f4af
--- /dev/null
+++ b/input/microalg_export_blockly.l
@@ -0,0 +1,95 @@
+(de Litteral (content)
+  (cond
+    ((num? content) (pack "<block type=\"nombre_litteral\"><field name=\"NUM\">"
+                          content
+                          "</field></block>"))
+    ((str? content) (pack "<block type=\"texte_litteral\"><field name=\"TEXT\">"
+                          content
+                          "</field></block>"))
+    (T "Littéral de type inconnu.")
+  ))
+(de insertion_next (src) (car (insertion_next_aux (reverse src))))
+(de insertion_next_aux (src)
+  (if (<= (length src) 1) src
+                          # Déplacement de la tête, enfermée dans un (Next ),
+                          # À la fin du deuxième élément.
+                          # La queue restant inchangée.
+                          (let (tete           (car src)
+                                deuxieme_instr (cadr src)
+                                reste          (cddr src))
+                               (queue 'deuxieme_instr (list 'Next tete))
+                               (insertion_next_aux
+                                 (cons
+                                   deuxieme_instr
+                                   reste)
+                               )
+                          )
+  ))
+(de Next (content)
+  (pack "<next>"
+        content
+        "</next>"))
+(de !!! (content next)
+  (pack "<block type=\"commentaire\"><field name=\"COMZ\">"
+        content
+        "</field>"
+        next
+        "</block>"))
+(de + (A B)
+   (pack "<block type=\"operations\"><field name=\"OP\">ADD</field>"
+         "<value name=\"A\">" A "</value>"
+         "<value name=\"B\">" B "</value>"
+         "</block>"))
+(de - (A B)
+   (pack "<block type=\"operations\"><field name=\"OP\">MINUS</field>"
+         "<value name=\"A\">" A "</value>"
+         "<value name=\"B\">" B "</value>"
+         "</block>"))
+(de * (A B)
+   (pack "<block type=\"operations\"><field name=\"OP\">MULTIPLY</field>"
+         "<value name=\"A\">" A "</value>"
+         "<value name=\"B\">" B "</value>"
+         "</block>"))
+(de / (A B)
+   (pack "<block type=\"operations\"><field name=\"OP\">DIVIDE</field>"
+         "<value name=\"A\">" A "</value>"
+         "<value name=\"B\">" B "</value>"
+         "</block>"))
+(de Afficher (content next)
+  (pack "<block type=\"afficher\">"
+        "<value name=\"VALUE\">"
+        content
+        "</value>"
+        next
+        "</block>"))
+(de Concatener inputs
+  (pack "<block type=\"concatener\">"
+        "<mutation items=\"" (length inputs) "\"></mutation>"
+        (mapcar '((i input) (pack "<value name=\"ADD" i "\">" (eval input) "</value>")) (range 0 (dec (length inputs))) inputs)
+        "</block>"))
+(de Demander ()
+  "<block type=\"demander\"></block>")
+(de Nombre (content)
+  (pack "<block type=\"nombre\">"
+        "<value name=\"VALUE\">"
+        content
+        "</value>"
+        "</block>"))
+(de Nombre? (content)
+  (pack "<block type=\"nombre?\">"
+        "<value name=\"VALUE\">"
+        content
+        "</value>"
+        "</block>"))
+(de Texte (content)
+  (pack "<block type=\"texte\">"
+        "<value name=\"VALUE\">"
+        content
+        "</value>"
+        "</block>"))
+(de Texte? (content)
+  (pack "<block type=\"texte?\">"
+        "<value name=\"VALUE\">"
+        content
+        "</value>"
+        "</block>"))
diff --git a/input/static/microalg_web/emulisp/emulisp_core.js b/input/static/microalg_web/emulisp/emulisp_core.js
index 0aee9ce..3987f15 100644
--- a/input/static/microalg_web/emulisp/emulisp_core.js
+++ b/input/static/microalg_web/emulisp/emulisp_core.js
@@ -1,22 +1,23 @@
-/* 13may14jk
+/* 15sep14jk
  * (c) Jon Kleiser
  */

 var EMULISP_CORE = (function () {

-var VERSION = [2, 0, 0, 3],
+var VERSION = [2, 0, 2, 0],
+   MONLEN = [31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
    BOXNAT_EXP = "Boxed native object expected",
    BOOL_EXP = "Boolean expected", CELL_EXP = "Cell expected", LIST_EXP = "List expected",
    NUM_EXP = "Number expected", SYM_EXP = "Symbol expected", VAR_EXP = "Variable expected",
    EXEC_OR_NUM_EXP = "Executable or Number expected",
+   CHANNEL_NOT_SUPPORTED = "EmuLisp only supports the NIL channel",
    BAD_ARG = "Bad argument", BAD_DOT = "Bad dotted pair", BAD_INPUT = "Bad input", DIV_0 = "Div/0",
    NOT_MAK = "Not making", PROT_SYM = "Protected symbol", UNDEF = "Undefined",
    JS_CTORNAME_EXP = "Constructor name expected", JS_RESERVED = "Reserved word";

-/** bla */
 function getFileSync(fileUrl) {
    var req = new XMLHttpRequest();
-   var OK = fileUrl.match(/^https?:/) ? 200 : 200;
+   var OK = fileUrl.match(/^file:/) ? 0 : 200;
    req.open("GET", fileUrl, false);        // synchronous
    if (req.overrideMimeType) req.overrideMimeType("text/plain; charset=utf-8");
    req.send(null);
@@ -29,6 +30,12 @@ function getFileSync(fileUrl) {
 var NILTYPE = 0, NUMBERTYPE = 1, SYMBOLTYPE = 2, CELLTYPE = 3, TRUETYPE = 4;

 Number.prototype.TYPEVAL = NUMBERTYPE;
+Function.prototype.TYPEVAL = NUMBERTYPE;
+
+function logLisp(msg, x) {
+   console.log("%s: %s", msg, x.toString());
+   return x;
+}

 function lispToStr(x) {
    //if (!confirm("lispToStr: " + x.toString() + ", " + x.TYPEVAL)) throw new Error("lispToStr aborted");
@@ -92,14 +99,14 @@ Cell.prototype.toValueString = function() {
 function Symbol(name, val) {
    this.name = name;
    this.trans = false;
-   this.cdr = (val === undefined) ? NIL : val;
+   this.car = (val === undefined) ? NIL : val; // Ersatz-like
    this.props = NIL;
 }

 function newTransSymbol(name) {
    var ts = new Symbol(name);
    ts.trans = true;
-   ts.cdr = ts;
+   ts.car = ts;    // Ersatz-like
    return ts;
 }

@@ -113,7 +120,7 @@ function box(val) {
 Symbol.prototype.TYPEVAL = SYMBOLTYPE;

 Symbol.prototype.getVal = function() {
-   return this.cdr;
+   return this.car;    // Ersatz-like
 }

 Symbol.prototype.valueOf = function() {
@@ -122,7 +129,7 @@ Symbol.prototype.valueOf = function() {

 Symbol.prototype.setVal = function(val) {
    if (this.lock) throw new Error(newErrMsg(PROT_SYM, this));
-   this.cdr = val;
+   this.car = val; // Ersatz-like
 }

 // Internal symbol names can consist of any printable (non-whitespace) character,
@@ -130,6 +137,7 @@ Symbol.prototype.setVal = function(val) {
 // It is possible, though, to include these special characters into symbol names
 // by escaping them with a backslash '\'.
 Symbol.prototype.escName = function() {
+   if (this.name instanceof Number) return this.name;
    var eName = this.name.replace(/\\/g, "\\\\");
    eName = eName.replace(/\"/g, "\\\"");
    eName = eName.replace(/\^/g, "\\^");
@@ -154,13 +162,13 @@ Symbol.prototype.toValueString = function() {

 Symbol.prototype.pushValue = function(val) {
    if (this.stack === undefined) this.stack = [];
-   this.stack.push(this.cdr);
-   this.cdr = val;
+   this.stack.push(this.car);  // Ersatz-like
+   this.car = val; // Ersatz-like
 }

 Symbol.prototype.popValue = function() {
-   var pv = this.cdr;
-   this.cdr = this.stack.pop();
+   var pv = this.car;  // Ersatz-like
+   this.car = this.stack.pop();    // Ersatz-like
    //if (this.stack.length === 0) delete this.stack;
    return pv;
 }
@@ -180,6 +188,21 @@ function setSymbolValue(s, val) {
    s.setVal(val);
 }

+function needVar(ex, x) {
+   if (x instanceof Number) throw new Error(newErrMsg(VAR_EXP, x));    // Ersatz-like
+   // TODO: handle ex
+}
+
+function indx(x, y) {  // Ersatz-like
+   var i = 1, z = y;
+   while (y instanceof Cell) {
+       if (eqVal(x, y.car)) return i;
+       ++i;
+       if (z === (y = y.cdr)) return 0;
+   }
+   return 0;
+}
+
 function Source(text, chars) {
    this.src = text;
    // character limitation for symbols
@@ -261,7 +284,7 @@ Source.prototype.getSymbolBeforePos = function(endPos) {

 var NIL = new Symbol("NIL");   NIL.car = NIL;  NIL.cdr = NIL;  NIL.props = NIL;
        NIL.lock = true; NIL.TYPEVAL = NILTYPE; NIL.bool = false;
-var T = new Symbol("T");   T.cdr = T;  T.lock = true; T.TYPEVAL = TRUETYPE; T.bool = true;
+var T = new Symbol("T");   T.car = T;  T.lock = true; T.TYPEVAL = TRUETYPE; T.bool = true;
 var A1 = new Symbol("@", NIL), A2 = new Symbol("@@", NIL), A3 = new Symbol("@@@", NIL);
 var ZERO = new Number(0), ONE = new Number(1);
 var gEmptyObj = {};
@@ -282,20 +305,12 @@ function prepareNewState(optionalState) {
        compExprArr: [],    // sort expression stack
        evFrames: NIL,
        trcIndent: "",
-       startupMillis: (new Date()).getTime()
+       startupMillis: Date.now()
    };
    QUOTE = getSymbol("quote");
    getSymbol("*EMUENV").setVal(new Symbol(emuEnv()));
 }

-//var gSym = {NIL: NIL, T: T, "@": A1, "@@": A2, "@@@": A3};   // dictionary/index for internal symbols
-//var gTrans = {}; // dictionary/index for transient symbols (strings)
-//var gParseCache = {};
-//var mk = []; // 'make' stack
-//var evFrames = NIL;
-//var gTrcIndent = "";
-//var startupMillis = (new Date()).getTime();
-
 function mkNew() { cst.mk.unshift({h: NIL, t: NIL}); }
 function linkc(c) {
    if (cst.mk.length === 0) throw new Error(newErrMsg(NOT_MAK));
@@ -303,13 +318,18 @@ function linkc(c) {
    if (cst.mk[0].h === NIL) { cst.mk[0].h = c; } else { cst.mk[0].t.cdr = c; }
    while (c.cdr !== NIL) { c = c.cdr; }; cst.mk[0].t = c; return c.car;
 }
-function link(x) {
-   if (cst.mk.length === 0) throw new Error(newErrMsg(NOT_MAK));
+function mkResult() { return cst.mk.shift().h; }
+
+function List() {
+   this.list = NIL;
+   this.last = NIL;
+}
+
+List.prototype.link = function(x) {
    var c = new Cell(x, NIL);
-   if (cst.mk[0].h === NIL) { cst.mk[0].h = c; } else { cst.mk[0].t.cdr = c; }
-   cst.mk[0].t = c; return x;
+   if (this.list === NIL) { this.list = c; } else { this.last.cdr = c; }
+   this.last = c;
 }
-function mkResult() { return cst.mk.shift().h; }

 function getString(str, editMode) {
    var s = (str in gEmptyObj) ? undefined : cst.tSym[str];
@@ -322,12 +342,10 @@ function getString(str, editMode) {

 function newErrMsg(msg, badValue) {
    getSymbol("*Msg").setVal(newTransSymbol(msg));
-   return (badValue === undefined) ? msg : lispToStr(badValue) + " -- " + msg;
+   return (badValue === undefined) ? msg + "" : lispToStr(badValue) + " -- " + msg;
 }

-function aTrue(val) { if (val !== NIL) { A1.pushValue(val); return true; } else return false; }
-
-function aPop(val) { A1.popValue(); return val; }
+function aTrue(val) { if (val !== NIL) { A1.setVal(val); return true; } else return false; }

 function car(c) { if (c.car) return c.car; else throw new Error(newErrMsg(LIST_EXP)); }
 function cdr(c) { if ((c instanceof Cell) || (c === NIL)) return c.cdr;
@@ -338,6 +356,13 @@ function numeric(val) {
    throw new Error(newErrMsg(NUM_EXP, val));
 }

+function validTime1970(y, m, d) {
+   numeric(y); numeric(m); numeric(d);
+   if (m<1 || m>12 || d<1 || d>MONLEN[m] && (m!=2 || d!=29 || y%4!=0 || y%100==0 && y%400!=0)) return null;
+   var ms1970 = Date.UTC(y, m - 1, d);
+   return (y >= 100) ? ms1970 : ms1970 - 59958144000000;
+}
+
 function nth(lst, n) {
    if (lst instanceof Cell) {
        if (n <= 0) return NIL;
@@ -373,9 +398,9 @@ function getAlg(c) {
                    do { s = s.cdr; } while ((s !== NIL) && (++k < 0));
                }
            } else {
-               return NIL;
+               s = NIL;
            }
-       } else throw new Error(newErrMsg(SYM_EXP));
+       } else throw new Error(newErrMsg(SYM_EXP, s));
        c = c.cdr;
    }
    return s;
@@ -399,7 +424,6 @@ function iter(c) {
        if (cond) {
            if (cMatch) {
                v = prog(cv.cdr.cdr);
-               if (cv.car === T) aPop(v);
                return {v: v, m: true};
            }
        } else v = evalLisp(cv);
@@ -422,7 +446,7 @@ function div(c, divFn) {
 }

 function eqVal(a, b) {
-   //alert("eqVal() " + a + ", " + b);
+   //console.log("eqVal(%s, %s)", a, b);
    if (a.TYPEVAL === b.TYPEVAL) {
        if (a === b) return true;
        if (a.TYPEVAL === CELLTYPE) {
@@ -517,9 +541,9 @@ function idxDelete(owner, v) {
    return tree;
 }

-function idxLinkSorted(tree) {
+function idxLinkSorted(tree, resList) {
    while (tree !== NIL)
-   { idxLinkSorted(tree.cdr.car); link(tree.car); tree = tree.cdr.cdr; }
+   { idxLinkSorted(tree.cdr.car, resList); resList.link(tree.car); tree = tree.cdr.cdr; }
 }

 /*
@@ -589,14 +613,15 @@ function cachedTextParse(str) {

 function unevalArgs(lst) {
    // Putting elements of lst into anonymous symbols
-   mkNew(); while (lst !== NIL) { link(box(lst.car)); lst = lst.cdr; }
-   return mkResult();
+   var a = new List(); while (lst !== NIL) { a.link(box(lst.car)); lst = lst.cdr; }
+   return a.list;
 }

 function applyFn(rawFn, lst, more) {
+   if (! (lst instanceof Cell)) lst = NIL;
    if (more !== NIL) {
-       mkNew(); do { link(evalLisp(more.car)); more = more.cdr; } while (more !== NIL);
-       cst.mk[0].t.cdr = lst; lst = mkResult();
+       var m = new List(); do { m.link(evalLisp(more.car)); more = more.cdr; } while (more !== NIL);
+       m.last.cdr = lst; lst = m.list;
    }
    var fn = evalLisp(rawFn); if (! (fn instanceof Symbol)) fn = box(fn);
    return evalLisp(new Cell(fn, unevalArgs(lst)));
@@ -608,6 +633,19 @@ function printx(c, x) { var arr = [];
    _stdPrint(arr.join(" ") + x); return c.car;
 }

+function rand(c, picoSize) {
+   var r = Math.random();  // range 0.0 .. 1.0
+   if (c === NIL) {
+       if (picoSize) r = Math.floor((2 * r - 1) * picoSize);
+       return new Number(r);
+   }
+   var n = evalLisp(c.car);
+   if (n === T) return (r < 0.5) ? NIL : T;
+   r = (-numeric(n) + numeric(evalLisp(c.cdr.car)) + (picoSize ? 1 : 0)) * r + n;
+   if (picoSize) r = Math.floor(r);
+   return new Number(r);
+}
+
 function ascending(a, b) { return ltVal(a, b) ? -1 : eqVal(a, b) ? 0 : 1; }
 //function descending(a, b) { return ltVal(a, b) ? 1 : eqVal(a, b) ? 0 : -1; }

@@ -619,14 +657,21 @@ function CompExpr(fn) {
 }

 CompExpr.prototype.evalTrue = function(a, b) {
-   this.arg1Sym.cdr = a;   // faster than this.arg1Sym.setVal(a);
-   this.arg2Sym.cdr = b;
+   this.arg1Sym.car = a;   // Ersatz-like, faster than this.arg1Sym.setVal(a);
+   this.arg2Sym.car = b;   // Ersatz-like
    return (evalLisp(this.expr) === T);
 }

 function lispFnOrder(a, b) { return cst.compExprArr[0].evalTrue(a, b) ? -1 : 1; }

 var coreFunctions = {
+   "and": function(c) { var v = NIL; while (c instanceof Cell) { v = evalLisp(c.car);
+           if (!aTrue(v)) return NIL; c = c.cdr; } return v;
+   },
+   "any": function(c) { var cv = evalLisp(c.car);
+       if (cv instanceof Symbol) return cachedTextParse(cv.valueOf()).car;
+       throw new Error(newErrMsg(SYM_EXP, cv));
+   },
    "apply": function(c) { return applyFn(c.car, evalLisp(c.cdr.car), c.cdr.cdr); },
    "arg": function(c) { var n = 0, f = cst.evFrames.car;
        if (c !== NIL) {
@@ -636,11 +681,12 @@ var coreFunctions = {
        return f.car;
    },
    "args": function(c) { return (cst.evFrames.car.cdr === NIL) ? NIL : T; },
-   "bench": function(c) { var t0 = (new Date()).getTime(), r = prog(c);
-       _stdPrint(((new Date()).getTime() - t0) / 1000 + " sec\n"); return r;
+   "atom": function(c) { return (evalLisp(c.car) instanceof Cell) ? NIL : T; },
+   "bench": function(c) { var t0 = Date.now(), r = prog(c);
+       _stdPrint((Date.now() - t0) / 1000 + " sec\n"); return r;
    },
+   "bool": function(c) { return (evalLisp(c.car) === NIL) ? NIL : T; },
    "box": function(c) { return box(evalLisp(c.car)); },
-   /** bye */
    "bye": function(c) { prog(getSymbol("*Bye").getVal());
        if (emuEnv() == "nodejs") { var prv = evalLisp(c.car);
            process.exit((prv instanceof Number) ? prv : 0); } else {
@@ -664,7 +710,7 @@ var coreFunctions = {
    },
    "cond": function(c) {
        while (c.car instanceof Cell) {
-           if (aTrue(evalLisp(c.car.car))) return aPop(prog(c.car.cdr));
+           if (aTrue(evalLisp(c.car.car))) return prog(c.car.cdr);
            c = c.cdr;
        }
        return NIL;
@@ -674,18 +720,32 @@ var coreFunctions = {
        while (c !== NIL) { var d = new Cell(t.cdr, evalLisp(c.car)); t.cdr = d; t = d; c = c.cdr; }
        return r;
    },
+   "date": function(c) { var MSPD = 86400000, D1970 = 719469, a1 = evalLisp(c.car), ms1970;
+       if ((c === NIL) || (a1 === T)) {
+           ms1970 = Date.now();
+           if (c === NIL) ms1970 -= (new Date()).getTimezoneOffset() * 60000;  // local (non-UTC)
+           return new Number(Math.floor(ms1970 / MSPD) + D1970);
+       }
+       if (a1 instanceof Cell) {
+           ms1970 = validTime1970(a1.car, a1.cdr.car,  a1.cdr.cdr.car);
+           return (ms1970 !== null) ? new Number(ms1970 / MSPD + D1970) : NIL;
+       }
+       if (a1 instanceof Number) {
+           if (c.cdr !== NIL) {
+               ms1970 = validTime1970(a1, evalLisp(c.cdr.car),  evalLisp(c.cdr.cdr.car));
+               return (ms1970 !== null) ? new Number(ms1970 / MSPD + D1970) : NIL;
+           }
+           var d = new Date((a1 - D1970) * MSPD);
+           return new Cell(new Number(d.getUTCFullYear()), new Cell(new Number(d.getUTCMonth() + 1),
+               new Cell(new Number(d.getUTCDate()), NIL)));
+       }
+       return NIL;
+   },
    "de": function(c) { var old = c.car.getVal();
        setSymbolValue(c.car, c.cdr);
        if ((old !== NIL) && !eqVal(c.cdr, old)) _warn("# " + c.car.valueOf() + " redefined");
        return c.car;
    },
-   "def": function(c) {
-       var name = evalLisp(c.car);
-       var old = name.getVal();
-       setSymbolValue(name, c.cdr);
-       if ((old !== NIL) && !eqVal(c.cdr, old)) _warn("# " + name.valueOf() + " redefined");
-       return name;
-   },
    "dec": function(c) {
        if (c === NIL) return NIL;
        var ns = evalLisp(c.car);
@@ -696,13 +756,13 @@ var coreFunctions = {
    "delete": function(c) { var a = evalLisp(c.car), lst = evalLisp(c.cdr.car);
        if (!(lst instanceof Cell)) return lst;
        if (eqVal(a, lst.car)) return lst.cdr;
-       mkNew(); link(lst.car); lst = lst.cdr;
+       var r = new List(); r.link(lst.car); lst = lst.cdr;
        while (lst instanceof Cell) {
-           if (eqVal(a, lst.car)) { cst.mk[0].t.cdr = lst.cdr; return mkResult(); }
-           link(lst.car); lst = lst.cdr;
+           if (eqVal(a, lst.car)) { r.last.cdr = lst.cdr; return r.list; }
+           r.link(lst.car); lst = lst.cdr;
        }
-       cst.mk[0].t.cdr = lst;  // taking care of dotted tail
-       return mkResult();
+       r.last.cdr = lst;   // taking care of dotted tail
+       return r.list;
    },
    "do": function(c) {
        var n = evalLisp(c.car);
@@ -760,20 +820,41 @@ var coreFunctions = {
        s.popValue();   if (s2 != null) s2.popValue();
        return v;
    },
+   "format": function(c) { var cv = evalLisp(c.car);
+       // Decimal and thousands separators not implemented yet:
+       // http://www.software-lab.de/doc/refF.html#format
+       if (cv instanceof Number) return newTransSymbol(cv);
+       if (cv.trans) {
+           return isNaN(cv.name)? NIL : new Number(parseFloat(cv.name));
+       }
+       return NIL;
+   },
+   "ge0": function(c) { var cv = evalLisp(c.car);
+       return ((cv instanceof Number) && (cv >= 0)) ? cv : NIL; },
    "get": function(c) { return getAlg(evalArgs(c)); },
    "getl": function(c) { var s = getAlg(evalArgs(c));
        if (s instanceof Symbol) return s.props;
        throw new Error(newErrMsg(SYM_EXP, s));
    },
+   "gt0": function(c) { var cv = evalLisp(c.car);
+       return ((cv instanceof Number) && (cv > 0)) ? cv : NIL; },
    "idx": function(c) { var s = evalLisp(c.car);
        if (!(s instanceof Symbol)) return NIL;
-       if (c.cdr === NIL) { mkNew(); idxLinkSorted(s.getVal()); return mkResult(); }
+       if (c.cdr === NIL) { var r = new List(); idxLinkSorted(s.getVal(), r); return r.list; }
        var a = evalLisp(c.cdr.car);
        if (c.cdr.cdr === NIL) return idxLookup(s, a);
        return (evalLisp(c.cdr.cdr.car) === NIL) ? idxDelete(s, a) : idxInsert(s, a);
    },
-   "if": function(c) { return aTrue(evalLisp(c.car)) ? aPop(evalLisp(c.cdr.car)) : prog(c.cdr.cdr); },
-   "ifn": function(c) { return aTrue(evalLisp(c.car)) ? aPop(prog(c.cdr.cdr)) : evalLisp(c.cdr.car); },
+   "if": function(c) { return aTrue(evalLisp(c.car)) ? evalLisp(c.cdr.car) : prog(c.cdr.cdr); },
+   "ifn": function(c) { return aTrue(evalLisp(c.car)) ? prog(c.cdr.cdr) : evalLisp(c.cdr.car); },
+   "in": function(c) { // For now only the NIL channel is supported, just for compat with the use of 'read'.
+       var chan = c.car;
+       if (chan === NIL) {
+           return prog(c.cdr);
+       } else {
+           throw new Error(newErrMsg(CHANNEL_NOT_SUPPORTED, chan));
+       }
+   },
    "inc": function(c) {
        if (c === NIL) return NIL;
        var ns = evalLisp(c.car);
@@ -781,6 +862,11 @@ var coreFunctions = {
        var v = new Number(ns.getVal() + ((c.cdr !== NIL) ? numeric(evalLisp(c.cdr.car)) : 1));
        ns.setVal(v); return v;
    },
+   "index": function(c) { var i = indx(evalLisp(c.car), evalLisp(c.cdr.car));
+       return (i === 0) ? NIL : new Number(i);
+   },
+   "le0": function(c) { var cv = evalLisp(c.car);
+       return ((cv instanceof Number) && (cv <= 0)) ? cv : NIL; },
    "length": function(c) { var cv = evalLisp(c.car), v = 0;
        if (cv instanceof Number) { v = cv.toString().length; }
        else if (cv instanceof Symbol) { v = cv.lock ? cv.toValueString().length :
@@ -825,35 +911,40 @@ var coreFunctions = {
    "loop": function(c) {
        var v = NIL; while (true) { var r = iter(c); v = r.v; if (r.m) break; }; return v;
    },
+   "lt0": function(c) { var cv = evalLisp(c.car);
+       return ((cv instanceof Number) && (cv < 0)) ? cv : NIL; },
    "make": function(c) { mkNew(); prog(c); return mkResult(); },
    "mapc": function(c) { var r = NIL, fn = evalLisp(c.car), ci = evalArgs(c.cdr);
        if (! (fn instanceof Symbol)) fn = box(fn);
-       while (ci.car !== NIL) { var cj = ci; mkNew();
-           while (cj !== NIL) { link(cj.car.car); cj.car = cj.car.cdr; cj = cj.cdr; }
-           r = evalLisp(new Cell(fn, unevalArgs(mkResult())));
+       while (ci.car !== NIL) { var cj = ci, a = new List();
+           while (cj !== NIL) { a.link(cj.car.car); cj.car = cj.car.cdr; cj = cj.cdr; }
+           r = evalLisp(new Cell(fn, unevalArgs(a.list)));
        }
        return r;
    },
-   "mapcar": function(c) { var fn = evalLisp(c.car), ci = evalArgs(c.cdr);
+   "mapcar": function(c) { var fn = evalLisp(c.car), ci = evalArgs(c.cdr), r = new List();
        if (! (fn instanceof Symbol)) fn = box(fn);
-       mkNew();
-       while (ci.car !== NIL) { var cj = ci; mkNew();
-           //if (!confirm(lispToStr(cj))) throw new Error("mapcar aborted");
-           while (cj !== NIL) { link(cj.car.car); cj.car = cj.car.cdr; cj = cj.cdr; }
-           link(evalLisp(new Cell(fn, unevalArgs(mkResult()))));
+       while (ci.car !== NIL) { var cj = ci, a = new List();
+           while (cj !== NIL) { a.link(cj.car.car); cj.car = cj.car.cdr; cj = cj.cdr; }
+           r.link(evalLisp(new Cell(fn, unevalArgs(a.list))));
        }
-       return mkResult();
+       return r.list;
    },
    "n0": function(c) { return eqVal(evalLisp(c.car), ZERO) ? NIL : T; },
    "next": function(c) { cst.evFrames.car = cst.evFrames.car.cdr; return cst.evFrames.car.car; },
    "not": function(c) { return (evalLisp(c.car) === NIL) ? T : NIL; },
+   "nT": function(c) { return (evalLisp(c.car) === T) ? NIL : T; },
    "nth": function(c) { var lst = evalArgs(c); c = lst.cdr;
        do { lst = nth(lst.car, numeric(c.car)); c = c.cdr; } while(c !== NIL); return lst; },
+   "num?": function(c) { var v = evalLisp(c.car); return (v instanceof Number) ? v : NIL; },
    "or": function(c) { while (c instanceof Cell) { var v = evalLisp(c.car);
-           if (v !== NIL) return v; c = c.cdr; } return NIL;
+           if (aTrue(v)) return v; c = c.cdr; } return NIL;
    },
    // pack has no support for circular lists, same as in PicoLisp
-   "pack": function(c) { return (c !== NIL) ? newTransSymbol(valueToStr(evalArgs(c))) : NIL; },
+   "pack": function(c) {
+       if (c !== NIL) { var s = valueToStr(evalArgs(c)); if (s !== "") return newTransSymbol(s); }
+       return NIL;
+   },
    "pass": function(c) { return applyFn(c.car, cst.evFrames.car.cdr, c.cdr); },
    "pop": function(c) { var cv = evalLisp(c.car);
        if (cv.getVal) {
@@ -864,6 +955,12 @@ var coreFunctions = {
        }
        throw new Error(newErrMsg(VAR_EXP, cv));
    },
+   "pre?": function(c) {
+       var s1 = valueToStr(evalLisp(c.car)), v2 = evalLisp(c.cdr.car), s2 = valueToStr(v2);
+       if (s1 !== s2.substring(0, s1.length)) return NIL;
+       // Handling Cell and Number like PicoLisp, not like Ersatz ...
+       return ((v2 instanceof Cell) || (v2 instanceof Number)) ? newTransSymbol(s2) : v2;
+   },
    "prin": function(c) {
        c = evalArgs(c); _stdPrint(c.toValueString());
        while (c.cdr !== NIL) { c = c.cdr; }; return c.car;
@@ -871,7 +968,7 @@ var coreFunctions = {
    "prinl": function(c) {
        c = evalArgs(c); _stdPrint(c.toValueString() + "\n");
        while (c.cdr !== NIL) { c = c.cdr; }; return c.car;
-   },  
+   },
    "print": function(c) { return printx(c, ""); },
    "println": function(c) { return printx(c, "\n"); },
    "printsp": function(c) { return printx(c, " "); },
@@ -884,10 +981,10 @@ var coreFunctions = {
        throw new Error(newErrMsg(VAR_EXP, t));
    },
    "put": function(c) {
-       var kc, vc;
-       c = evalArgs(c); mkNew();
-       do { link(c.car); kc = c.cdr; vc = kc.cdr; c = c.cdr; } while (vc.cdr !== NIL);
-       var s = getAlg(mkResult()), k = kc.car;
+       var kc, vc, a = new List();
+       c = evalArgs(c);
+       do { a.link(c.car); kc = c.cdr; vc = kc.cdr; c = c.cdr; } while (vc.cdr !== NIL);
+       var s = getAlg(a.list), k = kc.car;
        if (!(s instanceof Symbol)) throw new Error(newErrMsg(SYM_EXP, s));
        if (s === NIL) throw new Error(newErrMsg(PROT_SYM, s));
        if (eqVal(k, ZERO)) {
@@ -922,18 +1019,33 @@ var coreFunctions = {
        throw new Error(newErrMsg(VAR_EXP, s));
    },
    "quote": function(c) { return c; },
-   "rand": function(c) { var r = Math.random();
-       if (c === NIL) return new Number(r);    // range 0.0 .. 1.0
-       var n = evalLisp(c.car);
-       if (n === T) return (r < 0.5) ? NIL : T;
-       return new Number((-numeric(n) + numeric(evalLisp(c.cdr.car))) * r + n);
+   "quit": function(c) {
+       var value = evalLisp(c.cdr.car);
+       if (value == NIL) throw new Error(newErrMsg(evalLisp(c.car)));
+       else throw new Error(newErrMsg(evalLisp(c.car), value));
    },
+   "rand": function(c) { return rand(c, 2147483648); },
+   "randfloat": function(c) { return rand(c); },   // not std. PicoLisp
    "range": function(c) {
        var n = numeric(evalLisp(c.car)), n2 = numeric(evalLisp(c.cdr.car)), s = evalLisp(c.cdr.cdr.car);
        if (s === NIL) { s = 1; } else if (numeric(s) <= 0) throw new Error(newErrMsg(BAD_ARG, s));
        if (n > n2) s = -s;
-       mkNew(); do { link(n); n = new Number(n + s); } while ((s > 0) ? (n <= n2) : (n >= n2));
-       return mkResult();
+       var r = new List(); do { r.link(n); n = new Number(n + s); } while ((s > 0) ? (n <= n2) : (n >= n2));
+       return r.list;
+   },
+   "read": function(c) { // No support (yet) for the two parameters (non-split chars and comment char).
+       if (emuEnv() == 'nodejs') {
+           var readlinesync = require('readline-sync');
+           readlinesync.setPrompt("");
+           _stdPrompt = readlinesync.prompt;
+       } else {
+           if (typeof stdPrompt != "undefined") {
+               var _stdPrompt = stdPrompt;
+           } else {
+               var _stdPrompt = window.prompt;
+           }
+       }
+       return newTransSymbol(_stdPrompt());
    },
    "rest": function(c) { return cst.evFrames.car.cdr; },
    "reverse": function(c) { var lst = evalLisp(c.car), r = NIL;
@@ -941,6 +1053,28 @@ var coreFunctions = {
        do { r = new Cell(lst.car, r); lst = lst.cdr; } while (lst instanceof Cell);
        return r;
    },
+   "round": function(c) {
+       var len = evalLisp(c.cdr.car);
+       if (len == NIL) len = 3;
+       var power_of_ten = Math.pow(10, len);
+       var num = evalLisp(c.car);
+       return newTransSymbol((Math.round(num * power_of_ten) / power_of_ten).toString());
+   },
+   "run": function(c) {    // TODO: binding env. offset cnt
+       c = evalLisp(c.car);
+       // Reuse prog here.
+       var v = NIL; while (c instanceof Cell) { v = evalLisp(c.car); c = c.cdr; }; return v;
+   },
+   "set": function(c) {
+       var v = NIL;
+       while (c instanceof Cell) {
+           v = (c.cdr instanceof Cell) ? evalLisp(c.cdr.car) : NIL;
+           needVar(c, c.car);
+           evalLisp(c.car).car = v;
+           c = (c.cdr instanceof Cell) ? c.cdr.cdr : NIL;
+       }
+       return v;
+   },
    "setq": function(c) {
        var v = NIL;
        while (c instanceof Cell) {
@@ -949,7 +1083,7 @@ var coreFunctions = {
            c = (c.cdr instanceof Cell) ? c.cdr.cdr : NIL;
        }
        return v;
-   },  
+   },
    "sort": function(c) {
        var lst = evalLisp(c.car);
        if (lst instanceof Cell) {
@@ -967,6 +1101,26 @@ var coreFunctions = {
        }
        return lst;
    },
+   "space": function(c) { var n = 1, s;
+       if (c.car !== NIL) n = numeric(evalLisp(c.car));
+       for (s = ""; s.length < n; s += " ") {}
+       if (s > "") _stdPrint(s);
+       return new Number(n);
+   },
+   "split": function(c) {
+       var lst = evalLisp(c.car);
+       if (lst instanceof Cell) {
+           var x = c.cdr, arr = []; while (x !== NIL) { arr.push(evalLisp(x.car)); x = x.cdr; }
+           var r1 = new List(), r2 = new List();
+           do { var i; for (i=0; i<arr.length && !eqVal(lst.car, arr[i]); i++) {}
+               if (i<arr.length) { r1.link(r2.list); r2 = new List(); } else r2.link(lst.car);
+               lst = lst.cdr;
+           } while (lst instanceof Cell);
+           r1.link(r2.list);
+           return r1.list;
+       }
+       return lst;
+   },
    "str": function(c) {
        var cv = evalLisp(c.car);
        if (cv instanceof Symbol) {
@@ -982,7 +1136,9 @@ var coreFunctions = {
        if (cv === NIL) return NIL;
        throw new Error(newErrMsg(CELL_EXP, cv));
    },
+   "str?": function(c) { var v = evalLisp(c.car); return ((v instanceof Symbol) && v.trans) ? v : NIL; },
    "sym": function(c) { return newTransSymbol(evalLisp(c.car).toString()); },
+   "sym?": function(c) { return (evalLisp(c.car) instanceof Symbol) ? T : NIL; },
    "tail": function(c) {
        var cl = evalLisp(c.car), lst = evalLisp(c.cdr.car);
        if (cl instanceof Cell) {
@@ -1024,9 +1180,13 @@ var coreFunctions = {
        setSymbolValue(s, f);
        return s;
    },
-   "usec": function(c) { return new Number(((new Date()).getTime() - cst.startupMillis) * 1000); },
+   "usec": function(c) { return new Number((Date.now() - cst.startupMillis) * 1000); },
+   "val": function(c) { var x = evalLisp(c.car); needVar(c, x); return x.car; },
    "version": function(c) { if (!aTrue(evalLisp(c.car))) _stdPrint(VERSION.join(".") + " JS\n");
-       mkNew(); for (var i=0; i<VERSION.length; i++) { link(VERSION[i]); }; return mkResult(); },
+       var v = new List(); for (var i=0; i<VERSION.length; i++) { v.link(VERSION[i]); }; return v.list; },
+   "while": function(c) {
+       var v = NIL; while (aTrue(evalLisp(c.car))) { v = prog(c.cdr); }; return v;
+   },
    "yoke": function(c) { if (cst.mk.length === 0) throw new Error(newErrMsg(NOT_MAK));
        var tn = (cst.mk[0].t === NIL);
        do { var h = new Cell(evalLisp(c.car), cst.mk[0].h);
@@ -1071,10 +1231,13 @@ var coreFunctions = {
    "/": function(c) { return div(c, function(a, b) { return a / b; }); },  // floating point division
    "/t": function(c) { return div(c, function(a, b) { var d = a / b;
        return (d >= 0) ? Math.floor(d) : Math.ceil(d); }); },   // truncated division
+   "%": function(c) { return div(c, function(a, b) { return a % b; }); },
    "=": function(c) { var cv = evalLisp(c.car), d = c, dv;
        while (d.cdr !== NIL) { d = d.cdr; dv = evalLisp(d.car); if (!eqVal(cv, dv)) return NIL; }; return T; },
+   "=0": function(c) { return eqVal(evalLisp(c.car), ZERO) ? ZERO : NIL; },
    "==": function(c) { var cv = evalLisp(c.car), d = c, dv;
        while (d.cdr !== NIL) { d = d.cdr; dv = evalLisp(d.car); if (cv !== dv) return NIL; }; return T; },
+   "=T": function(c) { return (evalLisp(c.car) === T) ? T : NIL; },
    "<": function(c) { var cv = evalLisp(c.car), d = c, dv;
        while (d.cdr !== NIL) {
            d = d.cdr; dv = evalLisp(d.car); if (!ltVal(cv, dv)) return NIL;
@@ -1087,6 +1250,8 @@ var coreFunctions = {
            cv = dv;
        }; return T;
    },
+   "<>": function(c) { var cv = evalLisp(c.car), d = c, dv;
+       while (d.cdr !== NIL) { d = d.cdr; dv = evalLisp(d.car); if (!eqVal(cv, dv)) return T; }; return NIL; },
    ">": function(c) { var cv = evalLisp(c.car), d = c, dv;
        while (d.cdr !== NIL) {
            d = d.cdr; dv = evalLisp(d.car); if (!ltVal(dv, cv)) return NIL;
@@ -1150,14 +1315,14 @@ function evalDef(def, inExprLst) {
 }

 function evalLisp(lst) {
-   if (lst instanceof Symbol) return lst.cdr;
+   if (lst instanceof Symbol) return lst.car;  // Ersatz-like
    if (lst instanceof Cell) {
-       if (typeof lst.car.cdr === "function") {
-           return lst.car.cdr(lst.cdr);
+       if (typeof lst.car.car === "function") {
+           return lst.car.car(lst.cdr);    // Ersatz-like
        }
        if (lst.car instanceof Symbol) {
-           if (lst.car.cdr === NIL) throw new Error(newErrMsg(UNDEF, lst.car));
-           return evalDef(lst.car.cdr, lst.cdr);
+           if (lst.car.car === NIL) throw new Error(newErrMsg(UNDEF, lst.car));
+           return evalDef(lst.car.car, lst.cdr);   // Ersatz-like
        }
        if ((lst.car.car === QUOTE) && (lst.car.cdr instanceof Cell)) {
            return evalDef(lst.car.cdr, lst.cdr);
@@ -1197,10 +1362,10 @@ function loadJavaScript(fileUrl) {
 }

 function _stdPrint(text) {
-   if (typeof stdPrint === "function") stdPrint(text)
+   if (typeof stdPrint === "function") stdPrint(text, cst)
    else // when function stdPrint is not available in front end
    //if (!confirm("_stdPrint:\n" + text)) throw new Error("_stdPrint aborted");
-   console.log(text.replace(/\n$/g,''));  // last new line offered by console.log
+   console.log(text);
 }

 function _warn(msg) {
@@ -1276,9 +1441,9 @@ function symbolRefUrl(symbolName) {
    }
 }

-var pub = {    
+var pub = {
    init: function(optionalState) { prepareNewState(optionalState); },
-   
+
    currentState: function() { return cst; },

    forSymbolWithNameDefineFun: function(name, jsFn) {
@@ -1286,13 +1451,13 @@ var pub = {
        var sym = new Symbol(name, jsFn);
        cst.iSym[name] = sym;
    },
-   
+
    forSymbolWithNamePushValue: function(name, value) {
        var sym = getSymbol(name);
        sym.pushValue(value);
        return sym;
    },
-   
+
    forSymbolWithNameSetValue: function(name, value) {
        return getSymbol(name).setVal(value);
    },
@@ -1309,9 +1474,13 @@ var pub = {
    evalArgs: evalArgs, evalLisp: evalLisp, lispToStr: lispToStr, loadLisp: loadLisp, newErrMsg: newErrMsg,
    newTransSymbol: newTransSymbol, prog: prog, valueToStr: valueToStr, Params: Params,
    NIL: NIL, T: T,
+
+   getFileSync: getFileSync,

    eval: function(code) {
-       return prog(parseList(new Source(code))).toString();
+       var result = prog(parseList(new Source(code)));
+       A3.setVal(A2.getVal()); A2.setVal(A1.getVal()); A1.setVal(result);
+       return result.toString();
    }
 }

diff --git a/input/static/microalg_web/ide_injections.js b/input/static/microalg_web/ide_injections.js
index 6f99ca8..db8cba8 100644
--- a/input/static/microalg_web/ide_injections.js
+++ b/input/static/microalg_web/ide_injections.js
@@ -1,3 +1,68 @@
+// Load microalg.l.
+// http://stackoverflow.com/questions/984510/what-is-my-script-src-url
+var this_script_url = (function(scripts) {
+    var scripts = document.getElementsByTagName('script'),
+    script = scripts[scripts.length - 1];
+    if (script.getAttribute.length !== undefined) {
+        return script.src
+    }
+    return script.getAttribute('src', -1)
+}());
+var this_script_path = 'static/microalg_web/ide_injections.js';
+var root_path = this_script_url.slice(0, -this_script_path.length);
+var microalg_l_src =
+    EMULISP_CORE.getFileSync(root_path + 'microalg.l');
+var microalg_export_src =
+    EMULISP_CORE.getFileSync(root_path + 'microalg_export.l');
+var microalg_export_blockly_src =
+    EMULISP_CORE.getFileSync(root_path + 'microalg_export_blockly.l');
+
+// Editor states are stored with key = div id to print
+var emulisp_states = {};
+
+// The marvellous PicoLisp prompt:
+var malg_prompt = ": ";
+
+function cleanTransient(text) {
+    text = text.replace(/\^J/g,'\n');  // PicoLisp control char
+    text = text.replace(/\\"/g,'"');   // unescape double quotes
+    text = text.replace(/\n$/,'');     // remove last newline
+    if (text.charAt(0) == '"' && text.charAt(text.length-1) == '"') {
+        text = text.slice(1, -1);      // remove enclosing quotes
+    }
+    return text;
+}
+
+function stdPrint(text, state) {
+    var target = $('#' + state.context.display_elt);
+    text = cleanTransient(text);
+    if (state.context.type == 'editor') {
+        if (target.html() == " " && text != "") {
+            target.html("");            // clean the target
+        }
+        if (typeof Showdown != 'undefined') {
+            text = new Showdown.converter().makeHtml(text);
+        }
+        target.html(target.html() + text);
+    }
+    if (state.context.type == 'repl') {
+        var repl_elt = $('#' + state.context.display_elt);
+        if (text !== undefined && text != '' && text != 'NIL') {
+            repl_elt.val(repl_elt.val() + "\n" + text);
+        }
+    }
+    if (state.context.type == 'jrepl') {
+        state.context.term.echo(text);
+    }
+}
+
+function stdPrompt() {
+    var last_line_displayed = cleanTransient(EMULISP_CORE.eval('*LastStdOut'));
+    var user_input = window.prompt(last_line_displayed);
+    if (user_input !== null) return user_input;
+    else throw new Error("Opération 'Demander' annulée.")
+}
+
 function onCtrlEnter(elt, f) {
     elt.keydown(function (e) {
         if ((e.keyCode == 10 || e.keyCode == 13) && e.ctrlKey) {
@@ -6,13 +71,49 @@ function onCtrlEnter(elt, f) {
     });
 }

+function ide_action(editor_elt) {
+    // Compute the target HTML elt.
+    var elt_id = editor_elt.attr('id').slice(0, -('-malg-editor'.length));
+    var display_target_id = elt_id + '-displaytarget';
+    // Init the state and load it with MicroAlg.
+    EMULISP_CORE.init();
+    EMULISP_CORE.eval(microalg_l_src);
+    // Custom state for a custom display in the page.
+    EMULISP_CORE.currentState().context = {type: 'editor', display_elt: display_target_id};
+    // Process src.
+    var src = editor_elt.val();
+    // The editor is in a hiddable div,
+    // createRichInput put the editor in a sub div,
+    // that's why we use parent().parent().parent()
+    var error_elt = editor_elt.parent().parent().parent().find('.malg-error').first();
+    var display_elt = editor_elt.parent().parent().parent().find('.malg-display').first();
+    display_elt.html(' ');
+    try {
+        error_elt.text('');
+        EMULISP_CORE.eval(src);
+    } catch(e) {
+        error_elt.text(e.message);
+    }
+    EMULISP_CORE.eval('(setq *LastStdOut "?")');
+    if (typeof(Storage) !== "undefined") {
+        var key = 'microalg_src_' + elt_id;
+        localStorage[key] = src;
+    }
+}
+
 function inject_microalg_editor_in(elt_id, config, msg) {
+    // Build the html and bind to ide_action.
+    var display_target_id = elt_id + '-displaytarget';
     var script_container = $('#' + elt_id);
-    var script_string = '<textarea id="' + elt_id + '-malg-editor" class="malg-editor" cols="80" rows="2" >' + msg + '</textarea>' +
+    var hidden = config.hidden ? ' style="display:none;"' : '';
+    var script_string = '<div ' + hidden + '><textarea id="' + elt_id + '-malg-editor" ' +
+                        'class="malg-editor" cols="80" rows="2"' +
+                        'spellcheck="false">' + msg + '</textarea></div>' +
+            '<input type="button" onclick="ide_action($(\'#' + elt_id + '-malg-editor\'))" value="OK" class="malg-ok"/>' +
             '<div class="malg-error" style="color: red;"></div>' +
-            '<div class="malg-display"> </div>';
+            '<div id="' + display_target_id + '" class="malg-display"> </div>';
     script_container.html(script_string);
-    var editor = script_container.find('.malg-editor').first();
+    var editor = $('#' + elt_id + '-malg-editor');
     // Load local storage in the editor.
     if (config.localStorage && typeof(Storage)!=="undefined") {
         var key = 'microalg_src_' + elt_id;
@@ -22,64 +123,219 @@ function inject_microalg_editor_in(elt_id, config, msg) {
     }
     createRichInput(editor);
     onCtrlEnter(editor, ide_action);
-    function ide_action(editor_elt) {
-        var src = editor_elt.val();
-        // createRichInput put the editor in a sub div, that's why we use
-        // parent().parent()
-        var error_elt = editor_elt.parent().parent().find('.malg-error').first();
-        var display_elt = editor_elt.parent().parent().find('.malg-display').first();
-        display_elt.html(' ');
-        try {
-            EMULISP_CORE.eval(src).toString();
-            error_elt.text('');
-        } catch(e) {
-            error_elt.text(e.toString());
-        }
-        var stdout = EMULISP_CORE.currentState().iSym['*StdOut'].cdr.name;
-        if (stdout == '') {
-            stdout = ' ';
-        }
-        display_elt.html(stdout);
-        EMULISP_CORE.currentState().iSym['*StdOut'].cdr.name = '';
-        if (config.localStorage && typeof(Storage)!=="undefined") {
-            localStorage[key] = src;
+}
+
+function repl_action(repl_elt) {
+    // Fetch the relevant state.
+    EMULISP_CORE.init(emulisp_states[repl_elt.attr('id')]);
+    var result;
+    var repl_content = repl_elt.val();
+    var src = repl_content.slice(EMULISP_CORE.currentState().old_src.length,
+                                 repl_content.length);
+    try {
+        result = EMULISP_CORE.eval(src);
+    } catch(e) {
+        if (e.message == "Function 'bye' not supported") {
+            // Destroy the textarea (parent.parent is because of parenedit).
+            repl_elt.parent().parent().html('');
+            return;
+        } else {
+            repl_elt.val(repl_elt.val() + "\n" + e.message);
         }
     }
+    if (typeof result != "undefined" && result != '""') {
+        repl_elt.val(repl_elt.val() + "\n-> " + cleanTransient(result));
+    }
+    EMULISP_CORE.eval('(setq *LastStdOut "?")');
+    repl_elt.val(repl_elt.val() + "\n" + malg_prompt);
+    EMULISP_CORE.currentState().old_src = repl_elt.val();
 }

 function inject_microalg_repl_in(elt_id, msg) {
-    var malg_prompt = ": ";
+    // Compute the target HTML elt.
+    var repl_id = elt_id + '-malg-repl';
+    // Init the state and load it with MicroAlg.
+    EMULISP_CORE.init();
+    EMULISP_CORE.eval(microalg_l_src);
+    // Custom state for a custom display in the REPL.
+    EMULISP_CORE.currentState().context = {type: 'repl', display_elt: repl_id};
+    emulisp_states[repl_id] = EMULISP_CORE.currentState();
+    // Build the html and bind to ide_action.
     var repl_container = $('#' + elt_id);
-    var repl_string = '<textarea id="malg-repl" class="malg-repl" rows="2" >' + malg_prompt + msg + '</textarea>';
+    var rows = msg.split('\n').length;
+    var repl_string = '<textarea id="' + repl_id + '" class="malg-repl" rows="' + (rows+2) + '" spellcheck="false">' + malg_prompt + msg + '</textarea>' +
+        '<input type="button" onclick="repl_action($(\'#' + elt_id + '-malg-repl\'))" value="OK" class="malg-ok"/>';
     repl_container.html(repl_string);
-    var repl = repl_container.find('.malg-repl').first();
+    var repl = $('#' + repl_id);
     createRichInput(repl);
     onCtrlEnter(repl, repl_action);
-    var old_src = malg_prompt;
-    function repl_action(repl_elt) {
-        var result = '';
-        var repl_content = repl_elt.val();
-        var src = repl_content.slice(old_src.length, repl_content.length);
-        try {
-            result = EMULISP_CORE.eval(src).toString();
-        } catch(e) {
-            if (e.toString() == "Error: Function 'bye' not supported") {
-                repl_container.html('');
-            } else {
-                repl_elt.val(repl_elt.val() + "\n" + e.toString());
+    EMULISP_CORE.currentState().old_src = malg_prompt;
+}
+
+function inject_microalg_jrepl_in(elt_id, msg) {
+    $('#' + elt_id).terminal(function(command, term) {
+        if (command !== '') {
+            // Fetch the relevant state.
+            EMULISP_CORE.init(emulisp_states[elt_id]);
+            try {
+                var result = EMULISP_CORE.eval(command);
+                if (result != '""') {
+                    term.echo('-> ' + cleanTransient(result.toString()));
+                }
+            } catch(e) {
+                if (e.message == "Function 'bye' not supported") {
+                    term.destroy();
+                } else {
+                    term.error(e.message);
+                }
             }
+            EMULISP_CORE.eval('(setq *LastStdOut "?")');
         }
-        if (result != '' && result != 'NIL') {
-            repl_elt.val(repl_elt.val() + "\n-> " + result);
-            $.modal('<div class="web-ide">' + result + '</div>',
-                    {onClose: function (dialog) {$.modal.close();repl_elt.focus();}});
-        }
-        var stdout = EMULISP_CORE.currentState().iSym['*StdOut'].cdr.name;
-        if (stdout != '' && stdout != 'NIL') {
-            repl_elt.val(repl_elt.val() + "\n" + stdout);
+    }, {
+        greetings: msg,
+        name: 'malg_repl',
+        height: 150,
+        prompt: ': ',
+        clear: false,
+        exit: false,
+        keypress: function(e, term) {
+            // http://stackoverflow.com/questions/23817604/how-to-hook-on-keypress-and-grab-the-current-content-of-the-terminal
+            setTimeout(function() {
+                if (false) console.log(term.html());
+            }, 5);
+        },
+        onInit: function(term) {
+            // Init the state and load it with MicroAlg.
+            EMULISP_CORE.init();
+            EMULISP_CORE.eval(microalg_l_src);
+            // Custom state for a custom display in the REPL.
+            EMULISP_CORE.currentState().context = {type: 'jrepl', term: term};
+            emulisp_states[elt_id] = EMULISP_CORE.currentState();
+        },
+        keydown: function(e) {
+            if (e.which === 76 && e.ctrlKey) { // CTRL+L
+                return true;
+            }
         }
-        repl_elt.val(repl_elt.val() + "\n" + malg_prompt);
-        EMULISP_CORE.currentState().iSym['*StdOut'].cdr.name = '';
-        old_src = repl_elt.val();
+    });
+}
+
+function malg2blockly(src) {
+    EMULISP_CORE.init();
+    EMULISP_CORE.eval(microalg_export_src);
+    var litteraux_proteges = EMULISP_CORE.eval("(proteger_litteraux  " + src + ")");
+    EMULISP_CORE.eval(microalg_export_blockly_src);
+    var avec_des_next = EMULISP_CORE.eval("(insertion_next '" + litteraux_proteges + ")");
+    // Le car pour récupérer l’unique élément de la liste finale.
+    var xml = cleanTransient(EMULISP_CORE.eval('(pack (car ' + avec_des_next + ')'));
+    xml = '<xml xmlns="http://www.w3.org/1999/xhtml"><block type="programme"><value name="VALUE">' +
+          xml +
+          '</value></block></xml>';
+    EMULISP_CORE.init();
+    EMULISP_CORE.eval(microalg_l_src);
+    return xml;
+}
+
+function inject_microalg_blockly_in(elt_id, editor_id, msg) {
+    var blockly_container = $('#' + elt_id);
+    // Injection de HTML dans une iframe car besoin de plusieurs Blockly.
+    // http://stackoverflow.com/questions/13214419/alternatives-to-iframe-srcdoc
+    // Le code MicroAlg doit être sur une ligne pour passer dans le js généré:
+    if (typeof msg != "undefined") {
+        msg = msg.replace(/(\r\n|\n|\r)/gm, "");
+    } else {
+        msg = "";
+    }
+    // Ensuite le contenu de la toolbox:
+    var toolbox_string =
+            '<xml id="' + elt_id + '-toolbox" style="display: none">' +
+            ' <category name="Commandes">' +
+            '  <block type="commentaire"></block>' +
+            '  <block type="afficher"></block>' +
+            '  <block type="concatener"></block>' +
+            '  <block type="demander"></block>' +
+            '  <block type="operations"></block>' +
+            ' </category>' +
+            ' <category name="Autres">' +
+            '  <block type="texte_litteral"></block>' +
+            '  <block type="nombre_litteral"></block>' +
+            ' </category>' +
+            '</xml>';
+    // La page:
+    var content = '<!DOCTYPE html>' +
+'<html>\n' +
+'  <head>\n' +
+'    <meta charset="utf-8">\n' +
+'    <script type="text/javascript" src="web/blockly/blockly_compressed.js"></script>\n' +
+'    <script type="text/javascript" src="web/blockly_microalg.js"></script>\n' +
+'    <style>\n' +
+'      html, body {\n' +
+'        background-color: #fff;\n' +
+'        margin: 0;\n' +
+'        padding: 0;\n' +
+'        overflow: hidden;\n' +
+'        height: 100%;\n' +
+'      }\n' +
+'      .blocklySvg {\n' +
+'        height: 100%;\n' +
+'        width: 100%;\n' +
+'      }\n' +
+'    </style>\n' +
+'    <script>\n' +
+'      function init() {\n' +
+'        Blockly.inject(document.body,\n' +
+'            {path: "../../web/blockly/",\n' +
+'             comments: false,\n' +
+'             disable: false,\n' +
+'             toolbox: document.getElementById("' + elt_id + '-toolbox")});\n' +
+'        // Let the top-level application know that Blockly is ready.\n' +
+'        window.parent.blocklyLoaded(Blockly, "' + editor_id + '", \'' + msg + '\');\n' +
+'      }\n' +
+'    </script>\n' +
+'  </head>\n' +
+'  <body onload="init()">\n' + toolbox_string
+'  </body>\n' +
+'</html>';
+    // Création de l’iframe et injection.
+    var iframe_id = elt_id + '-iframe';
+    var style = 'seamless class="malg-blockly-iframe" scrolling="no"';
+    blockly_container.html('<iframe id="' + iframe_id + '" ' + style + '></iframe>');
+    var iframeDocument = document.querySelector('#' + iframe_id).contentWindow.document;
+    iframeDocument.open('text/html', 'replace');
+    iframeDocument.write(content);
+    iframeDocument.close();
+    // La suite se passe dans blocklyLoaded ci-dessous, une fois que chaque
+    // iframe est chargée.
+}
+
+function blocklyLoaded(blockly, editor_id, msg) {
+    if (typeof msg != 'undefined') {
+        var xml_text = malg2blockly(msg);
+        var xml = blockly.Xml.textToDom(xml_text);
+        blockly.Xml.domToWorkspace(blockly.mainWorkspace, xml);
     }
+    blockly.addChangeListener(function () {
+        var raw_src = blockly.MicroAlg.workspaceToCode();
+        // Ne garder que le code entre les marqueurs:
+        var src = /.*««««««««««([^]*)»»»»»»»»»».*/.exec(raw_src)[1];
+        var textarea = $('#' + editor_id);
+        textarea.val(src);
+        textarea.click();  // Trigger a parenedit redraw.
+    });
 }
+
+// http://www.sitepoint.com/jquery-set-focus-character-range/
+$.fn.selectRange = function(start, end) {
+    return this.each(function() {
+        if (this.setSelectionRange) {
+            this.focus();
+            this.setSelectionRange(start, end);
+        } else if (this.createTextRange) {
+            var range = this.createTextRange();
+            range.collapse(true);
+            range.moveEnd('character', end);
+            range.moveStart('character', start);
+            range.select();
+        }
+    });
+};
diff --git a/input/static/microalg_web/jquery.simplemodal.1.4.4.min.js b/input/static/microalg_web/jquery.simplemodal.1.4.4.min.js
deleted file mode 100644
index 382c736..0000000
--- a/input/static/microalg_web/jquery.simplemodal.1.4.4.min.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * SimpleModal 1.4.4 - jQuery Plugin
- * http://simplemodal.com/
- * Copyright (c) 2013 Eric Martin
- * Licensed under MIT and GPL
- * Date: Sun, Jan 20 2013 15:58:56 -0800
- */
-(function(b){"function"===typeof define&&define.amd?define(["jquery"],b):b(jQuery)})(function(b){var j=[],n=b(document),k=navigator.userAgent.toLowerCase(),l=b(window),g=[],o=null,p=/msie/.test(k)&&!/opera/.test(k),q=/opera/.test(k),m,r;m=p&&/msie 6./.test(k)&&"object"!==typeof window.XMLHttpRequest;r=p&&/msie 7.0/.test(k);b.modal=function(a,h){return b.modal.impl.init(a,h)};b.modal.close=function(){b.modal.impl.close()};b.modal.focus=function(a){b.modal.impl.focus(a)};b.modal.setContainerDimensions=
-function(){b.modal.impl.setContainerDimensions()};b.modal.setPosition=function(){b.modal.impl.setPosition()};b.modal.update=function(a,h){b.modal.impl.update(a,h)};b.fn.modal=function(a){return b.modal.impl.init(this,a)};b.modal.defaults={appendTo:"body",focus:!0,opacity:50,overlayId:"simplemodal-overlay",overlayCss:{},containerId:"simplemodal-container",containerCss:{},dataId:"simplemodal-data",dataCss:{},minHeight:null,minWidth:null,maxHeight:null,maxWidth:null,autoResize:!1,autoPosition:!0,zIndex:1E3,
-close:!0,closeHTML:'<a class="modalCloseImg" title="Close"></a>',closeClass:"simplemodal-close",escClose:!0,overlayClose:!1,fixed:!0,position:null,persist:!1,modal:!0,onOpen:null,onShow:null,onClose:null};b.modal.impl={d:{},init:function(a,h){if(this.d.data)return!1;o=p&&!b.support.boxModel;this.o=b.extend({},b.modal.defaults,h);this.zIndex=this.o.zIndex;this.occb=!1;if("object"===typeof a){if(a=a instanceof b?a:b(a),this.d.placeholder=!1,0<a.parent().parent().size()&&(a.before(b("<span></span>").attr("id",
-"simplemodal-placeholder").css({display:"none"})),this.d.placeholder=!0,this.display=a.css("display"),!this.o.persist))this.d.orig=a.clone(!0)}else if("string"===typeof a||"number"===typeof a)a=b("<div></div>").html(a);else return alert("SimpleModal Error: Unsupported data type: "+typeof a),this;this.create(a);this.open();b.isFunction(this.o.onShow)&&this.o.onShow.apply(this,[this.d]);return this},create:function(a){this.getDimensions();if(this.o.modal&&m)this.d.iframe=b('<iframe src="javascript:false;"></iframe>').css(b.extend(this.o.iframeCss,
-{display:"none",opacity:0,position:"fixed",height:g[0],width:g[1],zIndex:this.o.zIndex,top:0,left:0})).appendTo(this.o.appendTo);this.d.overlay=b("<div></div>").attr("id",this.o.overlayId).addClass("simplemodal-overlay").css(b.extend(this.o.overlayCss,{display:"none",opacity:this.o.opacity/100,height:this.o.modal?j[0]:0,width:this.o.modal?j[1]:0,position:"fixed",left:0,top:0,zIndex:this.o.zIndex+1})).appendTo(this.o.appendTo);this.d.container=b("<div></div>").attr("id",this.o.containerId).addClass("simplemodal-container").css(b.extend({position:this.o.fixed?
-"fixed":"absolute"},this.o.containerCss,{display:"none",zIndex:this.o.zIndex+2})).append(this.o.close&&this.o.closeHTML?b(this.o.closeHTML).addClass(this.o.closeClass):"").appendTo(this.o.appendTo);this.d.wrap=b("<div></div>").attr("tabIndex",-1).addClass("simplemodal-wrap").css({height:"100%",outline:0,width:"100%"}).appendTo(this.d.container);this.d.data=a.attr("id",a.attr("id")||this.o.dataId).addClass("simplemodal-data").css(b.extend(this.o.dataCss,{display:"none"})).appendTo("body");this.setContainerDimensions();
-this.d.data.appendTo(this.d.wrap);(m||o)&&this.fixIE()},bindEvents:function(){var a=this;b("."+a.o.closeClass).bind("click.simplemodal",function(b){b.preventDefault();a.close()});a.o.modal&&a.o.close&&a.o.overlayClose&&a.d.overlay.bind("click.simplemodal",function(b){b.preventDefault();a.close()});n.bind("keydown.simplemodal",function(b){a.o.modal&&9===b.keyCode?a.watchTab(b):a.o.close&&a.o.escClose&&27===b.keyCode&&(b.preventDefault(),a.close())});l.bind("resize.simplemodal orientationchange.simplemodal",
-function(){a.getDimensions();a.o.autoResize?a.setContainerDimensions():a.o.autoPosition&&a.setPosition();m||o?a.fixIE():a.o.modal&&(a.d.iframe&&a.d.iframe.css({height:g[0],width:g[1]}),a.d.overlay.css({height:j[0],width:j[1]}))})},unbindEvents:function(){b("."+this.o.closeClass).unbind("click.simplemodal");n.unbind("keydown.simplemodal");l.unbind(".simplemodal");this.d.overlay.unbind("click.simplemodal")},fixIE:function(){var a=this.o.position;b.each([this.d.iframe||null,!this.o.modal?null:this.d.overlay,
-"fixed"===this.d.container.css("position")?this.d.container:null],function(b,e){if(e){var f=e[0].style;f.position="absolute";if(2>b)f.removeExpression("height"),f.removeExpression("width"),f.setExpression("height",'document.body.scrollHeight > document.body.clientHeight ? document.body.scrollHeight : document.body.clientHeight + "px"'),f.setExpression("width",'document.body.scrollWidth > document.body.clientWidth ? document.body.scrollWidth : document.body.clientWidth + "px"');else{var c,d;a&&a.constructor===
-Array?(c=a[0]?"number"===typeof a[0]?a[0].toString():a[0].replace(/px/,""):e.css("top").replace(/px/,""),c=-1===c.indexOf("%")?c+' + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"':parseInt(c.replace(/%/,""))+' * ((document.documentElement.clientHeight || document.body.clientHeight) / 100) + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"',a[1]&&(d="number"===typeof a[1]?
-a[1].toString():a[1].replace(/px/,""),d=-1===d.indexOf("%")?d+' + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"':parseInt(d.replace(/%/,""))+' * ((document.documentElement.clientWidth || document.body.clientWidth) / 100) + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"')):(c='(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (t = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"',
-d='(document.documentElement.clientWidth || document.body.clientWidth) / 2 - (this.offsetWidth / 2) + (t = document.documentElement.scrollLeft ? document.documentElement.scrollLeft : document.body.scrollLeft) + "px"');f.removeExpression("top");f.removeExpression("left");f.setExpression("top",c);f.setExpression("left",d)}}})},focus:function(a){var h=this,a=a&&-1!==b.inArray(a,["first","last"])?a:"first",e=b(":input:enabled:visible:"+a,h.d.wrap);setTimeout(function(){0<e.length?e.focus():h.d.wrap.focus()},
-10)},getDimensions:function(){var a="undefined"===typeof window.innerHeight?l.height():window.innerHeight;j=[n.height(),n.width()];g=[a,l.width()]},getVal:function(a,b){return a?"number"===typeof a?a:"auto"===a?0:0<a.indexOf("%")?parseInt(a.replace(/%/,""))/100*("h"===b?g[0]:g[1]):parseInt(a.replace(/px/,"")):null},update:function(a,b){if(!this.d.data)return!1;this.d.origHeight=this.getVal(a,"h");this.d.origWidth=this.getVal(b,"w");this.d.data.hide();a&&this.d.container.css("height",a);b&&this.d.container.css("width",
-b);this.setContainerDimensions();this.d.data.show();this.o.focus&&this.focus();this.unbindEvents();this.bindEvents()},setContainerDimensions:function(){var a=m||r,b=this.d.origHeight?this.d.origHeight:q?this.d.container.height():this.getVal(a?this.d.container[0].currentStyle.height:this.d.container.css("height"),"h"),a=this.d.origWidth?this.d.origWidth:q?this.d.container.width():this.getVal(a?this.d.container[0].currentStyle.width:this.d.container.css("width"),"w"),e=this.d.data.outerHeight(!0),f=
-this.d.data.outerWidth(!0);this.d.origHeight=this.d.origHeight||b;this.d.origWidth=this.d.origWidth||a;var c=this.o.maxHeight?this.getVal(this.o.maxHeight,"h"):null,d=this.o.maxWidth?this.getVal(this.o.maxWidth,"w"):null,c=c&&c<g[0]?c:g[0],d=d&&d<g[1]?d:g[1],i=this.o.minHeight?this.getVal(this.o.minHeight,"h"):"auto",b=b?this.o.autoResize&&b>c?c:b<i?i:b:e?e>c?c:this.o.minHeight&&"auto"!==i&&e<i?i:e:i,c=this.o.minWidth?this.getVal(this.o.minWidth,"w"):"auto",a=a?this.o.autoResize&&a>d?d:a<c?c:a:f?
-f>d?d:this.o.minWidth&&"auto"!==c&&f<c?c:f:c;this.d.container.css({height:b,width:a});this.d.wrap.css({overflow:e>b||f>a?"auto":"visible"});this.o.autoPosition&&this.setPosition()},setPosition:function(){var a,b;a=g[0]/2-this.d.container.outerHeight(!0)/2;b=g[1]/2-this.d.container.outerWidth(!0)/2;var e="fixed"!==this.d.container.css("position")?l.scrollTop():0;this.o.position&&"[object Array]"===Object.prototype.toString.call(this.o.position)?(a=e+(this.o.position[0]||a),b=this.o.position[1]||b):
-a=e+a;this.d.container.css({left:b,top:a})},watchTab:function(a){if(0<b(a.target).parents(".simplemodal-container").length){if(this.inputs=b(":input:enabled:visible:first, :input:enabled:visible:last",this.d.data[0]),!a.shiftKey&&a.target===this.inputs[this.inputs.length-1]||a.shiftKey&&a.target===this.inputs[0]||0===this.inputs.length)a.preventDefault(),this.focus(a.shiftKey?"last":"first")}else a.preventDefault(),this.focus()},open:function(){this.d.iframe&&this.d.iframe.show();b.isFunction(this.o.onOpen)?
-this.o.onOpen.apply(this,[this.d]):(this.d.overlay.show(),this.d.container.show(),this.d.data.show());this.o.focus&&this.focus();this.bindEvents()},close:function(){if(!this.d.data)return!1;this.unbindEvents();if(b.isFunction(this.o.onClose)&&!this.occb)this.occb=!0,this.o.onClose.apply(this,[this.d]);else{if(this.d.placeholder){var a=b("#simplemodal-placeholder");this.o.persist?a.replaceWith(this.d.data.removeClass("simplemodal-data").css("display",this.display)):(this.d.data.hide().remove(),a.replaceWith(this.d.orig))}else this.d.data.hide().remove();
-this.d.container.hide().remove();this.d.overlay.hide();this.d.iframe&&this.d.iframe.hide().remove();this.d.overlay.remove();this.d={}}}}});
diff --git a/input/static/microalg_web/jquery.simplemodal.css b/input/static/microalg_web/jquery.simplemodal.css
deleted file mode 100644
index b77b88b..0000000
--- a/input/static/microalg_web/jquery.simplemodal.css
+++ /dev/null
@@ -1,25 +0,0 @@
-#simplemodal-container {
-    height:360px;
-    width:600px;
-    color: black;
-    background-color: white;
-    border:4px solid #444;
-    padding:12px;
-}
-#simplemodal-container .simplemodal-data {
-    padding:8px;
-}
-#simplemodal-container a.modalCloseImg {
-    color: black;
-    width:25px;
-    height:29px;
-    display:inline;
-    z-index:3200;
-    position:absolute;
-    top: 0px;
-    right: -10px;
-    cursor:pointer;
-}
-#simplemodal-container a.modalCloseImg:before {
-    content: "X";
-}
diff --git a/input/static/microalg_web/microalg_export.l b/input/static/microalg_web/microalg_export.l
new file mode 100644
index 0000000..5f869d5
--- /dev/null
+++ b/input/static/microalg_web/microalg_export.l
@@ -0,0 +1,9 @@
+(de proteger_litteraux src (proteger_litteraux_aux src))
+(de proteger_litteraux_aux (src)
+   (cond
+      ((num? src) (list 'Litteral src))
+      ((str? src) (list 'Litteral src))
+      ((sym? src) src)
+      (T          (mapcar 'proteger_litteraux_aux src))
+    )
+)
\ No newline at end of file
diff --git a/input/static/microalg_web/microalg_export_blockly.l b/input/static/microalg_web/microalg_export_blockly.l
new file mode 100644
index 0000000..c91f4af
--- /dev/null
+++ b/input/static/microalg_web/microalg_export_blockly.l
@@ -0,0 +1,95 @@
+(de Litteral (content)
+  (cond
+    ((num? content) (pack "<block type=\"nombre_litteral\"><field name=\"NUM\">"
+                          content
+                          "</field></block>"))
+    ((str? content) (pack "<block type=\"texte_litteral\"><field name=\"TEXT\">"
+                          content
+                          "</field></block>"))
+    (T "Littéral de type inconnu.")
+  ))
+(de insertion_next (src) (car (insertion_next_aux (reverse src))))
+(de insertion_next_aux (src)
+  (if (<= (length src) 1) src
+                          # Déplacement de la tête, enfermée dans un (Next ),
+                          # À la fin du deuxième élément.
+                          # La queue restant inchangée.
+                          (let (tete           (car src)
+                                deuxieme_instr (cadr src)
+                                reste          (cddr src))
+                               (queue 'deuxieme_instr (list 'Next tete))
+                               (insertion_next_aux
+                                 (cons
+                                   deuxieme_instr
+                                   reste)
+                               )
+                          )
+  ))
+(de Next (content)
+  (pack "<next>"
+        content
+        "</next>"))
+(de !!! (content next)
+  (pack "<block type=\"commentaire\"><field name=\"COMZ\">"
+        content
+        "</field>"
+        next
+        "</block>"))
+(de + (A B)
+   (pack "<block type=\"operations\"><field name=\"OP\">ADD</field>"
+         "<value name=\"A\">" A "</value>"
+         "<value name=\"B\">" B "</value>"
+         "</block>"))
+(de - (A B)
+   (pack "<block type=\"operations\"><field name=\"OP\">MINUS</field>"
+         "<value name=\"A\">" A "</value>"
+         "<value name=\"B\">" B "</value>"
+         "</block>"))
+(de * (A B)
+   (pack "<block type=\"operations\"><field name=\"OP\">MULTIPLY</field>"
+         "<value name=\"A\">" A "</value>"
+         "<value name=\"B\">" B "</value>"
+         "</block>"))
+(de / (A B)
+   (pack "<block type=\"operations\"><field name=\"OP\">DIVIDE</field>"
+         "<value name=\"A\">" A "</value>"
+         "<value name=\"B\">" B "</value>"
+         "</block>"))
+(de Afficher (content next)
+  (pack "<block type=\"afficher\">"
+        "<value name=\"VALUE\">"
+        content
+        "</value>"
+        next
+        "</block>"))
+(de Concatener inputs
+  (pack "<block type=\"concatener\">"
+        "<mutation items=\"" (length inputs) "\"></mutation>"
+        (mapcar '((i input) (pack "<value name=\"ADD" i "\">" (eval input) "</value>")) (range 0 (dec (length inputs))) inputs)
+        "</block>"))
+(de Demander ()
+  "<block type=\"demander\"></block>")
+(de Nombre (content)
+  (pack "<block type=\"nombre\">"
+        "<value name=\"VALUE\">"
+        content
+        "</value>"
+        "</block>"))
+(de Nombre? (content)
+  (pack "<block type=\"nombre?\">"
+        "<value name=\"VALUE\">"
+        content
+        "</value>"
+        "</block>"))
+(de Texte (content)
+  (pack "<block type=\"texte\">"
+        "<value name=\"VALUE\">"
+        content
+        "</value>"
+        "</block>"))
+(de Texte? (content)
+  (pack "<block type=\"texte?\">"
+        "<value name=\"VALUE\">"
+        content
+        "</value>"
+        "</block>"))
diff --git a/input/static/microalg_web/parenedit.css b/input/static/microalg_web/parenedit.css
index 4db0e98..1bc4508 100644
--- a/input/static/microalg_web/parenedit.css
+++ b/input/static/microalg_web/parenedit.css
@@ -36,7 +36,6 @@ input,.richtext {
 input, textarea, .richtext > pre {
     font-family: 'Courier New', Courier, monospace;
     font-size: 100%;
-    width: 100%;
     line-height: 1.5;
 }
 #test:focus, input:focus, .fake_focus {
@@ -60,7 +59,7 @@ input, textarea, .richtext > pre {
     background: #ffd;
 }
 .paren_4 {
-    background: #cfc;
+    background: #dfb;
 }
 .paren_5 {
     background: #cef;
diff --git a/input/static/microalg_web/showdown.js b/input/static/microalg_web/showdown.js
new file mode 100644
index 0000000..65ee602
--- /dev/null
+++ b/input/static/microalg_web/showdown.js
@@ -0,0 +1,62 @@
+//
+// showdown.js -- A javascript port of Markdown.
+//
+// Copyright (c) 2007 John Fraser.
+//
+// Original Markdown Copyright (c) 2004-2005 John Gruber
+//   <http://daringfireball.net/projects/markdown/>
+//
+// Redistributable under a BSD-style open source license.
+// See license.txt for more information.
+//
+// The full source distribution is at:
+//
+//             A A L
+//             T C A
+//             T K B
+//
+//   <http://www.attacklab.net/>
+//
+//
+// Wherever possible, Showdown is a straight, line-by-line port
+// of the Perl version of Markdown.
+//
+// This is not a normal parser design; it's basically just a
+// series of string substitutions.  It's hard to read and
+// maintain this way,  but keeping Showdown close to the original
+// design makes it easier to port new features.
+//
+// More importantly, Showdown behaves like markdown.pl in most
+// edge cases.  So web applications can do client-side preview
+// in Javascript, and then build identical HTML on the server.
+//
+// This port needs the new RegExp functionality of ECMA 262,
+// 3rd Edition (i.e. Javascript 1.5).  Most modern web browsers
+// should do fine.  Even with the new regular expression features,
+// We do a lot of work to emulate Perl's regex functionality.
+// The tricky changes in this file mostly have the "attacklab:"
+// label.  Major or self-explanatory changes don't.
+//
+// Smart diff tools like Araxis Merge will be able to match up
+// this file with markdown.pl in a useful way.  A little tweaking
+// helps: in a copy of markdown.pl, replace "#" with "//" and
+// replace "$text" with "text".  Be sure to ignore whitespace
+// and line endings.
+//
+//
+// Showdown usage:
+//
+//   var text = "Markdown *rocks*.";
+//
+//   var converter = new Showdown.converter();
+//   var html = converter.makeHtml(text);
+//
+//   alert(html);
+//
+// Note: move the sample code to the bottom of this
+// file before uncommenting it.
+//
+//
+// Showdown namespace
+//
+var Showdown={extensions:{}},forEach=Showdown.forEach=function(a,b){if(typeof a.forEach=="function")a.forEach(b);else{var c,d=a.length;for(c=0;c<d;c++)b(a[c],c,a)}},stdExtName=function(a){return a.replace(/[_-]||\s/g,"").toLowerCase()};Showdown.converter=function(a){var b,c,d,e=0,f=[],g=[];if(typeof module!="undefind"&&typeof exports!="undefined"&&typeof require!="undefind"){var h=require("fs");if(h){var i=h.readdirSync((__dirname||".")+"/extensions").filter(function(a){return~a.indexOf(".js")}).map(function(a){return a.replace(/\.js$/,"")});Showdown.forEach(i,function(a){var b=stdExtName(a);Showdown.extensions[b]=require("./extensions/"+a)})}}this.makeHtml=function(a){return b={},c={},d=[],a=a.replace(/~/g,"~T"),a=a.replace(/\$/g,"~D"),a=a.replace(/\r\n/g,"\n"),a=a.replace(/\r/g,"\n"),a="\n\n"+a+"\n\n",a=M(a),a=a.replace(/^[ \t]+$/mg,""),Showdown.forEach(f,function(b){a=k(b,a)}),a=z(a),a=m(a),a=l(a),a=o(a),a=K(a),a=a.replace(/~D/g,"$$"),a=a.replace(/~T/g,"~"),Showdown.forEach(g,function(b){a=k(b,a)}),a};if(a&&a.extensions){var j=this;Showdown.forEach(a.extensions,function(a){typeof a=="string"&&(a=Showdown.extensions[stdExtName(a)]);if(typeof a!="function")throw"Extension '"+a+"' could not be loaded.  It was either not found or is not a valid extension.";Showdown.forEach(a(j),function(a){a.type?a.type==="language"||a.type==="lang"?f.push(a):(a.type==="output"||a.type==="html")&&g.push(a):g.push(a)})})}var k=function(a,b){if(a.regex){var c=new RegExp(a.regex,"g");return b.replace(c,a.replace)}if(a.filter)return a.filter(b)},l=function(a){return a+="~0",a=a.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|(?=~0))/gm,function(a,d,e,f,g){return d=d.toLowerCase(),b[d]=G(e),f?f+g:(g&&(c[d]=g.replace(/"/g,""")),"")}),a=a.replace(/~0/,""),a},m=function(a){a=a.replace(/\n/g,"\n\n");var b="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del|style|section|header|footer|nav|article|aside",c="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside";return a=a.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,n),a=a.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside)\b[^\r]*?<\/\2>[ \t]*(?=\n+)\n)/gm,n),a=a.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,n),a=a.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,n),a=a.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,n),a=a.replace(/\n\n/g,"\n"),a},n=function(a,b){var c=b;return c=c.replace(/\n\n/g,"\n"),c=c.replace(/^\n/,""),c=c.replace(/\n+$/g,""),c="\n\n~K"+(d.push(c)-1)+"K\n\n",c},o=function(a){a=v(a);var b=A("<hr />");return a=a.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,b),a=a.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,b),a=a.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,b),a=x(a),a=y(a),a=E(a),a=m(a),a=F(a),a},p=function(a){return a=B(a),a=q(a),a=H(a),a=t(a),a=r(a),a=I(a),a=G(a),a=D(a),a=a.replace(/  +\n/g," <br />\n"),a},q=function(a){var b=/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;return a=a.replace(b,function(a){var b=a.replace(/(.)<\/?code>(?=.)/g,"$1`");return b=N(b,"\\`*_"),b}),a},r=function(a){return a=a.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,s),a=a.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?(?:\(.*?\).*?)?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,s),a=a.replace(/(\[([^\[\]]+)\])()()()()()/g,s),a},s=function(a,d,e,f,g,h,i,j){j==undefined&&(j="");var k=d,l=e,m=f.toLowerCase(),n=g,o=j;if(n==""){m==""&&(m=l.toLowerCase().replace(/ ?\n/g," ")),n="#"+m;if(b[m]!=undefined)n=b[m],c[m]!=undefined&&(o=c[m]);else{if(!(k.search(/\(\s*\)$/m)>-1))return k;n=""}}n=N(n,"*_");var p='<a href="'+n+'"';return o!=""&&(o=o.replace(/"/g,"""),o=N(o,"*_"),p+=' title="'+o+'"'),p+=">"+l+"</a>",p},t=function(a){return a=a.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,u),a=a.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,u),a},u=function(a,d,e,f,g,h,i,j){var k=d,l=e,m=f.toLowerCase(),n=g,o=j;o||(o="");if(n==""){m==""&&(m=l.toLowerCase().replace(/ ?\n/g," ")),n="#"+m;if(b[m]==undefined)return k;n=b[m],c[m]!=undefined&&(o=c[m])}l=l.replace(/"/g,"""),n=N(n,"*_");var p='<img src="'+n+'" alt="'+l+'"';return o=o.replace(/"/g,"""),o=N(o,"*_"),p+=' title="'+o+'"',p+=" />",p},v=function(a){function b(a){return a.replace(/[^\w]/g,"").toLowerCase()}return a=a.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,function(a,c){return A('<h1 id="'+b(c)+'">'+p(c)+"</h1>")}),a=a.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,function(a,c){return A('<h2 id="'+b(c)+'">'+p(c)+"</h2>")}),a=a.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,function(a,c,d){var e=c.length;return A("<h"+e+' id="'+b(d)+'">'+p(d)+"</h"+e+">")}),a},w,x=function(a){a+="~0";var b=/^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;return e?a=a.replace(b,function(a,b,c){var d=b,e=c.search(/[*+-]/g)>-1?"ul":"ol";d=d.replace(/\n{2,}/g,"\n\n\n");var f=w(d);return f=f.replace(/\s+$/,""),f="<"+e+">"+f+"</"+e+">\n",f}):(b=/(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g,a=a.replace(b,function(a,b,c,d){var e=b,f=c,g=d.search(/[*+-]/g)>-1?"ul":"ol",f=f.replace(/\n{2,}/g,"\n\n\n"),h=w(f);return h=e+"<"+g+">\n"+h+"</"+g+">\n",h})),a=a.replace(/~0/,""),a};w=function(a){return e++,a=a.replace(/\n{2,}$/,"\n"),a+="~0",a=a.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,function(a,b,c,d,e){var f=e,g=b,h=c;return g||f.search(/\n{2,}/)>-1?f=o(L(f)):(f=x(L(f)),f=f.replace(/\n$/,""),f=p(f)),"<li>"+f+"</li>\n"}),a=a.replace(/~0/g,""),e--,a};var y=function(a){return a+="~0",a=a.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,function(a,b,c){var d=b,e=c;return d=C(L(d)),d=M(d),d=d.replace(/^\n+/g,""),d=d.replace(/\n+$/g,""),d="<pre><code>"+d+"\n</code></pre>",A(d)+e}),a=a.replace(/~0/,""),a},z=function(a){return a+="~0",a=a.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g,function(a,b,c){var d=b,e=c;return e=C(e),e=M(e),e=e.replace(/^\n+/g,""),e=e.replace(/\n+$/g,""),e="<pre><code"+(d?' class="'+d+'"':"")+">"+e+"\n</code></pre>",A(e)}),a=a.replace(/~0/,""),a},A=function(a){return a=a.replace(/(^\n+|\n+$)/g,""),"\n\n~K"+(d.push(a)-1)+"K\n\n"},B=function(a){return a=a.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(a,b,c,d,e){var f=d;return f=f.replace(/^([ \t]*)/g,""),f=f.replace(/[ \t]*$/g,""),f=C(f),b+"<code>"+f+"</code>"}),a},C=function(a){return a=a.replace(/&/g,"&"),a=a.replace(/</g,"<"),a=a.replace(/>/g,">"),a=N(a,"*_{}[]\\",!1),a},D=function(a){return a=a.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,"<strong>$2</strong>"),a=a.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,"<em>$2</em>"),a},E=function(a){return a=a.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(a,b){var c=b;return c=c.replace(/^[ \t]*>[ \t]?/gm,"~0"),c=c.replace(/~0/g,""),c=c.replace(/^[ \t]+$/gm,""),c=o(c),c=c.replace(/(^|\n)/g,"$1  "),c=c.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm,function(a,b){var c=b;return c=c.replace(/^  /mg,"~0"),c=c.replace(/~0/g,""),c}),A("<blockquote>\n"+c+"\n</blockquote>")}),a},F=function(a){a=a.replace(/^\n+/g,""),a=a.replace(/\n+$/g,"");var b=a.split(/\n{2,}/g),c=[],e=b.length;for(var f=0;f<e;f++){var g=b[f];g.search(/~K(\d+)K/g)>=0?c.push(g):g.search(/\S/)>=0&&(g=p(g),g=g.replace(/^([ \t]*)/g,"<p>"),g+="</p>",c.push(g))}e=c.length;for(var f=0;f<e;f++)while(c[f].search(/~K(\d+)K/)>=0){var h=d[RegExp.$1];h=h.replace(/\$/g,"$$$$"),c[f]=c[f].replace(/~K\d+K/,h)}return c.join("\n\n")},G=function(a){return a=a.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&"),a=a.replace(/<(?![a-z\/?\$!])/gi,"<"),a},H=function(a){return a=a.replace(/\\(\\)/g,O),a=a.replace(/\\([`*_{}\[\]()>#+-.!])/g,O),a},I=function(a){return a=a.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,'<a href="$1">$1</a>'),a=a.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,function(a,b){return J(K(b))}),a},J=function(a){var b=[function(a){return"&#"+a.charCodeAt(0)+";"},function(a){return"&#x"+a.charCodeAt(0).toString(16)+";"},function(a){return a}];return a="mailto:"+a,a=a.replace(/./g,function(a){if(a=="@")a=b[Math.floor(Math.random()*2)](a);else if(a!=":"){var c=Math.random();a=c>.9?b[2](a):c>.45?b[1](a):b[0](a)}return a}),a='<a href="'+a+'">'+a+"</a>",a=a.replace(/">.+:/g,'">'),a},K=function(a){return a=a.replace(/~E(\d+)E/g,function(a,b){var c=parseInt(b);return String.fromCharCode(c)}),a},L=function(a){return a=a.replace(/^(\t|[ ]{1,4})/gm,"~0"),a=a.replace(/~0/g,""),a},M=function(a){return a=a.replace(/\t(?=\t)/g,"    "),a=a.replace(/\t/g,"~A~B"),a=a.replace(/~B(.+?)~A/g,function(a,b,c){var d=b,e=4-d.length%4;for(var f=0;f<e;f++)d+=" ";return d}),a=a.replace(/~A/g,"    "),a=a.replace(/~B/g,""),a},N=function(a,b,c){var d="(["+b.replace(/([\[\]\\])/g,"\\$1")+"])";c&&(d="\\\\"+d);var e=new RegExp(d,"g");return a=a.replace(e,O),a},O=function(a,b){var c=b.charCodeAt(0);return"~E"+c+"E"}},typeof module!="undefined"&&(module.exports=Showdown),typeof define=="function"&&define.amd&&define("showdown",function(){return Showdown});
\ No newline at end of file
diff --git a/input/static/microalg_web/style.css b/input/static/microalg_web/style.css
index 831ce1c..20eda6f 100644
--- a/input/static/microalg_web/style.css
+++ b/input/static/microalg_web/style.css
@@ -3,7 +3,7 @@ body.microalg {
     margin: 0px auto;
     font-family: Verdana, Arial, sans-serif;
     color: black;
-    background-color: white;
+    background-color: #D1D1D1;
 }
 .microalg a:link {color:#337;}
 .microalg a:visited {color:#333;}
@@ -26,16 +26,29 @@ body.microalg {
     white-space: nowrap;
 }
 .microalg pre code {
+    display: block;
     white-space: pre;
     position: relative;
 }
-#script-container, #repl-container {
+.malg-container {
     width: 95%;
     margin: 0px auto;
 }
+.malg-blockly-iframe {
+    height: 350px;
+    width: 100%;
+    border-style: solid;
+    border-width: 1px;
+    border-color: #aaa;
+}
 .malg-editor, .malg-repl {
     width: 100%;
 }
+.malg-ok {
+    margin: 2px;
+    background-color: #ddd;
+    width: 100%;
+}
 .malg-display {
     border-color: black;
     border-style: solid;
@@ -43,8 +56,20 @@ body.microalg {
     -webkit-border-radius: 5px;
     -moz-border-radius: 5px;
     border-radius: 5px;
+    background-color: white;
+}
+.malg-display p {
+    margin: 0px;
+    margin-left: 3px;
 }
-#links {
+.microalg .terminal, .microalg .cmd {
+    width: 93%;
+    margin: 0px auto;
+    color: black;
+    background-color: white;
+    font-family: 'Courier New', Courier, monospace;
+}
+.link-right {
     text-align: right;
 }
 #bla {
@@ -54,3 +79,12 @@ body.microalg {
 #bla h1 {
     text-align: center;
 }
+.navbar {
+    position: fixed;
+    top: 0;
+    right: 0;
+    margin: 1em;
+    padding: 1em;
+    z-index: 100;
+    background-color: #eee;
+}
diff --git a/macros.py b/macros.py
index 6ca402a..c33f314 100644
--- a/macros.py
+++ b/macros.py
@@ -52,11 +52,9 @@ parenedit = """    <script src="static/microalg_web/parenedit.js" type="text/jav
 microalg = """    <script src="static/microalg_web/emulisp/emulisp_core.js" type="text/javascript"></script>
     <link rel="stylesheet" type="text/css" href="static/microalg_web/style.css" />
     <script type="text/javascript" src="static/microalg_web/ide_injections.js"></script>
-    <!-- http://code.google.com/p/simplemodal/ -->
-    <script type="text/javascript" src="static/microalg_web/jquery.simplemodal.1.4.4.min.js"></script>
-    <link type="text/css" href="static/microalg_web/jquery.simplemodal.css" rel="stylesheet" />
     <script type="text/javascript" src="static/microalg_web/parenedit.js"></script>
-    <link type="text/css" href="static/microalg_web/parenedit.css" rel="stylesheet" />""",
+    <link type="text/css" href="static/microalg_web/parenedit.css" rel="stylesheet" />
+    <script type="text/javascript" src="static/microalg_web/showdown.js"></script>""",
 timeline = """    <meta name="apple-mobile-web-app-capable" content="yes">
     <meta name="apple-touch-fullscreen" content="yes">
     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">









Page générée le 23/09/2014, 22h30'42" (page virtuelle).
historique global

 TogetherJS