Index: gc_lang/fr/build.py
==================================================================
--- gc_lang/fr/build.py
+++ gc_lang/fr/build.py
@@ -91,10 +91,12 @@
             hZip.write(spDict+"/"+sp+"/"+sp+".dic", "content/dictionaries/"+sp+"/"+sp+".dic")
             hZip.write(spDict+"/"+sp+"/"+sp+".aff", "content/dictionaries/"+sp+"/"+sp+".aff")
     hZip.close()
     spfDebugProfile = dVars['win_tb_debug_extension_path']  if platform.system() == "Windows"  else dVars['linux_tb_debug_extension_path']
     helpers.unzip(spfZip, spfDebugProfile)
+    spfBetaProfile = dVars['win_tb_beta_extension_path']  if platform.system() == "Windows"  else dVars['linux_tb_beta_extension_path']
+    helpers.unzip(spfZip, spfBetaProfile)
 
 
 def _createOptionsForThunderbird (dVars):
     dVars['sXULTabs'] = ""
     dVars['sXULTabPanels'] = ""
Index: gc_lang/fr/config.ini
==================================================================
--- gc_lang/fr/config.ini
+++ gc_lang/fr/config.ini
@@ -46,12 +46,19 @@
 linux_fx_nightly_path = /usr/bin/firefox
 
 # Thunderbird
 tb_identifier = French-GC-TB@grammalecte.net
 tb_name = Grammalecte [fr]
+win_tb_path = C:\Program Files (x86)\Mozilla Thunderbird\thunderbird.exe
+#win_tb_beta_path = C:\Program Files (x86)\Mozilla Thunderbird (Beta)\thunderbird.exe
+win_tb_beta_path = C:\Program Files\Thunderbird Daily\thunderbird.exe
+linux_tb_path = /usr/bin/thunderbird
+linux_tb_beta_path = /usr/bin/thunderbird
 win_tb_debug_extension_path = D:\_temp\tb-debug.profile\extensions\French-GC-TB@grammalecte.net
 linux_tb_debug_extension_path = ~/tb-debug.profile/extensions/French-GC-TB@grammalecte.net
+win_tb_beta_extension_path = D:\_temp\tb-beta.profile\extensions\French-GC-TB@grammalecte.net
+linux_tb_beta_extension_path = ~/tb-beta.profile/extensions/French-GC-TB@grammalecte.net
 # Set Thunderbird folder in your PATH variable
 # Create a local profile:
 #     	thunderbird -CreateProfile "debug _build\tb-debug.profile"
 # Or you can use the GUI with:
 #	 	thunderbird -P
Index: gc_lang/fr/tb/chrome.manifest
==================================================================
--- gc_lang/fr/tb/chrome.manifest
+++ gc_lang/fr/tb/chrome.manifest
@@ -4,6 +4,6 @@
 resource grammalecte ./grammalecte-js/
 locale	grammarchecker	fr	locale/fr/
 locale	grammarchecker	en	locale/en/
 skin	grammarchecker	classic/1.0	skin/
 overlay	chrome://messenger/content/messengercompose/messengercompose.xul	chrome://grammarchecker/content/overlay.xul
-style	chrome://global/content/customizeToolbar.xul	chrome://grammarchecker/content/overlay.css
+style	chrome://messenger/content/customizeToolbar.xul	chrome://grammarchecker/content/overlay.css
Index: gc_lang/fr/tb/content/about.css
==================================================================
--- gc_lang/fr/tb/content/about.css
+++ gc_lang/fr/tb/content/about.css
@@ -23,5 +23,24 @@
 	font-size: 16px;
 	text-align: center;
 	color: hsl(210, 50%, 50%);
 	cursor: pointer;
 }
