Index: gc_core/js/helpers.js
==================================================================
--- gc_core/js/helpers.js
+++ gc_core/js/helpers.js
@@ -30,10 +30,19 @@
     } else {
         console.error(sMsg);
     }
 }
 
+function inspect (o) {
+    let sMsg = "__inspect__: " + typeof o;
+    for (let sParam in o) {
+        sMsg += "\n" + sParam + ": " + o.sParam;
+    }
+    sMsg += "\n" + JSON.stringify(o) + "\n__end__";
+    echo(sMsg);
+}
+
 
 // load ressources in workers (suggested by Mozilla extensions reviewers)
 // for more options have a look here: https://gist.github.com/Noitidart/ec1e6b9a593ec7e3efed
 // if not in workers, use sdk/data.load() instead
 function loadFile (spf) {
@@ -78,11 +87,12 @@
         obj[k] = v;
     }
     return obj;
 }
 
+exports.setLogOutput = setLogOutput;
 exports.echo = echo;
 exports.logerror = logerror;
+exports.inspect = inspect;
 exports.objectToMap = objectToMap;
 exports.mapToObject = mapToObject;
-exports.setLogOutput = setLogOutput;
 exports.loadFile = loadFile;

Index: gc_lang/fr/modules-js/lexicographe.js
==================================================================
--- gc_lang/fr/modules-js/lexicographe.js
+++ gc_lang/fr/modules-js/lexicographe.js
@@ -161,10 +161,11 @@
 
 const _dSeparator = new Map ([
     ['.', "point"],
     ['·', "point médian"],
     ['…', "points de suspension"],
+    [':', "deux-points"],
     [';', "point-virgule"],
     [',', "virgule"],
     ['?', "point d’interrogation"],
     ['!', "point d’exclamation"],
     ['(', "parenthèse ouvrante"],
@@ -171,18 +172,25 @@
     [')', "parenthèse fermante"],
     ['[', "crochet ouvrante"],
     [']', "crochet fermante"],
     ['{', "accolade ouvrante"],
     ['}', "accolade fermante"],
+    ['-', "tiret"],
     ['—', "tiret cadratin"],
     ['–', "tiret demi-cadratin"],
     ['«', "guillemet ouvrant (chevrons)"],
     ['»', "guillemet fermant (chevrons)"],
     ['“', "guillemet ouvrant double"],
     ['”', "guillemet fermant double"],
     ['‘', "guillemet ouvrant"],
     ['’', "guillemet fermant"],
+    ['/', "signe de la division"],
+    ['+', "signe de l’addition"],
+    ['*', "signe de la multiplication"],
+    ['=', "signe de l’égalité"],
+    ['<', "inférieur à"],
+    ['>', "supérieur à"],
 ]);
 
 
 class Lexicographe {
 
@@ -193,56 +201,58 @@
         this._zTag = new RegExp ("[:;/][a-zA-Zà-ö0-9À-Öø-ÿØ-ßĀ-ʯ*][^:;/]*", "g");
     };
 
     getInfoForToken (oToken) {
         // Token: .sType, .sValue, .nStart, .nEnd
+        // return a list [type, token_string, values]
         let m = null;
         try {
             helpers.echo(oToken);
             switch (oToken.sType) {
                 case 'SEPARATOR':
-                    return [oToken.sType, oToken.sValue, _dSeparator._get(oToken.sValue, "caractère indéterminé")];
+                    return { sType: oToken.sType, sValue: oToken.sValue, aLabel: [_dSeparator._get(oToken.sValue, "caractère indéterminé")] };
                     break;
                 case 'NUM':
-                    return [oToken.sType, oToken.sValue, "nombre"];
+                    return { sType: oToken.sType, sValue: oToken.sValue, aLabel: ["nombre"] };
                     break;
                 case 'LINK':
-                    return [oToken.sType, oToken.sValue.slice(0,40)+"…", "hyperlien"];
+                    return { sType: oToken.sType, sValue: oToken.sValue.slice(0,40)+"…", aLabel: ["hyperlien"] };
                     break;
                 case 'ELPFX':
-                    sTemp = oToken.sValue.replace("’", "'").replace("`", "'").toLowerCase();
-                    return [oToken.sType, oToken.sValue, _dPFX._get(sTemp, "préfixe élidé inconnu")];
+                    let sTemp = oToken.sValue.replace("’", "").replace("'", "").replace("`", "").toLowerCase();
+                    return { sType: oToken.sType, sValue: oToken.sValue, aLabel: [_dPFX._get(sTemp, "préfixe élidé inconnu")] };
                     break;
                 case 'WORD': 
                     if (oToken.sValue._count("-") > 4) {
-                        return ["COMPLEX", oToken.sValue, "élément complexe indéterminé"];
+                        return { sType: "COMPLEX", sValue: oToken.sValue, aLabel: ["élément complexe indéterminé"] };
                     }
                     else if (this.oDict.isValidToken(oToken.sValue)) {
                         let lMorph = this.oDict.getMorph(oToken.sValue);
                         let aElem = [ for (s of lMorph) if (s.includes(":")) this._formatTags(s) ];
-                        return [ oToken.sType, oToken.sValue, [aElem] ];
+                        return { sType: oToken.sType, sValue: oToken.sValue, aLabel: aElem};
                     }
                     else if (m = this._zCompoundWord.exec(oToken.sValue)) {
                         // mots composés
                         let lMorph = this.oDict.getMorph(m[1]);
                         let aElem = [ for (s of lMorph) if (s.includes(":")) this._formatTags(s) ];
                         aElem.push("-" + m[2] + ": " + this._formatSuffix(m[2].toLowerCase()));
-                        return [ oToken.sType, oToken.sValue, [aElem] ];
+                        return { sType: oToken.sType, sValue: oToken.sValue, aLabel: aElem };
                     }
                     else {
-                        return ["INCONNU", oToken.sValue, "inconnu du dictionnaire"];
+                        return { sType: "UNKNOWN", sValue: oToken.sValue, aLabel: ["inconnu du dictionnaire"] };
                     }
                     break;
             }
         }
         catch (e) {
             helpers.logerror(e);
         }
-        return null
+        return null;
     };
 
     getHTMLForText (sText) {
+        // deprecated
         sText = sText.replace(/[.,.?!:;…\/()\[\]“”«»"„{}–—#+*<>%=\n]/g, " ").replace(/\s+/g, " ");
         let iStart = 0;
         let iEnd = 0;
         let sHtml = '<div class="paragraph">\n';
         while ((iEnd = sText.indexOf(" ", iStart)) !== -1) {
@@ -252,10 +262,11 @@
         sHtml += this.getHTMLForToken(sText.slice(iStart));
         return sHtml + '</div>\n';
     };
 
     getHTMLForToken (sWord) {
+        // deprecated
         try {
             if (!sWord) {
                 return "";
             }
             if (sWord._count("-") > 4) {

Index: gc_lang/fr/xpi/data/lxg_panel.css
==================================================================
--- gc_lang/fr/xpi/data/lxg_panel.css
+++ gc_lang/fr/xpi/data/lxg_panel.css
@@ -40,28 +40,43 @@
     color: hsla(0, 0%, 96%, 1);
     border-radius: 5px;
     text-align: center;
     font-size: 20px;
 }
+#wordlist .token {
+    margin: 8px;
+}
 #wordlist ul {
     margin: 0 0 5px 40px;
 }
 #wordlist b {
-    background-color: hsla(150, 50%, 50%, 1);
+    background-color: hsla(150, 10%, 50%, 1);
     color: hsla(0, 0%, 96%, 1);
     padding: 2px 5px;
     border-radius: 2px;
     text-decoration: none;
 }
-#wordlist b.unknown {
+#wordlist b.WORD {
+    background-color: hsla(150, 50%, 50%, 1);
+}
+#wordlist b.ELPFX {
+    background-color: hsla(150, 30%, 50%, 1);
+}
+#wordlist b.UNKNOWN {
     background-color: hsla(0, 50%, 50%, 1);
 }