+
+
+/*
+    TB Next: fix dialogheaders
+*/
+dialogheader {
+  -moz-binding: url("chrome://messenger/content/generalBindings.xml#dialogheader");
+  margin: 0 5px 5px;
+  border: 1px solid ThreeDDarkShadow;
+  padding: 5px 8px;
+  background-color: Highlight;
+  color: HighlightText;
+}
+
+.dialogheader-title {
+  margin: 0 !important;
+  font-size: larger;
+  font-weight: bold;
+}
Index: gc_lang/fr/tb/content/about.js
==================================================================
--- gc_lang/fr/tb/content/about.js
+++ gc_lang/fr/tb/content/about.js
@@ -1,15 +1,10 @@
 // JavaScript
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
-//const { require } = Cu.import("resource://gre/modules/commonjs/toolkit/require.js", {});
-
-function echo (...args) {
-    Services.console.logStringMessage(args.join(" -- ") + "\n");
-}
 
 
 function openInBrowserURL (sURL) {
     // method found in S3.Google.Translator
     try {
Index: gc_lang/fr/tb/content/about.xul
==================================================================
--- gc_lang/fr/tb/content/about.xul
+++ gc_lang/fr/tb/content/about.xul
@@ -32,7 +32,7 @@
   
   
 
-  
+  
 
Index: gc_lang/fr/tb/content/conjugueur.css
==================================================================
--- gc_lang/fr/tb/content/conjugueur.css
+++ gc_lang/fr/tb/content/conjugueur.css
@@ -39,5 +39,24 @@
 	font-weight: bold;
 }
 label.cj {
 	font-size: 11px;
 }
+
+
+/*
+    TB Next: fix dialogheaders
+*/
+dialogheader {
+  -moz-binding: url("chrome://messenger/content/generalBindings.xml#dialogheader");
+  margin: 0 5px 5px;
+  border: 1px solid ThreeDDarkShadow;
+  padding: 5px 8px;
+  background-color: Highlight;
+  color: HighlightText;
+}
+
+.dialogheader-title {
+  margin: 0 !important;
+  font-size: larger;
+  font-weight: bold;
+}
Index: gc_lang/fr/tb/content/conjugueur.js
==================================================================
--- gc_lang/fr/tb/content/conjugueur.js
+++ gc_lang/fr/tb/content/conjugueur.js
@@ -1,44 +1,39 @@
 // JavaScript
 
 const Cu = Components.utils;
-const { require } = Cu.import("resource://gre/modules/commonjs/toolkit/require.js", {});
-const conj = require("resource://grammalecte/fr/conj.js");
-
-
-function echo (...args) {
-    Services.console.logStringMessage(args.join(" -- ") + "\n");
-}
+//const { require } = Cu.import("resource://gre/modules/commonjs/toolkit/require.js", {});
+//const conj = require("resource://grammalecte/fr/conj.js");
 
 
 let oConj = {
     init: function () {
-        let that = this;
+        console.log("Init conjugueur");
         try {
             // button
-            document.getElementById('conjugate').addEventListener("click", function (event) {
-                that.getVerbAndConjugate();
+            document.getElementById('conjugate').addEventListener("click", (xEvent) => {
+                this.getVerbAndConjugate();
             });
             // text field
-            document.getElementById('verb').addEventListener("change", function (event) {
-                that.getVerbAndConjugate();
+            document.getElementById('verb').addEventListener("change", (xEvent) => {
+                this.getVerbAndConjugate();
             });
             // options
-            document.getElementById('oneg').addEventListener("click", function (event) {
-                that._displayResults();
-            });
-            document.getElementById('opro').addEventListener("click", function (event) {
-                that._displayResults();
-            });
-            document.getElementById('oint').addEventListener("click", function (event) {
-                that._displayResults();
-            });
-            document.getElementById('ofem').addEventListener("click", function (event) {
-                that._displayResults();
-            });
-            document.getElementById('otco').addEventListener("click", function (event) {
-                that._displayResults();
+            document.getElementById('oneg').addEventListener("click", (xEvent) => {
+                this._displayResults();
+            });
+            document.getElementById('opro').addEventListener("click", (xEvent) => {
+                this._displayResults();
+            });
+            document.getElementById('oint').addEventListener("click", (xEvent) => {
+                this._displayResults();
+            });
+            document.getElementById('ofem').addEventListener("click", (xEvent) => {
+                this._displayResults();
+            });
+            document.getElementById('otco').addEventListener("click", (xEvent) => {
+                this._displayResults();
             });
         }
         catch (e) {
             Cu.reportError(e);
         }
@@ -83,11 +78,11 @@
                     document.getElementById('verb').style = "color: #BB4411;";
                 } else {
                     document.getElementById('verb_title').textContent = sVerb;
                     document.getElementById('verb').style = "color: #999999;";
                     document.getElementById('verb').value = "";
-                    this.oVerb = new conj.Verb(sVerb);
+                    this.oVerb = new Verb(sVerb);
                     let sRawInfo = this.oVerb._sRawInfo;
                     document.getElementById('info').textContent = this.oVerb.sInfo;
                     document.getElementById('opro').textContent = "pronominal";
                     if (sRawInfo.endsWith("zz")) {
                         document.getElementById('opro').checked = false;
@@ -271,7 +266,7 @@
             Cu.reportError(e);
         }
     }
 };
 
+conj.init(helpers.loadFile("resource://grammalecte/fr/conj_data.json"));
 oConj.init();
-
Index: gc_lang/fr/tb/content/conjugueur.xul
==================================================================
--- gc_lang/fr/tb/content/conjugueur.xul
+++ gc_lang/fr/tb/content/conjugueur.xul
@@ -152,7 +152,9 @@
         
       
     
   
 
-  
+  
+  
+  
 
Index: gc_lang/fr/tb/content/editor.js
==================================================================
--- gc_lang/fr/tb/content/editor.js
+++ gc_lang/fr/tb/content/editor.js
@@ -19,15 +19,13 @@
 
     * _getParsableNodes (xRootNode=this.xEditor.rootElement) {
         // recursive function
         try {
             for (let xNode of xRootNode.childNodes) {
-                //echo("tag: " + xNode.tagName);
                 if (xNode.className !== "moz-cite-prefix" && xNode.tagName !== "BLOCKQUOTE"
                     && (xNode.nodeType == Node.TEXT_NODE || (xNode.nodeType == Node.ELEMENT_NODE && !xNode.textContent.startsWith(">")))
                     && xNode.textContent !== "") {
-                    //echo("<"+xNode.tagName+">["+xNode.textContent+"]");
                     if (xNode.tagName === undefined) {
                         if (!prefs.getBoolPref("bCheckSignature") && xNode.textContent.startsWith("-- ")) {
                             break;
                         }
                         yield xNode;
Index: gc_lang/fr/tb/content/gc_options.css
==================================================================
--- gc_lang/fr/tb/content/gc_options.css
+++ gc_lang/fr/tb/content/gc_options.css
@@ -3,5 +3,24 @@
 .section {
 	font-size: 16px;
 	font-weight: bold;
 	color: hsl(210, 50%, 50%);
 }
+
+/*
+    TB Next: fix dialogheaders
+*/
+dialogheader {
+  -moz-binding: url("chrome://messenger/content/generalBindings.xml#dialogheader");
+  margin: 0 5px 5px;
+  border: 1px solid ThreeDDarkShadow;
+  padding: 5px 8px;
+  background-color: Highlight;
+  color: HighlightText;
+}
+
+.dialogheader-title {
+  margin: 0 !important;
+  font-size: larger;
+  font-weight: bold;
+}
+
Index: gc_lang/fr/tb/content/gc_options.js
==================================================================
--- gc_lang/fr/tb/content/gc_options.js
+++ gc_lang/fr/tb/content/gc_options.js
@@ -2,28 +2,22 @@
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getBranch("extensions.grammarchecker.");
-//const { require } = Cu.import("resource://gre/modules/commonjs/toolkit/require.js", {});
 
-function echo (...args) {
-    Services.console.logStringMessage(args.join(" -- ") + "\n");
-}
 
 var oOptControl = {
     oOptions: null,
     load: function () {
         this._setDialogOptions(false);
     },
     _setDialogOptions: function (bDefaultOptions=false) {
         try {
             sOptions = bDefaultOptions ? prefs.getCharPref("sGCDefaultOptions") : prefs.getCharPref("sGCOptions");
-            //echo(">> " + sOptions);
             this.oOptions = JSON.parse(sOptions);
             for (let sParam in this.oOptions) {
-                //echo(sParam + ":" + oOptions[sParam]);
                 if (document.getElementById("option_"+sParam) !== null) {
                     document.getElementById("option_"+sParam).checked = this.oOptions[sParam];
                 }
             }
         }
@@ -35,11 +29,10 @@
         try {
             for (let xNode of document.getElementsByClassName("option")) {
                 this.oOptions[xNode.id.slice(7)] = xNode.checked;
             }
             prefs.setCharPref("sGCOptions", JSON.stringify(this.oOptions));
-            //echo("<< " + JSON.stringify(this.oOptions));
         }
         catch (e) {
             Cu.reportError(e);
         }
     },
Index: gc_lang/fr/tb/content/gc_options.xul
==================================================================
--- gc_lang/fr/tb/content/gc_options.xul
+++ gc_lang/fr/tb/content/gc_options.xul
@@ -29,8 +29,8 @@
     
 ${sXULTabPanels}
     
   
 
-  
+  
 
 
ADDED   gc_lang/fr/tb/content/options.css
Index: gc_lang/fr/tb/content/options.css
==================================================================
--- /dev/null
+++ gc_lang/fr/tb/content/options.css
@@ -0,0 +1,19 @@
+/* CSS */
+
+/*
+    TB Next: fix dialogheaders
+*/
+dialogheader {
+  -moz-binding: url("chrome://messenger/content/generalBindings.xml#dialogheader");
+  margin: 0 5px 5px;
+  border: 1px solid ThreeDDarkShadow;
+  padding: 5px 8px;
+  background-color: Highlight;
+  color: HighlightText;
+}
+
+.dialogheader-title {
+  margin: 0 !important;
+  font-size: larger;
+  font-weight: bold;
+}
ADDED   gc_lang/fr/tb/content/options.js
Index: gc_lang/fr/tb/content/options.js
==================================================================
--- /dev/null
+++ gc_lang/fr/tb/content/options.js
@@ -0,0 +1,34 @@
+// JavaScript
+
+"use strict";
+
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getBranch("extensions.grammarchecker.");
+
+
+var oOptControl = {
+
+    load: function () {
+        try {
+            document.getElementById('check_signature').checked = prefs.getBoolPref('bCheckSignature');
+        }
+        catch (e) {
+            Cu.reportError(e);
+        }
+    },
+
+    save: function () {
+        try {
+            prefs.setBoolPref('bCheckSignature', document.getElementById('check_signature').checked);
+        }
+        catch (e) {
+            Cu.reportError(e);
+        }
+    }
+}
+
+
+oOptControl.load();
Index: gc_lang/fr/tb/content/options.xul
==================================================================
--- gc_lang/fr/tb/content/options.xul
+++ gc_lang/fr/tb/content/options.xul
@@ -1,24 +1,27 @@
-
+
 
+
 
-
+
 
-
 
-  
-    
-    
-      
-      
-    
-    
-    
-    
-  
-
+  
+  
+  
+
+  
+
+  
+
+
Index: gc_lang/fr/tb/content/overlay.css
==================================================================
--- gc_lang/fr/tb/content/overlay.css
+++ gc_lang/fr/tb/content/overlay.css
@@ -185,5 +185,24 @@
 .bs, .pleo, .neg, .redon1, .redon2, .mc, .date, .notype {
     background-color: hsl(180, 50%, 40%);
     color: hsl(180, 10%, 96%);
     border-radius: 3px;
 }
+
+
+/*
+    TB Next: fix dialogheaders
+*/
+dialogheader {
+  -moz-binding: url("chrome://messenger/content/generalBindings.xml#dialogheader");
+  margin: 0 5px 5px;
+  border: 1px solid ThreeDDarkShadow;
+  padding: 5px 8px;
+  background-color: Highlight;
+  color: HighlightText;
+}
+
+.dialogheader-title {
+  margin: 0 !important;
+  font-size: larger;
+  font-weight: bold;
+}
Index: gc_lang/fr/tb/content/overlay.js
==================================================================
--- gc_lang/fr/tb/content/overlay.js
+++ gc_lang/fr/tb/content/overlay.js
@@ -4,27 +4,18 @@
 
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
-const { require } = Cu.import("resource://gre/modules/commonjs/toolkit/require.js", {});
+//const { require } = Cu.import("resource://gre/modules/commonjs/toolkit/require.js", {});
 
 const { BasePromiseWorker } = Cu.import('resource://gre/modules/PromiseWorker.jsm', {});
 const Task = Cu.import("resource://gre/modules/Task.jsm").Task;
 const prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getBranch("extensions.grammarchecker.");
-//Cu.import("resource://gre/modules/Console.jsm"); // doesn’t work
-//const xConsole = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
-//xConsole.logStringMessage("Grammalecte: " + args.join(" · ")); // useless now. Use: Services.console.logStringMessage("***");
-
-const text = require("resource://grammalecte/text.js");
-const tf = require("resource://grammalecte/fr/textformatter.js");
-
-
-function echo (...args) {
-    dump(args.join(" -- ") + "\n");  // obsolete since TB 52?
-    Services.console.logStringMessage("Grammalecte: " + args.join(" · "));
-}
+
+//const text = require("resource://grammalecte/text.js");
+//const tf = require("resource://grammalecte/fr/textformatter.js");
 
 
 const oConverterToExponent = {
     dNumbers: new Map ([
         ["1", "¹"], ["2", "²"], ["3", "³"], ["4", "⁴"], ["5", "⁵"],
@@ -45,79 +36,79 @@
     xGCEWorker: null,
     bDictActive: null,
     loadGC: function () {
         if (this.xGCEWorker === null) {
             // Grammar checker
-            echo('Loading Grammalecte');
+            console.log('Loading Grammalecte');
             this.xGCEWorker = new BasePromiseWorker('chrome://promiseworker/content/gce_worker.js');
             let xPromise = this.xGCEWorker.post('loadGrammarChecker', [prefs.getCharPref("sGCOptions"), "Thunderbird"]);
             xPromise.then(
                 function (aVal) {
-                    echo(aVal);
+                    console.log(aVal);
                     prefs.setCharPref("sGCOptions", aVal);
                 },
-                function (aReason) { echo('Promise rejected - ', aReason); }
+                function (aReason) { console.log('Promise rejected - ', aReason); }
             ).catch(
-                function (aCaught) { echo('Promise Error - ', aCaught); }
+                function (aCaught) { console.log('Promise Error - ', aCaught); }
             );
         }
     },
     fullTests: function () {
-        echo('Performing tests... Wait...');
+        console.log('Performing tests... Wait...');
         let xPromise = this.xGCEWorker.post('fullTests', ['{"nbsp":true, "esp":true, "unit":true, "num":true}']);
         xPromise.then(
             function (aVal) {
-                echo('Done.');
-                echo(aVal);
+                console.log('Done.');
+                console.log(aVal);
             },
-            function (aReason) { echo('Promise rejected', aReason); }
+            function (aReason) { console.log('Promise rejected', aReason); }
         ).catch(
-            function (aCaught) { echo('Promise Error', aCaught); }
+            function (aCaught) { console.log('Promise Error', aCaught); }
         );
     },
     test: function (sText) {
-        echo("Test...");
+        console.log("Test...");
         let xPromise = this.xGCEWorker.post('parse', [sText, "FR", true]);
         xPromise.then(
             function (aVal) {
                 let lErr = JSON.parse(aVal);
                 if (lErr.length > 0) {
                     for (let dErr of lErr) {
-                        echo(text.getReadableError(dErr));
+                        console.log(text.getReadableError(dErr));
                     }
                 } else {
-                    echo("no error found");
+                    console.log("no error found");
                 }
             },
-            function (aReason) { echo('Promise rejected', aReason); }
+            function (aReason) { console.log('Promise rejected', aReason); }
         ).catch(
-            function (aCaught) { echo('Promise Error', aCaught); }
+            function (aCaught) { console.log('Promise Error', aCaught); }
         );
     },
     setOptions: function () {
-        echo('Set options');
+        console.log('Set options');
         let xPromise = this.xGCEWorker.post('setOptions', [prefs.getCharPref("sGCOptions")]);
         xPromise.then(
             function (aVal) {
-                echo(aVal);
+                console.log(aVal);
                 prefs.setCharPref("sGCOptions", aVal);
             },
-            function (aReason) { echo('Promise rejected', aReason); }
+            function (aReason) { console.log('Promise rejected', aReason); }
         ).catch(
-            function (aCaught) { echo('Promise Error', aCaught); }
+            function (aCaught) { console.log('Promise Error', aCaught); }
         );
     },
     resetOptions: function () {
         let xPromise = this.xGCEWorker.post('resetOptions');
         xPromise.then(
             function (aVal) {
-                echo(aVal);
+                console.log(aVal);
                 prefs.setCharPref("sGCOptions", aVal);
             },
-            function (aReason) { echo('Promise rejected', aReason); }
+            function (aReason) { console.log('Promise rejected', aReason); }
         ).catch(
-            function (aCaught) { echo('Promise Error', aCaught); }
+            function (aCaught) { console.log('Promise Error', aCaught); }
         );
     },
     _getGCResultPromise: function (sParagraph, sLang, bDebug, bContext) {
         // For some reason, you can’t use result of PromiseWorker within a Task,
         // you have to wrap it in a common Promise. Task and yield can be replaced with async / await when it is available.
@@ -186,11 +177,11 @@
             let xResultNode = document.getElementById("resnode"+iParagraph);
             xResultNode.textContent = "…………… réanalyse en cours ……………";
             let sParagraph = xEditor.getParagraph(iParagraph);
             let xPromise = this._getGCResultPromise(sParagraph, "FR", false, false);
             xPromise.then(function (res) {
-                //echo("res: " + res);
+                //console.log("res: " + res);
                 xResultNode.textContent = "";
                 let oRes = JSON.parse(res);
                 if (oRes.aGrammErr.length > 0 || oRes.aSpellErr.length > 0) {
                     that.fillResultNode(xResultNode, xEditor, sParagraph, iParagraph, oRes.aGrammErr, oRes.aSpellErr);
                 }
@@ -369,11 +360,11 @@
         xNodeSuggLine.appendChild(xNodeSuggButton);
         xNodeDiv.appendChild(xNodeSuggLine);
         return xNodeDiv;
     },
     loadUI: function() {
-        echo("loadUI");
+        console.log("loadUI");
         this._strings = document.getElementById("grammarchecker-strings");
         let that = this;
         let nsGrammarCommand = {
             isCommandEnabled: function (aCommand, dummy) {
                 return (IsDocumentEditable() && !IsInHTMLSourceMode());
@@ -472,24 +463,24 @@
     onOpenGCOptions: function (e) {
         let that = this;
         let xPromise = this.xGCEWorker.post('getDefaultOptions');
         xPromise.then(
             function (aVal) {
-                echo(aVal);
+                console.log(aVal);
                 prefs.setCharPref("sGCDefaultOptions", aVal);
             },
-            function (aReason) { echo('Promise rejected', aReason); }
+            function (aReason) { console.log('Promise rejected', aReason); }
         ).catch(
-            function (aCaught) { echo('Promise Error', aCaught); }
+            function (aCaught) { console.log('Promise Error', aCaught); }
         ).then(
             function () {
                 that.openDialog("chrome://grammarchecker/content/gc_options.xul", "", "chrome, dialog, modal, resizable=no");
                 that.setOptions();
             },
-            function (aReason) { echo('Error options dialog', aReason); }
+            function (aReason) { console.log('Error options dialog', aReason); }
         ).catch(
-            function (aCaught) { echo('Error', aCaught); }
+            function (aCaught) { console.log('Error', aCaught); }
         );
     },
     onOpenSpellOptions: function (e) {
         this.openDialog("chrome://grammarchecker/content/spell_options.xul", "", "chrome, dialog, modal, resizable=no");
     },
@@ -566,11 +557,11 @@
     saveOptions: function () {
         let oOptions = {};
         for (let xNode of document.getElementsByClassName("option")) {
             oOptions[xNode.id] = xNode.checked;
         }
-        //echo("save options: " + JSON.stringify(oOptions));
+        //console.log("save options: " + JSON.stringify(oOptions));
         prefs.setCharPref("sTFOptions", JSON.stringify(oOptions));
     },
     setOptionsInPanel: function (oOptions) {
         for (let sOptName in oOptions) {
             //console.log(sOptName + ":" + oOptions[sOptName]);
@@ -880,15 +871,15 @@
         return sText;
     },
     formatText: function (sText, sOptName) {
         let nCount = 0;
         try {
-            if (!tf.oReplTable.hasOwnProperty(sOptName)) {
-                echo("# Error. TF: there is no option “" + sOptName+ "”.");
+            if (!oReplTable.hasOwnProperty(sOptName)) {
+                console.log("# Error. TF: there is no option “" + sOptName+ "”.");
                 return [sText, nCount];
             }
-            for (let [zRgx, sRep] of tf.oReplTable[sOptName]) {
+            for (let [zRgx, sRep] of oReplTable[sOptName]) {
                 nCount += (sText.match(zRgx) || []).length;
                 sText = sText.replace(zRgx, sRep);
             }
         }
         catch (e) {
Index: gc_lang/fr/tb/content/overlay.xul
==================================================================
--- gc_lang/fr/tb/content/overlay.xul
+++ gc_lang/fr/tb/content/overlay.xul
@@ -4,14 +4,16 @@
 
 
 
 
-  
-  
-  
-  
+  
+  
+  
+  
+  
+  
 
   
     
   
 
Index: gc_lang/fr/tb/content/spell_options.css
==================================================================
--- gc_lang/fr/tb/content/spell_options.css
+++ gc_lang/fr/tb/content/spell_options.css
@@ -13,5 +13,24 @@
 }
 
 .dicdescr {
 	margin-left: 27px;
 }
+
+
+/*
+    TB Next: fix dialogheaders
+*/
+dialogheader {
+  -moz-binding: url("chrome://messenger/content/generalBindings.xml#dialogheader");
+  margin: 0 5px 5px;
+  border: 1px solid ThreeDDarkShadow;
+  padding: 5px 8px;
+  background-color: Highlight;
+  color: HighlightText;
+}
+
+.dialogheader-title {
+  margin: 0 !important;
+  font-size: larger;
+  font-weight: bold;
+}
Index: gc_lang/fr/tb/content/spell_options.js
==================================================================
--- gc_lang/fr/tb/content/spell_options.js
+++ gc_lang/fr/tb/content/spell_options.js
@@ -5,16 +5,10 @@
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).getBranch("extensions.grammarchecker.");
-const { require } = Cu.import("resource://gre/modules/commonjs/toolkit/require.js", {});
-
-
-function echo (...args) {
-	Services.console.logStringMessage(args.join(" -- ") + "\n");
-}
 
 
 var oDialogControl = {
 	load: function () {
 		try {
Index: gc_lang/fr/tb/content/spell_options.xul
==================================================================
--- gc_lang/fr/tb/content/spell_options.xul
+++ gc_lang/fr/tb/content/spell_options.xul
@@ -32,9 +32,9 @@
     &option.reform.descr;
     
     &option.allvar.descr;
   
 
-  
-  
+  
+  
 
 
Index: gc_lang/fr/tb/content/spellchecker.js
==================================================================
--- gc_lang/fr/tb/content/spellchecker.js
+++ gc_lang/fr/tb/content/spellchecker.js
@@ -1,24 +1,25 @@
 // JavaScript
 
 /*
-	Hunspell wrapper
-
-	XPCOM obsolete (?), but there is nothing else...
-	Overly complicated and weird. To throw away ASAP if possible.
-
-	And you can’t access to this from a PromiseWorker (it sucks).
-
-	https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/mozISpellCheckingEngine
-	https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Using_spell_checking_in_XUL
-	https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIFile
-	https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/FileUtils.jsm
+    Hunspell wrapper
+
+    XPCOM obsolete (?), but there is nothing else...
+    Overly complicated and weird. To throw away ASAP if possible.
+
+    And you can’t access to this from a PromiseWorker (it sucks).
+
+    https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/mozISpellCheckingEngine
+    https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/Using_spell_checking_in_XUL
+    https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/nsIFile
+    https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/FileUtils.jsm
 */
 
 "use strict";
 
 /*
+// Loaded in another file
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const { require } = Cu.import("resource://gre/modules/commonjs/toolkit/require.js", {});
 */
@@ -25,130 +26,126 @@
 
 
 const FileUtils = Cu.import("resource://gre/modules/FileUtils.jsm").FileUtils;
 const AddonManager = Cu.import("resource://gre/modules/AddonManager.jsm").AddonManager;
 
-/*
-const parser = Cc["@mozilla.org/parserutils;1"].getService(Ci.nsIParserUtils);
-const persodict = Cc["@mozilla.org/spellchecker/personaldictionary;1"].getService(Ci.mozIPersonalDictionary);
-*/
-
-const system = require("sdk/system");
-
 
 var oSpellControl = {
-	xSCEngine: null,
-	init: function () {
-		if (this.xSCEngine === null) {
-			try {
-				let sSpellchecker = "@mozilla.org/spellchecker/myspell;1";
-				if ("@mozilla.org/spellchecker/hunspell;1" in Cc) {
-				    sSpellchecker = "@mozilla.org/spellchecker/hunspell;1";
-				}
-				if ("@mozilla.org/spellchecker/engine;1" in Cc) {
-				    sSpellchecker = "@mozilla.org/spellchecker/engine;1";
-				}
-				this.xSCEngine = Cc[sSpellchecker].getService(Ci.mozISpellCheckingEngine);
-			}
-			catch (e) {
-				echo("Can’t initiate the spellchecker.");
-				Cu.reportError(e);
-			}
-		}
-	},
-	getDictionariesList: function () {
-		this.init();
-		try {
-			let l = {};
-			let c = {};
-			this.xSCEngine.getDictionaryList(l, c);
-			return l.value;
-		}
-		catch (e) {
-			Cu.reportError(e);
-			return [];
-		}
-	},
-	setDictionary: function (sLocale) {
-		if (this.getDictionariesList().includes(sLocale)) {
-			try {
-				this.xSCEngine.dictionary = sLocale; // en-US, fr, etc.
-				return true;
-			}
-			catch (e) {
-				Cu.reportError(e);
-				return false;
-			}
-		} else {
-			echo("Warning. No dictionary for locale: " + sLocale);
-			echo("Existing dictionaries: " + this.getDictionariesList().join(" | "));
-		}
-		return false;
-	},
-	check: function (sWord) {
-		// todo: check in personal dict?
-		try {
-			return this.xSCEngine.check(sWord);
-		}
-		catch (e) {
-			Cu.reportError(e);
-			return false;
-		}
-	},
-	suggest: function (sWord) {
-		try {
-			let lSugg = {};
-			this.xSCEngine.suggest(sWord, lSugg, {});
-			return lSugg.value;
-		   	// lSugg.value is a JavaScript Array of strings
-		}
-		catch (e) {
-			Cu.reportError(e);
-			return ['#Erreur.'];
-		}
-	},
-	addDirectory: function (sFolder) {
-		try {
-			let xNsiFolder = new FileUtils.File(sFolder);
-			this.xSCEngine.addDirectory(xNsiFolder);
-		}
-		catch (e) {
-			echo("Unable to add directory: " + sFolder);
-			Cu.reportError(e);
-		}
-	},
-	removeDirectory: function (sFolder) {
-		// does not work but no exception raised (bug?)
-		try {
-			let xNsiFolder = new FileUtils.File(sFolder);
-			this.xSCEngine.removeDirectory(xNsiFolder);
-		}
-		catch (e) {
-			echo("Unable to remove directory: " + sFolder);
-			Cu.reportError(e);
-		}
-	},
-	setExtensionDictFolder: function (sDictName, bActivate) {
-		try {
-			let that = this;
-			let sPath = "/content/dictionaries/" + sDictName;
-			AddonManager.getAddonByID("French-GC-TB@grammalecte.net", function (addon) {
-				let xURI = addon.getResourceURI(sPath);
-				//console.log("> " + xURI.path);
-				let sFolder = xURI.path;
-				if (system.platform === "winnt") {
-					sFolder = sFolder.slice(1).replace(/\//g, "\\\\");
-				}
-				//console.log("> " + sFolder);
-				if (bActivate) {
-					that.addDirectory(sFolder);
-				} else {
-					that.removeDirectory(sFolder);
-				}
-			});
-		}
-		catch (e) {
-			echo("Unable to add extension folder");
-			Cu.reportError(e);
-		}
-	}
+    xSCEngine: null,
+    init: function () {
+        if (this.xSCEngine === null) {
+            try {
+                let sSpellchecker = "@mozilla.org/spellchecker/myspell;1";
+                if ("@mozilla.org/spellchecker/hunspell;1" in Cc) {
+                    sSpellchecker = "@mozilla.org/spellchecker/hunspell;1";
+                }
+                if ("@mozilla.org/spellchecker/engine;1" in Cc) {
+                    sSpellchecker = "@mozilla.org/spellchecker/engine;1";
+                }
+                this.xSCEngine = Cc[sSpellchecker].getService(Ci.mozISpellCheckingEngine);
+            }
+            catch (e) {
+                console.log("Can’t initiate the spellchecker.");
+                Cu.reportError(e);
+            }
+        }
+    },
+    getDictionariesList: function () {
+        this.init();
+        try {
+            let l = {};
+            let c = {};
+            this.xSCEngine.getDictionaryList(l, c);
+            return l.value;
+        }
+        catch (e) {
+            Cu.reportError(e);
+            return [];
+        }
+    },
+    setDictionary: function (sLocale) {
+        if (this.getDictionariesList().includes(sLocale)) {
+            try {
+                this.xSCEngine.dictionary = sLocale; // en-US, fr, etc.
+                return true;
+            }
+            catch (e) {
+                Cu.reportError(e);
+                return false;
+            }
+        } else {
+            console.log("Warning. No dictionary for locale: " + sLocale);
+            console.log("Existing dictionaries: " + this.getDictionariesList().join(" | "));
+        }
+        return false;
+    },
+    check: function (sWord) {
+        // todo: check in personal dict?
+        try {
+            return this.xSCEngine.check(sWord);
+        }
+        catch (e) {
+            Cu.reportError(e);
+            return false;
+        }
+    },
+    suggest: function (sWord) {
+        try {
+            let lSugg = {};
+            this.xSCEngine.suggest(sWord, lSugg, {});
+            return lSugg.value;
+            // lSugg.value is a JavaScript Array of strings
+        }
+        catch (e) {
+            Cu.reportError(e);
+            return ['#Erreur.'];
+        }
+    },
+    addDirectory: function (sFolder) {
+        try {
+            let xNsiFolder = new FileUtils.File(sFolder);
+            this.xSCEngine.addDirectory(xNsiFolder);
+        }
+        catch (e) {
+            console.log("Unable to add directory: " + sFolder);
+            Cu.reportError(e);
+        }
+    },
+    removeDirectory: function (sFolder) {
+        // does not work but no exception raised (bug?)
+        try {
+            let xNsiFolder = new FileUtils.File(sFolder);
+            this.xSCEngine.removeDirectory(xNsiFolder);
+        }
+        catch (e) {
+            console.log("Unable to remove directory: " + sFolder);
+            Cu.reportError(e);
+        }
+    },
+    setExtensionDictFolder: function (sDictName, bActivate) {
+        try {
+            let that = this;
+            let sPath = "/content/dictionaries/" + sDictName;
+            AddonManager.getAddonByID("French-GC-TB@grammalecte.net", function (addon) {
+                let xURI = addon.getResourceURI(sPath);
+                //console.log(xURI);
+                let sFolder = xURI.filePath;
+                if (sFolder !== undefined) {
+                    if (/^\/[A-Z]:\//.test(sFolder)) {
+                        // Windows path
+                        sFolder = sFolder.slice(1).replace(/\//g, "\\\\");
+                    }
+                    console.log("folder: " + sFolder);
+                    if (bActivate) {
+                        that.addDirectory(sFolder);
+                    } else {
+                        that.removeDirectory(sFolder);
+                    }
+                }
+            });
+        }
+        catch (e) {
+            console.log("Unable to add extension folder");
+            Cu.reportError(e);
+        }
+    }
 };
Index: gc_lang/fr/tb/install.rdf
==================================================================
--- gc_lang/fr/tb/install.rdf
+++ gc_lang/fr/tb/install.rdf
@@ -7,11 +7,11 @@
     2
     ${version}
     ${author}
     ${description}
     ${link}
-    chrome://grammarchecker/content/options.xul
+    
     true
 
     
       
         {3550f703-e582-4d05-9a08-453d09bdfdc6} 
Index: gc_lang/fr/tb/locale/en/options.dtd
==================================================================
--- gc_lang/fr/tb/locale/en/options.dtd
+++ gc_lang/fr/tb/locale/en/options.dtd
@@ -1,7 +1,6 @@
-
-
-
-
-
+
+
 
 
+
+
Index: gc_lang/fr/tb/locale/fr/options.dtd
==================================================================
--- gc_lang/fr/tb/locale/fr/options.dtd
+++ gc_lang/fr/tb/locale/fr/options.dtd
@@ -1,7 +1,4 @@
-
-
-
-
-
+
+
 
 
Index: graphspell-js/helpers.js
==================================================================
--- graphspell-js/helpers.js
+++ graphspell-js/helpers.js
@@ -51,18 +51,11 @@
         // 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
         try {
             let xRequest;
-            if (typeof XMLHttpRequest !== "undefined") {
-                xRequest = new XMLHttpRequest();
-            } else {
-                // JS sucks again… necessary for Thunderbird
-                let { Cc, Ci } = require("chrome");
-                xRequest = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance();
-                xRequest.QueryInterface(Ci.nsIXMLHttpRequest);
-            }
+            xRequest = new XMLHttpRequest();
             xRequest.open('GET', spf, false); // 3rd arg is false for synchronous, sync is acceptable in workers
             xRequest.overrideMimeType('text/json');
             xRequest.send();
             return xRequest.responseText;
         }
Index: make.py
==================================================================
--- make.py
+++ make.py
@@ -363,10 +363,11 @@
     xParser.add_argument("-aed", "--add_extended_dictionary", help="add extended dictionary to the build", action="store_true")
     xParser.add_argument("-apd", "--add_personal_dictionary", help="add personal dictionary to the build", action="store_true")
     xParser.add_argument("-fx", "--firefox", help="Launch Firefox Developper for WebExtension testing", action="store_true")
     xParser.add_argument("-we", "--web_ext", help="Launch Firefox Nightly for WebExtension testing", action="store_true")
     xParser.add_argument("-tb", "--thunderbird", help="Launch Thunderbird", action="store_true")
+    xParser.add_argument("-tbb", "--thunderbird_beta", help="Launch Thunderbird Beta", action="store_true")
     xParser.add_argument("-i", "--install", help="install the extension in Writer (path of unopkg must be set in config.ini)", action="store_true")
     xArgs = xParser.parse_args()
 
     if xArgs.build_data:
         xArgs.build_data_before = True
@@ -448,15 +449,21 @@
                         spfFirefox = dVars['win_fx_nightly_path']  if platform.system() == "Windows"  else dVars['linux_fx_nightly_path']
                     os.system(r'web-ext run --firefox="' + spfFirefox + '" --browser-console --firefox-profile=debug')            
 
             # Thunderbird
             if xArgs.thunderbird:
-                os.system("thunderbird -jsconsole -P debug")
+                spfThunderbird = '"'+dVars['win_tb_path']+'"'  if platform.system() == "Windows"  else dVars['linux_tb_path']
+                print(spfThunderbird)
+                os.system(spfThunderbird + ' -jsconsole -P debug')
+            if xArgs.thunderbird_beta:
+                spfThunderbird = '"'+dVars['win_tb_beta_path']+'"'  if platform.system() == "Windows"  else dVars['linux_tb_beta_path']
+                print(spfThunderbird)
+                os.system(spfThunderbird + ' -jsconsole -P beta')
         else:
             print("Folder not found: gc_lang/"+sLang)
 
     oNow = datetime.datetime.now()
     print("============== MAKE GRAMMALECTE [finished] at {0.hour:>2} h {0.minute:>2} min {0.second:>2} s ==============".format(oNow))
 
 
 if __name__ == '__main__':
     main()