-#wordlist b.nb {
+#wordlist b.NUM {
+    background-color: hsla(180, 50%, 50%, 1);
+}
+#wordlist b.COMPLEX {
+    background-color: hsla(60, 50%, 50%, 1);
+}
+#wordlist b.SEPARATOR {
     background-color: hsla(210, 50%, 50%, 1);
 }
-#wordlist b.mbok {
-    background-color: hsla(60, 50%, 50%, 1);
+#wordlist b.LINK {
+    background-color: hsla(270, 50%, 50%, 1);
 }
 #wordlist s {
     color: hsla(0, 0%, 60%, 1);
     text-decoration: none;
 }

Index: gc_lang/fr/xpi/data/lxg_panel.js
==================================================================
--- gc_lang/fr/xpi/data/lxg_panel.js
+++ gc_lang/fr/xpi/data/lxg_panel.js
@@ -25,22 +25,19 @@
     self.port.emit('openConjugueur');
 });
 */
 
 self.port.on("addSeparator", function (sText) {
-    if (document.getElementById("wordlist").innerHTML !== "") {
-        let xElem = document.createElement("p");
-        xElem.className = "separator";
-        xElem.innerHTML = sText;
-        document.getElementById("wordlist").appendChild(xElem);
-    }
+    addSeparator(sText);
+});
+
+self.port.on("addParagraphElems", function (sJSON) {
+    addParagraphElems(sJSON);
 });
 
-self.port.on("addElem", function (sHtml) {
-    let xElem = document.createElement("div");
-    xElem.innerHTML = sHtml;
-    document.getElementById("wordlist").appendChild(xElem);
+self.port.on("addMessage", function (sClass, sText) {
+    addMessage(sClass, sText);
 });
 
 self.port.on("clear", function (sHtml) {
     document.getElementById("wordlist").textContent = "";
 });
@@ -65,10 +62,71 @@
         }
     },
     false
 );
 
+
+/*
+    Actions
+*/
+
+function addSeparator (sText) {
+    if (document.getElementById("wordlist").textContent !== "") {
+        let xElem = document.createElement("p");
+        xElem.className = "separator";
+        xElem.textContent = sText;
+        document.getElementById("wordlist").appendChild(xElem);
+    }
+}
+
+function addMessage (sClass, sText) {
+    let xNode = document.createElement("p");
+    xNode.className = sClass;
+    xNode.textContent = sText;
+    document.getElementById("wordlist").appendChild(xNode);
+}
+
+function addParagraphElems (sJSON) {
+    try {
+        let xNodeDiv = document.createElement("div");
+        xNodeDiv.className = "paragraph";
+        let lElem = JSON.parse(sJSON);
+        for (let oToken of lElem) {
+            xNodeDiv.appendChild(createTokenNode(oToken));
+        }
+        document.getElementById("wordlist").appendChild(xNodeDiv);
+    }
+    catch (e) {
+        console.error("\n" + e.fileName + "\n" + e.name + "\nline: " + e.lineNumber + "\n" + e.message);
+        console.error(sJSON);
+    }
+}
+
+function createTokenNode (oToken) {
+    let xTokenNode = document.createElement("div");
+    xTokenNode.className = "token " + oToken.sType;
+    let xTokenValue = document.createElement("b");
+    xTokenValue.className = oToken.sType;
+    xTokenValue.textContent = oToken.sValue;
+    xTokenNode.appendChild(xTokenValue);
+    let xSep = document.createElement("s");
+    xSep.textContent = " : ";
+    xTokenNode.appendChild(xSep);
+    if (oToken.aLabel.length === 1) {
+        xTokenNode.appendChild(document.createTextNode(oToken.aLabel[0]));
+    } else {
+        let xTokenList = document.createElement("ul");
+        for (let sLabel of oToken.aLabel) {
+            let xTokenLine = document.createElement("li");
+            xTokenLine.textContent = sLabel;
+            xTokenList.appendChild(xTokenLine);
+        }
+        xTokenNode.appendChild(xTokenList);
+    }
+    return xTokenNode;
+}
+
 
 // display selection
 
 function displayClasses () {
     setHidden("ok", document.getElementById("ok").checked);

Index: gc_lang/fr/xpi/gce_worker.js
==================================================================
--- gc_lang/fr/xpi/gce_worker.js
+++ gc_lang/fr/xpi/gce_worker.js
@@ -145,24 +145,17 @@
 }
 
 
 // Lexicographer
 
-function analyzeWords (sText) {
-    getListOfElements(sText);
-    return oLxg.getHTMLForText(sText);
-}
-
 function getListOfElements (sText) {
     try {
-        helpers.echo("=================================================");
         let aElem = [];
         let aRes = null;
         for (let oToken of oTokenizer.genTokens(sText)) {
             aRes = oLxg.getInfoForToken(oToken);
             if (aRes) {
-                helpers.echo(aRes);
                 aElem = aElem.concat(aRes);
             }
         }
         return JSON.stringify(aElem);
     }

Index: gc_lang/fr/xpi/ui.js
==================================================================
--- gc_lang/fr/xpi/ui.js
+++ gc_lang/fr/xpi/ui.js
@@ -569,19 +569,19 @@
     let nParagraph = 0; // non empty paragraphs
     let sRes = "";
     try {
         for (let sParagraph of text.getParagraph(sText)) {
             if (sParagraph.trim() !== "") {
-                sRes = await xGCEWorker.post('analyzeWords', [sParagraph])
-                xLxgPanel.port.emit("addElem", sRes);
+                sRes = await xGCEWorker.post('getListOfElements', [sParagraph]);
+                xLxgPanel.port.emit("addParagraphElems", sRes);
                 nParagraph += 1;
             }
         }
-        xLxgPanel.port.emit("addElem", '<p class="message">' + _("numberOfParagraphs") + " " + nParagraph + '</p>');
+        xLxgPanel.port.emit("addMessage", 'message', _("numberOfParagraphs") + " " + nParagraph);
     }
     catch (e) {
-        xLxgPanel.port.emit("addElem", '<p class="bug">'+e.message+"</p>");
+        xLxgPanel.port.emit("addMessage", 'bug', e.message);
     }
     xLxgPanel.port.emit("stopWaitIcon");
 }